Merge branch 'main' into feature/resolve-todo

This commit is contained in:
Vadim Zakharenko 2021-08-04 00:12:37 +03:00
commit 90ae7b92a0
39 changed files with 687 additions and 1160 deletions

View File

@ -7,20 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
0DB729122674E2DB0011F7A1 /* EIP712ParameterEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729022674E2DB0011F7A1 /* EIP712ParameterEncoder.swift */; };
0DB729142674E2DB0011F7A1 /* EIP712Parameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729042674E2DB0011F7A1 /* EIP712Parameter.swift */; };
0DB729152674E2DB0011F7A1 /* EIP712Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729052674E2DB0011F7A1 /* EIP712Error.swift */; };
0DB729162674E2DB0011F7A1 /* EIP712SimpleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729062674E2DB0011F7A1 /* EIP712SimpleValue.swift */; };
0DB729172674E2DB0011F7A1 /* EIP712StructType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729072674E2DB0011F7A1 /* EIP712StructType.swift */; };
0DB729182674E2DB0011F7A1 /* EIP712Signer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729082674E2DB0011F7A1 /* EIP712Signer.swift */; };
0DB729192674E2DB0011F7A1 /* EIP712TypedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729092674E2DB0011F7A1 /* EIP712TypedData.swift */; };
0DB7291A2674E2DB0011F7A1 /* EIP712Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB7290A2674E2DB0011F7A1 /* EIP712Type.swift */; };
0DB7291B2674E2DB0011F7A1 /* EIP712Hash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB7290B2674E2DB0011F7A1 /* EIP712Hash.swift */; };
0DB7291C2674E2DB0011F7A1 /* EIP712Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB7290D2674E2DB0011F7A1 /* EIP712Value.swift */; };
0DB7291D2674E2DB0011F7A1 /* EIP712Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB7290E2674E2DB0011F7A1 /* EIP712Hashable.swift */; };
0DB7291E2674E2DB0011F7A1 /* EIP712Signable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB7290F2674E2DB0011F7A1 /* EIP712Signable.swift */; };
0DB7291F2674E2DB0011F7A1 /* EIP712Representable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729102674E2DB0011F7A1 /* EIP712Representable.swift */; };
0DB729202674E2DB0011F7A1 /* EIP712Domain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB729112674E2DB0011F7A1 /* EIP712Domain.swift */; };
0DC850E726B73A5900809E82 /* AuthenticationReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC850E626B73A5900809E82 /* AuthenticationReason.swift */; };
28BDF30EBC80362870C988B6 /* Pods_Encrypted_Ink.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E2A642C960E4952955E6E82 /* Pods_Encrypted_Ink.framework */; };
2C03D1D2269B407900EF10EA /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C03D1D1269B407900EF10EA /* NetworkMonitor.swift */; };
@ -32,10 +18,12 @@
2C1995562674D0F300A8E370 /* Ethereum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1995552674D0F300A8E370 /* Ethereum.swift */; };
2C208A9F26813408005BA500 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C208A9E26813408005BA500 /* Secrets.swift */; };
2C528A16267FA8EB00CA3ADD /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C528A15267FA8EB00CA3ADD /* Defaults.swift */; };
2C603D0226B6E13F00956955 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C603D0126B6E13F00956955 /* String.swift */; };
2C6706A5267A6BFE006AAEF2 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6706A4267A6BFE006AAEF2 /* Bundle.swift */; };
2C6B964C26B9D92500D2C819 /* NSColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6B964B26B9D92500D2C819 /* NSColor.swift */; };
2C6B964F26B9D98C00D2C819 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C6B964E26B9D98C00D2C819 /* Colors.xcassets */; };
2C78F8282683BDCC00C10670 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C78F8272683BDCC00C10670 /* Alert.swift */; };
2C797E7E267BB88800F2CE2D /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C797E7D267BB88800F2CE2D /* WelcomeViewController.swift */; };
2C8A09B52675101300993638 /* AccountsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09B42675101300993638 /* AccountsService.swift */; };
2C8A09C6267513FC00993638 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09C5267513FC00993638 /* Agent.swift */; };
2C8A09D42675184700993638 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09D32675184700993638 /* Window.swift */; };
2C8A09D726751A0C00993638 /* WalletConnect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09D626751A0C00993638 /* WalletConnect.swift */; };
@ -52,28 +40,16 @@
2CC0CDBE2692027E0072922A /* PriceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC0CDBD2692027E0072922A /* PriceService.swift */; };
2CC8946F269A2E8C00879245 /* SessionStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC8946E269A2E8C00879245 /* SessionStorage.swift */; };
2CC89471269A334A00879245 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC89470269A334A00879245 /* UserDefaults.swift */; };
2CD0669126B5537B00728C20 /* InkWallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CD0668B26B2142000728C20 /* InkWallet.swift */; };
2CD0669226B5537B00728C20 /* WalletsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CD0668926B213E500728C20 /* WalletsManager.swift */; };
2CD0B3F526A0DAA900488D92 /* NSPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CD0B3F426A0DAA900488D92 /* NSPasteboard.swift */; };
2CD0B3F726AC619900488D92 /* AddAccountOptionCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CD0B3F626AC619900488D92 /* AddAccountOptionCellView.swift */; };
2CDAB3722675B3F0009F8B97 /* PasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDAB3712675B3F0009F8B97 /* PasswordViewController.swift */; };
2CE3D012267F73C00032A62E /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE3D011267F73C00032A62E /* Transaction.swift */; };
2CE3D015267F73E80032A62E /* AccountWithKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE3D014267F73E80032A62E /* AccountWithKey.swift */; };
2CE3D015267F73E80032A62E /* LegacyAccountWithKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE3D014267F73E80032A62E /* LegacyAccountWithKey.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0DB729022674E2DB0011F7A1 /* EIP712ParameterEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712ParameterEncoder.swift; sourceTree = "<group>"; };
0DB729042674E2DB0011F7A1 /* EIP712Parameter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Parameter.swift; sourceTree = "<group>"; };
0DB729052674E2DB0011F7A1 /* EIP712Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Error.swift; sourceTree = "<group>"; };
0DB729062674E2DB0011F7A1 /* EIP712SimpleValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712SimpleValue.swift; sourceTree = "<group>"; };
0DB729072674E2DB0011F7A1 /* EIP712StructType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712StructType.swift; sourceTree = "<group>"; };
0DB729082674E2DB0011F7A1 /* EIP712Signer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Signer.swift; sourceTree = "<group>"; };
0DB729092674E2DB0011F7A1 /* EIP712TypedData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712TypedData.swift; sourceTree = "<group>"; };
0DB7290A2674E2DB0011F7A1 /* EIP712Type.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Type.swift; sourceTree = "<group>"; };
0DB7290B2674E2DB0011F7A1 /* EIP712Hash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Hash.swift; sourceTree = "<group>"; };
0DB7290D2674E2DB0011F7A1 /* EIP712Value.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Value.swift; sourceTree = "<group>"; };
0DB7290E2674E2DB0011F7A1 /* EIP712Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Hashable.swift; sourceTree = "<group>"; };
0DB7290F2674E2DB0011F7A1 /* EIP712Signable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Signable.swift; sourceTree = "<group>"; };
0DB729102674E2DB0011F7A1 /* EIP712Representable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Representable.swift; sourceTree = "<group>"; };
0DB729112674E2DB0011F7A1 /* EIP712Domain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Domain.swift; sourceTree = "<group>"; };
0DC850E626B73A5900809E82 /* AuthenticationReason.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationReason.swift; sourceTree = "<group>"; };
2C03D1D1269B407900EF10EA /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
2C03D1D4269B428C00EF10EA /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
@ -87,10 +63,12 @@
2C1995552674D0F300A8E370 /* Ethereum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ethereum.swift; sourceTree = "<group>"; };
2C208A9E26813408005BA500 /* Secrets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Secrets.swift; sourceTree = "<group>"; };
2C528A15267FA8EB00CA3ADD /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; };
2C603D0126B6E13F00956955 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
2C6706A4267A6BFE006AAEF2 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
2C6B964B26B9D92500D2C819 /* NSColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSColor.swift; sourceTree = "<group>"; };
2C6B964E26B9D98C00D2C819 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
2C78F8272683BDCC00C10670 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
2C797E7D267BB88800F2CE2D /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
2C8A09B42675101300993638 /* AccountsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsService.swift; sourceTree = "<group>"; };
2C8A09C5267513FC00993638 /* Agent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Agent.swift; sourceTree = "<group>"; };
2C8A09D32675184700993638 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
2C8A09D626751A0C00993638 /* WalletConnect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnect.swift; sourceTree = "<group>"; };
@ -113,7 +91,7 @@
2CD0B3F626AC619900488D92 /* AddAccountOptionCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountOptionCellView.swift; sourceTree = "<group>"; };
2CDAB3712675B3F0009F8B97 /* PasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordViewController.swift; sourceTree = "<group>"; };
2CE3D011267F73C00032A62E /* Transaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = "<group>"; };
2CE3D014267F73E80032A62E /* AccountWithKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountWithKey.swift; sourceTree = "<group>"; };
2CE3D014267F73E80032A62E /* LegacyAccountWithKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAccountWithKey.swift; sourceTree = "<group>"; };
35AD6E3AC630C8A9B4EC16D9 /* Pods-Encrypted Ink.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Encrypted Ink.release.xcconfig"; path = "Target Support Files/Pods-Encrypted Ink/Pods-Encrypted Ink.release.xcconfig"; sourceTree = "<group>"; };
3E2A642C960E4952955E6E82 /* Pods_Encrypted_Ink.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Encrypted_Ink.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A858B0F7D88913EAB1FA50B0 /* Pods-Encrypted Ink.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Encrypted Ink.debug.xcconfig"; path = "Target Support Files/Pods-Encrypted Ink/Pods-Encrypted Ink.debug.xcconfig"; sourceTree = "<group>"; };
@ -131,35 +109,6 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0DB729012674E2DB0011F7A1 /* EIP712 */ = {
isa = PBXGroup;
children = (
0DB729022674E2DB0011F7A1 /* EIP712ParameterEncoder.swift */,
0DB729042674E2DB0011F7A1 /* EIP712Parameter.swift */,
0DB729052674E2DB0011F7A1 /* EIP712Error.swift */,
0DB729062674E2DB0011F7A1 /* EIP712SimpleValue.swift */,
0DB729072674E2DB0011F7A1 /* EIP712StructType.swift */,
0DB729082674E2DB0011F7A1 /* EIP712Signer.swift */,
0DB729092674E2DB0011F7A1 /* EIP712TypedData.swift */,
0DB7290A2674E2DB0011F7A1 /* EIP712Type.swift */,
0DB7290B2674E2DB0011F7A1 /* EIP712Hash.swift */,
0DB729112674E2DB0011F7A1 /* EIP712Domain.swift */,
0DB7290C2674E2DB0011F7A1 /* Protocols */,
);
path = EIP712;
sourceTree = "<group>";
};
0DB7290C2674E2DB0011F7A1 /* Protocols */ = {
isa = PBXGroup;
children = (
0DB7290D2674E2DB0011F7A1 /* EIP712Value.swift */,
0DB7290E2674E2DB0011F7A1 /* EIP712Hashable.swift */,
0DB7290F2674E2DB0011F7A1 /* EIP712Signable.swift */,
0DB729102674E2DB0011F7A1 /* EIP712Representable.swift */,
);
path = Protocols;
sourceTree = "<group>";
};
0DC850E926B73A8200809E82 /* Models */ = {
isa = PBXGroup;
children = (
@ -203,6 +152,7 @@
2C8A09C2267513A700993638 /* Ethereum */,
2C8A09E5267595C200993638 /* Views */,
2C1995432674C4BA00A8E370 /* Assets.xcassets */,
2C6B964E26B9D98C00D2C819 /* Colors.xcassets */,
2C1995452674C4BA00A8E370 /* Main.storyboard */,
2C208AA126813497005BA500 /* Supporting Files */,
);
@ -224,6 +174,8 @@
children = (
2C6706A4267A6BFE006AAEF2 /* Bundle.swift */,
2C03D1D4269B428C00EF10EA /* Notification.swift */,
2C603D0126B6E13F00956955 /* String.swift */,
2C6B964B26B9D92500D2C819 /* NSColor.swift */,
2CD0B3F426A0DAA900488D92 /* NSPasteboard.swift */,
2CC89470269A334A00879245 /* UserDefaults.swift */,
);
@ -234,9 +186,8 @@
isa = PBXGroup;
children = (
2C1995552674D0F300A8E370 /* Ethereum.swift */,
2CE3D014267F73E80032A62E /* AccountWithKey.swift */,
2CE3D014267F73E80032A62E /* LegacyAccountWithKey.swift */,
2CE3D011267F73C00032A62E /* Transaction.swift */,
0DB729012674E2DB0011F7A1 /* EIP712 */,
);
path = Ethereum;
sourceTree = "<group>";
@ -270,7 +221,6 @@
2C91742B267D2A7900049075 /* Services */ = {
isa = PBXGroup;
children = (
2C8A09B42675101300993638 /* AccountsService.swift */,
2C03D1D1269B407900EF10EA /* NetworkMonitor.swift */,
2C901C4C268A033100D0926A /* GasService.swift */,
2CC0CDBD2692027E0072922A /* PriceService.swift */,
@ -366,6 +316,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2C6B964F26B9D98C00D2C819 /* Colors.xcassets in Resources */,
2C1995442674C4BA00A8E370 /* Assets.xcassets in Resources */,
2C1995472674C4BA00A8E370 /* Main.storyboard in Resources */,
);
@ -438,47 +389,36 @@
buildActionMask = 2147483647;
files = (
2C901C4A2689F01700D0926A /* Strings.swift in Sources */,
0DB729152674E2DB0011F7A1 /* EIP712Error.swift in Sources */,
2C6706A5267A6BFE006AAEF2 /* Bundle.swift in Sources */,
2CC0CDBE2692027E0072922A /* PriceService.swift in Sources */,
2C8A09C6267513FC00993638 /* Agent.swift in Sources */,
2C8A09D42675184700993638 /* Window.swift in Sources */,
0DB7291A2674E2DB0011F7A1 /* EIP712Type.swift in Sources */,
2C208A9F26813408005BA500 /* Secrets.swift in Sources */,
2CC8946F269A2E8C00879245 /* SessionStorage.swift in Sources */,
0DB7291D2674E2DB0011F7A1 /* EIP712Hashable.swift in Sources */,
0DB729162674E2DB0011F7A1 /* EIP712SimpleValue.swift in Sources */,
0DB729122674E2DB0011F7A1 /* EIP712ParameterEncoder.swift in Sources */,
0DC850E726B73A5900809E82 /* AuthenticationReason.swift in Sources */,
2CD0669126B5537B00728C20 /* InkWallet.swift in Sources */,
2C8A09D726751A0C00993638 /* WalletConnect.swift in Sources */,
2C03D1D2269B407900EF10EA /* NetworkMonitor.swift in Sources */,
2C8A09E326757FC000993638 /* AccountCellView.swift in Sources */,
0DB729202674E2DB0011F7A1 /* EIP712Domain.swift in Sources */,
0DB729182674E2DB0011F7A1 /* EIP712Signer.swift in Sources */,
2C6B964C26B9D92500D2C819 /* NSColor.swift in Sources */,
2C603D0226B6E13F00956955 /* String.swift in Sources */,
2CC89471269A334A00879245 /* UserDefaults.swift in Sources */,
2C78F8282683BDCC00C10670 /* Alert.swift in Sources */,
2C8A09EE2675965F00993638 /* WaitingViewController.swift in Sources */,
2C797E7E267BB88800F2CE2D /* WelcomeViewController.swift in Sources */,
0DB729192674E2DB0011F7A1 /* EIP712TypedData.swift in Sources */,
2CD0669226B5537B00728C20 /* WalletsManager.swift in Sources */,
2CD0B3F726AC619900488D92 /* AddAccountOptionCellView.swift in Sources */,
2C8A09E82675960D00993638 /* ErrorViewController.swift in Sources */,
2C1995422674C4B900A8E370 /* ImportViewController.swift in Sources */,
2C8E47A326A322E8007B8354 /* RightClickTableView.swift in Sources */,
2C901C472689E6D400D0926A /* ApproveTransactionViewController.swift in Sources */,
2C8A09B52675101300993638 /* AccountsService.swift in Sources */,
2CDAB3722675B3F0009F8B97 /* PasswordViewController.swift in Sources */,
2C1995402674C4B900A8E370 /* AppDelegate.swift in Sources */,
0DB729142674E2DB0011F7A1 /* EIP712Parameter.swift in Sources */,
2C901C4D268A033100D0926A /* GasService.swift in Sources */,
2C528A16267FA8EB00CA3ADD /* Defaults.swift in Sources */,
2CD0B3F526A0DAA900488D92 /* NSPasteboard.swift in Sources */,
2CE3D015267F73E80032A62E /* AccountWithKey.swift in Sources */,
2CE3D015267F73E80032A62E /* LegacyAccountWithKey.swift in Sources */,
2CE3D012267F73C00032A62E /* Transaction.swift in Sources */,
0DB7291B2674E2DB0011F7A1 /* EIP712Hash.swift in Sources */,
0DB729172674E2DB0011F7A1 /* EIP712StructType.swift in Sources */,
0DB7291F2674E2DB0011F7A1 /* EIP712Representable.swift in Sources */,
0DB7291E2674E2DB0011F7A1 /* EIP712Signable.swift in Sources */,
0DB7291C2674E2DB0011F7A1 /* EIP712Value.swift in Sources */,
2C8A09EB2675964700993638 /* ApproveViewController.swift in Sources */,
2C03D1D5269B428C00EF10EA /* Notification.swift in Sources */,
2C1995562674D0F300A8E370 /* Ethereum.swift in Sources */,
@ -626,7 +566,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 22;
CURRENT_PROJECT_VERSION = 23;
DEVELOPMENT_TEAM = XWNXDSM6BU;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "$(SRCROOT)/Encrypted Ink/Supporting Files/Info.plist";
@ -653,7 +593,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 22;
CURRENT_PROJECT_VERSION = 23;
DEVELOPMENT_TEAM = XWNXDSM6BU;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "$(SRCROOT)/Encrypted Ink/Supporting Files/Info.plist";

View File

@ -11,7 +11,6 @@ class Agent: NSObject {
private override init() { super.init() }
private var statusBarItem: NSStatusItem!
private let accountsService = AccountsService.shared
private var hasPassword = Keychain.shared.password != nil
private var didEnterPasswordOnStart = false
@ -72,9 +71,9 @@ class Agent: NSObject {
}
let windowController = Window.showNew()
let completion = onSelectedAccount(session: session)
let completion = onSelectedWallet(session: session)
let accountsList = instantiate(AccountsListViewController.self)
accountsList.onSelectedAccount = completion
accountsList.onSelectedWallet = completion
windowController.contentViewController = accountsList
}
@ -120,9 +119,9 @@ class Agent: NSObject {
showInitialScreen(wcSession: session)
}
func getAccountSelectionCompletionIfShouldSelect() -> ((AccountWithKey) -> Void)? {
func getWalletSelectionCompletionIfShouldSelect() -> ((InkWallet) -> Void)? {
let session = getSessionFromPasteboard()
return onSelectedAccount(session: session)
return onSelectedWallet(session: session)
}
lazy private var statusBarMenu: NSMenu = {
@ -223,10 +222,10 @@ class Agent: NSObject {
}
}
private func onSelectedAccount(session: WCSession?) -> ((AccountWithKey) -> Void)? {
private func onSelectedWallet(session: WCSession?) -> ((InkWallet) -> Void)? {
guard let session = session else { return nil }
return { [weak self] account in
self?.connectWallet(session: session, account: account)
return { [weak self] wallet in
self?.connectWallet(session: session, wallet: wallet)
}
}
@ -285,12 +284,12 @@ class Agent: NSObject {
}
}
private func connectWallet(session: WCSession, account: AccountWithKey) {
private func connectWallet(session: WCSession, wallet: InkWallet) {
let windowController = Window.showNew()
let window = windowController.window
windowController.contentViewController = WaitingViewController.withReason("Connecting")
WalletConnect.shared.connect(session: session, address: account.address) { [weak window] _ in
WalletConnect.shared.connect(session: session, walletId: wallet.id) { [weak window] _ in
if window?.isVisible == true {
Window.closeAllAndActivateBrowser()
}

View File

@ -9,6 +9,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private let gasService = GasService.shared
private let priceService = PriceService.shared
private let networkMonitor = NetworkMonitor.shared
private let walletsManager = WalletsManager.shared
private var didFinishLaunching = false
private var initialInputLink: String?
@ -34,6 +35,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
gasService.start()
priceService.start()
networkMonitor.start()
walletsManager.start()
didFinishLaunching = true
if let link = initialInputLink {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17156"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -724,7 +724,7 @@
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="feM-MO-Msf">
<rect key="frame" x="14" y="258" width="222" height="68"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Import Account" id="bI7-hT-bS8">
<textFieldCell key="cell" alignment="center" title="Import Account" id="bI7-hT-bS8">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -736,18 +736,18 @@
<constraint firstAttribute="width" constant="190" id="Pi7-Hn-MhX"/>
<constraint firstAttribute="height" constant="150" id="WsU-g6-Y2a"/>
</constraints>
<textFieldCell key="cell" controlSize="large" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="border" focusRingType="none" alignment="left" placeholderString="Private Key or Secret Words or Keystore" drawsBackground="YES" id="qLb-bZ-IWI">
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="border" focusRingType="none" alignment="left" placeholderString="Private Key or Secret Words or Keystore" drawsBackground="YES" id="qLb-bZ-IWI">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OMb-fe-Srm">
<rect key="frame" x="69" y="32" width="113" height="28"/>
<rect key="frame" x="61" y="32" width="129" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="h7j-aQ-ZXs">
<rect key="frame" x="-6" y="-6" width="74" height="40"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Nwp-G5-xI6">
<rect key="frame" x="-6" y="-7" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Nwp-G5-xI6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -759,8 +759,8 @@ Gw
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t6z-sy-JB9">
<rect key="frame" x="68" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WW9-Lc-I9e">
<rect key="frame" x="76" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WW9-Lc-I9e">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -810,8 +810,8 @@ DQ
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dkh-kG-EFj">
<rect key="frame" x="53" y="292" width="144" height="34"/>
<textFieldCell key="cell" controlSize="large" enabled="NO" allowsUndo="NO" alignment="center" title="Accounts" id="9No-vQ-vBK">
<rect key="frame" x="54" y="292" width="143" height="34"/>
<textFieldCell key="cell" enabled="NO" allowsUndo="NO" alignment="center" title="Accounts" id="9No-vQ-vBK">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -842,11 +842,11 @@ DQ
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="AccountCellView" id="Lp1-Zy-70c" customClass="AccountCellView" customModule="Encrypted_Ink" customModuleProvider="target">
<rect key="frame" x="10" y="0.0" width="205" height="40"/>
<rect key="frame" x="0.0" y="0.0" width="193" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="pdB-jS-gKE">
<rect key="frame" x="51" y="5" width="30" height="30"/>
<rect key="frame" x="45" y="5" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="Dmm-fG-F82"/>
<constraint firstAttribute="height" constant="30" id="MNb-Zm-mjg"/>
@ -859,7 +859,7 @@ DQ
</userDefinedRuntimeAttributes>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sef-hl-iPV">
<rect key="frame" x="85" y="8" width="62" height="25"/>
<rect key="frame" x="79" y="8" width="62" height="24"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Label" id="2a6-9c-AzV">
<font key="font" metaFont="systemBold" size="21"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -879,11 +879,11 @@ DQ
</connections>
</tableCellView>
<tableCellView identifier="AddAccountOptionCellView" id="1KM-PS-nEo" customClass="AddAccountOptionCellView" customModule="Encrypted_Ink" customModuleProvider="target">
<rect key="frame" x="10" y="40" width="205" height="40"/>
<rect key="frame" x="0.0" y="40" width="193" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BpB-tc-JdP">
<rect key="frame" x="18" y="8" width="181" height="25"/>
<rect key="frame" x="18" y="8" width="169" height="24"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="Label" id="FAR-3U-VR0">
<font key="font" metaFont="systemBold" size="21"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -919,7 +919,7 @@ DQ
</scroller>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ngQ-Bn-Kwd">
<rect key="frame" x="205" y="292" width="33" height="34"/>
<rect key="frame" x="205" y="291" width="33" height="34"/>
<buttonCell key="cell" type="inline" title="+" bezelStyle="inline" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="JVh-da-a0h">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="systemBold" size="29"/>
@ -961,18 +961,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="GMd-25-PjD">
<rect key="frame" x="14" y="292" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Waiting" id="Ab0-0l-hhQ">
<textFieldCell key="cell" alignment="center" title="Waiting" id="Ab0-0l-hhQ">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PTt-zv-8aY">
<rect key="frame" x="106" y="32" width="39" height="28"/>
<rect key="frame" x="102" y="32" width="47" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R7c-Oj-Adu">
<rect key="frame" x="-6" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aLn-BL-hyz">
<rect key="frame" x="-6" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aLn-BL-hyz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<connections>
@ -1021,18 +1021,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="PdV-Nr-oOY">
<rect key="frame" x="14" y="292" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Approve" id="wVk-Ux-kpU">
<textFieldCell key="cell" alignment="center" title="Approve" id="wVk-Ux-kpU">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Oc8-WJ-ABp">
<rect key="frame" x="69" y="32" width="113" height="28"/>
<rect key="frame" x="61" y="32" width="129" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="orP-ex-jdo">
<rect key="frame" x="-6" y="-6" width="74" height="40"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="DGh-ds-l2H">
<rect key="frame" x="-6" y="-7" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="DGh-ds-l2H">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1044,8 +1044,8 @@ Gw
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xdD-3r-3YP">
<rect key="frame" x="68" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="O71-BW-3Sb">
<rect key="frame" x="76" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="O71-BW-3Sb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1067,17 +1067,17 @@ DQ
</customSpacing>
</stackView>
<scrollView focusRingType="none" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="Ra3-Vm-T8F">
<rect key="frame" x="30" y="90" width="190" height="184"/>
<rect key="frame" x="30" y="83" width="190" height="191"/>
<clipView key="contentView" id="3HR-u1-79b">
<rect key="frame" x="1" y="1" width="188" height="182"/>
<rect key="frame" x="1" y="1" width="188" height="189"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" selectable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="nek-dW-Y7v">
<rect key="frame" x="0.0" y="0.0" width="188" height="182"/>
<rect key="frame" x="0.0" y="0.0" width="188" height="189"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="188" height="182"/>
<size key="minSize" width="188" height="189"/>
<size key="maxSize" width="240" height="10000000"/>
<attributedString key="textStorage">
<fragment content="Meta">
@ -1100,7 +1100,7 @@ DQ
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="GFp-c5-pAF">
<rect key="frame" x="173" y="1" width="16" height="182"/>
<rect key="frame" x="173" y="1" width="16" height="189"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
@ -1170,18 +1170,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="T7q-cN-jjc">
<rect key="frame" x="14" y="292" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Error" id="t7e-cO-oeN">
<textFieldCell key="cell" alignment="center" title="Error" id="t7e-cO-oeN">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="owz-dg-x2C">
<rect key="frame" x="106" y="32" width="39" height="28"/>
<rect key="frame" x="102" y="32" width="47" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bRK-Tb-oKu">
<rect key="frame" x="-6" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="xDk-iz-zcw">
<rect key="frame" x="-6" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="xDk-iz-zcw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1201,8 +1201,8 @@ DQ
</customSpacing>
</stackView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="M1Q-Iu-4dD">
<rect key="frame" x="34" y="175" width="182" height="25"/>
<textFieldCell key="cell" controlSize="large" selectable="YES" alignment="center" title="Failed to connect" id="qy3-O0-p56">
<rect key="frame" x="34" y="175" width="182" height="24"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="Failed to connect" id="qy3-O0-p56">
<font key="font" metaFont="systemBold" size="21"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1239,18 +1239,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Gdw-Q4-bo0">
<rect key="frame" x="14" y="258" width="222" height="68"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Create Password" id="8sq-L4-gvS">
<textFieldCell key="cell" alignment="center" title="Create Password" id="8sq-L4-gvS">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uA2-QK-zWz">
<rect key="frame" x="69" y="32" width="113" height="28"/>
<rect key="frame" x="61" y="32" width="129" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cWf-at-SKV">
<rect key="frame" x="-6" y="-6" width="74" height="40"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="r4y-I1-eGI">
<rect key="frame" x="-6" y="-7" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="r4y-I1-eGI">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1262,8 +1262,8 @@ Gw
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="N8k-Bq-UNz">
<rect key="frame" x="68" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="SbB-to-6AA">
<rect key="frame" x="76" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="SbB-to-6AA">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1289,7 +1289,7 @@ DQ
<constraints>
<constraint firstAttribute="width" constant="160" id="PLH-bk-GPE"/>
</constraints>
<secureTextFieldCell key="cell" controlSize="large" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" placeholderString="Password" drawsBackground="YES" usesSingleLineMode="YES" id="Szl-cI-dPv">
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" placeholderString="Password" drawsBackground="YES" usesSingleLineMode="YES" id="Szl-cI-dPv">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1300,7 +1300,7 @@ DQ
</secureTextField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="H35-O5-AZi">
<rect key="frame" x="18" y="234" width="214" height="16"/>
<textFieldCell key="cell" controlSize="large" selectable="YES" alignment="center" title="Multiline Label" id="5g8-hW-z0n">
<textFieldCell key="cell" selectable="YES" alignment="center" title="Multiline Label" id="5g8-hW-z0n">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1342,18 +1342,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="vxh-AZ-Ll4">
<rect key="frame" x="14" y="292" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Encrypted Ink" id="dja-pE-CfV">
<textFieldCell key="cell" alignment="center" title="Encrypted Ink" id="dja-pE-CfV">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zQX-EK-b8k">
<rect key="frame" x="80" y="32" width="91" height="28"/>
<rect key="frame" x="76" y="32" width="99" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ADG-H0-CZa">
<rect key="frame" x="-6" y="-6" width="103" height="40"/>
<buttonCell key="cell" type="push" title="Get Started" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jgC-3I-6bj">
<rect key="frame" x="-6" y="-7" width="111" height="32"/>
<buttonCell key="cell" type="push" title="Get Started" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jgC-3I-6bj">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1373,8 +1373,8 @@ DQ
</customSpacing>
</stackView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="BvK-xX-gxC">
<rect key="frame" x="26" y="162" width="198" height="50"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Sign crypto transactions." id="4kF-cy-CbK">
<rect key="frame" x="26" y="163" width="198" height="48"/>
<textFieldCell key="cell" alignment="center" title="Sign crypto transactions." id="4kF-cy-CbK">
<font key="font" metaFont="systemBold" size="21"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1411,18 +1411,18 @@ DQ
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="g2G-lh-X1F">
<rect key="frame" x="14" y="310" width="222" height="34"/>
<textFieldCell key="cell" controlSize="large" alignment="center" title="Approve" id="t64-oP-3Nm">
<textFieldCell key="cell" alignment="center" title="Approve" id="t64-oP-3Nm">
<font key="font" metaFont="systemHeavy" size="29"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MlF-7E-1Jg">
<rect key="frame" x="69" y="32" width="113" height="28"/>
<rect key="frame" x="61" y="32" width="129" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sUu-V8-fLA">
<rect key="frame" x="-6" y="-6" width="74" height="40"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="i8C-YK-VwR">
<rect key="frame" x="-6" y="-7" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="i8C-YK-VwR">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1434,8 +1434,8 @@ Gw
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JBO-6Q-5eC">
<rect key="frame" x="68" y="-6" width="51" height="40"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="U44-9K-bLF">
<rect key="frame" x="76" y="-7" width="59" height="32"/>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="U44-9K-bLF">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
@ -1457,17 +1457,17 @@ DQ
</customSpacing>
</stackView>
<scrollView focusRingType="none" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="7mi-1e-Qtu">
<rect key="frame" x="30" y="138" width="190" height="154"/>
<rect key="frame" x="30" y="131" width="190" height="161"/>
<clipView key="contentView" id="lLN-8O-Blv">
<rect key="frame" x="1" y="1" width="188" height="152"/>
<rect key="frame" x="1" y="1" width="188" height="159"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" selectable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="EuZ-5b-EGk">
<rect key="frame" x="0.0" y="0.0" width="188" height="152"/>
<rect key="frame" x="0.0" y="0.0" width="188" height="159"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="188" height="152"/>
<size key="minSize" width="188" height="159"/>
<size key="maxSize" width="240" height="10000000"/>
<attributedString key="textStorage">
<fragment content="Meta">
@ -1490,26 +1490,26 @@ DQ
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="bKL-hk-a9Y">
<rect key="frame" x="173" y="1" width="16" height="152"/>
<rect key="frame" x="173" y="1" width="16" height="159"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<stackView distribution="fillProportionally" orientation="horizontal" alignment="centerY" spacing="4" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pLD-2z-PFP">
<rect key="frame" x="45" y="86" width="160" height="16"/>
<rect key="frame" x="45" y="79" width="160" height="16"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jcz-Um-efA">
<rect key="frame" x="-2" y="0.0" width="24" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="Vdv-zQ-pVQ"/>
</constraints>
<textFieldCell key="cell" controlSize="large" lineBreakMode="clipping" alignment="left" title="🐢" id="QsW-fn-Al5">
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="🐢" id="QsW-fn-Al5">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<slider horizontalHuggingPriority="500" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oWb-y6-1VO">
<rect key="frame" x="22" y="0.0" width="116" height="17"/>
<rect key="frame" x="24" y="1" width="112" height="15"/>
<sliderCell key="cell" controlSize="mini" enabled="NO" state="on" alignment="left" maxValue="100" doubleValue="33.333333333333336" tickMarkPosition="below" numberOfTickMarks="4" sliderType="linear" id="8pg-D3-7EG"/>
<connections>
<action selector="sliderValueChanged:" target="eky-J7-eog" id="iz9-9b-wNa"/>
@ -1517,7 +1517,7 @@ DQ
</slider>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WJV-V3-GWm">
<rect key="frame" x="138" y="0.0" width="24" height="16"/>
<textFieldCell key="cell" controlSize="large" lineBreakMode="clipping" alignment="right" title="🐇" id="aKB-6P-OIS">
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="🐇" id="aKB-6P-OIS">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -1569,7 +1569,7 @@ DQ
</customSpacing>
</stackView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="xby-TM-Wec">
<rect key="frame" x="28" y="111" width="194" height="14"/>
<rect key="frame" x="28" y="105" width="194" height="14"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="center" id="606-Ge-1Pk">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xB0",
"green" : "0x17",
"red" : "0x1B"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x5A",
"green" : "0x5D",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x14",
"green" : "0x00",
"red" : "0xFA"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,66 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Domain.swift
//
// Created by Igor Shmakov on 09/04/2019
//
import Foundation
public struct EIP712Domain: EIP712Representable {
public let name: String?
public let version: String?
public let chainID: Int?
public let verifyingContract: String?
public let salt: Data?
public var typeName: String {
return "EIP712Domain"
}
public func values() throws -> [EIP712Value] {
var data = [EIP712Value]()
if let name = name {
data.append(EIP712SimpleValue(
parameter: EIP712Parameter(name: "name", type: .string),
value: name)
)
}
if let version = version {
data.append(EIP712SimpleValue(
parameter: EIP712Parameter(name: "version", type: .string),
value: version)
)
}
if let chainID = chainID {
data.append(EIP712SimpleValue(
parameter: EIP712Parameter(name: "chainId", type: .uint(len: 256)),
value: chainID)
)
}
if let verifyingContract = verifyingContract {
data.append(EIP712SimpleValue(
parameter: EIP712Parameter(name: "verifyingContract", type: .address),
value: verifyingContract)
)
}
if let salt = salt {
data.append(EIP712SimpleValue(
parameter: EIP712Parameter(name: "salt", type: .fixedBytes(len: 32)),
value: salt)
)
}
return data
}
}

View File

@ -1,30 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Error.swift
//
// Created by Igor Shmakov on 09/04/2019
//
import Foundation
public enum EIP712Error: Error {
case notImplemented
case invalidInput
case invalidMessage
case invalidParameter(name: String)
case invalidType(name: String)
case invalidTypedData
case invalidTypedDataPrimaryType
case invalidTypedDataDomain
case invalidTypedDataMessage
case invalidTypedDataType
case invalidTypedDataValue
case integerOverflow
case signatureVerificationError
}

View File

@ -1,33 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// Hash:.swift
//
// Created by Igor Shmakov on 09/04/2019
//
import Foundation
public class EIP712Hash: EIP712Hashable {
private let typedData: EIP712Hashable
private let domain: EIP712Hashable
public init(domain: EIP712Hashable, typedData: EIP712Hashable) {
self.domain = domain
self.typedData = typedData
}
public func hash() throws -> Data {
guard
let domainData = try? domain.hash(),
let structData = try? typedData.hash()
else {
throw EIP712Error.invalidMessage
}
return (Data(hex: "0x1901") + domainData + structData).sha3(.keccak256)
}
}

View File

@ -1,111 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Parameter.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
public enum EIP712ParameterType {
case bool
case address
case string
case bytes
case fixedBytes(len: Int)
case uint(len: Int)
case int(len: Int)
case object(name: String)
private static func parseBytesSize(type: String, prefix: String) throws -> Int {
guard type.starts(with: prefix) else {
throw EIP712Error.invalidType(name: type)
}
guard let size = Int(type.dropFirst(prefix.count)) else {
throw EIP712Error.invalidType(name: type)
}
if size < 1 || size > 32 {
throw EIP712Error.invalidType(name: type)
}
return size
}
private static func parseIntSize(type: String, prefix: String) throws -> Int {
guard type.starts(with: prefix) else {
throw EIP712Error.invalidType(name: type)
}
guard let size = Int(type.dropFirst(prefix.count)) else {
if type == prefix {
return 256
}
throw EIP712Error.invalidType(name: type)
}
if size < 8 || size > 256 || size % 8 != 0 {
throw EIP712Error.invalidType(name: type)
}
return size
}
public static func parse(type: String) throws -> EIP712ParameterType {
if type == "bool" {
return .bool
}
if type == "address" {
return .address
}
if type == "string" {
return .string
}
if type == "bytes" {
return .bytes
}
if type.hasPrefix("uint") {
return try .uint(len: parseIntSize(type: type, prefix: "uint"))
}
if type.hasPrefix("int") {
return try .int(len: parseIntSize(type: type, prefix: "int"))
}
if type.hasPrefix("bytes") {
return try .fixedBytes(len: parseBytesSize(type: type, prefix: "bytes"))
}
return object(name: type)
}
public func raw() -> String {
switch self {
case .bool: return "bool"
case .address: return "address"
case .string: return "string"
case let .fixedBytes(len): return "bytes\(len)"
case let .uint(len): return "uint\(len)"
case let .int(len): return "int\(len)"
case .bytes: return "bytes"
case let .object(name): return name
}
}
}
public struct EIP712Parameter {
public let name: String
public let type: EIP712ParameterType
public func encode() -> String {
return "\(type.raw()) \(name)"
}
}

View File

@ -1,119 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712ValueEncoder.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
import BigInt
import Web3Swift
public final class EIP712ValueEncoder {
private let type: EIP712ParameterType
private let value: Any
public init(type: EIP712ParameterType, value: Any) {
self.type = type
self.value = value
}
public func makeABIEncodedParameter() throws -> ABIEncodedParameter {
switch type {
case .bool:
return try encodeBool()
case .address:
return try encodeAddress()
case .string:
return try encodeString()
case .fixedBytes:
return try encodeFixedBytes()
case .uint, .int:
return try encodeInt()
case .bytes:
return try encodeBytes()
case .object:
return try encodeObject()
}
}
}
extension EIP712ValueEncoder {
private func encodeBool() throws -> ABIEncodedParameter {
guard let bool = value as? Bool else {
throw EIP712Error.invalidTypedDataValue
}
return ABIBoolean(origin: bool)
}
private func encodeAddress() throws -> ABIEncodedParameter {
guard let value = value as? String else {
throw EIP712Error.invalidTypedDataValue
}
return ABIAddress(address: EthAddress(hex: value))
}
private func encodeString() throws -> ABIEncodedParameter {
guard let value = value as? String, let data = value.data(using: .utf8) else {
throw EIP712Error.invalidTypedDataValue
}
return ABIFixedBytes(origin: SimpleBytes(bytes: data.sha3(.keccak256)))
}
private func encodeFixedBytes() throws -> ABIEncodedParameter {
let data: Data
if let value = value as? String {
data = Data(hex: value)
} else if let value = value as? Data {
data = value
} else {
throw EIP712Error.invalidTypedDataValue
}
return ABIFixedBytes(origin: SimpleBytes(bytes: data))
}
private func encodeInt() throws -> ABIEncodedParameter {
let number: Int
if let value = value as? Int {
number = value
} else if let str = value as? String {
return ABIUnsignedNumber(origin: EthNumber(hex: str))
} else {
throw EIP712Error.invalidTypedDataValue
}
return ABIUnsignedNumber(origin: EthNumber(value: number))
}
private func encodeBytes() throws -> ABIEncodedParameter {
let data: Data
if let value = value as? String {
data = Data(hex: value)
} else if let value = value as? Data {
data = value
} else {
throw EIP712Error.invalidTypedDataValue
}
return ABIFixedBytes(origin: SimpleBytes(bytes: data.sha3(.keccak256)))
}
private func encodeObject() throws -> ABIEncodedParameter {
guard let value = value as? EIP712Representable else {
throw EIP712Error.invalidTypedDataValue
}
return ABIFixedBytes(origin: SimpleBytes(bytes: try value.hash()))
}
}

View File

@ -1,33 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Signer.swift
//
// Created by Igor Shmakov on 19/04/2019
//
import Foundation
import Web3Swift
import CryptoSwift
public final class EIP712Signer: EIP712Signable {
private let privateKey: EthPrivateKey
public init(privateKey: EthPrivateKey) {
self.privateKey = privateKey
}
public func sign(hash: EIP712Hashable) throws -> SECP256k1Signature {
let signature = SECP256k1Signature(
digest: SimpleBytes(bytes: try hash.hash()),
privateKey: privateKey
)
return signature
}
}

View File

@ -1,24 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712SimpleValue.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
import Web3Swift
public struct EIP712SimpleValue: EIP712Value {
public let parameter: EIP712Parameter
public let value: Any
public func makeABIEncodedParameter() throws -> ABIEncodedParameter {
let encoder = EIP712ValueEncoder(type: parameter.type, value: value)
return try encoder.makeABIEncodedParameter()
}
}

View File

@ -1,32 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712StructType.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
public struct EIP712StructType {
public let types: [EIP712Type]
public var primaryType: EIP712Type? {
return types.first
}
public init(primary: EIP712Type, referenced: [EIP712Type] = []) {
types = [primary] + referenced.sorted { $0.name < $1.name }
}
public func encode() -> String {
return types.reduce("") { $0 + $1.encode()}
}
public func hashType() throws -> Data {
return encode().data(using: .utf8)!.sha3(.keccak256)
}
}

View File

@ -1,22 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Type.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
public struct EIP712Type {
public let name: String
public let parameters: [EIP712Parameter]
public func encode() -> String {
let encodedParameters = parameters.map { $0.encode() }.joined(separator: ",")
return "\(name)(\(encodedParameters))"
}
}

View File

@ -1,160 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712TypedData.swift
//
// Created by Igor Shmakov on 12/04/2019
//
import Foundation
import SwiftyJSON
import Web3Swift
import BigInt
public class EIP712TypedData {
public private(set) var type: EIP712StructType!
public private(set) var domain: EIP712Domain!
public private(set) var encodedData: Data!
private let domainType = "EIP712Domain"
public convenience init(jsonData: Data) throws {
try self.init(json: try JSON(data: jsonData))
}
public convenience init(jsonString: String) throws {
try self.init(json: JSON(parseJSON: jsonString))
}
public convenience init(jsonObject: Any) throws {
try self.init(json: JSON(jsonObject))
}
public init(json: JSON) throws {
let jsonDomain = json["domain"]
let jsonMessage = json["message"]
guard
let jsonTypes = json["types"].dictionary,
let primaryTypeName = json["primaryType"].string
else {
throw EIP712Error.invalidTypedData
}
let types = try parseTypes(jsonTypes: jsonTypes)
guard let primaryType = types[primaryTypeName] else {
throw EIP712Error.invalidTypedDataPrimaryType
}
guard let domainType = types[domainType] else {
throw EIP712Error.invalidTypedDataDomain
}
let dependencies = try findTypeDependencies(primaryType: primaryType, types: types).filter { $0.name != primaryType.name }
let type = EIP712StructType(primary: primaryType, referenced: dependencies)
let domain = try parseDomain(jsonDomain: jsonDomain, type: domainType)
let data = try encodeData(data: jsonMessage, primaryType: primaryType, types: types)
self.type = type
self.encodedData = data
self.domain = domain
}
private func parseDomain(jsonDomain: JSON, type: EIP712Type) throws -> EIP712Domain {
return EIP712Domain(name: jsonDomain["name"].string,
version: jsonDomain["version"].string,
chainID: jsonDomain["chainId"].int,
verifyingContract: jsonDomain["verifyingContract"].string,
salt: jsonDomain["salt"].string?.data(using: .utf8))
}
private func parseTypes(jsonTypes: [String: JSON]) throws -> [String: EIP712Type] {
var types = [String: EIP712Type]()
for (name, jsonParameters) in jsonTypes {
var parameters = [EIP712Parameter]()
for (_, jsonParameter) in jsonParameters {
guard
let name = jsonParameter["name"].string,
let type = jsonParameter["type"].string
else {
throw EIP712Error.invalidTypedDataType
}
parameters.append(EIP712Parameter(name: name, type: try EIP712ParameterType.parse(type: type)))
}
let type = EIP712Type(name: name, parameters: parameters)
types[name] = type
}
return types
}
private func findTypeDependencies(primaryType: EIP712Type, types: [String: EIP712Type], results: [EIP712Type] = []) throws -> [EIP712Type] {
var results = results
if (results.contains(where: { $0.name == primaryType.name }) || types[primaryType.name] == nil) {
return results
}
results.append(primaryType)
for parameter in primaryType.parameters {
if let type = types[parameter.type.raw()] {
let dependencies = try findTypeDependencies(primaryType: type, types: types, results: results)
results += dependencies.filter { dep in !results.contains(where: { res in res.name == dep.name }) }
}
}
return results
}
private func encodeType(primaryType: EIP712Type, types: [String: EIP712Type]) throws -> EIP712StructType {
let dependencies = try findTypeDependencies(primaryType: primaryType, types: types).filter { $0.name != primaryType.name }
return EIP712StructType(primary: primaryType, referenced: dependencies)
}
private func hashType(primaryType: EIP712Type, types: [String: EIP712Type]) throws -> Data {
let type = try encodeType(primaryType: primaryType, types: types)
return try type.hashType()
}
private func encodeData(data: JSON, primaryType: EIP712Type, types: [String: EIP712Type]) throws -> Data {
var encodedTypes: [EIP712ParameterType] = [.fixedBytes(len: 32)]
var encodedValues: [Any] = [try self.hashType(primaryType: primaryType, types: types)]
for parameter in primaryType.parameters {
let value = data[parameter.name]
if let type = types[parameter.type.raw()] {
encodedTypes.append(.fixedBytes(len: 32))
let data = try encodeData(data: value, primaryType: type, types: types)
encodedValues.append(data.sha3(.keccak256))
} else {
encodedTypes.append(parameter.type)
encodedValues.append(value.object)
}
}
let parameters = try zip(encodedTypes, encodedValues).map {
try EIP712ValueEncoder(type: $0.0, value: $0.1).makeABIEncodedParameter()
}
return try EncodedABITuple(parameters: parameters).value()
}
}
extension EIP712TypedData: EIP712Hashable {
public func hash() throws -> Data {
return encodedData.sha3(.keccak256)
}
}

View File

@ -1,16 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Hashable.swift
//
// Created by Igor Shmakov on 17/04/2019
//
import Foundation
public protocol EIP712Hashable {
func hash() throws -> Data
}

View File

@ -1,64 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Representable.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
import Web3Swift
public protocol EIP712Representable: EIP712Hashable {
var typeName: String { get }
func values() throws -> [EIP712Value]
}
public extension EIP712Representable {
func typeDependencies() throws -> [EIP712Type] {
let types = try values()
.compactMap { $0.value as? EIP712Representable }
.flatMap { [try $0.type()] + (try $0.typeDependencies()) }
var uniqueTypes = [EIP712Type]()
for type in types {
if !uniqueTypes.contains(where: { $0.name == type.name }) {
uniqueTypes.append(type)
}
}
return uniqueTypes
}
func type() throws -> EIP712Type {
return EIP712Type(name: typeName, parameters: try values().map { $0.parameter })
}
func encodeType() throws -> EIP712StructType {
let primary = try type()
let referenced = try typeDependencies().filter { $0.name != primary.name }
return EIP712StructType(primary: primary, referenced: referenced)
}
func hashType() throws -> Data {
return try encodeType().hashType()
}
func encodeData() throws -> Data {
var parameters: [ABIEncodedParameter] = [ABIFixedBytes(origin: SimpleBytes(bytes: try hashType()))]
parameters += try values().map { try $0.makeABIEncodedParameter() }
return try EncodedABITuple(parameters: parameters).value()
}
func hash() throws -> Data {
return try encodeData().sha3(.keccak256)
}
}

View File

@ -1,35 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Signable.swift
//
// Created by Igor Shmakov on 19/04/2019
//
import Foundation
import Web3Swift
public protocol EIP712Signable {
func sign(hash: EIP712Hashable) throws -> SECP256k1Signature
}
public extension EIP712Signable {
func signatureData(hash: EIP712Hashable) throws -> Data {
let signature = try sign(hash: hash)
let message = ConcatenatedBytes(
bytes: [
try signature.r(),
try signature.s(),
SimpleBytes(bytes: [UInt8(try signature.recoverID().value() + 27)])
]
)
return try message.value()
}
}

View File

@ -1,20 +0,0 @@
//
// This source file is part of the 0x.swift open source project
// Copyright 2019 The 0x.swift Authors
// Licensed under Apache License v2.0
//
// EIP712Value.swift
//
// Created by Igor Shmakov on 15/04/2019
//
import Foundation
import Web3Swift
public protocol EIP712Value {
var parameter: EIP712Parameter { get }
var value: Any { get }
func makeABIEncodedParameter() throws -> ABIEncodedParameter
}

View File

@ -1,14 +1,17 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
import WalletCore
import Web3Swift
import CryptoSwift
struct Ethereum {
enum Errors: Error {
enum Error: Swift.Error {
case invalidInputData
case failedToSendTransaction
case failedToSign
case keyNotFound
}
private let queue = DispatchQueue(label: "Ethereum", qos: .default)
@ -21,8 +24,9 @@ struct Ethereum {
apiKey: Secrets.alchemy
)
func sign(message: String, account: AccountWithKey) throws -> String {
let ethPrivateKey = EthPrivateKey(hex: account.privateKey)
func sign(message: String, wallet: InkWallet) throws -> String {
guard let privateKeyString = wallet.ethereumPrivateKeyString else { throw Error.keyNotFound }
let ethPrivateKey = EthPrivateKey(hex: privateKeyString)
let signature = SECP256k1Signature(
privateKey: ethPrivateKey,
@ -39,32 +43,33 @@ struct Ethereum {
return data.toPrefixedHexString()
}
func signPersonal(message: String, account: AccountWithKey) throws -> String {
let ethPrivateKey = EthPrivateKey(hex: account.privateKey)
func signPersonal(message: String, wallet: InkWallet) throws -> String {
guard let privateKeyString = wallet.ethereumPrivateKeyString else { throw Error.keyNotFound }
let ethPrivateKey = EthPrivateKey(hex: privateKeyString)
let signed = SignedPersonalMessageBytes(message: message, signerKey: ethPrivateKey)
let data = try signed.value().toPrefixedHexString()
return data
}
func sign(typedData: String, account: AccountWithKey) throws -> String {
let data = try EIP712TypedData(jsonString: typedData)
let hash = EIP712Hash(domain: data.domain, typedData: data)
let privateKey = EthPrivateKey(hex: account.privateKey)
let signer = EIP712Signer(privateKey: privateKey)
return try signer.signatureData(hash: hash).toPrefixedHexString()
func sign(typedData: String, wallet: InkWallet) throws -> String {
guard let ethereumPrivateKey = wallet.ethereumPrivateKey else { throw Error.keyNotFound }
let digest = EthereumAbi.encodeTyped(messageJson: typedData)
guard let signed = ethereumPrivateKey.sign(digest: digest, curve: CoinType.ethereum.curve)?.toPrefixedHexString() else { throw Error.failedToSign }
return signed
}
func send(transaction: Transaction, account: AccountWithKey) throws -> String {
let bytes = signedTransactionBytes(transaction: transaction, account: account)
func send(transaction: Transaction, wallet: InkWallet) throws -> String {
let bytes = try signedTransactionBytes(transaction: transaction, wallet: wallet)
let response = try SendRawTransactionProcedure(network: network, transactionBytes: bytes).call()
guard let hash = response["result"].string else {
throw Errors.failedToSendTransaction
throw Error.failedToSendTransaction
}
return hash
}
private func signedTransactionBytes(transaction: Transaction, account: AccountWithKey) -> EthContractCallBytes {
let senderKey = EthPrivateKey(hex: account.privateKey)
private func signedTransactionBytes(transaction: Transaction, wallet: InkWallet) throws -> EthContractCallBytes {
guard let privateKeyString = wallet.ethereumPrivateKeyString else { throw Error.keyNotFound }
let senderKey = EthPrivateKey(hex: privateKeyString)
let contractAddress = EthAddress(hex: transaction.to)
let functionCall = BytesFromHexString(hex: transaction.data)
let bytes: EthContractCallBytes

View File

@ -2,7 +2,7 @@
import Foundation
struct AccountWithKey: Codable {
struct LegacyAccountWithKey: Codable {
let privateKey: String
let address: String
}

View File

@ -0,0 +1,9 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Cocoa
extension NSColor {
static let inkGreen = NSColor(named: "InkGreen")!
}

View File

@ -0,0 +1,11 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
extension String {
var maybeJSON: Bool {
return hasPrefix("{") && hasSuffix("}") && count > 3
}
}

View File

@ -5,6 +5,7 @@ enum AuthenticationReason {
case sendTransaction
case removeAccount
case showPrivateKey
case showSecretWords
case signAction(title: String)
var title: String {
@ -17,6 +18,8 @@ enum AuthenticationReason {
return "Remove account"
case .showPrivateKey:
return "Show private key"
case .showSecretWords:
return "Show secret words"
case .signAction(let title):
return title
}

View File

@ -5,14 +5,14 @@ import Cocoa
class AccountsListViewController: NSViewController {
private let agent = Agent.shared
private let accountsService = AccountsService.shared
private var accounts = [AccountWithKey]()
private let walletsManager = WalletsManager.shared
private var cellModels = [CellModel]()
var onSelectedAccount: ((AccountWithKey) -> Void)?
var onSelectedWallet: ((InkWallet) -> Void)?
var newWalletId: String?
enum CellModel {
case account(AccountWithKey)
case wallet
case addAccountOption(AddAccountOption)
}
@ -45,42 +45,46 @@ class AccountsListViewController: NSViewController {
}
}
private var wallets: [InkWallet] {
return walletsManager.wallets
}
override func viewDidLoad() {
super.viewDidLoad()
setupAccountsMenu()
reloadAccounts()
reloadTitle()
updateCellModels()
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSApplication.didBecomeActiveNotification, object: nil)
}
override func viewDidAppear() {
super.viewDidAppear()
blinkNewWalletCellIfNeeded()
}
private func setupAccountsMenu() {
let menu = NSMenu()
menu.delegate = self
menu.addItem(NSMenuItem(title: "Copy address", action: #selector(didClickCopyAddress(_:)), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "View on Zerion", action: #selector(didClickViewOnZerion(_:)), keyEquivalent: ""))
menu.addItem(.separator())
menu.addItem(NSMenuItem(title: "Show private key", action: #selector(didClickExportAccount(_:)), keyEquivalent: "")) // TODO: show different texts for secret words export
menu.addItem(NSMenuItem(title: "Show account key", action: #selector(didClickExportAccount(_:)), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Remove account", action: #selector(didClickRemoveAccount(_:)), keyEquivalent: ""))
menu.addItem(.separator())
menu.addItem(NSMenuItem(title: "How to WalletConnect?", action: #selector(showInstructionsAlert), keyEquivalent: ""))
tableView.menu = menu
}
private func reloadAccounts() {
accounts = accountsService.getAccounts()
}
private func reloadTitle() {
titleLabel.stringValue = onSelectedAccount != nil && !accounts.isEmpty ? "Select\nAccount" : "Accounts"
addButton.isHidden = accounts.isEmpty
titleLabel.stringValue = onSelectedWallet != nil && !wallets.isEmpty ? "Select\nAccount" : "Accounts"
addButton.isHidden = wallets.isEmpty
}
@objc private func didBecomeActive() {
guard view.window?.isVisible == true else { return }
if let completion = agent.getAccountSelectionCompletionIfShouldSelect() {
onSelectedAccount = completion
if let completion = agent.getWalletSelectionCompletionIfShouldSelect() {
onSelectedWallet = completion
}
reloadTitle()
}
@ -103,24 +107,32 @@ class AccountsListViewController: NSViewController {
}
@objc private func didClickCreateAccount() {
accountsService.createAccount()
reloadAccounts()
let wallet = try? walletsManager.createWallet()
newWalletId = wallet?.id
reloadTitle()
updateCellModels()
tableView.reloadData()
blinkNewWalletCellIfNeeded()
// TODO: show backup phrase
}
private func blinkNewWalletCellIfNeeded() {
guard let id = newWalletId else { return }
newWalletId = nil
guard let row = wallets.firstIndex(where: { $0.id == id }), row < cellModels.count else { return }
tableView.scrollRowToVisible(row)
(tableView.rowView(atRow: row, makeIfNecessary: true) as? AccountCellView)?.blink()
}
@objc private func didClickImportAccount() {
let importViewController = instantiate(ImportViewController.self)
importViewController.onSelectedAccount = onSelectedAccount
importViewController.onSelectedWallet = onSelectedWallet
view.window?.contentViewController = importViewController
}
@objc private func didClickViewOnZerion(_ sender: AnyObject) {
let row = tableView.deselectedRow
guard row >= 0 else { return }
let address = accounts[row].address
guard row >= 0, let address = wallets[row].ethereumAddress else { return }
if let url = URL(string: "https://app.zerion.io/\(address)/overview") {
NSWorkspace.shared.open(url)
}
@ -128,8 +140,8 @@ class AccountsListViewController: NSViewController {
@objc private func didClickCopyAddress(_ sender: AnyObject) {
let row = tableView.deselectedRow
guard row >= 0 else { return }
NSPasteboard.general.clearAndSetString(accounts[row].address)
guard row >= 0, let address = wallets[row].ethereumAddress else { return }
NSPasteboard.general.clearAndSetString(address)
}
@objc private func didClickRemoveAccount(_ sender: AnyObject) {
@ -151,34 +163,46 @@ class AccountsListViewController: NSViewController {
}
@objc private func didClickExportAccount(_ sender: AnyObject) {
// TODO: show different texts for secret words export
let row = tableView.deselectedRow
guard row >= 0 else { return }
let isMnemonic = wallets[row].isMnemonic
let alert = Alert()
alert.messageText = "Private key gives full access to your funds."
alert.messageText = "\(isMnemonic ? "Secret words give" : "Private key gives") full access to your funds."
alert.alertStyle = .critical
alert.addButton(withTitle: "I understand the risks")
alert.addButton(withTitle: "Cancel")
if alert.runModal() == .alertFirstButtonReturn {
agent.askAuthentication(on: view.window, getBackTo: self, onStart: false, reason: .showPrivateKey) { [weak self] allowed in
let reason: AuthenticationReason = isMnemonic ? .showSecretWords : .showPrivateKey
agent.askAuthentication(on: view.window, getBackTo: self, onStart: false, reason: reason) { [weak self] allowed in
Window.activateWindow(self?.view.window)
if allowed {
self?.showPrivateKey(index: row)
self?.showKey(index: row, mnemonic: isMnemonic)
}
}
}
}
private func showPrivateKey(index: Int) {
let privateKey = accounts[index].privateKey
private func showKey(index: Int, mnemonic: Bool) {
let wallet = wallets[index]
let secret: String
if mnemonic, let mnemonicString = try? walletsManager.exportMnemonic(wallet: wallet) {
secret = mnemonicString
} else if let data = try? walletsManager.exportPrivateKey(wallet: wallet) {
secret = data.hexString
} else {
return
}
let alert = Alert()
alert.messageText = "Private key"
alert.informativeText = privateKey
alert.messageText = mnemonic ? "Secret words" : "Private key"
alert.informativeText = secret
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Copy")
if alert.runModal() != .alertFirstButtonReturn {
NSPasteboard.general.clearAndSetString(privateKey)
NSPasteboard.general.clearAndSetString(secret)
}
}
@ -187,19 +211,19 @@ class AccountsListViewController: NSViewController {
}
private func removeAccountAtIndex(_ index: Int) {
accountsService.removeAccount(accounts[index])
accounts.remove(at: index)
let wallet = wallets[index]
try? walletsManager.delete(wallet: wallet)
reloadTitle()
updateCellModels()
tableView.reloadData()
}
private func updateCellModels() {
if accounts.isEmpty {
if wallets.isEmpty {
cellModels = [.addAccountOption(.createNew), .addAccountOption(.importExisting)]
tableView.shouldShowRightClickMenu = false
} else {
cellModels = accounts.map { .account($0) }
cellModels = Array(repeating: CellModel.wallet, count: wallets.count)
tableView.shouldShowRightClickMenu = true
}
}
@ -213,9 +237,10 @@ extension AccountsListViewController: NSTableViewDelegate {
let model = cellModels[row]
switch model {
case let .account(account):
if let onSelectedAccount = onSelectedAccount {
onSelectedAccount(account)
case .wallet:
let wallet = wallets[row]
if let onSelectedWallet = onSelectedWallet {
onSelectedWallet(wallet)
} else {
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) { [weak self] _ in
var point = NSEvent.mouseLocation
@ -242,9 +267,10 @@ extension AccountsListViewController: NSTableViewDataSource {
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
let model = cellModels[row]
switch model {
case let .account(account):
case .wallet:
let wallet = wallets[row]
let rowView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("AccountCellView"), owner: self) as? AccountCellView
rowView?.setup(address: account.address)
rowView?.setup(address: wallet.ethereumAddress ?? "")
return rowView
case let .addAccountOption(addAccountOption):
let rowView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("AddAccountOptionCellView"), owner: self) as? AddAccountOptionCellView
@ -254,7 +280,7 @@ extension AccountsListViewController: NSTableViewDataSource {
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
if case .account = cellModels[row] {
if case .wallet = cellModels[row] {
return 50
} else {
return 44

View File

@ -4,9 +4,9 @@ import Cocoa
class ImportViewController: NSViewController {
private let accountsService = AccountsService.shared
var onSelectedAccount: ((AccountWithKey) -> Void)?
private var inputValidationResult = AccountsService.InputValidationResult.invalid
private let walletsManager = WalletsManager.shared
var onSelectedWallet: ((InkWallet) -> Void)?
private var inputValidationResult = WalletsManager.InputValidationResult.invalid
@IBOutlet weak var textField: NSTextField! {
didSet {
@ -51,21 +51,23 @@ class ImportViewController: NSViewController {
}
private func importWith(input: String, password: String?) {
if accountsService.addAccount(input: input, password: password) != nil {
showAccountsList()
} else {
do {
let wallet = try walletsManager.addWallet(input: input, inputPassword: password)
showAccountsList(newWalletId: wallet.id)
} catch {
Alert.showWithMessage("Failed to import account", style: .critical)
}
}
private func showAccountsList() {
private func showAccountsList(newWalletId: String?) {
let accountsListViewController = instantiate(AccountsListViewController.self)
accountsListViewController.onSelectedAccount = onSelectedAccount
accountsListViewController.onSelectedWallet = onSelectedWallet
accountsListViewController.newWalletId = newWalletId
view.window?.contentViewController = accountsListViewController
}
@IBAction func cancelButtonTapped(_ sender: NSButton) {
showAccountsList()
showAccountsList(newWalletId: nil)
}
}
@ -73,7 +75,7 @@ class ImportViewController: NSViewController {
extension ImportViewController: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
inputValidationResult = accountsService.validateAccountInput(textField.stringValue)
inputValidationResult = walletsManager.validateWalletInput(textField.stringValue)
okButton.isEnabled = inputValidationResult != .invalid
}

View File

@ -39,7 +39,7 @@ class PasswordViewController: NSViewController {
switch reason {
case .none, .start:
reasonLabel.stringValue = ""
case .sendTransaction, .removeAccount, .showPrivateKey, .signAction:
case .sendTransaction, .removeAccount, .showPrivateKey, .showSecretWords, .signAction:
reasonLabel.stringValue = "to " + (reason?.title.lowercased() ?? "")
}
}

View File

@ -1,89 +0,0 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
import WalletCore
struct AccountsService {
private init() {}
private let keychain = Keychain.shared
static let shared = AccountsService()
enum InputValidationResult {
case valid, invalid, requiresPassword
}
func validateAccountInput(_ input: String) -> InputValidationResult {
if Mnemonic.isValid(mnemonic: input) {
return .valid
} else if let data = Data(hexString: input) {
return PrivateKey.isValid(data: data, curve: CoinType.ethereum.curve) ? .valid : .invalid
} else {
return input.maybeJSON ? .requiresPassword : .invalid
}
}
func createAccount() {
guard let password = keychain.password?.data(using: .utf8) else { return }
let key = StoredKey(name: "", password: password)
guard let privateKey = key.wallet(password: password)?.getKeyForCoin(coin: .ethereum) else { return }
_ = saveAccount(privateKey: privateKey)
}
func addAccount(input: String, password: String?) -> AccountWithKey? {
let key: PrivateKey
if Mnemonic.isValid(mnemonic: input) {
key = HDWallet(mnemonic: input, passphrase: "").getKeyForCoin(coin: .ethereum)
} else if let data = Data(hexString: input), let privateKey = PrivateKey(data: data) {
key = privateKey
} else if input.maybeJSON,
let password = password,
let json = input.data(using: .utf8),
let jsonKey = StoredKey.importJSON(json: json),
let data = jsonKey.decryptPrivateKey(password: Data(password.utf8)),
let privateKey = PrivateKey(data: data) {
key = privateKey
} else {
return nil
}
let account = saveAccount(privateKey: key)
return account
}
private func saveAccount(privateKey: PrivateKey) -> AccountWithKey? {
let address = CoinType.ethereum.deriveAddress(privateKey: privateKey).lowercased()
// TODO: use checksum address
let account = AccountWithKey(privateKey: privateKey.data.hexString, address: address)
var accounts = getAccounts()
guard !accounts.contains(where: { $0.address == address }) else { return nil }
accounts.append(account)
keychain.save(accounts: accounts)
return account
}
func removeAccount(_ account: AccountWithKey) {
var accounts = getAccounts()
accounts.removeAll(where: {$0.address == account.address })
keychain.save(accounts: accounts)
}
func getAccounts() -> [AccountWithKey] {
return keychain.accounts
}
func getAccountForAddress(_ address: String) -> AccountWithKey? {
let allAccounts = getAccounts()
return allAccounts.first(where: { $0.address == address.lowercased() })
}
}
private extension String {
var maybeJSON: Bool {
return hasPrefix("{") && hasSuffix("}") && count > 3
}
}

View File

@ -4,15 +4,38 @@ import Foundation
struct Keychain {
private let prefix = "ink.encrypted.macos."
private init() {}
static let shared = Keychain()
private enum Key: String {
case accounts = "ethereum.keys"
case password = "password"
private enum ItemKey {
case legacyAccounts
case password
case wallet(id: String)
private static let commonPrefix = "ink.encrypted.macos."
private static let walletPrefix = "wallet."
private static let fullWalletPrefix = commonPrefix + walletPrefix
private static let fullWalletPrefixCount = fullWalletPrefix.count
var stringValue: String {
let key: String
switch self {
case .legacyAccounts:
key = "ethereum.keys"
case .password:
key = "password"
case let .wallet(id: id):
key = ItemKey.walletPrefix + id
}
return ItemKey.commonPrefix + key
}
static func walletId(key: String) -> String? {
guard key.hasPrefix(fullWalletPrefix) else { return nil }
return String(key.dropFirst(fullWalletPrefixCount))
}
}
var password: String? {
@ -28,63 +51,84 @@ struct Keychain {
save(data: data, key: .password)
}
var accounts: [AccountWithKey] {
if let data = get(key: .accounts), let accounts = try? JSONDecoder().decode([AccountWithKey].self, from: data) {
// MARK: - WalletCore
func getAllWalletsIds() -> [String] {
let allKeys = allStoredItemsKeys()
let ids = allKeys.compactMap { ItemKey.walletId(key: $0) }
return ids
}
func getWalletData(id: String) -> Data? {
return get(key: .wallet(id: id))
}
func saveWallet(id: String, data: Data) throws {
save(data: data, key: .wallet(id: id))
}
func removeWallet(id: String) throws {
removeData(forKey: .wallet(id: id))
}
func removeAllWallets() throws {
for id in getAllWalletsIds() {
removeData(forKey: .wallet(id: id))
}
}
// MARK: - Legacy
func getLegacyAccounts() throws -> [LegacyAccountWithKey] {
if let data = get(key: .legacyAccounts), let accounts = try? JSONDecoder().decode([LegacyAccountWithKey].self, from: data) {
return accounts
} else {
return []
}
}
func save(accounts: [AccountWithKey]) {
guard let data = try? JSONEncoder().encode(accounts) else { return }
save(data: data, key: .accounts)
}
// MARK: - WalletCore
func getAllWalletsIds() -> [String] {
// TODO: implement
return []
}
func getWalletData(id: String) -> Data? {
// TODO: implement
return nil
}
func saveWallet(id: String, data: Data) throws {
// TODO: implement
}
func removeWallet(id: String) throws {
// TODO: implement
}
func removeAllWallets() throws {
// TODO: implement
func removeLegacyAccounts() throws {
removeData(forKey: .legacyAccounts)
}
// MARK: Private
private func save(data: Data, key: Key) {
let query = [kSecClass as String: kSecClassGenericPassword as String,
kSecAttrAccount as String: prefix + key.rawValue,
kSecValueData as String: data] as [String: Any]
private func save(data: Data, key: ItemKey) {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.stringValue,
kSecValueData as String: data]
SecItemDelete(query as CFDictionary)
SecItemAdd(query as CFDictionary, nil)
}
private func get(key: Key) -> Data? {
guard let returnDataQueryValue = kCFBooleanTrue else { return nil }
let query = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: prefix + key.rawValue,
kSecReturnData as String: returnDataQueryValue,
kSecMatchLimit as String: kSecMatchLimitOne] as [String: Any]
var dataTypeRef: AnyObject?
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
if status == noErr, let data = dataTypeRef as? Data {
private func allStoredItemsKeys() -> [String] {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecReturnData as String: false,
kSecReturnAttributes as String: true,
kSecMatchLimit as String: kSecMatchLimitAll]
var items: CFTypeRef?
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &items)
if status == noErr, let items = items as? [[String: Any]], !items.isEmpty {
return items.compactMap { $0[kSecAttrAccount as String] as? String }
} else {
return []
}
}
private func removeData(forKey key: ItemKey) {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.stringValue]
SecItemDelete(query as CFDictionary)
}
private func get(key: ItemKey) -> Data? {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key.stringValue,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var item: CFTypeRef?
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &item)
if status == noErr, let data = item as? Data {
return data
} else {
return nil

View File

@ -7,7 +7,7 @@ class SessionStorage {
struct Item: Codable {
let session: WCSession
let address: String
let walletId: String
let clientId: String
let sessionDetails: WCSessionRequestParam
}
@ -51,8 +51,8 @@ class SessionStorage {
}
}
func add(interactor: WCInteractor, address: String, sessionDetails: WCSessionRequestParam) {
let item = Item(session: interactor.session, address: address, clientId: interactor.clientId, sessionDetails: sessionDetails)
func add(interactor: WCInteractor, walletId: String, sessionDetails: WCSessionRequestParam) {
let item = Item(session: interactor.session, walletId: walletId, clientId: interactor.clientId, sessionDetails: sessionDetails)
WCSessionStore.store(interactor.session, peerId: sessionDetails.peerId, peerMeta: sessionDetails.peerMeta)
Defaults.storedSessions[interactor.clientId] = item
didInteractWith(clientId: interactor.clientId)

View File

@ -15,9 +15,19 @@ class AccountCellView: NSTableRowView {
@IBOutlet weak var addressTextField: NSTextField!
func setup(address: String) {
addressImageView.image = Blockies(seed: address).createImage()
addressImageView.image = Blockies(seed: address.lowercased()).createImage()
let without0x = address.dropFirst(2)
addressTextField.stringValue = without0x.prefix(4) + "..." + without0x.suffix(4)
}
func blink() {
let initialBackgroundColor = backgroundColor
backgroundColor = .inkGreen
NSAnimationContext.runAnimationGroup { [weak self] context in
context.duration = 1.2
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
self?.animator().backgroundColor = initialBackgroundColor
}
}
}

View File

@ -8,7 +8,7 @@ class WalletConnect {
private let sessionStorage = SessionStorage.shared
private let networkMonitor = NetworkMonitor.shared
private let ethereum = Ethereum.shared
private let accountsService = AccountsService.shared
private let walletsManager = WalletsManager.shared
static let shared = WalletConnect()
@ -24,10 +24,10 @@ class WalletConnect {
return WCSession.from(string: link)
}
func connect(session: WCSession, address: String, uuid: UUID = UUID(), completion: @escaping ((Bool) -> Void)) {
func connect(session: WCSession, walletId: String, uuid: UUID = UUID(), completion: @escaping ((Bool) -> Void)) {
let clientMeta = WCPeerMeta(name: "Encrypted Ink", url: "https://encrypted.ink", description: "Ethereum agent for macOS", icons: ["https://encrypted.ink/icon.png"])
let interactor = WCInteractor(session: session, meta: clientMeta, uuid: uuid)
configure(interactor: interactor, address: address)
configure(interactor: interactor, walletId: walletId)
interactor.connect().done { connected in
completion(connected)
@ -45,7 +45,7 @@ class WalletConnect {
for item in items {
guard let uuid = UUID(uuidString: item.clientId) else { continue }
connect(session: item.session, address: item.address, uuid: uuid) { _ in }
connect(session: item.session, walletId: item.walletId, uuid: uuid) { _ in }
peers[item.clientId] = item.sessionDetails.peerMeta
}
}
@ -83,7 +83,8 @@ class WalletConnect {
return peers[id]
}
private func configure(interactor: WCInteractor, address: String) {
private func configure(interactor: WCInteractor, walletId: String) {
guard let address = walletsManager.getWallet(id: walletId)?.ethereumAddress else { return }
let accounts = [address]
let chainId = 1
@ -92,7 +93,7 @@ class WalletConnect {
interactor.onSessionRequest = { [weak self, weak interactor] (id, peerParam) in
guard let interactor = interactor else { return }
self?.peers[interactor.clientId] = peerParam.peerMeta
self?.sessionStorage.add(interactor: interactor, address: address, sessionDetails: peerParam)
self?.sessionStorage.add(interactor: interactor, walletId: walletId, sessionDetails: peerParam)
interactor.approveSession(accounts: accounts, chainId: chainId).cauterize()
}
@ -103,12 +104,12 @@ class WalletConnect {
}
interactor.eth.onSign = { [weak self, weak interactor] (id, payload) in
self?.approveSign(id: id, payload: payload, address: address, interactor: interactor)
self?.approveSign(id: id, payload: payload, walletId: walletId, interactor: interactor)
self?.sessionStorage.didInteractWith(clientId: interactor?.clientId)
}
interactor.eth.onTransaction = { [weak self, weak interactor] (id, event, transaction) in
self?.approveTransaction(id: id, wct: transaction, address: address, interactor: interactor)
self?.approveTransaction(id: id, wct: transaction, walletId: walletId, interactor: interactor)
self?.sessionStorage.didInteractWith(clientId: interactor?.clientId)
}
}
@ -124,7 +125,7 @@ class WalletConnect {
}
}
private func approveTransaction(id: Int64, wct: WCEthereumTransaction, address: String, interactor: WCInteractor?) {
private func approveTransaction(id: Int64, wct: WCEthereumTransaction, walletId: String, interactor: WCInteractor?) {
guard let to = wct.to else {
rejectRequest(id: id, interactor: interactor, message: "Something went wrong.")
return
@ -134,14 +135,14 @@ class WalletConnect {
let transaction = Transaction(from: wct.from, to: to, nonce: wct.nonce, gasPrice: wct.gasPrice, gas: wct.gas, value: wct.value, data: wct.data)
Agent.shared.showApprove(transaction: transaction, peerMeta: peer) { [weak self, weak interactor] transaction in
if let transaction = transaction {
self?.sendTransaction(transaction, address: address, requestId: id, interactor: interactor)
self?.sendTransaction(transaction, walletId: walletId, requestId: id, interactor: interactor)
} else {
self?.rejectRequest(id: id, interactor: interactor, message: "Cancelled")
}
}
}
private func approveSign(id: Int64, payload: WCEthereumSignPayload, address: String, interactor: WCInteractor?) {
private func approveSign(id: Int64, payload: WCEthereumSignPayload, walletId: String, interactor: WCInteractor?) {
var message: String?
let title: String
switch payload {
@ -161,7 +162,7 @@ class WalletConnect {
let peer = getPeerOfInteractor(interactor)
Agent.shared.showApprove(title: title, meta: message ?? "", peerMeta: peer) { [weak self, weak interactor] approved in
if approved {
self?.sign(id: id, message: message, payload: payload, address: address, interactor: interactor)
self?.sign(id: id, message: message, payload: payload, walletId: walletId, interactor: interactor)
} else {
self?.rejectRequest(id: id, interactor: interactor, message: "Cancelled")
}
@ -172,31 +173,31 @@ class WalletConnect {
interactor?.rejectRequest(id: id, message: message).cauterize()
}
private func sendTransaction(_ transaction: Transaction, address: String, requestId: Int64, interactor: WCInteractor?) {
guard let account = accountsService.getAccountForAddress(address) else {
private func sendTransaction(_ transaction: Transaction, walletId: String, requestId: Int64, interactor: WCInteractor?) {
guard let wallet = walletsManager.getWallet(id: walletId) else {
rejectRequest(id: requestId, interactor: interactor, message: "Something went wrong.")
return
}
guard let hash = try? ethereum.send(transaction: transaction, account: account) else {
guard let hash = try? ethereum.send(transaction: transaction, wallet: wallet) else {
rejectRequest(id: requestId, interactor: interactor, message: "Failed to send")
return
}
interactor?.approveRequest(id: requestId, result: hash).cauterize()
}
private func sign(id: Int64, message: String?, payload: WCEthereumSignPayload, address: String, interactor: WCInteractor?) {
guard let message = message, let account = accountsService.getAccountForAddress(address) else {
private func sign(id: Int64, message: String?, payload: WCEthereumSignPayload, walletId: String, interactor: WCInteractor?) {
guard let message = message, let wallet = walletsManager.getWallet(id: walletId) else {
rejectRequest(id: id, interactor: interactor, message: "Something went wrong.")
return
}
var signed: String?
switch payload {
case .personalSign:
signed = try? ethereum.signPersonal(message: message, account: account)
signed = try? ethereum.signPersonal(message: message, wallet: wallet)
case .signTypeData:
signed = try? ethereum.sign(typedData: message, account: account)
signed = try? ethereum.sign(typedData: message, wallet: wallet)
case .sign:
signed = try? ethereum.sign(message: message, account: account)
signed = try? ethereum.sign(message: message, wallet: wallet)
}
guard let result = signed else {
rejectRequest(id: id, interactor: interactor, message: "Something went wrong.")

View File

@ -0,0 +1,66 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
// Rewrite of Wallet.swift from Trust Wallet Core.
import Foundation
import WalletCore
final class InkWallet: Hashable, Equatable {
let id: String
var key: StoredKey
var accounts: [Account] {
return (0..<key.accountCount).compactMap({ key.account(index: $0) })
}
init(id: String, key: StoredKey) {
self.id = id
self.key = key
}
func getAccount(password: String, coin: CoinType) throws -> Account {
let wallet = key.wallet(password: Data(password.utf8))
guard let account = key.accountForCoin(coin: coin, wallet: wallet) else { throw KeyStore.Error.invalidPassword }
return account
}
func getAccounts(password: String, coins: [CoinType]) throws -> [Account] {
guard let wallet = key.wallet(password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
return coins.compactMap({ key.accountForCoin(coin: $0, wallet: wallet) })
}
func privateKey(password: String, coin: CoinType) throws -> PrivateKey {
guard let privateKey = key.privateKey(coin: coin, password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
return privateKey
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: InkWallet, rhs: InkWallet) -> Bool {
return lhs.id == rhs.id
}
}
extension InkWallet {
var ethereumAddress: String? {
return accounts.first(where: { $0.coin == .ethereum })?.address
}
var ethereumPrivateKey: PrivateKey? {
guard let password = Keychain.shared.password else { return nil }
return try? privateKey(password: password, coin: .ethereum)
}
var ethereumPrivateKeyString: String? {
return ethereumPrivateKey?.data.hexString
}
var isMnemonic: Bool {
return key.isMnemonic
}
}

View File

@ -0,0 +1,195 @@
// Copyright © 2021 Encrypted Ink. All rights reserved.
// Rewrite of KeyStore.swift from Trust Wallet Core.
import Foundation
import WalletCore
final class WalletsManager {
enum Error: Swift.Error {
case keychainAccessFailure
case invalidInput
}
enum InputValidationResult {
case valid, invalid, requiresPassword
}
static let shared = WalletsManager()
private let keychain = Keychain.shared
private(set) var wallets = [InkWallet]()
private init() {}
func start() {
try? load()
try? migrateFromLegacyIfNeeded()
}
func validateWalletInput(_ input: String) -> InputValidationResult {
if Mnemonic.isValid(mnemonic: input) {
return .valid
} else if let data = Data(hexString: input) {
return PrivateKey.isValid(data: data, curve: CoinType.ethereum.curve) ? .valid : .invalid
} else {
return input.maybeJSON ? .requiresPassword : .invalid
}
}
func createWallet() throws -> InkWallet {
guard let password = keychain.password else { throw Error.keychainAccessFailure }
return try createWallet(name: defaultWalletName, password: password, coin: .ethereum)
}
func getWallet(id: String) -> InkWallet? {
return wallets.first(where: { $0.id == id })
}
func addWallet(input: String, inputPassword: String?) throws -> InkWallet {
guard let password = keychain.password else { throw Error.keychainAccessFailure }
let name = defaultWalletName
let coin = CoinType.ethereum
if Mnemonic.isValid(mnemonic: input) {
return try importMnemonic(input, name: name, encryptPassword: password, coin: coin)
} else if let data = Data(hexString: input), PrivateKey.isValid(data: data, curve: coin.curve), let privateKey = PrivateKey(data: data) {
return try importPrivateKey(privateKey, name: name, password: password, coin: coin)
} else if input.maybeJSON, let inputPassword = inputPassword, let json = input.data(using: .utf8) {
return try importJSON(json, name: name, password: inputPassword, newPassword: password, coin: coin)
} else {
throw Error.invalidInput
}
}
private func createWallet(name: String, password: String, coin: CoinType) throws -> InkWallet {
let key = StoredKey(name: name, password: Data(password.utf8))
let id = makeNewWalletId()
let wallet = InkWallet(id: id, key: key)
_ = try wallet.getAccount(password: password, coin: coin)
wallets.append(wallet)
try save(wallet: wallet)
return wallet
}
private func importJSON(_ json: Data, name: String, password: String, newPassword: String, coin: CoinType) throws -> InkWallet {
guard let key = StoredKey.importJSON(json: json) else { throw KeyStore.Error.invalidKey }
guard let data = key.decryptPrivateKey(password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
if let mnemonic = checkMnemonic(data) { return try self.importMnemonic(mnemonic, name: name, encryptPassword: newPassword, coin: coin) }
guard let privateKey = PrivateKey(data: data) else { throw KeyStore.Error.invalidKey }
return try self.importPrivateKey(privateKey, name: name, password: newPassword, coin: coin)
}
private func checkMnemonic(_ data: Data) -> String? {
guard let mnemonic = String(data: data, encoding: .ascii), Mnemonic.isValid(mnemonic: mnemonic) else { return nil }
return mnemonic
}
private func importPrivateKey(_ privateKey: PrivateKey, name: String, password: String, coin: CoinType) throws -> InkWallet {
guard let newKey = StoredKey.importPrivateKey(privateKey: privateKey.data, name: name, password: Data(password.utf8), coin: coin) else { throw KeyStore.Error.invalidKey }
let id = makeNewWalletId()
let wallet = InkWallet(id: id, key: newKey)
_ = try wallet.getAccount(password: password, coin: coin)
wallets.append(wallet)
try save(wallet: wallet)
return wallet
}
private func importMnemonic(_ mnemonic: String, name: String, encryptPassword: String, coin: CoinType) throws -> InkWallet {
guard let key = StoredKey.importHDWallet(mnemonic: mnemonic, name: name, password: Data(encryptPassword.utf8), coin: coin) else { throw KeyStore.Error.invalidMnemonic }
let id = makeNewWalletId()
let wallet = InkWallet(id: id, key: key)
_ = try wallet.getAccount(password: encryptPassword, coin: coin)
wallets.append(wallet)
try save(wallet: wallet)
return wallet
}
func exportPrivateKey(wallet: InkWallet) throws -> Data {
guard let password = keychain.password else { throw Error.keychainAccessFailure }
guard let key = wallet.key.decryptPrivateKey(password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
return key
}
func exportMnemonic(wallet: InkWallet) throws -> String {
guard let password = keychain.password else { throw Error.keychainAccessFailure }
guard let mnemonic = wallet.key.decryptMnemonic(password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
return mnemonic
}
func update(wallet: InkWallet, password: String, newPassword: String) throws {
try update(wallet: wallet, password: password, newPassword: newPassword, newName: wallet.key.name)
}
func update(wallet: InkWallet, password: String, newName: String) throws {
try update(wallet: wallet, password: password, newPassword: password, newName: newName)
}
func delete(wallet: InkWallet) throws {
guard let password = keychain.password else { throw Error.keychainAccessFailure }
guard let index = wallets.firstIndex(of: wallet) else { throw KeyStore.Error.accountNotFound }
guard var privateKey = wallet.key.decryptPrivateKey(password: Data(password.utf8)) else { throw KeyStore.Error.invalidKey }
defer { privateKey.resetBytes(in: 0..<privateKey.count) }
wallets.remove(at: index)
try keychain.removeWallet(id: wallet.id)
}
func destroy() throws {
wallets.removeAll(keepingCapacity: false)
try keychain.removeAllWallets()
}
private func load() throws {
let ids = keychain.getAllWalletsIds()
for id in ids {
guard let data = keychain.getWalletData(id: id), let key = StoredKey.importJSON(json: data) else { continue }
let wallet = InkWallet(id: id, key: key)
wallets.append(wallet)
}
}
private func migrateFromLegacyIfNeeded() throws {
let legacyAccountsWithKeys = try keychain.getLegacyAccounts()
guard !legacyAccountsWithKeys.isEmpty, let password = keychain.password else { return }
for legacyAccount in legacyAccountsWithKeys {
if let data = Data(hexString: legacyAccount.privateKey), let privateKey = PrivateKey(data: data) {
_ = try importPrivateKey(privateKey, name: defaultWalletName, password: password, coin: .ethereum)
}
}
try keychain.removeLegacyAccounts()
}
private func update(wallet: InkWallet, password: String, newPassword: String, newName: String) throws {
guard let index = wallets.firstIndex(of: wallet) else { throw KeyStore.Error.accountNotFound }
guard var privateKeyData = wallet.key.decryptPrivateKey(password: Data(password.utf8)) else { throw KeyStore.Error.invalidPassword }
defer { privateKeyData.resetBytes(in: 0 ..< privateKeyData.count) }
let coins = wallet.accounts.map({ $0.coin })
guard !coins.isEmpty else { throw KeyStore.Error.accountNotFound }
if let mnemonic = checkMnemonic(privateKeyData),
let key = StoredKey.importHDWallet(mnemonic: mnemonic, name: newName, password: Data(newPassword.utf8), coin: coins[0]) {
wallets[index].key = key
} else if let key = StoredKey.importPrivateKey(
privateKey: privateKeyData, name: newName, password: Data(newPassword.utf8), coin: coins[0]) {
wallets[index].key = key
} else {
throw KeyStore.Error.invalidKey
}
_ = try wallets[index].getAccounts(password: newPassword, coins: coins)
try save(wallet: wallets[index])
}
private func save(wallet: InkWallet) throws {
guard let data = wallet.key.exportJSON() else { throw KeyStore.Error.invalidPassword }
try keychain.saveWallet(id: wallet.id, data: data)
}
private var defaultWalletName = ""
private func makeNewWalletId() -> String {
let uuid = UUID().uuidString
let date = Date().timeIntervalSince1970
let walletId = "\(uuid)-\(date)"
return walletId
}
}

View File

@ -5,7 +5,7 @@ target 'Encrypted Ink' do
use_frameworks!
pod 'Web3Swift.io', :git => 'https://github.com/zeriontech/Web3Swift.git', :branch => 'develop'
pod 'BlockiesSwift'
pod 'Kingfisher'
pod 'Kingfisher', '~> 5.0'
pod 'WalletConnect', git: 'https://github.com/grachyov/wallet-connect-swift', branch: 'master'
pod 'TrustWalletCore'
end

View File

@ -2,7 +2,9 @@ PODS:
- BigInt (5.2.0)
- BlockiesSwift (0.1.2)
- CryptoSwift (1.4.1)
- Kingfisher (6.3.0)
- Kingfisher (5.15.8):
- Kingfisher/Core (= 5.15.8)
- Kingfisher/Core (5.15.8)
- PromiseKit (6.15.3):
- PromiseKit/CorePromise (= 6.15.3)
- PromiseKit/Foundation (= 6.15.3)
@ -32,7 +34,7 @@ PODS:
DEPENDENCIES:
- BlockiesSwift
- Kingfisher
- Kingfisher (~> 5.0)
- TrustWalletCore
- WalletConnect (from `https://github.com/grachyov/wallet-connect-swift`, branch `master`)
- Web3Swift.io (from `https://github.com/zeriontech/Web3Swift.git`, branch `develop`)
@ -70,7 +72,7 @@ SPEC CHECKSUMS:
BigInt: f668a80089607f521586bbe29513d708491ef2f7
BlockiesSwift: 22d8d56dd187e6bfd16cb8c8fbd4fd4896c3e65d
CryptoSwift: 0bc800a7e6a24c4fc9ebeab97d44b0d5f73a78bd
Kingfisher: 6c3df386db71d82c0817a429d2c9421a77396529
Kingfisher: a3c03d702433fa6cfedabb2bddbe076fb8f2e902
PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
@ -80,6 +82,6 @@ SPEC CHECKSUMS:
WalletConnect: 1df75d4355b1cacfc27d7ef2416fae43862d0eb4
Web3Swift.io: 18fd06aed9d56df9c704f9c6f87b06675bb05b53
PODFILE CHECKSUM: 435285424c0821db925918dfd84843c031999350
PODFILE CHECKSUM: c3841a90049661c002c630eeb332566c663ce315
COCOAPODS: 1.10.1
COCOAPODS: 1.10.0