diff --git a/README.md b/README.md index f982876..5865d75 100644 --- a/README.md +++ b/README.md @@ -310,16 +310,16 @@ However, due to the way the Swift Decoding System works, we can not do the same ```swift enum NamedVariant { - case one(Int8) - case two(Int16) - case three(a: Int8, b: Int16) + case one(Int8) + case two(Int16) + case three(a: Int8, b: Int16) - enum CodingKeys: String, CandidCodingKey { - case one, two, three - } - enum ThreeCodingKeys: String, CandidCodingKey { - case a, b - } + enum CodingKeys: String, CandidCodingKey { + case one, two, three + } + enum ThreeCodingKeys: String, CandidCodingKey { + case a, b + } } ``` @@ -333,16 +333,16 @@ However, due to the way the Swift Decoding System works, we can not do the same ```swift enum UnnamedVariant { - case anyName(Int8) - case really(Int16) - case weCanChoose(here: Int8, too: Int16) + case anyName(Int8) + case really(Int16) + case weCanChoose(here: Int8, too: Int16) - enum CodingKeys: Int, CodingKey { - case anyName, really, weCanChoose // as long as we keep the order here - } - enum WeCanChooseCodingKeys: Int, CodingKey { - case here, too // and here - } + enum CodingKeys: Int, CodingKey { + case anyName, really, weCanChoose // as long as we keep the order here + } + enum WeCanChooseCodingKeys: Int, CodingKey { + case here, too // and here + } } ``` @@ -356,11 +356,11 @@ There are several ways to perform a request. Depending if it is a simple query o #### Define the method you wish to call : ```swift let method = ICPMethod( - canister: ICPSystemCanisters.ledger, - methodName: "account_balance", - arg: .record([ - "account": .blob(account.accountId) - ]) + canister: ICPSystemCanisters.ledger, + methodName: "account_balance", + arg: .record([ + "account": .blob(account.accountId) + ]) ) ``` #### Make the a simple query request: @@ -435,6 +435,19 @@ class SimpleSigningPrincipal: ICPSigningPrincipal { func sign(_ message: Data, domain: ICPDomainSeparator) async throws -> Data { return try ICPCryptography.ellipticSign(message, domain: domain, with: privateKey) } + + static func fromMnemonic(_ words: [String]) throws -> SimplePrincipal { + let seed = HdWalletKit.Mnemonic.seed(mnemonic: words)! + let xPrivKey = HDExtendedKeyVersion.xprv.rawValue + let privateKey = try HDPrivateKey(seed: seed, xPrivKey: xPrivKey) + .derived(at: 44, hardened: true) + .derived(at: 223, hardened: true) + .derived(at: 0, hardened: true) + .derived(at: 0, hardened: false) + .derived(at: 0, hardened: false) + let publicKey = privateKey.publicKey(compressed: false) + return try SimplePrincipal(privateKey: privateKey.raw, uncompressedPublicKey: publicKey.raw) + } } ``` @@ -443,4 +456,4 @@ class SimpleSigningPrincipal: ICPSigningPrincipal { - Serialisation of recursive candid values leads to infinite loop. - Encoding of `CandidFunctionProtocol` is not supported as there is no easy way to infer the `CandidTypes` used in the arguments and results of the function. This means that the automatic code generation will fail when calling a canister method that expects another function as input. - Encoding of `CandidServiceProtocol` is not supported because functions can not be encoded. -- Encoding of chained optionals loses the type after one optionality level. +- Encoding of optional `Structs` can not infer the members of the struct. These are encoded as `opt empty` which should be accepted by all canisters according to Candid specifications. diff --git a/Sources/Candid/Encoding/CandidDecoder.swift b/Sources/Candid/Encoding/CandidDecoder.swift index a08f08a..9c83c48 100644 --- a/Sources/Candid/Encoding/CandidDecoder.swift +++ b/Sources/Candid/Encoding/CandidDecoder.swift @@ -125,9 +125,7 @@ private class CandidSingleValueDecodingContainer: SingleValueDecodingContainer { } func decode(_ type: T.Type) throws -> T where T: Decodable { - guard let childValue = input.optionValue?.value else { - throw DecodingError.typeMismatch(T.self, .init(codingPath: codingPath, debugDescription: "not an optional")) - } + let childValue = input.optionValue?.value ?? input let decoder = CandidValueDecoder(childValue, codingPath + [IntCodingKey(intValue: 0)]) return try decoder.candidDecode() } diff --git a/Sources/IcpKit/ICPRequest/ICPService.swift b/Sources/IcpKit/ICPRequest/ICPService.swift index 3828c1d..c25ce7b 100644 --- a/Sources/IcpKit/ICPRequest/ICPService.swift +++ b/Sources/IcpKit/ICPRequest/ICPService.swift @@ -11,8 +11,12 @@ open class ICPService { public let canister: ICPPrincipal public let client: ICPRequestClient - public init(canister: ICPPrincipal, client: ICPRequestClient) { + public init(canister: ICPPrincipal, client: ICPRequestClient = ICPRequestClient()) { self.canister = canister self.client = client } + + public convenience init(_ canister: String, client: ICPRequestClient = ICPRequestClient()) throws { + self.init(canister: try ICPPrincipal(canister), client: client) + } } diff --git a/Tests/CodeGeneratorTests/CodeGeneratorTests.swift b/Tests/CodeGeneratorTests/CodeGeneratorTests.swift index d4c553c..1dc7918 100644 --- a/Tests/CodeGeneratorTests/CodeGeneratorTests.swift +++ b/Tests/CodeGeneratorTests/CodeGeneratorTests.swift @@ -166,6 +166,24 @@ let cValue: UnnamedType0 = .c(bool: true, int8: 7) XCTAssertEqual(generated, expected) } } + + func testIcrc7() async throws { + // GoldNFT + let service = try ICRC7.Service("io7gn-vyaaa-aaaak-qcbiq-cai") + let collectionMetadata = try await service.icrc7_collection_metadata() + let tokens = try await service.icrc7_tokens(prev: nil, take: nil) + for metadata in collectionMetadata { + print(metadata.tuple) + } + let token = tokens.first! + print(token) + let tokenMetadata = try await service.icrc7_token_metadata(token_ids: [token]).first!._1! + guard case .Text(let text) = tokenMetadata._1 else { + XCTFail() + return + } + print(text) + } } private class BundleDidProvider: CandidInterfaceDefinitionProvider { diff --git a/Tests/CodeGeneratorTests/DidFiles/ICRC7.did b/Tests/CodeGeneratorTests/DidFiles/ICRC7.did index affb095..2cecd66 100644 --- a/Tests/CodeGeneratorTests/DidFiles/ICRC7.did +++ b/Tests/CodeGeneratorTests/DidFiles/ICRC7.did @@ -55,7 +55,7 @@ service : { icrc7_tx_window : () -> (opt nat) query; icrc7_permitted_drift : () -> (opt nat) query; icrc7_token_metadata : (token_ids : vec nat) - -> (vec opt vec record { text; Value }) query; + -> (vec record { nat; opt record { text; Value } }) query; icrc7_owner_of : (token_ids : vec nat) -> (vec opt Account) query; icrc7_balance_of : (vec Account) -> (vec nat) query; diff --git a/Tests/CodeGeneratorTests/Generated/ICRC7.did.generated_swift b/Tests/CodeGeneratorTests/Generated/ICRC7.did.generated_swift index e5b1f39..89d1f32 100644 --- a/Tests/CodeGeneratorTests/Generated/ICRC7.did.generated_swift +++ b/Tests/CodeGeneratorTests/Generated/ICRC7.did.generated_swift @@ -139,7 +139,7 @@ enum ICRC7 { /// icrc7_tx_window : () -> (opt nat) query; /// icrc7_permitted_drift : () -> (opt nat) query; /// icrc7_token_metadata : (token_ids : vec nat) - /// -> (vec opt vec record { text; Value }) query; + /// -> (vec record { nat; opt record { text; Value } }) query; /// icrc7_owner_of : (token_ids : vec nat) /// -> (vec opt Account) query; /// icrc7_balance_of : (vec Account) -> (vec nat) query; @@ -256,9 +256,9 @@ enum ICRC7 { } /// icrc7_token_metadata : (token_ids : vec nat) - /// -> (vec opt vec record { text; Value }) query; - func icrc7_token_metadata(token_ids: [BigUInt], sender: ICPSigningPrincipal? = nil) async throws -> [[CandidTuple2]?] { - let caller = ICPQuery<[BigUInt], [[CandidTuple2]?]>(canister, "icrc7_token_metadata") + /// -> (vec record { nat; opt record { text; Value } }) query; + func icrc7_token_metadata(token_ids: [BigUInt], sender: ICPSigningPrincipal? = nil) async throws -> [CandidTuple2?>] { + let caller = ICPQuery<[BigUInt], [CandidTuple2?>]>(canister, "icrc7_token_metadata") let response = try await caller.callMethod(token_ids, client, sender: sender) return response } diff --git a/Tests/CodeGeneratorTests/Generated/ICRC7.did.swift b/Tests/CodeGeneratorTests/Generated/ICRC7.did.swift index e5b1f39..89d1f32 100644 --- a/Tests/CodeGeneratorTests/Generated/ICRC7.did.swift +++ b/Tests/CodeGeneratorTests/Generated/ICRC7.did.swift @@ -139,7 +139,7 @@ enum ICRC7 { /// icrc7_tx_window : () -> (opt nat) query; /// icrc7_permitted_drift : () -> (opt nat) query; /// icrc7_token_metadata : (token_ids : vec nat) - /// -> (vec opt vec record { text; Value }) query; + /// -> (vec record { nat; opt record { text; Value } }) query; /// icrc7_owner_of : (token_ids : vec nat) /// -> (vec opt Account) query; /// icrc7_balance_of : (vec Account) -> (vec nat) query; @@ -256,9 +256,9 @@ enum ICRC7 { } /// icrc7_token_metadata : (token_ids : vec nat) - /// -> (vec opt vec record { text; Value }) query; - func icrc7_token_metadata(token_ids: [BigUInt], sender: ICPSigningPrincipal? = nil) async throws -> [[CandidTuple2]?] { - let caller = ICPQuery<[BigUInt], [[CandidTuple2]?]>(canister, "icrc7_token_metadata") + /// -> (vec record { nat; opt record { text; Value } }) query; + func icrc7_token_metadata(token_ids: [BigUInt], sender: ICPSigningPrincipal? = nil) async throws -> [CandidTuple2?>] { + let caller = ICPQuery<[BigUInt], [CandidTuple2?>]>(canister, "icrc7_token_metadata") let response = try await caller.callMethod(token_ids, client, sender: sender) return response }