Skip to content

Commit 9f4003b

Browse files
author
Llorenç
committed
Support ICRC tokens multiple decimals
1 parent fff2fcf commit 9f4003b

File tree

4 files changed

+155
-42
lines changed

4 files changed

+155
-42
lines changed

package-lock.json

Lines changed: 105 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"@dfinity/utils": "^2.5.0",
4444
"@ledgerhq/hw-transport-node-hid-noevents": "^6.3.0",
4545
"@ledgerhq/hw-transport-webhid": "^6.27.1",
46-
"@zondax/ledger-icp": "^0.6.0",
46+
"@zondax/ledger-icp": "^3.2.6",
4747
"chalk": "^4.1.2",
4848
"commander": "^9.0.0",
4949
"node-fetch": "^2.6.1",

src/index.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ import {
3838
import { CANDID_PARSER_VERSION, HOTKEY_PERMISSIONS } from "./constants";
3939
import { AnonymousIdentity, Identity } from "@dfinity/agent";
4040
import { SnsGovernanceCanister, SnsNeuronId } from "@dfinity/sns";
41-
import { TokenAmountV2, fromNullable, toNullable } from "@dfinity/utils";
4241
import {
42+
TokenAmountV2,
43+
fromNullable,
44+
isNullish,
45+
toNullable,
46+
} from "@dfinity/utils";
47+
import {
48+
decodeIcrcAccount,
4349
encodeIcrcAccount,
4450
IcrcAccount,
4551
IcrcLedgerCanister,
52+
toTransferArg,
4653
} from "@dfinity/ledger-icrc";
4754
import chalk from "chalk";
4855
import {
@@ -293,13 +300,23 @@ async function icrcGetBalance(
293300
ok(`Account ${encodeIcrcAccount(account)} has balance ${balance} e8s`);
294301
}
295302

303+
async function supportedLedgers() {
304+
const identity = await getIdentity();
305+
const supportedTokens = await identity.getSupportedTokens();
306+
const ledgersTextInfo = supportedTokens.map(
307+
(ledger) =>
308+
`Token Symbol: ${ledger.tokenSymbol}, Canister Id: ${ledger.canisterId}, Decimals: ${ledger.decimals}`
309+
);
310+
ok(`Supported ledgers: ${ledgersTextInfo.join("\n")}`);
311+
}
312+
296313
// TODO: Add support for subaccounts
297314
async function icrcSendTokens({
298315
canisterId = MAINNET_LEDGER_CANISTER_ID,
299316
amount,
300317
to,
301318
}: {
302-
amount: TokenAmountV2;
319+
amount: bigint;
303320
to: IcrcAccount;
304321
canisterId: Principal;
305322
}) {
@@ -319,7 +336,7 @@ async function icrcSendTokens({
319336
owner: to.owner,
320337
subaccount: toNullable(to.subaccount),
321338
},
322-
amount: amount.toE8s(),
339+
amount: amount,
323340
fee,
324341
created_at_time: nowInBigIntNanoSeconds(),
325342
});
@@ -820,6 +837,11 @@ async function main() {
820837
)
821838
.action((args) => run(() => icrcGetBalance(args.canisterId)))
822839
)
840+
.addCommand(
841+
new Command("supported-tokens")
842+
.description("Get supported tokens of the ledger device.")
843+
.action((args) => run(supportedLedgers))
844+
)
823845
.addCommand(
824846
new Command("transfer")
825847
.description("Send tokens from the ICRC wallet to another account.")
@@ -836,7 +858,7 @@ async function main() {
836858
.requiredOption(
837859
"--amount <amount>",
838860
"Amount to transfer in e8s",
839-
tryParseE8s
861+
tryParseBigInt
840862
)
841863
.action(({ to, amount, canisterId }) => {
842864
run(() => icrcSendTokens({ to, amount, canisterId }));

src/ledger/identity.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
SignIdentity,
99
} from "@dfinity/agent";
1010
import { Principal } from "@dfinity/principal";
11-
import LedgerApp, { LedgerError, ResponseSign } from "@zondax/ledger-icp";
11+
import LedgerApp, { ResponseSign, TokenInfo } from "@zondax/ledger-icp";
1212
import { Secp256k1PublicKey } from "./secp256k1";
1313

1414
// @ts-ignore (no types are available)
@@ -18,6 +18,7 @@ import TransportNodeHidNoEvents from "@ledgerhq/hw-transport-node-hid-noevents";
1818
// Add polyfill for `window.fetch` for agent-js to work.
1919
// @ts-ignore (no types are available)
2020
import fetch from "node-fetch";
21+
import { isNullish, nonNullish } from "@dfinity/utils";
2122
global.fetch = fetch;
2223

2324
/**
@@ -101,7 +102,6 @@ export class LedgerIdentity extends SignIdentity {
101102
}
102103
}
103104
}
104-
105105
private static async _fetchPublicKeyFromDevice(
106106
app: LedgerApp,
107107
derivePath: string
@@ -110,11 +110,13 @@ export class LedgerIdentity extends SignIdentity {
110110
// @ts-ignore
111111
if (resp.returnCode == 28161) {
112112
throw "Please open the Internet Computer app on your wallet and try again.";
113-
} else if (resp.returnCode == LedgerError.TransactionRejected) {
113+
} else if (resp.returnCode == 65535) {
114114
throw "Ledger Wallet is locked. Unlock it and try again.";
115115
// @ts-ignore
116116
} else if (resp.returnCode == 65535) {
117117
throw "Unable to fetch the public key. Please try again.";
118+
} else if (isNullish(resp.publicKey)) {
119+
throw "Public key not available. Please try again.";
118120
}
119121

120122
// This type doesn't have the right fields in it, so we have to manually type it.
@@ -152,14 +154,29 @@ export class LedgerIdentity extends SignIdentity {
152154
public async getVersion(): Promise<Version> {
153155
return this._executeWithApp(async (app: LedgerApp) => {
154156
const res = await app.getVersion();
157+
// TODO: What would it mean if there is no version? Should we throw an error?
155158
return {
156-
major: res.major,
157-
minor: res.minor,
158-
patch: res.patch,
159+
major: res.major ?? 0,
160+
minor: res.minor ?? 0,
161+
patch: res.patch ?? 0,
159162
};
160163
});
161164
}
162165

166+
public async getSupportedTokens(): Promise<TokenInfo[]> {
167+
return this._executeWithApp(async (app: LedgerApp) => {
168+
const res = await app.tokenRegistry();
169+
if (nonNullish(res.tokenRegistry)) {
170+
return res.tokenRegistry;
171+
}
172+
throw new Error(
173+
`A ledger error happened during token registry fetch:
174+
Code: ${res.returnCode}
175+
Message: ${JSON.stringify(res.errorMessage)}`
176+
);
177+
});
178+
}
179+
163180
public getPublicKey(): PublicKey {
164181
return this._publicKey;
165182
}

0 commit comments

Comments
 (0)