Skip to content

Commit

Permalink
Merge pull request #4 from kosta-bity/icrc7
Browse files Browse the repository at this point in the history
fix icrc7
  • Loading branch information
kosta-bity authored Aug 20, 2024
2 parents 9575ca8 + 150382d commit 3e6c944
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 37 deletions.
61 changes: 37 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
```

Expand All @@ -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
}
}
```

Expand All @@ -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:
Expand Down Expand Up @@ -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)
}
}
```

Expand All @@ -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.
4 changes: 1 addition & 3 deletions Sources/Candid/Encoding/CandidDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ private class CandidSingleValueDecodingContainer: SingleValueDecodingContainer {
}

func decode<T>(_ 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()
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/IcpKit/ICPRequest/ICPService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
18 changes: 18 additions & 0 deletions Tests/CodeGeneratorTests/CodeGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion Tests/CodeGeneratorTests/DidFiles/ICRC7.did
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions Tests/CodeGeneratorTests/Generated/ICRC7.did.generated_swift
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, Value>]?] {
let caller = ICPQuery<[BigUInt], [[CandidTuple2<String, Value>]?]>(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<BigUInt, CandidTuple2<String, Value>?>] {
let caller = ICPQuery<[BigUInt], [CandidTuple2<BigUInt, CandidTuple2<String, Value>?>]>(canister, "icrc7_token_metadata")
let response = try await caller.callMethod(token_ids, client, sender: sender)
return response
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/CodeGeneratorTests/Generated/ICRC7.did.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, Value>]?] {
let caller = ICPQuery<[BigUInt], [[CandidTuple2<String, Value>]?]>(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<BigUInt, CandidTuple2<String, Value>?>] {
let caller = ICPQuery<[BigUInt], [CandidTuple2<BigUInt, CandidTuple2<String, Value>?>]>(canister, "icrc7_token_metadata")
let response = try await caller.callMethod(token_ids, client, sender: sender)
return response
}
Expand Down

0 comments on commit 3e6c944

Please sign in to comment.