diff --git a/README.md b/README.md index 7c4a61e..4e01603 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ A CLI to interact with the Internet Computer App on Ledger Nano S/X devices. +## Important Note + +> [!WARNING] +> The CLI does NOT support tokens with different than eight decimals at the moment. + +**Do NOT USE the cli to transfer ckETH or ckERC20 tokens.** + ## Quick Start - Install `node >= 18.13.0`. @@ -28,4 +35,4 @@ To execute a command, you can use `npm run execute -- `. For example - The command `ic-hardware-wallet --network https://nnsdapp.dfinity.network icp balance`. -- Would be `npm run execute -- --network https://nnsdapp.dfinity.network icp balance` for development. +- Would be `npm run execute -- --network http://127.0.0.1:4943 icp balance` for development. diff --git a/src/index.ts b/src/index.ts index 9bb4ac7..99d7577 100755 --- a/src/index.ts +++ b/src/index.ts @@ -5,15 +5,10 @@ */ import { Command, Option } from "commander"; import { - AccountIdentifier, - LedgerCanister, GenesisTokenCanister, GovernanceCanister, GovernanceError, - ICP, InsufficientAmountError, - InsufficientFundsError, - TokenAmount, Vote, Topic, } from "@dfinity/nns"; @@ -30,7 +25,6 @@ import { tryParseSnsNeuronId, } from "./parsers"; import { Principal } from "@dfinity/principal"; -import type { Secp256k1PublicKey } from "./ledger/secp256k1"; import { assertLedgerVersion, hasValidStake, @@ -41,26 +35,28 @@ import { nowInBigIntNanoSeconds, isCurrentVersionSmallerThanFullCandidParser, } from "./utils"; -import { - CANDID_PARSER_VERSION, - FULL_CANDID_PARSER_VERSION, - HOTKEY_PERMISSIONS, -} from "./constants"; +import { CANDID_PARSER_VERSION, HOTKEY_PERMISSIONS } from "./constants"; import { AnonymousIdentity, Identity } from "@dfinity/agent"; import { SnsGovernanceCanister, SnsNeuronId } from "@dfinity/sns"; -import { fromNullable, toNullable } from "@dfinity/utils"; +import { TokenAmountV2, fromNullable, toNullable } from "@dfinity/utils"; import { encodeIcrcAccount, IcrcAccount, IcrcLedgerCanister, } from "@dfinity/ledger-icrc"; import chalk from "chalk"; +import { + AccountIdentifier, + LedgerCanister, + InsufficientFundsError, +} from "@dfinity/ledger-icp"; // Add polyfill for `window` for `TransportWebHID` checks to work. import "node-window-polyfill/register"; // @ts-ignore (no types are available) import fetch from "node-fetch"; +import { Secp256k1PublicKey } from "./ledger/secp256k1"; (global as any).fetch = fetch; // Add polyfill for `window.fetch` for agent-js to work. @@ -112,7 +108,9 @@ async function snsListNeurons(canisterId: Principal) { neurons.forEach((n) => { const neuronId = fromNullable(n.id); if (neuronId !== undefined) { - log(`Neuron ID: ${subaccountToHexString(neuronId.id)}`); + log( + `Neuron ID: ${subaccountToHexString(Uint8Array.from(neuronId.id))}` + ); } else { log("Neuron ID: N/A"); } @@ -199,7 +197,7 @@ async function snsDisburse({ to, }: { neuronId: SnsNeuronId; - amount?: TokenAmount; + amount?: TokenAmountV2; to?: IcrcAccount; } & SnsCallParams) { const identity = await getIdentity(); @@ -301,7 +299,7 @@ async function icrcSendTokens({ amount, to, }: { - amount: TokenAmount; + amount: TokenAmountV2; to: IcrcAccount; canisterId: Principal; }) { @@ -344,7 +342,6 @@ async function getBalance() { const ledger = LedgerCanister.create({ agent: await getCurrentAgent(new AnonymousIdentity()), - hardwareWallet: await isCurrentVersionSmallerThanFullCandidParser(identity), }); const balance = await ledger.accountBalance({ @@ -360,11 +357,10 @@ async function getBalance() { * @param to The account identifier in hex. * @param amount Amount to send in e8s. */ -async function sendICP(to: AccountIdentifier, amount: ICP) { +async function sendICP(to: AccountIdentifier, amount: TokenAmountV2) { const identity = await getIdentity(); const ledger = LedgerCanister.create({ agent: await getCurrentAgent(identity), - hardwareWallet: await isCurrentVersionSmallerThanFullCandidParser(identity), }); const blockHeight = await ledger.transfer({ @@ -403,7 +399,7 @@ async function showInfo(showOnDevice?: boolean) { * * @param amount Amount to stake in e8s. */ -async function stakeNeuron(stake: ICP) { +async function stakeNeuron(stake: TokenAmountV2) { const identity = await getIdentity(); const ledger = LedgerCanister.create({ agent: await getCurrentAgent(identity), @@ -425,11 +421,15 @@ async function stakeNeuron(stake: ICP) { }); ok(`Staked neuron with ID: ${stakedNeuronId}`); - } catch (error) { + } catch (error: unknown) { if (error instanceof InsufficientAmountError) { err(`Cannot stake less than ${error.minimumAmount} e8s`); } else if (error instanceof InsufficientFundsError) { - err(`Your account has insufficient funds (${error.balance} e8s)`); + err( + `Your account has insufficient funds (${ + (error as InsufficientFundsError).balance + } e8s)` + ); } else { console.log(error); } diff --git a/src/ledger/secp256k1.ts b/src/ledger/secp256k1.ts index cd3711a..ce3b707 100644 --- a/src/ledger/secp256k1.ts +++ b/src/ledger/secp256k1.ts @@ -83,8 +83,8 @@ export class Secp256k1PublicKey implements PublicKey { return rawKey; } - private readonly rawKey: ArrayBuffer; - private readonly derKey: DerEncodedPublicKey; + readonly rawKey: ArrayBuffer; + readonly derKey: DerEncodedPublicKey; // `fromRaw` and `fromDer` should be used for instantiation, not this constructor. private constructor(key: ArrayBuffer) { @@ -107,9 +107,11 @@ export class Secp256k1PublicKey implements PublicKey { if (!isHex) { throw new Error(`${hexPubKey} is not a hex string.`); } - + if (hexPubKey.length < 130 || hexPubKey.length > 150) { - throw new Error(`The key must be >= 130 characters and <= 150 characters.`); + throw new Error( + `The key must be >= 130 characters and <= 150 characters.` + ); } return hexPubKey; diff --git a/src/parsers.ts b/src/parsers.ts index d95d673..460584e 100644 --- a/src/parsers.ts +++ b/src/parsers.ts @@ -1,9 +1,10 @@ import { InvalidArgumentError } from "commander"; -import { AccountIdentifier, ICPToken, Token, TokenAmount } from "@dfinity/nns"; +import { AccountIdentifier } from "@dfinity/ledger-icp"; import { Principal } from "@dfinity/principal"; import { hexToSnsNeuronId } from "./utils"; import { SnsNeuronId } from "@dfinity/sns"; import { decodeIcrcAccount, IcrcAccount } from "@dfinity/ledger-icrc"; +import { TokenAmountV2, ICPToken } from "@dfinity/utils"; export function tryParseInt(value: string): number { const parsedValue = parseInt(value, 10); @@ -64,9 +65,9 @@ export function tryParseSnsNeuronId(value: string): SnsNeuronId { } } -export function tryParseE8s(e8s: string): TokenAmount { +export function tryParseE8s(e8s: string): TokenAmountV2 { try { - return TokenAmount.fromE8s({ + return TokenAmountV2.fromUlps({ amount: tryParseBigInt(e8s), token: ICPToken, });