@ledgerhq/coin-internet_computer
Version:
Ledger Internet Computer integration
148 lines • 7.53 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchTxns = exports.fetchBalance = exports.ensureTransferCallAccepted = exports.broadcastTxn = exports.fetchBlockHeight = void 0;
const logs_1 = require("@ledgerhq/logs");
const ledger_live_icp_1 = require("@zondax/ledger-live-icp");
const agent_1 = require("@zondax/ledger-live-icp/agent");
const utils_1 = require("@zondax/ledger-live-icp/utils");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const invariant_1 = __importDefault(require("invariant"));
const consts_1 = require("../consts");
function toArrayBuffer(view) {
if (view instanceof ArrayBuffer) {
return view;
}
const copy = new Uint8Array(view.byteLength);
copy.set(view);
return copy.buffer;
}
function requestIdFromHex(hex) {
const bytes = Buffer.from(hex, "hex");
const copy = new Uint8Array(bytes);
return copy.buffer;
}
function throwIfLedgerTransferReplyIsErr(replyBuf) {
const transferIdlFunc = (0, ledger_live_icp_1.getCanisterIdlFunc)(ledger_live_icp_1.ledgerIdlFactory, "transfer");
const decoded = (0, ledger_live_icp_1.decodeCanisterIdlFunc)(transferIdlFunc, replyBuf);
const out = decoded[0];
if (out.Err) {
const message = JSON.stringify(out.Err, (_, v) => (typeof v === "bigint" ? v.toString() : v));
throw new Error(message);
}
}
async function fetchRootKey() {
const res = await fetch(`${consts_1.ICP_NETWORK_URL}/api/v2/status`);
if (res.status !== 200) {
throw new Error(`Failed to fetch status: ${await res.text()}`);
}
const status = agent_1.Cbor.decode(await res.arrayBuffer());
(0, invariant_1.default)(status.root_key, "[ICP](fetchRootKey) Missing root_key in status response");
return toArrayBuffer(status.root_key);
}
const fetchBlockHeight = async () => {
const canisterId = ledger_live_icp_1.Principal.fromText(consts_1.MAINNET_LEDGER_CANISTER_ID);
const queryBlocksRawRequest = {
start: BigInt(0),
length: BigInt(1),
};
const queryBlocksIdlFunc = (0, ledger_live_icp_1.getCanisterIdlFunc)(ledger_live_icp_1.ledgerIdlFactory, "query_blocks");
const queryBlocksargs = (0, ledger_live_icp_1.encodeCanisterIdlFunc)(queryBlocksIdlFunc, [queryBlocksRawRequest]);
const agent = await (0, agent_1.getAgent)(consts_1.ICP_NETWORK_URL);
const blockHeightRes = await agent.query(canisterId, {
arg: queryBlocksargs,
methodName: "query_blocks",
});
(0, invariant_1.default)(blockHeightRes.status === "replied", "[ICP](fetchBlockHeight) Query failed");
const decodedIdl = (0, ledger_live_icp_1.decodeCanisterIdlFunc)(queryBlocksIdlFunc, blockHeightRes.reply.arg);
const decoded = (0, utils_1.fromNullable)(decodedIdl);
(0, invariant_1.default)(decoded, "[ICP](fetchBlockHeight) Decoding failed");
return (0, bignumber_js_1.default)(decoded.chain_length.toString());
};
exports.fetchBlockHeight = fetchBlockHeight;
const broadcastTxn = async (payload, canisterId, type) => {
(0, logs_1.log)("debug", `[ICP] Broadcasting ${type} to ${canisterId}, body: ${payload.toString("hex")}`);
const res = await fetch(`${consts_1.ICP_NETWORK_URL}/api/v3/canister/${canisterId}/${type}`, {
body: payload,
method: "POST",
headers: {
"Content-Type": "application/cbor",
},
});
if (res.status === 200) {
return new Uint8Array(await res.arrayBuffer());
}
throw new Error(`Failed to broadcast transaction: ${await res.text()}`);
};
exports.broadcastTxn = broadcastTxn;
const ensureTransferCallAccepted = async (syncCallResponse, transferRequestIdHex) => {
const requestId = requestIdFromHex(transferRequestIdHex);
const canisterId = ledger_live_icp_1.Principal.fromText(consts_1.MAINNET_LEDGER_CANISTER_ID);
const top = agent_1.Cbor.decode(toArrayBuffer(syncCallResponse));
(0, invariant_1.default)(top.status === "replied" && top.certificate, "[ICP](ensureTransferCallAccepted) Decoding failed");
const rootKey = await fetchRootKey();
const cert = await agent_1.Certificate.create({
certificate: toArrayBuffer(top.certificate),
rootKey,
canisterId,
maxAgeInMinutes: 100,
});
const replyBuf = (0, agent_1.lookupResultToBuffer)(cert.lookup(["request_status", requestId, "reply"]));
(0, invariant_1.default)(replyBuf, "[ICP](ensureTransferCallAccepted) Reply status not found");
throwIfLedgerTransferReplyIsErr(replyBuf);
};
exports.ensureTransferCallAccepted = ensureTransferCallAccepted;
const fetchBalance = async (address) => {
const agent = await (0, agent_1.getAgent)(consts_1.ICP_NETWORK_URL);
const indexCanister = ledger_live_icp_1.Principal.fromText(consts_1.MAINNET_INDEX_CANISTER_ID);
const getBalanceIdlFunc = (0, ledger_live_icp_1.getCanisterIdlFunc)(ledger_live_icp_1.indexIdlFactory, "get_account_identifier_balance");
const getBalanceArgs = (0, ledger_live_icp_1.encodeCanisterIdlFunc)(getBalanceIdlFunc, [address]);
const balanceRes = await agent.query(indexCanister, {
arg: getBalanceArgs,
methodName: "get_account_identifier_balance",
});
if (balanceRes.status !== "replied") {
(0, logs_1.log)("debug", `[ICP](fetchBalance) Query failed: ${balanceRes.status}`);
return (0, bignumber_js_1.default)(0);
}
const decodedBalance = (0, ledger_live_icp_1.decodeCanisterIdlFunc)(getBalanceIdlFunc, balanceRes.reply.arg);
const balance = (0, utils_1.fromNullable)(decodedBalance);
if (!balance) {
return (0, bignumber_js_1.default)(0);
}
return (0, bignumber_js_1.default)(balance.toString());
};
exports.fetchBalance = fetchBalance;
const fetchTxns = async (address, startBlockHeight, stopBlockHeight = BigInt(0)) => {
if (startBlockHeight <= stopBlockHeight) {
return [];
}
const agent = await (0, agent_1.getAgent)(consts_1.ICP_NETWORK_URL);
const canisterId = ledger_live_icp_1.Principal.fromText(consts_1.MAINNET_INDEX_CANISTER_ID);
const transactionsRawRequest = {
account_identifier: address,
start: [startBlockHeight],
max_results: BigInt(consts_1.FETCH_TXNS_LIMIT),
};
const getTransactionsIdlFunc = (0, ledger_live_icp_1.getCanisterIdlFunc)(ledger_live_icp_1.indexIdlFactory, "get_account_identifier_transactions");
const getTransactionsArgs = (0, ledger_live_icp_1.encodeCanisterIdlFunc)(getTransactionsIdlFunc, [
transactionsRawRequest,
]);
const transactionsRes = await agent.query(canisterId, {
arg: getTransactionsArgs,
methodName: "get_account_identifier_transactions",
});
(0, invariant_1.default)(transactionsRes.status === "replied", "[ICP](fetchTxns) Query failed");
const decodedTransactions = (0, ledger_live_icp_1.decodeCanisterIdlFunc)(getTransactionsIdlFunc, transactionsRes.reply.arg);
const response = (0, utils_1.fromNullable)(decodedTransactions);
(0, invariant_1.default)(response, "[ICP](fetchTxns) Decoding failed");
if (response.Ok.transactions.length === 0) {
return [];
}
const nextTxns = await (0, exports.fetchTxns)(address, response.Ok.transactions.at(-1)?.id ?? BigInt(0), stopBlockHeight);
return [...response.Ok.transactions, ...nextTxns];
};
exports.fetchTxns = fetchTxns;
//# sourceMappingURL=api.js.map