Merge pull request #2 from zcash/app-navigation-scaffold
App navigation scaffold
48
README.md
|
@ -1 +1,47 @@
|
|||
# secant-ios-wallet
|
||||
# secant-ios-wallet
|
||||
|
||||
This wallet is a Dogfooding effort towards Zcash Halo Arc / NU5 efforts.
|
||||
|
||||
# Motivation
|
||||
Dogfooding - _transitive verb_ - is the practice of an organization using its own product. This app was created to help us learn.
|
||||
|
||||
Please take note: the wallet is not an official product by ECC, but rather a tool for learning about our libraries that it is built on. This means that we do not have robust infrastructure or user support for this application. We open sourced it as a resource to make wallet development easier for the Zcash ecosystem.
|
||||
|
||||
# Disclaimers
|
||||
There are some known areas for improvement:
|
||||
|
||||
- This app is mainly intended for learning and improving the related libraries that it uses. There may be bugs.
|
||||
- Traffic analysis, like in other cryptocurrency wallets, can leak some privacy of the user.
|
||||
- The wallet requires a trust in the server to display accurate transaction information.
|
||||
|
||||
See the [Wallet App Threat Model](https://zcash.readthedocs.io/en/latest/rtd_pages/wallet_threat_model.html)
|
||||
for more information about the security and privacy limitations of the wallet.
|
||||
|
||||
If you'd like to sign up to help us test, reach out on discord and let us know! We're always happy to get feedback!
|
||||
|
||||
# Description
|
||||
|
||||
iOS wallet using the Zcash iOS SDK that is maintained by core developers.
|
||||
|
||||
This a reference wallet for the following set of features:
|
||||
- z2z transactions w/ encrypted memos
|
||||
- reply-to formatted memos
|
||||
- z2t transactions
|
||||
- transparent receive-only
|
||||
- autoshielding on threshold from receive only t-address
|
||||
|
||||
note: z means sapling shielded addresses.
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions are very much welcomed! Please read our [Contributing Guidelines](/CONTRIBUTING.md) and [Code of Conduct](/CONDUCT.md). Our backlog has many Issues tagged with the `good first issue` label. Please fork the repo and make a pull request for us to review.
|
||||
|
||||
# Reporting an issue
|
||||
|
||||
If you wish to report a security issue, please follow our [Responsible Disclosure guidelines](https://github.com/zcash/ZcashLightClientKit/blob/master/responsible_disclosure.md).
|
||||
|
||||
For other kind of inquiries, feel welcome to open an Issue if you encounter a bug or would like to request a feature.
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
|
@ -7,12 +7,44 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0D170A7226BC802800EB6A46 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D170A7126BC802800EB6A46 /* Router.swift */; };
|
||||
0D1922EA26BDD96A00052649 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922E926BDD96A00052649 /* ViewModel.swift */; };
|
||||
0D1922ED26BDE0C600052649 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922EC26BDE0C600052649 /* AppRouter.swift */; };
|
||||
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */; };
|
||||
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1922F726BDEB3500052649 /* MockServices.swift */; };
|
||||
0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0D2ACE7F26C2C67100D62E3C /* Zboto.otf */; };
|
||||
0D2ACE8326C2C8B400D62E3C /* SwiftUI+secant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D2ACE8226C2C8B400D62E3C /* SwiftUI+secant.swift */; };
|
||||
0D32281926C5864B00262533 /* ProfileScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281726C5864B00262533 /* ProfileScreen.swift */; };
|
||||
0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281826C5864B00262533 /* ProfileScreenViewModel.swift */; };
|
||||
0D32281E26C5867D00262533 /* ScanQrScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281C26C5867D00262533 /* ScanQrScreen.swift */; };
|
||||
0D32281F26C5867D00262533 /* ScanQrScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32281D26C5867D00262533 /* ScanQrScreenViewModel.swift */; };
|
||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282126C586A800262533 /* HistoryScreen.swift */; };
|
||||
0D32282426C586A800262533 /* HistoryScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282226C586A800262533 /* HistoryScreenViewModel.swift */; };
|
||||
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282626C586E000262533 /* RequestZcashScreen.swift */; };
|
||||
0D32282926C586E000262533 /* RequestZcashScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282726C586E000262533 /* RequestZcashScreenViewModel.swift */; };
|
||||
0D32282D26C5870B00262533 /* SendScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282B26C5870B00262533 /* SendScreen.swift */; };
|
||||
0D32282E26C5870B00262533 /* SendScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32282C26C5870B00262533 /* SendScreenViewModel.swift */; };
|
||||
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32283026C5877A00262533 /* BalanceScreen.swift */; };
|
||||
0D32283326C5877A00262533 /* BalanceScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D32283126C5877A00262533 /* BalanceScreenViewModel.swift */; };
|
||||
0D354A0926D5A9D000315F45 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0626D5A9D000315F45 /* Services.swift */; };
|
||||
0D354A0A26D5A9D000315F45 /* KeyStoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0726D5A9D000315F45 /* KeyStoring.swift */; };
|
||||
0D354A0B26D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */; };
|
||||
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0826B364170058B01E /* SecantApp.swift */; };
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0A26B364170058B01E /* ContentView.swift */; };
|
||||
0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0C26B364180058B01E /* Assets.xcassets */; };
|
||||
0D4E7A1026B364180058B01E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D4E7A0F26B364180058B01E /* Preview Assets.xcassets */; };
|
||||
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A1A26B364180058B01E /* secantTests.swift */; };
|
||||
0D4E7A2626B364180058B01E /* secantUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A2526B364180058B01E /* secantUITests.swift */; };
|
||||
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C8D26C15D1D00E3B610 /* WelcomeScreen.swift */; };
|
||||
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C8E26C15D1D00E3B610 /* WelcomeScreenViewModel.swift */; };
|
||||
0DA13C9326C15E2F00E3B610 /* PlainButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9226C15E2F00E3B610 /* PlainButton.swift */; };
|
||||
0DA13C9726C186FF00E3B610 /* RestoreWalletScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9526C186FF00E3B610 /* RestoreWalletScreen.swift */; };
|
||||
0DA13C9826C186FF00E3B610 /* RestoreWalletScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9626C186FF00E3B610 /* RestoreWalletScreenViewModel.swift */; };
|
||||
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9A26C1942100E3B610 /* BackupWalletScreen.swift */; };
|
||||
0DA13C9D26C1942100E3B610 /* BackupWalletScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9B26C1942100E3B610 /* BackupWalletScreenViewModel.swift */; };
|
||||
0DA13CA126C1955600E3B610 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13C9F26C1955600E3B610 /* HomeScreen.swift */; };
|
||||
0DA13CA226C1955600E3B610 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13CA026C1955600E3B610 /* HomeScreenViewModel.swift */; };
|
||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA13CA426C1963000E3B610 /* Balance.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -33,7 +65,29 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0D4E7A0526B364170058B01E /* secant.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = secant.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D170A7126BC802800EB6A46 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
||||
0D1922E926BDD96A00052649 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
|
||||
0D1922EC26BDE0C600052649 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||
0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashSDKStubs.swift; sourceTree = "<group>"; };
|
||||
0D1922F726BDEB3500052649 /* MockServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServices.swift; sourceTree = "<group>"; };
|
||||
0D2ACE7F26C2C67100D62E3C /* Zboto.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Zboto.otf; sourceTree = "<group>"; };
|
||||
0D2ACE8226C2C8B400D62E3C /* SwiftUI+secant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUI+secant.swift"; sourceTree = "<group>"; };
|
||||
0D32281726C5864B00262533 /* ProfileScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileScreen.swift; sourceTree = "<group>"; };
|
||||
0D32281826C5864B00262533 /* ProfileScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D32281C26C5867D00262533 /* ScanQrScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQrScreen.swift; sourceTree = "<group>"; };
|
||||
0D32281D26C5867D00262533 /* ScanQrScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQrScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D32282126C586A800262533 /* HistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreen.swift; sourceTree = "<group>"; };
|
||||
0D32282226C586A800262533 /* HistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D32282626C586E000262533 /* RequestZcashScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestZcashScreen.swift; sourceTree = "<group>"; };
|
||||
0D32282726C586E000262533 /* RequestZcashScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestZcashScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D32282B26C5870B00262533 /* SendScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendScreen.swift; sourceTree = "<group>"; };
|
||||
0D32282C26C5870B00262533 /* SendScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D32283026C5877A00262533 /* BalanceScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceScreen.swift; sourceTree = "<group>"; };
|
||||
0D32283126C5877A00262533 /* BalanceScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0D354A0626D5A9D000315F45 /* Services.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = "<group>"; };
|
||||
0D354A0726D5A9D000315F45 /* KeyStoring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyStoring.swift; sourceTree = "<group>"; };
|
||||
0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MnemonicSeedPhraseHandling.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0526B364170058B01E /* secant-testnet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "secant-testnet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantApp.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
0D4E7A0C26B364180058B01E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
|
@ -45,6 +99,16 @@
|
|||
0D4E7A2126B364180058B01E /* secantUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = secantUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0D4E7A2526B364180058B01E /* secantUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = secantUITests.swift; sourceTree = "<group>"; };
|
||||
0D4E7A2726B364180058B01E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0DA13C8D26C15D1D00E3B610 /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
|
||||
0DA13C8E26C15D1D00E3B610 /* WelcomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0DA13C9226C15E2F00E3B610 /* PlainButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainButton.swift; sourceTree = "<group>"; };
|
||||
0DA13C9526C186FF00E3B610 /* RestoreWalletScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreWalletScreen.swift; sourceTree = "<group>"; };
|
||||
0DA13C9626C186FF00E3B610 /* RestoreWalletScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreWalletScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0DA13C9A26C1942100E3B610 /* BackupWalletScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupWalletScreen.swift; sourceTree = "<group>"; };
|
||||
0DA13C9B26C1942100E3B610 /* BackupWalletScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupWalletScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0DA13C9F26C1955600E3B610 /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
|
||||
0DA13CA026C1955600E3B610 /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
0DA13CA426C1963000E3B610 /* Balance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Balance.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -72,6 +136,129 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0D170A7326BC802E00EB6A46 /* Routers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D1922EC26BDE0C600052649 /* AppRouter.swift */,
|
||||
);
|
||||
path = Routers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D170A7426BC9B7500EB6A46 /* Mocked Dependencies */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D354A0726D5A9D000315F45 /* KeyStoring.swift */,
|
||||
0D354A0826D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift */,
|
||||
0D354A0626D5A9D000315F45 /* Services.swift */,
|
||||
);
|
||||
path = "Mocked Dependencies";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D1922E826BDD95000052649 /* Base */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D170A7126BC802800EB6A46 /* Router.swift */,
|
||||
0D1922E926BDD96A00052649 /* ViewModel.swift */,
|
||||
);
|
||||
path = Base;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D1922EB26BDD9A500052649 /* Screens */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32282F26C5874B00262533 /* Balance */,
|
||||
0D32282A26C586E700262533 /* Send ZEC */,
|
||||
0D32282526C586C600262533 /* Request ZEC */,
|
||||
0D32282026C5868F00262533 /* History */,
|
||||
0D32281B26C5865900262533 /* Scan */,
|
||||
0D32281526C5862400262533 /* Profile */,
|
||||
0DA13C9E26C1953F00E3B610 /* Home */,
|
||||
0DA13C9926C193FE00E3B610 /* Backup Wallet */,
|
||||
0DA13C9426C186B100E3B610 /* Restore Wallet */,
|
||||
0DA13C8C26C15CBE00E3B610 /* Welcome Screen */,
|
||||
);
|
||||
path = Screens;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D1922F026BDE27D00052649 /* Stubs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D1922F126BDE29300052649 /* ZcashSDKStubs.swift */,
|
||||
0D1922F726BDEB3500052649 /* MockServices.swift */,
|
||||
);
|
||||
path = Stubs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D2ACE7E26C2C65E00D62E3C /* Fonts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D2ACE7F26C2C67100D62E3C /* Zboto.otf */,
|
||||
);
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D2ACE8126C2C71100D62E3C /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D2ACE8226C2C8B400D62E3C /* SwiftUI+secant.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32281526C5862400262533 /* Profile */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32281726C5864B00262533 /* ProfileScreen.swift */,
|
||||
0D32281826C5864B00262533 /* ProfileScreenViewModel.swift */,
|
||||
);
|
||||
path = Profile;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32281B26C5865900262533 /* Scan */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32281C26C5867D00262533 /* ScanQrScreen.swift */,
|
||||
0D32281D26C5867D00262533 /* ScanQrScreenViewModel.swift */,
|
||||
);
|
||||
path = Scan;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32282026C5868F00262533 /* History */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32282126C586A800262533 /* HistoryScreen.swift */,
|
||||
0D32282226C586A800262533 /* HistoryScreenViewModel.swift */,
|
||||
);
|
||||
path = History;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32282526C586C600262533 /* Request ZEC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32282626C586E000262533 /* RequestZcashScreen.swift */,
|
||||
0D32282726C586E000262533 /* RequestZcashScreenViewModel.swift */,
|
||||
);
|
||||
path = "Request ZEC";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32282A26C586E700262533 /* Send ZEC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32282B26C5870B00262533 /* SendScreen.swift */,
|
||||
0D32282C26C5870B00262533 /* SendScreenViewModel.swift */,
|
||||
);
|
||||
path = "Send ZEC";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D32282F26C5874B00262533 /* Balance */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D32283026C5877A00262533 /* BalanceScreen.swift */,
|
||||
0D32283126C5877A00262533 /* BalanceScreenViewModel.swift */,
|
||||
);
|
||||
path = Balance;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D4E79FC26B364170058B01E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -85,7 +272,7 @@
|
|||
0D4E7A0626B364170058B01E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D4E7A0526B364170058B01E /* secant.app */,
|
||||
0D4E7A0526B364170058B01E /* secant-testnet.app */,
|
||||
0D4E7A1626B364180058B01E /* secantTests.xctest */,
|
||||
0D4E7A2126B364180058B01E /* secantUITests.xctest */,
|
||||
);
|
||||
|
@ -95,11 +282,20 @@
|
|||
0D4E7A0726B364170058B01E /* secant */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D2ACE8126C2C71100D62E3C /* Utilities */,
|
||||
0D2ACE7E26C2C65E00D62E3C /* Fonts */,
|
||||
0DA13CA326C1960A00E3B610 /* Models */,
|
||||
0DA13C9126C15E1900E3B610 /* UI Components */,
|
||||
0D1922F026BDE27D00052649 /* Stubs */,
|
||||
0D1922EB26BDD9A500052649 /* Screens */,
|
||||
0D1922E826BDD95000052649 /* Base */,
|
||||
0D170A7426BC9B7500EB6A46 /* Mocked Dependencies */,
|
||||
0D4E7A0826B364170058B01E /* SecantApp.swift */,
|
||||
0D4E7A0A26B364170058B01E /* ContentView.swift */,
|
||||
0D4E7A0C26B364180058B01E /* Assets.xcassets */,
|
||||
0D4E7A1126B364180058B01E /* Info.plist */,
|
||||
0D4E7A0E26B364180058B01E /* Preview Content */,
|
||||
0D170A7326BC802E00EB6A46 /* Routers */,
|
||||
);
|
||||
path = secant;
|
||||
sourceTree = "<group>";
|
||||
|
@ -130,12 +326,64 @@
|
|||
path = secantUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C8C26C15CBE00E3B610 /* Welcome Screen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13C8D26C15D1D00E3B610 /* WelcomeScreen.swift */,
|
||||
0DA13C8E26C15D1D00E3B610 /* WelcomeScreenViewModel.swift */,
|
||||
);
|
||||
path = "Welcome Screen";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9126C15E1900E3B610 /* UI Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13C9226C15E2F00E3B610 /* PlainButton.swift */,
|
||||
);
|
||||
path = "UI Components";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9426C186B100E3B610 /* Restore Wallet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13C9526C186FF00E3B610 /* RestoreWalletScreen.swift */,
|
||||
0DA13C9626C186FF00E3B610 /* RestoreWalletScreenViewModel.swift */,
|
||||
);
|
||||
path = "Restore Wallet";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9926C193FE00E3B610 /* Backup Wallet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13C9A26C1942100E3B610 /* BackupWalletScreen.swift */,
|
||||
0DA13C9B26C1942100E3B610 /* BackupWalletScreenViewModel.swift */,
|
||||
);
|
||||
path = "Backup Wallet";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13C9E26C1953F00E3B610 /* Home */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13C9F26C1955600E3B610 /* HomeScreen.swift */,
|
||||
0DA13CA026C1955600E3B610 /* HomeScreenViewModel.swift */,
|
||||
);
|
||||
path = Home;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0DA13CA326C1960A00E3B610 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA13CA426C1963000E3B610 /* Balance.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
0D4E7A0426B364170058B01E /* secant */ = {
|
||||
0D4E7A0426B364170058B01E /* secant-testnet */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0D4E7A2A26B364180058B01E /* Build configuration list for PBXNativeTarget "secant" */;
|
||||
buildConfigurationList = 0D4E7A2A26B364180058B01E /* Build configuration list for PBXNativeTarget "secant-testnet" */;
|
||||
buildPhases = (
|
||||
0D4E7A0126B364170058B01E /* Sources */,
|
||||
0D4E7A0226B364170058B01E /* Frameworks */,
|
||||
|
@ -145,9 +393,9 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = secant;
|
||||
name = "secant-testnet";
|
||||
productName = secant;
|
||||
productReference = 0D4E7A0526B364170058B01E /* secant.app */;
|
||||
productReference = 0D4E7A0526B364170058B01E /* secant-testnet.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
0D4E7A1526B364180058B01E /* secantTests */ = {
|
||||
|
@ -221,7 +469,7 @@
|
|||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0D4E7A0426B364170058B01E /* secant */,
|
||||
0D4E7A0426B364170058B01E /* secant-testnet */,
|
||||
0D4E7A1526B364180058B01E /* secantTests */,
|
||||
0D4E7A2026B364180058B01E /* secantUITests */,
|
||||
);
|
||||
|
@ -235,6 +483,7 @@
|
|||
files = (
|
||||
0D4E7A1026B364180058B01E /* Preview Assets.xcassets in Resources */,
|
||||
0D4E7A0D26B364180058B01E /* Assets.xcassets in Resources */,
|
||||
0D2ACE8026C2C67100D62E3C /* Zboto.otf in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -259,8 +508,39 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0D32281F26C5867D00262533 /* ScanQrScreenViewModel.swift in Sources */,
|
||||
0D32282E26C5870B00262533 /* SendScreenViewModel.swift in Sources */,
|
||||
0D32282D26C5870B00262533 /* SendScreen.swift in Sources */,
|
||||
0D1922ED26BDE0C600052649 /* AppRouter.swift in Sources */,
|
||||
0D1922F226BDE29300052649 /* ZcashSDKStubs.swift in Sources */,
|
||||
0DA13C9D26C1942100E3B610 /* BackupWalletScreenViewModel.swift in Sources */,
|
||||
0D354A0B26D5A9D000315F45 /* MnemonicSeedPhraseHandling.swift in Sources */,
|
||||
0D32281E26C5867D00262533 /* ScanQrScreen.swift in Sources */,
|
||||
0DA13C9C26C1942100E3B610 /* BackupWalletScreen.swift in Sources */,
|
||||
0DA13C9826C186FF00E3B610 /* RestoreWalletScreenViewModel.swift in Sources */,
|
||||
0D32283326C5877A00262533 /* BalanceScreenViewModel.swift in Sources */,
|
||||
0D32282326C586A800262533 /* HistoryScreen.swift in Sources */,
|
||||
0DA13CA526C1963000E3B610 /* Balance.swift in Sources */,
|
||||
0D1922F826BDEB3500052649 /* MockServices.swift in Sources */,
|
||||
0D4E7A0B26B364170058B01E /* ContentView.swift in Sources */,
|
||||
0D170A7226BC802800EB6A46 /* Router.swift in Sources */,
|
||||
0D354A0926D5A9D000315F45 /* Services.swift in Sources */,
|
||||
0DA13C9726C186FF00E3B610 /* RestoreWalletScreen.swift in Sources */,
|
||||
0D1922EA26BDD96A00052649 /* ViewModel.swift in Sources */,
|
||||
0D4E7A0926B364170058B01E /* SecantApp.swift in Sources */,
|
||||
0D32281A26C5864B00262533 /* ProfileScreenViewModel.swift in Sources */,
|
||||
0DA13CA126C1955600E3B610 /* HomeScreen.swift in Sources */,
|
||||
0DA13C9026C15D1D00E3B610 /* WelcomeScreenViewModel.swift in Sources */,
|
||||
0DA13C8F26C15D1D00E3B610 /* WelcomeScreen.swift in Sources */,
|
||||
0DA13C9326C15E2F00E3B610 /* PlainButton.swift in Sources */,
|
||||
0D32282826C586E000262533 /* RequestZcashScreen.swift in Sources */,
|
||||
0D32283226C5877A00262533 /* BalanceScreen.swift in Sources */,
|
||||
0D354A0A26D5A9D000315F45 /* KeyStoring.swift in Sources */,
|
||||
0DA13CA226C1955600E3B610 /* HomeScreenViewModel.swift in Sources */,
|
||||
0D32282926C586E000262533 /* RequestZcashScreenViewModel.swift in Sources */,
|
||||
0D2ACE8326C2C8B400D62E3C /* SwiftUI+secant.swift in Sources */,
|
||||
0D32281926C5864B00262533 /* ProfileScreen.swift in Sources */,
|
||||
0D32282426C586A800262533 /* HistoryScreenViewModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -285,12 +565,12 @@
|
|||
/* Begin PBXTargetDependency section */
|
||||
0D4E7A1826B364180058B01E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0D4E7A0426B364170058B01E /* secant */;
|
||||
target = 0D4E7A0426B364170058B01E /* secant-testnet */;
|
||||
targetProxy = 0D4E7A1726B364180058B01E /* PBXContainerItemProxy */;
|
||||
};
|
||||
0D4E7A2326B364180058B01E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 0D4E7A0426B364170058B01E /* secant */;
|
||||
target = 0D4E7A0426B364170058B01E /* secant-testnet */;
|
||||
targetProxy = 0D4E7A2226B364180058B01E /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
@ -418,7 +698,9 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = RLPRR8CPQG;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = secant/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
|
@ -426,10 +708,11 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.secant;
|
||||
MARKETING_VERSION = 0.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "co.electriccoin.secant-testnet";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -439,7 +722,9 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = RLPRR8CPQG;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = secant/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
|
@ -447,10 +732,11 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.secant;
|
||||
MARKETING_VERSION = 0.0.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "co.electriccoin.secant-testnet";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -471,7 +757,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/secant.app/secant";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/secant-testnet.app/secant-testnet";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -492,7 +778,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/secant.app/secant";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/secant-testnet.app/secant-testnet";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
@ -546,7 +832,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0D4E7A2A26B364180058B01E /* Build configuration list for PBXNativeTarget "secant" */ = {
|
||||
0D4E7A2A26B364180058B01E /* Build configuration list for PBXNativeTarget "secant-testnet" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0D4E7A2B26B364180058B01E /* Debug */,
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 6.0 KiB |
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Router.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/5/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public protocol Router: ObservableObject {
|
||||
associatedtype ViewOutput: View
|
||||
|
||||
func rootView() -> ViewOutput
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// ViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
open class BaseViewModel<S> {
|
||||
|
||||
public var services: S
|
||||
|
||||
public init(services: S) {
|
||||
self.services = services
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,15 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>co.electriccoin.backgroundProcessingTask</string>
|
||||
<string>co.electriccoin.backgroundAppRefreshTask</string>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Secant Testnet</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -15,11 +22,15 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Zboto.otf</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
@ -27,6 +38,11 @@
|
|||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
</array>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
|
@ -36,8 +52,6 @@
|
|||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// KeyStoring.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
protocol KeyStoring {
|
||||
func importBirthday(_ height: BlockHeight) throws
|
||||
func exportBirthday() throws -> BlockHeight
|
||||
func importPhrase(bip39 phrase: String) throws
|
||||
func exportPhrase() throws -> String
|
||||
var keysPresent: Bool { get }
|
||||
/**
|
||||
Use carefully: Deletes the seed phrase from the keychain
|
||||
*/
|
||||
func nukePhrase()
|
||||
|
||||
/**
|
||||
Use carefully: deletes the wallet birthday from the keychain
|
||||
*/
|
||||
func nukeBirthday()
|
||||
|
||||
/**
|
||||
There's no fate but what we make for ourselves - Sarah Connor
|
||||
*/
|
||||
func nukeWallet()
|
||||
}
|
||||
|
||||
enum KeyStoringError : Error {
|
||||
case alreadyImported
|
||||
case uninitializedWallet
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// MnemonicSeedPhraseHandling.swift
|
||||
// wallet
|
||||
//
|
||||
// Created by Francisco Gindre on 2/28/20.
|
||||
// Copyright © 2020 Francisco Gindre. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
enum MnemonicError: Error {
|
||||
case invalidSeed
|
||||
case checksumFailed
|
||||
}
|
||||
|
||||
protocol MnemonicSeedPhraseHandling {
|
||||
/**
|
||||
random 24 words mnemonic phrase
|
||||
*/
|
||||
func randomMnemonic() throws -> String
|
||||
/**
|
||||
random 24 words mnemonic phrase as array of words
|
||||
*/
|
||||
func randomMnemonicWords() throws -> [String]
|
||||
|
||||
/**
|
||||
generate deterministic seed from mnemonic phrase
|
||||
*/
|
||||
func toSeed(mnemonic: String) throws -> [UInt8]
|
||||
|
||||
/**
|
||||
get this mnemonic
|
||||
*/
|
||||
func asWords(mnemonic: String) throws -> [String]
|
||||
|
||||
/**
|
||||
validates whether the given mnemonic is correct
|
||||
*/
|
||||
func isValid(mnemonic: String) throws
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Services.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol Services {
|
||||
var networkProvider: ZcashNetworkProvider { get }
|
||||
var seedHandler: MnemonicSeedPhraseHandling { get }
|
||||
var keyStorage: KeyStoring { get }
|
||||
}
|
||||
|
||||
|
||||
protocol ZcashNetworkProvider {
|
||||
func currentNetwork() -> ZcashNetwork
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// Balance.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
/**
|
||||
Funds Expressed in Zatoshis
|
||||
*/
|
||||
protocol Funds {
|
||||
/**
|
||||
Confirmed, spendable funds
|
||||
*/
|
||||
var confirmed: Int64 { get }
|
||||
/**
|
||||
Unconfirmed, not yet spendable funds.
|
||||
*/
|
||||
var unconfirmed: Int64 { get }
|
||||
|
||||
/**
|
||||
Represents a null value of Funds with Zero confirmed and Zero unconfirmed funds.
|
||||
*/
|
||||
static var noFunds: Funds { get}
|
||||
}
|
||||
|
||||
/**
|
||||
Wallet Balance that condenses transpant, Sapling and Orchard funds
|
||||
*/
|
||||
protocol WalletBalance {
|
||||
/**
|
||||
Transparent funds. This is the sum of the UTXOs of the user found at a given time
|
||||
*/
|
||||
var transaparent: Funds { get }
|
||||
/**
|
||||
Funds on the Sapling shielded pool for a given user.
|
||||
*/
|
||||
var sapling: Funds { get }
|
||||
/**
|
||||
Funds on the Orchard shielded pool for a given user.
|
||||
*/
|
||||
var orchard: Funds { get }
|
||||
/**
|
||||
The sum of all confirmed funds, transparent, sapling and orchard funds (calculated)
|
||||
*/
|
||||
var totalAvailableBalance: Int64 { get }
|
||||
|
||||
/**
|
||||
the sum of all unconfirmed funds: transparent, sapling, and orchard funds (calculated
|
||||
*/
|
||||
var totalUnconfirmedBalance: Int64 { get }
|
||||
|
||||
|
||||
/**
|
||||
the sum of all funds confirmed and unconfirmed of all pools (transparent, sapling and orchard).
|
||||
*/
|
||||
var totalBalance: Int64 { get }
|
||||
|
||||
/**
|
||||
represents a the value of Zero funds.
|
||||
*/
|
||||
static var nullBalance: WalletBalance { get }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Concrete Wallet Balance.
|
||||
*/
|
||||
struct Balance: WalletBalance {
|
||||
var transaparent: Funds
|
||||
var sapling: Funds
|
||||
var orchard: Funds
|
||||
}
|
||||
|
||||
struct ZcashFunds: Funds {
|
||||
static var noFunds: Funds {
|
||||
ZcashFunds(confirmed: 0, unconfirmed: 0)
|
||||
}
|
||||
var confirmed: Int64
|
||||
var unconfirmed: Int64
|
||||
|
||||
}
|
||||
|
||||
extension WalletBalance {
|
||||
static var nullBalance: WalletBalance {
|
||||
Balance(transaparent: ZcashFunds.noFunds, sapling: ZcashFunds.noFunds, orchard: ZcashFunds.noFunds)
|
||||
}
|
||||
|
||||
var totalAvailableBalance: Int64 {
|
||||
transaparent.confirmed + sapling.confirmed + orchard.confirmed
|
||||
}
|
||||
|
||||
var totalUnconfirmedBalance: Int64 {
|
||||
transaparent.unconfirmed + sapling.unconfirmed + orchard.unconfirmed
|
||||
}
|
||||
|
||||
var totalBalance: Int64 {
|
||||
totalAvailableBalance + totalUnconfirmedBalance
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// AppRouterRouter.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
enum AppRouterScreen {
|
||||
case appLoading
|
||||
case createRestoreWallet
|
||||
case home
|
||||
}
|
||||
|
||||
class AppRouter: Router {
|
||||
|
||||
// MARK: - Published vars
|
||||
@Published var screen: AppRouterScreen = .appLoading
|
||||
|
||||
// MARK: - Private vars
|
||||
|
||||
// MARK: - Internal vars
|
||||
var services: Services
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(services: Services) {
|
||||
self.services = services
|
||||
}
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
@ViewBuilder func rootView() -> some View {
|
||||
// Add your content here
|
||||
NavigationView {
|
||||
AppRouterView(router: self)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func createNew() -> some View {
|
||||
Text("Create New")
|
||||
}
|
||||
|
||||
@ViewBuilder func home() -> some View {
|
||||
Text("Home Screen")
|
||||
}
|
||||
|
||||
@ViewBuilder func loadingScreen() -> some View {
|
||||
Text("Loading")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct AppRouterView: View {
|
||||
@StateObject var router: AppRouter
|
||||
|
||||
@ViewBuilder func viewForScreen(_ screen: AppRouterScreen) -> some View {
|
||||
switch self.router.screen {
|
||||
case .appLoading:
|
||||
self.router.loadingScreen()
|
||||
case .createRestoreWallet:
|
||||
self.router.createNew()
|
||||
case .home:
|
||||
self.router.home()
|
||||
}
|
||||
}
|
||||
var body: some View {
|
||||
viewForScreen(router.screen)
|
||||
.onAppear() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
if router.services.keyStorage.keysPresent {
|
||||
router.screen = .home
|
||||
} else {
|
||||
router.screen = .createRestoreWallet
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// BackupWalletScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol BackupWalletScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct BackupWalletScreen: View {
|
||||
@State var router: BackupWalletScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: BackupWalletScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct BackupWalletScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BackupWalletScreen(viewModel: BackupWalletScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// BackupWalletScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class BackupWalletScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// BalanceScreenScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol BalanceScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct BalanceScreen: View {
|
||||
@State var router: BalanceScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: BalanceScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct BalanceScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BalanceScreen(viewModel: BalanceScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// BalanceScreenScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class BalanceScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// HistoryScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol HistoryScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct HistoryScreen: View {
|
||||
@State var router: HistoryScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: HistoryScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct HistoryScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HistoryScreen(viewModel: HistoryScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// HistoryScreenScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class HistoryScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// HomeScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol HomeScreenRouter: AnyObject {
|
||||
func homeScreenScanQrScreen() -> ScanQrScreen
|
||||
func homeScreenProfileScreen() -> ProfileScreen
|
||||
func homeScreenHistoryScreen() -> HistoryScreen
|
||||
func homeScreenBalanceScreen() -> BalanceScreen
|
||||
func homeScreenRequestScreen() -> RequestZcashScreen
|
||||
func homeScreenSendScreen() -> SendScreen
|
||||
}
|
||||
|
||||
struct HomeScreen: View {
|
||||
@State var router: HomeScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: HomeScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Hello, World!")
|
||||
sendButton
|
||||
requestButton
|
||||
historyButton
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.navigationBarTitle("", displayMode: .inline)
|
||||
.navigationBarItems(
|
||||
leading: qrCodeButton,
|
||||
trailing: profileButton)
|
||||
}
|
||||
|
||||
@ViewBuilder var qrCodeButton: some View {
|
||||
Button(action: {}, label: {
|
||||
Image(systemName: "qrcode.viewfinder")
|
||||
.frame(width: 20, height: 20, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
|
||||
})
|
||||
.contentShape(Circle())
|
||||
|
||||
}
|
||||
|
||||
@ViewBuilder var profileButton: some View {
|
||||
Button(action: {}, label: {
|
||||
Image(systemName: "person.crop.circle")
|
||||
.frame(width: 20, height: 20, alignment: .center)
|
||||
})
|
||||
.contentShape(Circle())
|
||||
}
|
||||
|
||||
@ViewBuilder var requestButton: some View {
|
||||
Button(action: {}, label: {
|
||||
Text("Request ZEC")
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
}
|
||||
|
||||
@ViewBuilder var sendButton: some View {
|
||||
Button(action: {}, label: {
|
||||
Text("Send ZEC")
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
}
|
||||
|
||||
@ViewBuilder var historyButton: some View {
|
||||
Button(action: {}, label: {
|
||||
Text("History")
|
||||
})
|
||||
.buttonStyle(PlainButton(style: .light))
|
||||
}
|
||||
}
|
||||
|
||||
struct HomeScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
HomeScreen(viewModel: HomeScreenViewModel.mockWithValues(services: MockServices(), status: .offline, balance: mockBalance, fiatConversion: 1.12453))
|
||||
}
|
||||
}
|
||||
|
||||
static var mockBalance: WalletBalance {
|
||||
Balance(transaparent: ZcashFunds.noFunds, sapling: ZcashFunds(confirmed: 123456790, unconfirmed: 0), orchard: ZcashFunds(confirmed: 0, unconfirmed: 0))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// HomeScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class HomeScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
enum Status {
|
||||
case syncing(progress: Float)
|
||||
case offline
|
||||
case error(error: Error)
|
||||
}
|
||||
|
||||
@Published var balance: WalletBalance = Balance.nullBalance
|
||||
|
||||
@Published var fiatConversion: Decimal = 0
|
||||
|
||||
@Published var status = Status.offline
|
||||
|
||||
}
|
||||
|
||||
extension HomeScreenViewModel {
|
||||
static func mockWithValues(services: Services,
|
||||
status: Status,
|
||||
balance: WalletBalance,
|
||||
fiatConversion: Decimal) -> HomeScreenViewModel {
|
||||
let vm = HomeScreenViewModel(services: services)
|
||||
vm.status = status
|
||||
vm.balance = balance
|
||||
vm.fiatConversion = fiatConversion
|
||||
return vm
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// ProfileScreenScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol ProfileScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct ProfileScreen: View {
|
||||
@State var router: ProfileScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: ProfileScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ProfileScreen(viewModel: ProfileScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// ProfileScreenScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class ProfileScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// RequestZcashScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol RequestZcashScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct RequestZcashScreen: View {
|
||||
@State var router: RequestZcashScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: RequestZcashScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct RequestZcashScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RequestZcashScreen(viewModel: RequestZcashScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// RequestZcashScreenScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class RequestZcashScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// RestoreWalletScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol RestoreWalletScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct RestoreWalletScreen: View {
|
||||
@State var router: RestoreWalletScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: RestoreWalletScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Enter Seed Phrase")
|
||||
|
||||
TextEditor(text: $viewModel.seedText)
|
||||
|
||||
Button(action: {}, label: {
|
||||
Text("Restore Seed Phrase")
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.padding(.vertical)
|
||||
.navigationBarTitle(Text("Restore Wallet"), displayMode: .inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct RestoreWalletScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RestoreWalletScreen(viewModel: RestoreWalletScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// RestoreWalletScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class RestoreWalletScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
@Published var seedText: String = ""
|
||||
|
||||
func restore() {}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// ScanQrScreenScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol ScanQrScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct ScanQrScreen: View {
|
||||
@State var router: ScanQrScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: ScanQrScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct ScanQrScreenScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ScanQrScreen(viewModel: ScanQrScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// ScanQrScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class ScanQrScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// SendScreenScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol SendScreenRouter: AnyObject {
|
||||
}
|
||||
|
||||
struct SendScreen: View {
|
||||
@State var router: SendScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: SendScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct SendScreenScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SendScreen(viewModel: SendScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// SendScreenScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/12/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class SendScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// CreateNewWalletScreen.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol WelcomeScreenRouter: AnyObject {
|
||||
|
||||
}
|
||||
|
||||
struct WelcomeScreen: View {
|
||||
@State var router: WelcomeScreenRouter?
|
||||
|
||||
@ObservedObject var viewModel: WelcomeScreenViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
||||
Spacer()
|
||||
Text("Welcome and Onboarding")
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .center, spacing: 16) {
|
||||
Button(action: {
|
||||
self.viewModel.restoreWallet()
|
||||
}, label: {
|
||||
Text("RESTORE WALLET")
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
|
||||
Button(action: {
|
||||
self.viewModel.createNew()
|
||||
}, label: {
|
||||
Text("CREATE NEW WALLET")
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateNewWalletScreenPreviews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WelcomeScreen(viewModel: WelcomeScreenViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// CreateNewWalletScreenViewModel.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class WelcomeScreenViewModel: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
func createNew() {}
|
||||
func restoreWallet() {}
|
||||
}
|
|
@ -9,9 +9,11 @@ import SwiftUI
|
|||
|
||||
@main
|
||||
struct SecantApp: App {
|
||||
|
||||
@StateObject var appRouter = AppRouter(services: MockServices())
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
appRouter.rootView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// MockServices.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MockServices: Services {
|
||||
init(){}
|
||||
var networkProvider: ZcashNetworkProvider {
|
||||
MockNetworkProvider()
|
||||
}
|
||||
var seedHandler: MnemonicSeedPhraseHandling {
|
||||
MockMnemonicPhraseHandling()
|
||||
}
|
||||
|
||||
var keyStorage: KeyStoring {
|
||||
MockKeyStoring()
|
||||
}
|
||||
}
|
||||
|
||||
class MockNetworkProvider: ZcashNetworkProvider {
|
||||
func currentNetwork() -> ZcashNetwork {
|
||||
ZcashMainnet()
|
||||
}
|
||||
}
|
||||
|
||||
class MockMnemonicPhraseHandling: MnemonicSeedPhraseHandling {
|
||||
|
||||
class TestSeed {
|
||||
|
||||
/**
|
||||
test account: "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
|
||||
*/
|
||||
let seedString = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
|
||||
|
||||
func seed() -> [UInt8] {
|
||||
[UInt8](seedString)
|
||||
}
|
||||
}
|
||||
|
||||
func randomMnemonic() throws -> String {
|
||||
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread"
|
||||
}
|
||||
|
||||
func randomMnemonicWords() throws -> [String] {
|
||||
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread".components(separatedBy: " ")
|
||||
}
|
||||
|
||||
func toSeed(mnemonic: String) throws -> [UInt8] {
|
||||
TestSeed().seed()
|
||||
}
|
||||
|
||||
func asWords(mnemonic: String) throws -> [String] {
|
||||
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread".components(separatedBy: " ")
|
||||
}
|
||||
|
||||
func isValid(mnemonic: String) throws {}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class MockKeyStoring: KeyStoring {
|
||||
var birthday: BlockHeight?
|
||||
var phrase: String?
|
||||
func importBirthday(_ height: BlockHeight) throws {
|
||||
guard birthday == nil else {
|
||||
throw KeyStoringError.alreadyImported
|
||||
}
|
||||
birthday = height
|
||||
}
|
||||
|
||||
func exportBirthday() throws -> BlockHeight {
|
||||
guard let b = birthday else {
|
||||
throw KeyStoringError.uninitializedWallet
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func importPhrase(bip39 phrase: String) throws {
|
||||
guard self.phrase == nil else {
|
||||
throw KeyStoringError.alreadyImported
|
||||
}
|
||||
self.phrase = phrase
|
||||
}
|
||||
|
||||
func exportPhrase() throws -> String {
|
||||
guard let p = self.phrase else {
|
||||
throw KeyStoringError.uninitializedWallet
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
var keysPresent: Bool {
|
||||
return self.phrase != nil && self.birthday != nil
|
||||
}
|
||||
|
||||
func nukePhrase() {
|
||||
self.phrase = nil
|
||||
}
|
||||
|
||||
func nukeBirthday() {
|
||||
self.birthday = nil
|
||||
}
|
||||
|
||||
func nukeWallet() {
|
||||
nukePhrase()
|
||||
nukeBirthday()
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
//
|
||||
// ZcashSDKStubs.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public typealias BlockHeight = Int
|
||||
public protocol ZcashNetwork {
|
||||
var networkType: NetworkType { get }
|
||||
var constants: NetworkConstants.Type { get }
|
||||
}
|
||||
|
||||
public enum NetworkType {
|
||||
case mainnet
|
||||
case testnet
|
||||
|
||||
var networkId: UInt32 {
|
||||
switch self {
|
||||
case .mainnet:
|
||||
return 1
|
||||
case .testnet:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NetworkType {
|
||||
static func forChainName(_ chainame: String) -> NetworkType? {
|
||||
switch chainame {
|
||||
case "test":
|
||||
return .testnet
|
||||
case "main":
|
||||
return .mainnet
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ZcashNetworkBuilder {
|
||||
public static func network(for networkType: NetworkType) -> ZcashNetwork {
|
||||
switch networkType {
|
||||
case .mainnet:
|
||||
return ZcashMainnet()
|
||||
case .testnet:
|
||||
return ZcashTestnet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZcashTestnet: ZcashNetwork {
|
||||
var networkType: NetworkType = .testnet
|
||||
var constants: NetworkConstants.Type = ZcashSDKTestnetConstants.self
|
||||
}
|
||||
|
||||
class ZcashMainnet: ZcashNetwork {
|
||||
var networkType: NetworkType = .mainnet
|
||||
var constants: NetworkConstants.Type = ZcashSDKMainnetConstants.self
|
||||
}
|
||||
|
||||
/**
|
||||
Constants of ZcashLightClientKit. this constants don't
|
||||
*/
|
||||
public class ZcashSDK {
|
||||
|
||||
/**
|
||||
The number of zatoshi that equal 1 ZEC.
|
||||
*/
|
||||
public static var ZATOSHI_PER_ZEC: BlockHeight = 100_000_000
|
||||
|
||||
/**
|
||||
The theoretical maximum number of blocks in a reorg, due to other bottlenecks in the protocol design.
|
||||
*/
|
||||
public static var MAX_REORG_SIZE = 100
|
||||
/**
|
||||
The amount of blocks ahead of the current height where new transactions are set to expire. This value is controlled
|
||||
by the rust backend but it is helpful to know what it is set to and should be kept in sync.
|
||||
*/
|
||||
public static var EXPIRY_OFFSET = 20
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
/**
|
||||
Default size of batches of blocks to request from the compact block service.
|
||||
*/
|
||||
public static var DEFAULT_BATCH_SIZE = 100
|
||||
/**
|
||||
Default amount of time, in in seconds, to poll for new blocks. Typically, this should be about half the average
|
||||
block time.
|
||||
*/
|
||||
public static var DEFAULT_POLL_INTERVAL: TimeInterval = 20
|
||||
/**
|
||||
Default attempts at retrying.
|
||||
*/
|
||||
public static var DEFAULT_RETRIES: Int = 5
|
||||
/**
|
||||
The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than
|
||||
this before retyring.
|
||||
*/
|
||||
public static var DEFAULT_MAX_BACKOFF_INTERVAL: TimeInterval = 600
|
||||
/**
|
||||
Default number of blocks to rewind when a chain reorg is detected. This should be large enough to recover from the
|
||||
reorg but smaller than the theoretical max reorg size of 100.
|
||||
*/
|
||||
public static var DEFAULT_REWIND_DISTANCE: Int = 10
|
||||
/**
|
||||
The number of blocks to allow before considering our data to be stale. This usually helps with what to do when
|
||||
returning from the background and is exposed via the Synchronizer's isStale function.
|
||||
*/
|
||||
public static var DEFAULT_STALE_TOLERANCE: Int = 10
|
||||
|
||||
/**
|
||||
Default Name for LibRustZcash data.db
|
||||
*/
|
||||
public static var DEFAULT_DATA_DB_NAME = "data.db"
|
||||
|
||||
/**
|
||||
Default Name for Compact Block caches db
|
||||
*/
|
||||
public static var DEFAULT_CACHES_DB_NAME = "caches.db"
|
||||
/**
|
||||
Default name for pending transactions db
|
||||
*/
|
||||
public static var DEFAULT_PENDING_DB_NAME = "pending.db"
|
||||
|
||||
/**
|
||||
File name for the sapling spend params
|
||||
*/
|
||||
public static var SPEND_PARAM_FILE_NAME = "sapling-spend.params"
|
||||
/**
|
||||
File name for the sapling output params
|
||||
*/
|
||||
public static var OUTPUT_PARAM_FILE_NAME = "sapling-output.params"
|
||||
/**
|
||||
The Url that is used by default in zcashd.
|
||||
We'll want to make this externally configurable, rather than baking it into the SDK but
|
||||
this will do for now, since we're using a cloudfront URL that already redirects.
|
||||
*/
|
||||
public static var CLOUD_PARAM_DIR_URL = "https://z.cash/downloads/"
|
||||
}
|
||||
|
||||
public protocol NetworkConstants {
|
||||
|
||||
/**
|
||||
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks
|
||||
prior to this height, at all.
|
||||
*/
|
||||
static var SAPLING_ACTIVATION_HEIGHT: BlockHeight { get }
|
||||
|
||||
/**
|
||||
Default Name for LibRustZcash data.db
|
||||
*/
|
||||
static var DEFAULT_DATA_DB_NAME: String { get }
|
||||
/**
|
||||
Default Name for Compact Block caches db
|
||||
*/
|
||||
static var DEFAULT_CACHES_DB_NAME: String { get }
|
||||
/**
|
||||
Default name for pending transactions db
|
||||
*/
|
||||
static var DEFAULT_PENDING_DB_NAME: String { get }
|
||||
static var DEFAULT_DB_NAME_PREFIX: String { get }
|
||||
|
||||
/**
|
||||
fixed height where the SDK considers that the ZIP-321 was deployed. This is a workaround
|
||||
for librustzcash not figuring out the tx fee from the tx itself.
|
||||
*/
|
||||
static var FEE_CHANGE_HEIGHT: BlockHeight { get }
|
||||
|
||||
static func defaultFee(for height: BlockHeight) -> Int64
|
||||
|
||||
}
|
||||
|
||||
public extension NetworkConstants {
|
||||
|
||||
static func defaultFee(for height: BlockHeight = BlockHeight.max) -> Int64 {
|
||||
guard height >= FEE_CHANGE_HEIGHT else { return 10_000 }
|
||||
|
||||
return 1_000
|
||||
}
|
||||
}
|
||||
|
||||
public class ZcashSDKMainnetConstants: NetworkConstants {
|
||||
|
||||
private init() {}
|
||||
|
||||
/**
|
||||
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks
|
||||
prior to this height, at all.
|
||||
*/
|
||||
public static var SAPLING_ACTIVATION_HEIGHT: BlockHeight = 419_200
|
||||
|
||||
/**
|
||||
Default Name for LibRustZcash data.db
|
||||
*/
|
||||
public static var DEFAULT_DATA_DB_NAME = "data.db"
|
||||
/**
|
||||
Default Name for Compact Block caches db
|
||||
*/
|
||||
public static var DEFAULT_CACHES_DB_NAME = "caches.db"
|
||||
/**
|
||||
Default name for pending transactions db
|
||||
*/
|
||||
public static var DEFAULT_PENDING_DB_NAME = "pending.db"
|
||||
|
||||
public static var DEFAULT_DB_NAME_PREFIX = "ZcashSdk_mainnet_"
|
||||
|
||||
public static var FEE_CHANGE_HEIGHT: BlockHeight = 1_077_550
|
||||
}
|
||||
|
||||
public class ZcashSDKTestnetConstants: NetworkConstants {
|
||||
private init() {}
|
||||
|
||||
/**
|
||||
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks
|
||||
prior to this height, at all.
|
||||
*/
|
||||
public static var SAPLING_ACTIVATION_HEIGHT: BlockHeight = 280_000
|
||||
|
||||
/**
|
||||
Default Name for LibRustZcash data.db
|
||||
*/
|
||||
public static var DEFAULT_DATA_DB_NAME = "data.db"
|
||||
/**
|
||||
Default Name for Compact Block caches db
|
||||
*/
|
||||
public static var DEFAULT_CACHES_DB_NAME = "caches.db"
|
||||
/**
|
||||
Default name for pending transactions db
|
||||
*/
|
||||
public static var DEFAULT_PENDING_DB_NAME = "pending.db"
|
||||
|
||||
public static var DEFAULT_DB_NAME_PREFIX = "ZcashSdk_testnet_"
|
||||
|
||||
/**
|
||||
Estimated height where wallets are supposed to change the fee
|
||||
*/
|
||||
public static var FEE_CHANGE_HEIGHT: BlockHeight = 1_028_500
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// PlainButton.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PlainButton: ButtonStyle {
|
||||
enum Theme {
|
||||
case light
|
||||
case bold
|
||||
}
|
||||
|
||||
var style = Theme.bold
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.foregroundColor(style.foregroundColor)
|
||||
.padding()
|
||||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 48, maxHeight: 48)
|
||||
.background(style.background)
|
||||
}
|
||||
}
|
||||
|
||||
struct PlainButton_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, label: {
|
||||
/*@START_MENU_TOKEN@*/Text("Button")/*@END_MENU_TOKEN@*/
|
||||
})
|
||||
.buttonStyle(PlainButton())
|
||||
|
||||
Button(action: /*@START_MENU_TOKEN@*/{}/*@END_MENU_TOKEN@*/, label: {
|
||||
/*@START_MENU_TOKEN@*/Text("Button")/*@END_MENU_TOKEN@*/
|
||||
})
|
||||
.buttonStyle(PlainButton(style: .bold))
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
}
|
||||
}
|
||||
|
||||
extension PlainButton.Theme {
|
||||
var background: some View {
|
||||
switch self {
|
||||
case .bold:
|
||||
return Color.black
|
||||
default:
|
||||
return Color.white
|
||||
}
|
||||
}
|
||||
|
||||
var foregroundColor: Color {
|
||||
switch self {
|
||||
case .bold:
|
||||
return Color.white
|
||||
default:
|
||||
return Color.black
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// SwiftUI+secant.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/10/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
extension Font {
|
||||
static func zboto(_ size: CGFloat) -> Font {
|
||||
Font.custom("Zboto", size: size)
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant
|
||||
@testable import secant_testnet
|
||||
|
||||
class secantTests: XCTestCase {
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ln -s $PWD/xctemplates ~/Library/Developer/Xcode/Templates
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
Kind = "Xcode.IDEFoundation.TextSubstitutionFileTemplateKind";
|
||||
Platforms = (
|
||||
"com.apple.platform.iphoneos",
|
||||
"com.apple.platform.macosx",
|
||||
);
|
||||
Options = (
|
||||
{
|
||||
Description = "The name of the module to create";
|
||||
Identifier = productName;
|
||||
Name = Router;
|
||||
Required = YES;
|
||||
Type = text;
|
||||
Default = Router;
|
||||
},
|
||||
);
|
||||
}
|
31
xctemplates/Router Templates/Router.xctemplate/___FILEBASENAME___Router.swift
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
//___FILEHEADER___
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
class ___FILEBASENAMEASIDENTIFIER___: Router {
|
||||
|
||||
// MARK: - Published vars
|
||||
// Put published vars here
|
||||
|
||||
// MARK: - Private vars
|
||||
|
||||
// MARK: - Internal vars
|
||||
var services: Services
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(services: Services) {
|
||||
self.services = services
|
||||
}
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
func rootView() -> some View {
|
||||
// Add your content here
|
||||
NavigationView {
|
||||
Text("Hello Word")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 6.5 KiB |
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
Kind = "Xcode.IDEFoundation.TextSubstitutionFileTemplateKind";
|
||||
Platforms = (
|
||||
"com.apple.platform.iphoneos",
|
||||
);
|
||||
Options = (
|
||||
{
|
||||
Description = "The name of the module to create";
|
||||
Identifier = productName;
|
||||
Name = Module;
|
||||
Required = YES;
|
||||
Type = text;
|
||||
Default = Module;
|
||||
},
|
||||
);
|
||||
}
|
22
xctemplates/Router Templates/SwiftUI Screen.xctemplate/___FILEBASENAME___Screen.swift
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
//___FILEHEADER___
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol ___FILEBASENAMEASIDENTIFIER___Router: AnyObject {
|
||||
}
|
||||
|
||||
struct ___FILEBASENAMEASIDENTIFIER___: View {
|
||||
@State var router: ___FILEBASENAMEASIDENTIFIER___Router?
|
||||
|
||||
@ObservedObject var viewModel: ___FILEBASENAME___ViewModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct ___FILEBASENAMEASIDENTIFIER___Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
___FILEBASENAMEASIDENTIFIER___(viewModel: ___FILEBASENAME___ViewModel(services: MockServices()))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
//___FILEHEADER___
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class ___FILEBASENAMEASIDENTIFIER___: BaseViewModel<Services>, ObservableObject {
|
||||
|
||||
}
|