diff --git a/KinBase/KinBase.xcodeproj/project.pbxproj b/KinBase/KinBase.xcodeproj/project.pbxproj index 56643bf..9d7f9e6 100644 --- a/KinBase/KinBase.xcodeproj/project.pbxproj +++ b/KinBase/KinBase.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 13EBE3CD7DE1F81A8C3FAD8C /* Pods_KinBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 641FFE9E2F6D7EF63FD3F189 /* Pods_KinBase.framework */; }; + 537A853674F90E24C6AB923F /* Pods_KinBaseTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DC34DA43FCC42CF67BA8F4B /* Pods_KinBaseTests.framework */; }; 851B278A2432821D004EE486 /* SecureKeyStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B27892432821D004EE486 /* SecureKeyStorage.swift */; }; 851B278C24328225004EE486 /* KeyChainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B278B24328225004EE486 /* KeyChainStorageTests.swift */; }; 851B279624354AF9004EE486 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B279524354AF9004EE486 /* AppDelegate.swift */; }; @@ -184,9 +186,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 285C3EB9856DFBB98A11AB9B /* Pods-KinBaseTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBaseTests.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBaseTests/Pods-KinBaseTests.debug.xcconfig"; sourceTree = ""; }; - 37DFF5A47EDA55DD46001B68 /* Pods-KinBase.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBase.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBase/Pods-KinBase.debug.xcconfig"; sourceTree = ""; }; - 4022398A8F4ED08AED14DADF /* Pods_KinBaseTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KinBaseTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 19892C27E40C49FACB5A6177 /* Pods-KinBase.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBase.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBase/Pods-KinBase.debug.xcconfig"; sourceTree = ""; }; + 3DC34DA43FCC42CF67BA8F4B /* Pods_KinBaseTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KinBaseTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 403F37376E7F5C42418E59E7 /* Pods-KinBase.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBase.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBase/Pods-KinBase.release.xcconfig"; sourceTree = ""; }; + 4566609EC038826E908FA1FA /* Pods-KinBaseTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBaseTests.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBaseTests/Pods-KinBaseTests.release.xcconfig"; sourceTree = ""; }; + 641FFE9E2F6D7EF63FD3F189 /* Pods_KinBase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KinBase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8518B10D244C9BE300EC329B /* KinServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KinServiceTests.swift; sourceTree = ""; }; 851B27892432821D004EE486 /* SecureKeyStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureKeyStorage.swift; sourceTree = ""; }; 851B278B24328225004EE486 /* KeyChainStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyChainStorageTests.swift; sourceTree = ""; }; @@ -351,8 +355,7 @@ BDD51F04268014EE0061712E /* AirdropService.pbrpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AirdropService.pbrpc.h; sourceTree = ""; }; BDD51F05268014EE0061712E /* AirdropService.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AirdropService.pbobjc.m; sourceTree = ""; }; BDD51F06268014EE0061712E /* AirdropService.pbrpc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AirdropService.pbrpc.m; sourceTree = ""; }; - C134372677B828BB1D687DC1 /* Pods-KinBase.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBase.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBase/Pods-KinBase.release.xcconfig"; sourceTree = ""; }; - E5C131A473595E1648F2E14A /* Pods-KinBaseTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBaseTests.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBaseTests/Pods-KinBaseTests.release.xcconfig"; sourceTree = ""; }; + E3BEFD5BD9AE1D6F0ACCC40E /* Pods-KinBaseTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KinBaseTests.debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-KinBaseTests/Pods-KinBaseTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -367,7 +370,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E2F9CDCB79D6C73F8A77A660 /* Pods_KinBase.framework in Frameworks */, + 13EBE3CD7DE1F81A8C3FAD8C /* Pods_KinBase.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -376,7 +379,7 @@ buildActionMask = 2147483647; files = ( 85B024072371CC7700829FD6 /* KinBase.framework in Frameworks */, - BD4A83C675B37F7AAD4832EC /* Pods_KinBaseTests.framework in Frameworks */, + 537A853674F90E24C6AB923F /* Pods_KinBaseTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -387,8 +390,8 @@ isa = PBXGroup; children = ( 853EC7B824EF582F004E0BED /* KinGrpcApi.framework */, - 9AEB96831A9FD62C8C0DAA5F /* Pods_KinBase.framework */, - 4022398A8F4ED08AED14DADF /* Pods_KinBaseTests.framework */, + 641FFE9E2F6D7EF63FD3F189 /* Pods_KinBase.framework */, + 3DC34DA43FCC42CF67BA8F4B /* Pods_KinBaseTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -396,10 +399,10 @@ 75D5C99AC419743FE72EB422 /* Pods */ = { isa = PBXGroup; children = ( - 37DFF5A47EDA55DD46001B68 /* Pods-KinBase.debug.xcconfig */, - C134372677B828BB1D687DC1 /* Pods-KinBase.release.xcconfig */, - 285C3EB9856DFBB98A11AB9B /* Pods-KinBaseTests.debug.xcconfig */, - E5C131A473595E1648F2E14A /* Pods-KinBaseTests.release.xcconfig */, + 19892C27E40C49FACB5A6177 /* Pods-KinBase.debug.xcconfig */, + 403F37376E7F5C42418E59E7 /* Pods-KinBase.release.xcconfig */, + E3BEFD5BD9AE1D6F0ACCC40E /* Pods-KinBaseTests.debug.xcconfig */, + 4566609EC038826E908FA1FA /* Pods-KinBaseTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1010,7 +1013,7 @@ isa = PBXNativeTarget; buildConfigurationList = 85B024112371CC7700829FD6 /* Build configuration list for PBXNativeTarget "KinBase" */; buildPhases = ( - 7ECD479421B030586B57BD4D /* [CP] Check Pods Manifest.lock */, + 762BB07B4E0CC5D225E5F5AE /* [CP] Check Pods Manifest.lock */, 85B023F82371CC7700829FD6 /* Headers */, 85B023F92371CC7700829FD6 /* Sources */, 85B023FA2371CC7700829FD6 /* Frameworks */, @@ -1029,11 +1032,11 @@ isa = PBXNativeTarget; buildConfigurationList = 85B024142371CC7700829FD6 /* Build configuration list for PBXNativeTarget "KinBaseTests" */; buildPhases = ( - 3B87647EE58B13A77BB19BB8 /* [CP] Check Pods Manifest.lock */, + DEFE1F0A5BBDD338F2009E98 /* [CP] Check Pods Manifest.lock */, 85B024022371CC7700829FD6 /* Sources */, 85B024032371CC7700829FD6 /* Frameworks */, 85B024042371CC7700829FD6 /* Resources */, - 5B394E10F1418F8143C10834 /* [CP] Embed Pods Frameworks */, + D5F346929C7D29DF6629C589 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1117,7 +1120,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B87647EE58B13A77BB19BB8 /* [CP] Check Pods Manifest.lock */ = { + 762BB07B4E0CC5D225E5F5AE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1132,14 +1135,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-KinBaseTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-KinBase-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 5B394E10F1418F8143C10834 /* [CP] Embed Pods Frameworks */ = { + D5F346929C7D29DF6629C589 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1156,7 +1159,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-KinBaseTests/Pods-KinBaseTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 7ECD479421B030586B57BD4D /* [CP] Check Pods Manifest.lock */ = { + DEFE1F0A5BBDD338F2009E98 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1171,7 +1174,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-KinBase-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-KinBaseTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1522,7 +1525,7 @@ }; 85B024122371CC7700829FD6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 37DFF5A47EDA55DD46001B68 /* Pods-KinBase.debug.xcconfig */; + baseConfigurationReference = 19892C27E40C49FACB5A6177 /* Pods-KinBase.debug.xcconfig */; buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; @@ -1562,7 +1565,7 @@ }; 85B024132371CC7700829FD6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C134372677B828BB1D687DC1 /* Pods-KinBase.release.xcconfig */; + baseConfigurationReference = 403F37376E7F5C42418E59E7 /* Pods-KinBase.release.xcconfig */; buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; @@ -1603,7 +1606,7 @@ }; 85B024152371CC7700829FD6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 285C3EB9856DFBB98A11AB9B /* Pods-KinBaseTests.debug.xcconfig */; + baseConfigurationReference = E3BEFD5BD9AE1D6F0ACCC40E /* Pods-KinBaseTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; @@ -1624,7 +1627,7 @@ }; 85B024162371CC7700829FD6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E5C131A473595E1648F2E14A /* Pods-KinBaseTests.release.xcconfig */; + baseConfigurationReference = 4566609EC038826E908FA1FA /* Pods-KinBaseTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; diff --git a/KinBase/KinBase/Src/AppInfoProvider.swift b/KinBase/KinBase/Src/AppInfoProvider.swift index 8646132..990c9a9 100644 --- a/KinBase/KinBase/Src/AppInfoProvider.swift +++ b/KinBase/KinBase/Src/AppInfoProvider.swift @@ -13,6 +13,20 @@ public protocol AppInfoProvider { func getPassthroughAppUserCredentials() -> AppUserCredentials } +public class BasicAppInfoProvider: AppInfoProvider { + public var appInfo: AppInfo + private var appUserCredentials: AppUserCredentials + + public func getPassthroughAppUserCredentials() -> AppUserCredentials { + return self.appUserCredentials + } + + public init(appInfo: AppInfo, appUserId: String, appUserPasskey: String) { + self.appInfo = appInfo + self.appUserCredentials = AppUserCredentials.init(appUserId: appUserId, appUserPasskey: appUserPasskey) + } +} + public class DummyAppInfoProvider: AppInfoProvider { public var appInfo: AppInfo { return .testApp diff --git a/KinBase/KinBase/Src/KinEnvironment.swift b/KinBase/KinBase/Src/KinEnvironment.swift index d2dc743..1d5ad5a 100644 --- a/KinBase/KinBase/Src/KinEnvironment.swift +++ b/KinBase/KinBase/Src/KinEnvironment.swift @@ -67,7 +67,7 @@ public struct KinEnvironment { let logger = KinLoggerFactoryImpl(isLoggingEnabled: enableLogging) let networkHandler = NetworkOperationHandler() // If custom storagePath is set, use that. Otherwise provide a default. - let documentDirectory = storagePath ?? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let documentDirectory = storagePath ?? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("kin_storage", isDirectory: true) let storage = KinFileStorage(directory: documentDirectory, network: network) let grpcProxy = AgoraGrpcProxy( diff --git a/KinBase/KinBase/Src/Model/KinTransaction.swift b/KinBase/KinBase/Src/Model/KinTransaction.swift index 21924f4..7b571b9 100644 --- a/KinBase/KinBase/Src/Model/KinTransaction.swift +++ b/KinBase/KinBase/Src/Model/KinTransaction.swift @@ -247,6 +247,13 @@ extension KinTransactionHash: CustomStringConvertible { } } +// MARK: - Base58 - +extension KinTransactionHash { + public var base58: String { + return Base58.base58FromBytes(rawValue) + } +} + public struct KinTransactions { public let items: [KinTransaction] public let headPagingToken: PagingToken? diff --git a/KinBase/KinBase/Src/Storage/KinFileStorage.swift b/KinBase/KinBase/Src/Storage/KinFileStorage.swift index 34d4c52..e899fee 100644 --- a/KinBase/KinBase/Src/Storage/KinFileStorage.swift +++ b/KinBase/KinBase/Src/Storage/KinFileStorage.swift @@ -339,18 +339,14 @@ extension KinFileStorage: KinStorageType { userDefaults.removeObject(forKey: Constants.minFeeUserDefaultsKey) userDefaults.removeObject(forKey: Constants.minApiVersionUserDefaultsKey) userDefaults.removeObject(forKey: Constants.cidUserDefaultsKey) - return removeFileOrDirectory(kinStorageDirectory) + return removeFileOrDirectory(rootDirectory) } } // MARK: Private - File Location private extension KinFileStorage { - var kinStorageDirectory: URL { - return rootDirectory.appendingPathComponent("kin_storage", isDirectory: true) - } - var directoryForAllAccounts: URL { - return kinStorageDirectory.appendingPathComponent("kin_accounts", isDirectory: true) + return rootDirectory.appendingPathComponent("kin_accounts", isDirectory: true) } func directoryForAccount(_ account: PublicKey) -> URL { diff --git a/KinBase/README.md b/KinBase/README.md index 8c2c78a..6f3e4de 100644 --- a/KinBase/README.md +++ b/KinBase/README.md @@ -2,7 +2,7 @@ [![CocoaPods](https://img.shields.io/cocoapods/v/KinBase.svg?color=6f41e8)](https://cocoapods.org/pods/KinBase) -The KinBase module is the foundation upon which the rest of the sdk stands on, however can be used on it's own to headlessly access the Kin Blockchain. +The KinBase module is the foundation upon which the rest of the SDK stands. ## Installation Add the following to your Podfile. @@ -12,8 +12,8 @@ pod 'KinBase', '~> 1.0.1' ## Overview KinBase contains two main components that are to be instantiated and used by the developer: -- a `KinEnvironment` instance to describe the network (test or production), and provide some external dependencies -- a `KinAccountContext` instance to access functionality of a Kin account on the Kin Blockchain and also provides local storage for your private key, and data cache for account and payment history data. +- a `KinEnvironment` instance to describe the network (test or production) and provide some external dependencies +- a `KinAccountContext` instance to access functionality of a Kin account on the Kin Blockchain and also to provide local storage for your private key and data cache for account and payment history data Below you'll find a general overview on how to make use of KinBase, but also consider diving into the specific documentation [found here](https://kinecosystem.github.io/kin-ios/docs) for more details. @@ -21,32 +21,32 @@ Below you'll find a general overview on how to make use of KinBase, but also con Everything starts with a `KinEnvironment` instance that describes which blockchain, services, and storage will be used. For a default setup, simply ### Agora Kin Environment -The Agora Kin Environment is now the preferred method of communicating to the Kin Blockchain. Agora is a both a gateway to submit payments, but also a history collected that can be used to resolve your full payment history. -When submitting payments, a developer should properly configure an [Agora webhook](https://docs.kin.org/how-it-works#webhooks)., which acts as a delegate to approve and optionally co-sign a transaction to mediate transaction fees. -Agora can also store additional meta-data about your transaction concerning what your payments were for. This bundle of information is called an `Invoice`, and is offchain data which is referenced by the payment's associated Memo, both which you can read more about down below in the [Sending Payments section](#sending-payments) +The Agora Kin Environment is now the preferred method of communicating to the Kin Blockchain. Agora is both a gateway to submit payments and a history collector that can be used to resolve your full payment history. +When submitting payments, a developer should properly configure an [Agora webhook](https://docs.kin.org/how-it-works#webhooks), which acts as a delegate to approve and optionally co-sign a transaction to mediate transaction fees. +Agora can also store additional metadata about your transaction concerning what your payments were for. This bundle of information is called an `Invoice`: offchain data which is referenced by the payment's associated `Memo`, both which you can read more about below in the [Sending Payments](#sending-payments) section. You'll also need to tell the SDK a bit about your app in an AppInfoProvider implementation to work with some features (like paying and resolving Invoices and the [spend](../spend) module UI) There are two bundles of information an App provides through this interface: -- An AppInfo object to describe the App. This contains your Apps unique App Index whcih you can obtain by registering [here](https://docs.kin.org/app-registration) +- An AppInfo object to describe the App. This contains your App's unique App Index which you can obtain by registering [here](https://docs.kin.org/app-registration) - Passthrough Auth User Credentials are passed onto the webhook when submitting a transaction For more information regarding webhooks and webhook integration please read more about [how it works](https://docs.kin.org/how-it-works#webhooks). -```swift -let environment = KinEnvironment.Agora.mainNet(appInfoProvider: yourAppInfoProvider) -``` -### Horizon Kin Environment -Horizon access from the SDK has been deprecated. While it's still included in the SDK and can be used it may become unavailable in a future blockchain migration. ```swift -// DEPCRECATED - SEE KinEnvironment.Agora -// Main net -let accountCreationApi = YourAccountCreationApiImpl() -let whitelistingApi = YourWhitelistingApiImpl() -let environment: KinEnvironment = KinEnvironment.Horizon.mainNet(accountCreationApi: accountCreationApi, - whitelistingApi: whitelistingApi) - -// Test net -let environment: KinEnvironment = KinEnvironment.Horizon.testNet() +let environment = KinEnvironment.Agora.mainNet( + appInfoProvider: BasicAppInfoProvider( + appInfo: AppInfo( + appIdx: AppIndex(value: YOUR_APP_INDEX), + kinAccount: YOUR_PUBLIC_KEY, + name: "YOUR_APP_NAME", + appIconData: YOUR_APP_ICON_DATA + ), + appUserId: "YOUR_USER_ID", + appUserPasskey: "YOUR_USER_PASSKEY" + ) +) ``` + +### KinAccountContext For a given `KinAccount` that you want to operate on, you will need a `KinAccountContext` instance. This will be used to both create and access all `KinAccount` and `KinPayment`s. ```swift @@ -56,29 +56,6 @@ let context: KinAccountContext = .build() ``` -### Note on Upcoming Solana Migration -With the migration to Solana just around the corner, apps that want to continue to function during and post the move to the Solana blockchain are required to upgrade their `kin-ios` sdk to 0.4.0 or higher. -*Any application that does not upgrade will start to receive a `KinService.Errors.upgradeRequired` exception on any request made from `KinAccountContext`.* - -#### Testing migration within your app -To enable migration of Kin3 -> Kin4 accounts on testnet, `KinEnvironment.Builder` instances have a -new option, ``.testMigration()` that will force this sdk into a state where migration will occur on demand. -#### On Migration Day (Dec 8, 2020) -Apps should expect to see increased transaction times temporarily on the date of migration. -An on-demand migration will be attempted to trigger a migration, rebuild, and retry transactions that are submitted from an unmigrated account on this day and optimistically will complete successfully but are not guaranteed. -After all accounts have been migrated to Solana, transaction times should noticeably improve to around ~1s. Additional performance improvements are still possible and will roll out in future sdk releases. - -### Kin 2 Support -*For those apps that are still on Kin 2 and require support for Kin 2 -> Kin 4 migration* -You should configure your KinEnvironment to use the Kin 2 NetworkEnvironment as follows: -```kotlin -KinEnvironment.Agora.defaultEnvironmentSetup(network: .mainNetKin2, ...) -``` - -*Failure to do this will default your app to start on Kin 3 which will create a new account for your users on Kin3 with a 0 Kin balance. You probably do not want to do this.* - -### *As you may notice on the `KinAccountContext.Builder`, there are a few options on how to configure a `KinAccountContext`...* - ## Creating An Account If you want to create a new `KinAccount` use: ```swift @@ -87,18 +64,18 @@ If you want to create a new `KinAccount` use: ## Access An Existing Account If you want to access an existing `KinAccount` with options to send `KinPayment`s, input the `KinAccount.Id` with: ```swift -.useExistingAccount(KinAccount.Id("GATG_example_and_fake_key")) +.useExistingAccount(PublicKey(base58: "example_and_fake_key")) ``` *Note: this variant requires that the sdk knows about this `KinAccount`s `Key.PrivateKey` which can be imported the first time by:* ```swift -.importExistingPrivateKey(KinAccount.Key("key_containing_private_key")) +.importExistingPrivateKey(KeyPair(seed: Seed(base58: "private_seed"))) ``` ## Sending Payments -Sending `KinPayment`s are easy. Just add the amount and the destination `KinAccount.Id`. +Sending `KinPayment`s is easy. Just add the amount and the destination `PublicKey`. *Note: successive calls to this function before the previous is completed will be properly queued according to blockchain implementation needs.* ```swift -let paymentItem = KinPaymentItem(amount: Kin(5), destAccountId: KinAccount.Id("GATG_example_and_fake_key")) +let paymentItem = KinPaymentItem(amount: Kin(5), destAccount: PublicKey(base58: "example_and_fake_key")) context.sendKinPayment(paymentItem, memo: KinMemo(text: "my_memo")) .then { payment in // Payment Completed @@ -108,8 +85,8 @@ Sending a batch of payments to the blockchain to be completed together, in a sin *Note: This operation is atomic. All payments will either succeed or fail together.* ```swift -let payments = [KinPaymentItem(amount: Kin(5), destAccountId: KinAccount.Id("GATG_example_and_fake_key")), - KinPaymentItem(amount: Kin(30), destAccountId: KinAccount.Id("GATG_example_and_fake_key"))] +let payments = [KinPaymentItem(amount: Kin(5), destAccount: PublicKey(base58: "example_and_fake_key")), + KinPaymentItem(amount: Kin(30), destAccount: PublicKey(base58: "example_and_fake_key"))] context.sendKinPayments(payments, KinMemo(text: "my_memo")) .then { completedPayments in // Payments Completed @@ -117,7 +94,7 @@ context.sendKinPayments(payments, KinMemo(text: "my_memo")) ``` ### Are there Fees? -It depends. Normally by default payments on the Kin Blockchain are charged a minimal fee of 100 Quark (1 Quark = 0.001 Kin) each. The minimum required fee is dictated by the Blockchain. Fees on the Kin blockchain are an anti-spam feature intended to prevent malicious actors from spamming the network. Registered Kin apps are given a whitelisted account, which they can use to exempt their or their users' transactions using the [Sign Transaction webhook](https://docs.kin.org/how-it-works#sign-transaction). +It depends. By default, payments on the Kin Blockchain are charged a minimal fee of 100 Quark (1 Quark = 0.001 Kin) each. The minimum required fee is dictated by the Blockchain. Fees on the Kin blockchain are an anti-spam feature intended to prevent malicious actors from spamming the network. Registered Kin apps are given a whitelisted account, which they can use to exempt their or their users' transactions using the [Sign Transaction webhook](https://docs.kin.org/how-it-works#sign-transaction). When using KinAccountContext configured with the Agora KinEnvironment, by default a fee will not be added to the payment unless you specifically want your users to pay fees instead of you providing whitelisting. This can be achieved by overridintg and setting the `isWhitelistingAvailable` parameter to false in the `KinTransactionWhitelistingApi` instance when configuring your `KinEnvironment` instance. @@ -144,7 +121,7 @@ try KinBinaryMemo(typeId: KinBinaryMemo.TransferType.p2p.rawValue, ``` #### *Text Memos (Old style memos)* -In this SDK you can provide a text based memo by using the `KinMemo` class. This format should only be used be existing incumbant apps that have been issued AppIds and have yet to upgrade to the new Kin Binary Memo Format. +You can provide a text-based memo with the `KinMemo` class. This format should only be used be existing incumbant apps that have been issued AppIds and have yet to upgrade to the new Kin Binary Memo Format. #### Invoices [Invoices](https://docs.kin.org/how-it-works#invoices) are a great way to leverage Agora to store data about your payments off chain for you to retrieve later (e.g. in your payment history). They can be submitted to Agora via `payInvoice` method with a properly formatted KinBinaryMemo which is used to reference the applicable Invoice data at a later time. @@ -166,10 +143,10 @@ let invoice = try Invoice(lineItems: [lineItem]) To Execute a payment for an `Invoice` you can make use of the `payInvoice` convenience function on `KinAccountContext`. This essentially calls `sendPayment` on your behalf with the correctly formatted `KinBinaryMemo` and TransferType of `KinBinaryMemo.TransferType.Spend`. Invoices can and should also be used for other TransferTypes such as P2P and Earns. -*The destinationKinAccountId must match the expected & registered desginationKinAppIdx provided [during registration](https://docs.kin.org/app-registration)* +*The destinationKinAccountId must match the expected & registered destinationKinAppIdx provided [during registration](https://docs.kin.org/app-registration)* ```swift -payInvoice(processingAppIdx: yourAppIndx, - destinationAccount: destAccountId, +payInvoice(processingAppIdx: yourAppIdx, + destinationAccount: destAccount, invoice: yourInvoice, type: .spend).then { kinPayment -> // Payment Completed @@ -184,9 +161,9 @@ kinPayment.invoice *Note:* If you are paying for an `Invoice` you *must* use the Kin Binary Memo Format for the memo.* ## Retrieving Account Data -The `KinAccount.Id` for a given `KinAccountContext` instance is always available +The `PublicKey` for a given `KinAccountContext` instance is always available ```swift -context.accountId +context.accountPublicKey ``` If you require more than just the id, the full `KinAccount` is available by querying with: ```swift @@ -232,3 +209,4 @@ context.clearStorage() // The data with this KinAccountContext is now gone forever } ``` + diff --git a/README.md b/README.md index 333fbb2..ed851df 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![jazzy](https://img.shields.io/badge/docs-jazzy-blue)](https://kinecosystem.github.io/kin-ios/) [![CocoaPods](https://img.shields.io/cocoapods/v/KinBase.svg?color=6f41e8)](https://cocoapods.org/pods/KinBase) -Use the Kin SDK for iOS to enable the use of Kin inside of your app. Include only the functionality you need to provide the right experience to your users. Use just the base library to access the lightest-weight wrapper over the Kin crytocurrency. +Use the Kin SDK for iOS to enable the use of Kin inside of your app. Include only the functionality you need to provide the right experience to your users. Use just the base library to access the lightest-weight wrapper over the Kin cryptocurrency. ## Looking for a quick way to start? @@ -42,3 +42,7 @@ The [`/KinSampleApp`](KinSampleApp) directory includes a sample application. On ## Documentation Jazzy Documentation for all classes in all modules located [here](https://kinecosystem.github.io/kin-ios/) + +## Apps using legacy versions + +For specific instructions related to migrating from older versions, see the [migration help document](migration_help.md). diff --git a/migration_help.md b/migration_help.md new file mode 100644 index 0000000..06772a8 --- /dev/null +++ b/migration_help.md @@ -0,0 +1,23 @@ +# Migrating from 0.x to 1.x + +## Kin Storage +Prior to version 1.0.0, Kin accounts were stored inside directories specific to the environment being used (mainnet, testnet, kin2, kin3, etc). This has been deprecated in version 1.0.0. The new default is for Kin storage to reside at `~/Documents/kin_storage`. Legacy apps wishing to maintain support for their existing users are advised to use the `storagePath` parameter when initializing their `KinEnvironment` to provide a custom storage path pointing to where previous versions of the Kin SDK stored its data. + +### Sample code for migrating apps +``` +var customPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! +customPath.appendPathComponent("kin_storage") +customPath.appendPathComponent("env") +customPath.appendPathComponent("[ENV_ID]") +let environment = KinEnvironment.Agora.mainNet(appInfoProvider: yourAppInfoProvider, storagePath: customPath) +``` + +#### Kin3 apps +Replace `[ENV_ID]` in the above example with `9DKMS82DC5KMSRJ5EGG3M824CLHMARB2CLP20CHG64S0====` + +#### Kin2 apps +Replace `[ENV_ID]` in the above example with `A1QM4R39CCG4ER3FC9GMO82BD5N20HB3DTPNISRKCLMI0JJ5EHRMUSJB40TI0IJLDPII0CHG64S0====` + +#### Other +The `ENV_ID` referenced above is the base32hex encoding of the KinNetwork.Id your app was using prior to updating to 1.x. Listed above are the encodings for Kin3 mainnet and Kin2 mainnet, but if you had a different configuration, you can use a base32hex encoder to generate the appropriate string, or check an existing device to find the precise storage location it was using. +