Skip to content

Commit

Permalink
feat: icp coin modularization (#8560)
Browse files Browse the repository at this point in the history
* feat: icp coin modularization

* feat: rename icp to files to intenet computer

* fix conflicts

* adress comments

* adress comments

* address comments

* remove comment

* remove package in desktop

* lint stuff

* update pnpm-lock file

* fix stuffs
  • Loading branch information
Canestin authored Dec 11, 2024
1 parent 23bb5d6 commit 013e3ac
Show file tree
Hide file tree
Showing 63 changed files with 937 additions and 511 deletions.
6 changes: 6 additions & 0 deletions .changeset/twelve-squids-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@ledgerhq/coin-internet_computer": minor
"@ledgerhq/live-common": minor
---

internet computer coin modularization
38 changes: 38 additions & 0 deletions libs/coin-modules/coin-internet_computer/.unimportedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"entry": [
"src/api/api.ts",
"src/api/index.ts",
"src/bridge/bridgeHelpers/account.ts",
"src/bridge/bridgeHelpers/addresses.ts",
"src/bridge/bridgeHelpers/fee.ts",
"src/bridge/bridgeHelpers/icpRosetta/index.ts",
"src/bridge/bridgeHelpers/icpRosetta/types.ts",
"src/bridge/bridgeHelpers/icpRosetta/utils.ts",
"src/bridge/broadcast.ts",
"src/bridge/buildOptimisticOperation.ts",
"src/bridge/createTransaction.ts",
"src/bridge/deviceTransactionConfig.ts",
"src/bridge/estimateMaxSpendable.ts",
"src/bridge/getTransactionStatus.ts",
"src/bridge/index.ts",
"src/bridge/prepareTransaction.ts",
"src/bridge/signOperation.ts",
"src/bridge/transaction.ts",
"src/common-logic/index.ts",
"src/common-logic/utils.ts",
"src/consts.ts",
"src/errors.ts",
"src/hw-signMessage.ts",
"src/signer/getAddress.ts",
"src/signer/index.ts",
"src/test/bot-specs.ts",
"src/test/bridgeDatasetTest.ts",
"src/test/cli.ts",
"src/test/index.ts",
"src/test/speculos-deviceActions.ts",
"src/types/common.ts",
"src/types/index.ts",
"src/types/signer.ts"
],
"ignoreUnresolved": ["form-data", "proxy-from-env", "follow-redirects"]
}
9 changes: 9 additions & 0 deletions libs/coin-modules/coin-internet_computer/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
module.exports = {
collectCoverageFrom: ["src/**/*.ts"],
coverageDirectory: "coverage",
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["lib/", "lib-es/"],
passWithNoTests: true,
};
128 changes: 128 additions & 0 deletions libs/coin-modules/coin-internet_computer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
{
"name": "@ledgerhq/coin-internet_computer",
"version": "1.3.2",
"description": "Ledger Internet Computer integration",
"keywords": [
"Ledger",
"LedgerWallet",
"ICP",
"Internet Computer"
],
"repository": {
"type": "git",
"url": "https://github.com/LedgerHQ/ledger-live.git"
},
"bugs": {
"url": "https://github.com/LedgerHQ/ledger-live/issues"
},
"homepage": "https://github.com/LedgerHQ/ledger-live/tree/develop/libs/coin-modules/coin-internet_computer",
"publishConfig": {
"access": "public"
},
"typesVersions": {
"*": {
"lib/*": [
"lib/*"
],
"lib-es/*": [
"lib-es/*"
],
"specs": [
"lib/test/bot-specs"
],
"*": [
"lib/*",
"lib/api/*",
"lib/bridge/*",
"lib/common-logic/*",
"lib/signer/*",
"lib/test/*",
"lib/types/*"
]
}
},
"exports": {
"./lib/*": "./lib/*.js",
"./lib-es/*": "./lib-es/*.js",
"./api": {
"require": "./lib/api/index.js",
"default": "./lib-es/api/index.js"
},
"./deviceTransactionConfig": {
"require": "./lib/bridge/deviceTransactionConfig.js",
"default": "./lib-es/bridge/deviceTransactionConfig.js"
},
"./logic": {
"require": "./lib/common-logic/index.js",
"default": "./lib-es/common-logic/index.js"
},
"./signer": {
"require": "./lib/signer/index.js",
"default": "./lib-es/signer/index.js"
},
"./specs": {
"require": "./lib/test/bot-specs.js",
"default": "./lib-es/test/bot-specs.js"
},
"./transaction": {
"require": "./lib/bridge/transaction.js",
"default": "./lib-es/bridge/transaction.js"
},
"./types": {
"require": "./lib/types/index.js",
"default": "./lib-es/types/index.js"
},
"./*": {
"require": "./lib/*.js",
"default": "./lib-es/*.js"
},
".": {
"require": "./lib/index.js",
"default": "./lib-es/index.js"
},
"./package.json": "./package.json"
},
"license": "Apache-2.0",
"dependencies": {
"@ledgerhq/coin-framework": "workspace:^",
"@ledgerhq/cryptoassets": "workspace:^",
"@ledgerhq/devices": "workspace:*",
"@ledgerhq/errors": "workspace:^",
"@ledgerhq/live-env": "workspace:^",
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
"@dfinity/agent": "^0.21.0",
"@dfinity/candid": "^0.21.0",
"@dfinity/principal": "^0.15.6",
"bignumber.js": "^9.1.2",
"invariant": "^2.2.2",
"lodash": "^4.17.21",
"simple-cbor": "^0.4.1",
"big-integer": "^1.6.51",
"rxjs": "^7.8.1",
"expect": "^27.4.6"
},
"devDependencies": {
"@types/invariant": "^2.2.2",
"@types/jest": "^29.5.10",
"@types/lodash": "^4.14.191",
"@types/semver": "^7.5.8",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"axios": "1.7.7"
},
"scripts": {
"clean": "rimraf lib lib-es",
"build": "tsc && tsc -m ES6 --outDir lib-es",
"coverage": "jest --coverage --testPathIgnorePatterns='/bridge.integration.test.ts|node_modules|lib-es|lib/' --passWithNoTests && mv coverage/coverage-final.json coverage/coverage-icp.json",
"prewatch": "pnpm build",
"watch": "tsc --watch",
"watch:es": "tsc --watch -m ES6 --outDir lib-es",
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
"lint:fix": "pnpm lint --fix",
"test": "jest",
"unimported": "unimported"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { log } from "@ledgerhq/logs";
import { AxiosRequestConfig, AxiosResponse } from "axios";
import { getEnv } from "@ledgerhq/live-env";
import network from "@ledgerhq/live-network/network";
import { ICP_BLK_NAME_ROSETTA, ICP_NET_ID_ROSETTA } from "../../consts";
import { ICP_BLK_NAME_ROSETTA, ICP_NET_ID_ROSETTA } from "../consts";
import {
ICPRosettaBlockHeightResponse,
ICPRosettaGetBalancesResponse,
ICPRosettaGetTxnsHistoryResponse,
} from "./icpRosetta/types";
} from "../bridge/bridgeHelpers/icpRosetta/types";

const getICPURL = (path?: string): string => {
const baseUrl = getEnv("API_ICP_ENDPOINT");
Expand Down
1 change: 1 addition & 0 deletions libs/coin-modules/coin-internet_computer/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./api";
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { GetAccountShape } from "../../../../bridge/jsHelpers";
import { decodeAccountId, encodeAccountId } from "../../../../account";
import { fetchBalances, fetchBlockHeight, fetchTxns } from "./api";
import type { GetAccountShape } from "@ledgerhq/coin-framework/bridge/jsHelpers";
import { decodeAccountId, encodeAccountId } from "@ledgerhq/coin-framework/account/index";
import { fetchBalances, fetchBlockHeight, fetchTxns } from "../../api";
import flatMap from "lodash/flatMap";
import { Account } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
import { ICPRosettaGetTxnsHistoryResponse } from "./icpRosetta/types";
import { ICP_FEES } from "../../consts";
import { encodeOperationId } from "../../../../operation";
import { normalizeEpochTimestamp } from "../../utils";
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
import { normalizeEpochTimestamp } from "../../common-logic/utils";
import { InternetComputerOperation } from "../../types";
import invariant from "invariant";
import { deriveAddressFromPubkey } from "./icpRosetta";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { log } from "@ledgerhq/logs";
import { Account } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
import { MAX_MEMO_VALUE } from "../../consts";
import { fetchBalances } from "./api";
import { fetchBalances } from "../../api";

export const getAddress = (
a: Account,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import Transport from "@ledgerhq/hw-transport";
import { Account } from "@ledgerhq/types-live";
import { Transaction } from "../../../types";
import ICP from "@zondax/ledger-icp";
import { constructionInvoke, getICPRosettaNetworkIdentifier } from "../api";
import { constructionInvoke, getICPRosettaNetworkIdentifier } from "../../../api";
import {
ICPRosettaConstructionCombineRequest,
ICPRosettaConstructionCombineResponse,
Expand All @@ -18,8 +16,10 @@ import {
import { ingressExpiry, generateOperations, generateSignaturesPayload } from "./utils";
import { Cbor } from "@dfinity/agent";
import { Principal } from "@dfinity/principal";
import { isError } from "../../../utils";
import BigNumber from "bignumber.js";
import { ICP_SEND_TXN_TYPE } from "../../../consts";
import { SignerContext } from "@ledgerhq/coin-framework/signer";
import { ICPSigner } from "../../../types";

export const getUnsignedTransaction = async (
transaction: Transaction,
Expand Down Expand Up @@ -53,22 +53,23 @@ export const getUnsignedTransaction = async (
};

export const signICPTransaction = async ({
signerContext,
deviceId,
unsignedTxn,
transport,
path,
payloads,
pubkey,
}: {
signerContext: SignerContext<ICPSigner>;
deviceId: string;
unsignedTxn: string;
transport: Transport;
path: string;
payloads: ICPRosettaConstructionPayloadsResponse["payloads"];
pubkey: string;
}): Promise<{
signatures: { txnSig: string; readSig: string };
signedTxn: string;
}> => {
const icp = new ICP(transport);
const decodedTxn: any = Cbor.decode(Buffer.from(unsignedTxn, "hex"));
const txnReqFromCbor = decodedTxn.updates[0][1];
const expiry = new ingressExpiry(BigNumber(decodedTxn.ingress_expiries[0].toString()));
Expand All @@ -86,13 +87,15 @@ export const signICPTransaction = async ({
content: submitReq,
});

const signedTxnRes = await icp.sign(path, Buffer.from(txnBlobToSign), 0);
isError(signedTxnRes);
const { r } = await signerContext(deviceId, async signer => {
const r = await signer.sign(path, Buffer.from(txnBlobToSign), ICP_SEND_TXN_TYPE);
return { r };
});

const result = {
signatures: {
readSig: "",
txnSig: Buffer.from(signedTxnRes.signatureRS ?? "").toString("hex"),
txnSig: Buffer.from(r.signatureRS ?? "").toString("hex"),
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AccountBridge } from "@ledgerhq/types-live";
import { broadcastTxn } from "./bridge/bridgeHelpers/icpRosetta";
import { Transaction } from "./types";
import { broadcastTxn } from "./bridgeHelpers/icpRosetta";
import { Transaction } from "../types";

export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({
signedOperation: { signature, operation },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { OperationType } from "@ledgerhq/types-live";
import { encodeOperationId } from "../../operation";
import { InternetComputerOperation } from "./types";
import { getAddress } from "./bridge/bridgeHelpers/addresses";
import { Account, OperationType } from "@ledgerhq/types-live";
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
import { InternetComputerOperation, Transaction } from "../types";
import { getAddress } from "./bridgeHelpers/addresses";

export const buildOptimisticOperation = async (
account,
transaction,
hash,
account: Account,
transaction: Transaction,
hash: string,
operationType: OperationType = "OUT",
): Promise<InternetComputerOperation> => {
const { id: accountId } = account;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AccountBridge } from "@ledgerhq/types-live";
import { Transaction } from "./types";
import { Transaction } from "../types";
import BigNumber from "bignumber.js";
import { getEstimatedFees } from "./bridge/bridgeHelpers/fee";
import { getEstimatedFees } from "./bridgeHelpers/fee";

export const createTransaction: AccountBridge<Transaction>["createTransaction"] = () => {
// log("debug", "[createTransaction] creating base tx");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
import { log } from "@ledgerhq/logs";
import { Account, AccountLike } from "@ledgerhq/types-live";
import { formatCurrencyUnit } from "../../currencies";
import type { DeviceTransactionField } from "../../transaction";
import { Transaction, TransactionStatus } from "./types";
import { methodToString } from "./utils";
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
import type { CommonDeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";

import { Transaction, TransactionStatus } from "../types";
import { methodToString } from "../common-logic/utils";

const currency = getCryptoCurrencyById("internet_computer");

Expand All @@ -15,8 +16,8 @@ function getDeviceTransactionConfig({
parentAccount: Account | null | undefined;
transaction: Transaction;
status: TransactionStatus;
}): Array<DeviceTransactionField> {
const fields: Array<DeviceTransactionField> = [];
}): Array<CommonDeviceTransactionField> {
const fields: Array<CommonDeviceTransactionField> = [];
fields.push({
type: "text",
label: "Transaction Type",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BigNumber from "bignumber.js";
import { AccountBridge } from "@ledgerhq/types-live";
import { getEstimatedFees } from "./bridge/bridgeHelpers/fee";
import { Transaction } from "./types";
import { getEstimatedFees } from "./bridgeHelpers/fee";
import { Transaction } from "../types";

export const estimateMaxSpendable: AccountBridge<Transaction>["estimateMaxSpendable"] = async ({
account,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
} from "@ledgerhq/errors";
import BigNumber from "bignumber.js";
import { AccountBridge } from "@ledgerhq/types-live";
import { getAddress, validateAddress, validateMemo } from "./bridge/bridgeHelpers/addresses";
import { Transaction, TransactionStatus } from "./types";
import { InvalidMemoICP } from "./errors";
import { getAddress, validateAddress, validateMemo } from "./bridgeHelpers/addresses";
import { Transaction, TransactionStatus } from "../types";
import { InvalidMemoICP } from "../errors";

export const getTransactionStatus: AccountBridge<Transaction>["getTransactionStatus"] = async (
account,
Expand Down
Loading

0 comments on commit 013e3ac

Please sign in to comment.