diff --git a/modules/sdk-coin-icp/src/icp.ts b/modules/sdk-coin-icp/src/icp.ts index 0ff9eb3746..cb22caab29 100644 --- a/modules/sdk-coin-icp/src/icp.ts +++ b/modules/sdk-coin-icp/src/icp.ts @@ -20,7 +20,7 @@ import { TssVerifyAddressOptions, VerifyTransactionOptions, } from '@bitgo/sdk-core'; -import { coins, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; +import { coins, NetworkType, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; import { Principal } from '@dfinity/principal'; import axios from 'axios'; import BigNumber from 'bignumber.js'; @@ -30,6 +30,7 @@ import * as mpc from '@bitgo/sdk-lib-mpc'; import { CurveType, LEDGER_CANISTER_ID, + TESTNET_LEDGER_CANISTER_ID, PayloadsData, PUBLIC_NODE_REQUEST_ENDPOINT, PublicNodeSubmitResponse, @@ -249,7 +250,9 @@ export class Icp extends BaseCoin { private getPublicNodeBroadcastEndpoint(): string { const nodeUrl = this.getPublicNodeUrl(); - const principal = Principal.fromUint8Array(LEDGER_CANISTER_ID); + const ledgerCanisterId = + this._staticsCoin.network.type === NetworkType.TESTNET ? TESTNET_LEDGER_CANISTER_ID : LEDGER_CANISTER_ID; + const principal = Principal.fromUint8Array(ledgerCanisterId); const canisterIdHex = principal.toText(); const endpoint = `${nodeUrl}${PUBLIC_NODE_REQUEST_ENDPOINT}${canisterIdHex}/call`; return endpoint; @@ -263,7 +266,7 @@ export class Icp extends BaseCoin { */ protected async getAccountBalance(publicKeyHex: string): Promise { const principalId = utils.getPrincipalIdFromPublicKey(publicKeyHex).toText(); - const agent = new IcpAgent(this.getPublicNodeUrl()); + const agent = new IcpAgent(this.getPublicNodeUrl(), this._staticsCoin); return agent.getBalance(principalId); } @@ -277,7 +280,7 @@ export class Icp extends BaseCoin { * @throws Will propagate any errors encountered while communicating with the ICP node. */ protected async getFeeData(): Promise { - const agent = new IcpAgent(this.getPublicNodeUrl()); + const agent = new IcpAgent(this.getPublicNodeUrl(), this._staticsCoin); return await agent.getFee(); } diff --git a/modules/sdk-coin-icp/src/lib/icpAgent.ts b/modules/sdk-coin-icp/src/lib/icpAgent.ts index e5af5e051d..f8c7e76c26 100644 --- a/modules/sdk-coin-icp/src/lib/icpAgent.ts +++ b/modules/sdk-coin-icp/src/lib/icpAgent.ts @@ -1,14 +1,24 @@ import { Principal } from '@dfinity/principal'; import { HttpAgent, replica, AgentCanister } from 'ic0'; import utils from './utils'; -import { ACCOUNT_BALANCE_CALL, LEDGER_CANISTER_ID, ICRC1_FEE_KEY, METADATA_CALL, DEFAULT_SUBACCOUNT } from './iface'; +import { + ACCOUNT_BALANCE_CALL, + LEDGER_CANISTER_ID, + TESTNET_LEDGER_CANISTER_ID, + ICRC1_FEE_KEY, + METADATA_CALL, + DEFAULT_SUBACCOUNT, +} from './iface'; import BigNumber from 'bignumber.js'; +import { NetworkType, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; export class IcpAgent { private readonly host: string; + private readonly staticsCoin: Readonly; - constructor(host: string) { + constructor(host: string, staticsCoin: Readonly) { this.host = host; + this.staticsCoin = staticsCoin; } /** @@ -35,7 +45,9 @@ export class IcpAgent { private getLedger(): AgentCanister { const agent = this.createAgent(); const ic = replica(agent, { local: true }); - return ic(Principal.fromUint8Array(LEDGER_CANISTER_ID).toText()); + const ledgerCanisterId = + this.staticsCoin.network.type === NetworkType.TESTNET ? TESTNET_LEDGER_CANISTER_ID : LEDGER_CANISTER_ID; + return ic(Principal.fromUint8Array(ledgerCanisterId).toText()); } /** diff --git a/modules/sdk-coin-icp/src/lib/iface.ts b/modules/sdk-coin-icp/src/lib/iface.ts index decca1dcfd..fc38858523 100644 --- a/modules/sdk-coin-icp/src/lib/iface.ts +++ b/modules/sdk-coin-icp/src/lib/iface.ts @@ -6,6 +6,7 @@ import { export const MAX_INGRESS_TTL = 5 * 60 * 1000_000_000; // 5 minutes in nanoseconds export const PERMITTED_DRIFT = 60 * 1000_000_000; // 60 seconds in nanoseconds export const LEDGER_CANISTER_ID = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 2, 1, 1]); // Uint8Array value for "00000000000000020101" and the string value is "ryjl3-tyaaa-aaaaa-aaaba-cai" +export const TESTNET_LEDGER_CANISTER_ID = new Uint8Array([0, 0, 0, 0, 1, 0, 130, 251, 1, 1]); // Uint8Array value for "00000000010082fb0101" and the string value is "xafvr-biaaa-aaaai-aql5q-cai" export const ROOT_PATH = 'm/0'; export const ACCOUNT_BALANCE_CALL = 'icrc1_balance_of'; export const PUBLIC_NODE_REQUEST_ENDPOINT = '/api/v3/canister/'; @@ -36,7 +37,11 @@ export enum MethodName { } export enum Network { - ID = '00000000000000020101', // ICP does not have different network IDs for mainnet and testnet + ID = '00000000000000020101', +} + +export enum TestNetwork { + ID = '00000000010082fb0101', } export interface IcpTransactionData { diff --git a/modules/sdk-coin-icp/src/lib/transferBuilder.ts b/modules/sdk-coin-icp/src/lib/transferBuilder.ts index eb6b47fee8..1990caa68b 100644 --- a/modules/sdk-coin-icp/src/lib/transferBuilder.ts +++ b/modules/sdk-coin-icp/src/lib/transferBuilder.ts @@ -38,7 +38,10 @@ export class TransferBuilder extends TransactionBuilder { } this.validateTransaction(this._transaction); this.buildIcpTransactionData(); - const unsignedTransactionBuilder = new UnsignedTransactionBuilder(this._transaction.icpTransaction); + const unsignedTransactionBuilder = new UnsignedTransactionBuilder( + this._transaction.icpTransaction, + this._coinConfig + ); const payloadsData = await unsignedTransactionBuilder.getUnsignedTransaction(); this._transaction.payloadsData = payloadsData; return this._transaction; diff --git a/modules/sdk-coin-icp/src/lib/unsignedTransactionBuilder.ts b/modules/sdk-coin-icp/src/lib/unsignedTransactionBuilder.ts index cc359e4a85..b2c071b9cd 100644 --- a/modules/sdk-coin-icp/src/lib/unsignedTransactionBuilder.ts +++ b/modules/sdk-coin-icp/src/lib/unsignedTransactionBuilder.ts @@ -10,13 +10,18 @@ import { MAX_INGRESS_TTL, PERMITTED_DRIFT, LEDGER_CANISTER_ID, + TESTNET_LEDGER_CANISTER_ID, } from './iface'; import utils from './utils'; +import { NetworkType, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; export class UnsignedTransactionBuilder { private _icpTransactionPayload: IcpTransaction; - constructor(icpTransactionPayload: IcpTransaction) { + private readonly _staticsCoin: Readonly; + + constructor(icpTransactionPayload: IcpTransaction, staticsCoin: Readonly) { this._icpTransactionPayload = icpTransactionPayload; + this._staticsCoin = staticsCoin; } async getUnsignedTransaction(): Promise { @@ -104,7 +109,9 @@ export class UnsignedTransactionBuilder { async getUpdate(sendArgs: SendArgs, publicKeyHex: string): Promise { const principalId = utils.getPrincipalIdFromPublicKey(publicKeyHex).toUint8Array(); const senderBlob = Buffer.from(principalId); - const canisterIdBuffer = Buffer.from(LEDGER_CANISTER_ID); + const ledgerCanisterId = + this._staticsCoin.network.type === NetworkType.TESTNET ? TESTNET_LEDGER_CANISTER_ID : LEDGER_CANISTER_ID; + const canisterIdBuffer = Buffer.from(ledgerCanisterId); const args = await utils.toArg(sendArgs); const update: HttpCanisterUpdate = { canister_id: canisterIdBuffer, diff --git a/modules/sdk-coin-icp/test/resources/icp.ts b/modules/sdk-coin-icp/test/resources/icp.ts index 30acd3dd48..554668cd95 100644 --- a/modules/sdk-coin-icp/test/resources/icp.ts +++ b/modules/sdk-coin-icp/test/resources/icp.ts @@ -192,12 +192,12 @@ export const PayloadsData = { payloads: [ { account_identifier: { address: '0af815da8259ba8bb3d34fbfb2ac730f07a1adc81438d40d667d91b408b25f2f' }, - hex_bytes: '0a69632d72657175657374523de3c7c5b4613155b74ede2e54493f6acbe8bf6d910154fbbb3a98ba3e0098', + hex_bytes: '0a69632d726571756573745f47cbcfe8333d5bf3806a121a6d17b0a71f895f311664cb476a18b166a0e867', signature_type: 'ecdsa', }, ], unsigned_transaction: - 'b90002677570646174657381826b5452414e53414354494f4eb900056b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f70626361726758400a0308d20912040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b000000000000000070696e67726573735f6578706972696573811b1832d4ce93deb200', + 'b90002677570646174657381826b5452414e53414354494f4eb900056b63616e69737465725f69644a00000000010082fb01016b6d6574686f645f6e616d656773656e645f70626361726758400a0308d20912040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b000000000000000070696e67726573735f6578706972696573811b1832d4ce93deb200', }; export const OnChainTransactionHash = '87f2e7ca80961bdc3a1fe761553a8a7f8ac5bf28b71f4e1fba807cf352a27f52'; @@ -205,7 +205,7 @@ export const OnChainTransactionHash = '87f2e7ca80961bdc3a1fe761553a8a7f8ac5bf28b export const PayloadsDataWithDefaultMemo = { payloads: [ { - hex_bytes: '0a69632d726571756573747c1aff6d0edea47545315bb0be0230b5908e94aa5fcb8040e0680f30da5d4359', + hex_bytes: '0a69632d726571756573746a1eaf27e1b00fafe135ba095d0a1f9cdbd91d36babb7d3e5884a773e516843d', account_identifier: { address: '0af815da8259ba8bb3d34fbfb2ac730f07a1adc81438d40d667d91b408b25f2f', }, @@ -213,14 +213,14 @@ export const PayloadsDataWithDefaultMemo = { }, ], unsigned_transaction: - 'b90002677570646174657381826b5452414e53414354494f4eb900056b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583f0a02080012040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b000000000000000070696e67726573735f6578706972696573811b1832d4ce93deb200', + 'b90002677570646174657381826b5452414e53414354494f4eb900056b63616e69737465725f69644a00000000010082fb01016b6d6574686f645f6e616d656773656e645f706263617267583f0a02080012040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b000000000000000070696e67726573735f6578706972696573811b1832d4ce93deb200', }; export const Signatures = [ { signing_payload: { account_identifier: { address: '0af815da8259ba8bb3d34fbfb2ac730f07a1adc81438d40d667d91b408b25f2f' }, - hex_bytes: '0a69632d72657175657374523de3c7c5b4613155b74ede2e54493f6acbe8bf6d910154fbbb3a98ba3e0098', + hex_bytes: '0a69632d726571756573745f47cbcfe8333d5bf3806a121a6d17b0a71f895f311664cb476a18b166a0e867', signature_type: 'ecdsa', }, signature_type: 'ecdsa', @@ -230,7 +230,7 @@ export const Signatures = [ curve_type: 'secp256k1', }, hex_bytes: - 'dee0c728fc06d2140ad84e2be5f983626114d49f51216d4070bfccbfba79041d77648bad917428a8adb9908828458488562f0d159571382b1ac50fb81d5165c9', + 'f2b4cd71e3399e3859fd89f675a0cb6a6a4227fb5cf089e88b627c1168f8ef430d9bb49deed0c30f36b3032d917912d5fcbbb67b05b9a4af743c83b151b15a3b', }, ]; @@ -240,7 +240,7 @@ export const SignaturesWithDefaultMemo = [ account_identifier: { address: '0af815da8259ba8bb3d34fbfb2ac730f07a1adc81438d40d667d91b408b25f2f', }, - hex_bytes: '0a69632d726571756573747c1aff6d0edea47545315bb0be0230b5908e94aa5fcb8040e0680f30da5d4359', + hex_bytes: '0a69632d726571756573746a1eaf27e1b00fafe135ba095d0a1f9cdbd91d36babb7d3e5884a773e516843d', signature_type: 'ecdsa', }, signature_type: 'ecdsa', @@ -250,15 +250,15 @@ export const SignaturesWithDefaultMemo = [ curve_type: 'secp256k1', }, hex_bytes: - '32f3f90cbf76f81a4ffcba7819dad2b483afe1515d87711475d4af2e993a444b7f22ef303273d40fc18c7b4ffa3295c5cb58886a9e8cf252cedd1cdb8be5a399', + 'fb5346def1d12b2cc246ff807447b95b4202af7f430f24d9de14ad7035b92c1621bef73f82221e00df12552958f27736e73f550e6aa5768dfac931945ff2d41a', }, ]; export const SignedTransaction = - 'b9000367636f6e74656e74b900066c726571756573745f747970656463616c6c6b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f70626361726758400a0308d20912040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b1832d4ce93deb2006d73656e6465725f7075626b6579d84058583056301006072a8648ce3d020106052b8104000a034200042ab77b959e28c4fa47fa8fb9e57cec3d66df5684d076ac2e4c5f28fd69a23dd31a59f908c8add51eab3530b4ac5d015166eaf2198c52fa9a8df7cfaeb8fdb7d46a73656e6465725f7369675840dee0c728fc06d2140ad84e2be5f983626114d49f51216d4070bfccbfba79041d77648bad917428a8adb9908828458488562f0d159571382b1ac50fb81d5165c9'; + 'b9000367636f6e74656e74b900066c726571756573745f747970656463616c6c6b63616e69737465725f69644a00000000010082fb01016b6d6574686f645f6e616d656773656e645f70626361726758400a0308d20912040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b1832d4ce93deb2006d73656e6465725f7075626b6579d84058583056301006072a8648ce3d020106052b8104000a034200042ab77b959e28c4fa47fa8fb9e57cec3d66df5684d076ac2e4c5f28fd69a23dd31a59f908c8add51eab3530b4ac5d015166eaf2198c52fa9a8df7cfaeb8fdb7d46a73656e6465725f7369675840f2b4cd71e3399e3859fd89f675a0cb6a6a4227fb5cf089e88b627c1168f8ef430d9bb49deed0c30f36b3032d917912d5fcbbb67b05b9a4af743c83b151b15a3b'; export const SignedTransactionWithDefaultMemo = - 'b9000367636f6e74656e74b900066c726571756573745f747970656463616c6c6b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583f0a02080012040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b1832d4ce93deb2006d73656e6465725f7075626b6579d84058583056301006072a8648ce3d020106052b8104000a034200042ab77b959e28c4fa47fa8fb9e57cec3d66df5684d076ac2e4c5f28fd69a23dd31a59f908c8add51eab3530b4ac5d015166eaf2198c52fa9a8df7cfaeb8fdb7d46a73656e6465725f736967584032f3f90cbf76f81a4ffcba7819dad2b483afe1515d87711475d4af2e993a444b7f22ef303273d40fc18c7b4ffa3295c5cb58886a9e8cf252cedd1cdb8be5a399'; + 'b9000367636f6e74656e74b900066c726571756573745f747970656463616c6c6b63616e69737465725f69644a00000000010082fb01016b6d6574686f645f6e616d656773656e645f706263617267583f0a02080012040a02080a1a0308904e2a220a20c3d30f404955975adaba89f2e1ebc75c1f44a6a204578afce8f3780d64fe252e3a0a0880a48596eb92b599186673656e646572581dd5fc1dc4d74d4aa35d81cf345533d20548113412d32fffdcece2f68a026e696e67726573735f6578706972791b1832d4ce93deb2006d73656e6465725f7075626b6579d84058583056301006072a8648ce3d020106052b8104000a034200042ab77b959e28c4fa47fa8fb9e57cec3d66df5684d076ac2e4c5f28fd69a23dd31a59f908c8add51eab3530b4ac5d015166eaf2198c52fa9a8df7cfaeb8fdb7d46a73656e6465725f7369675840fb5346def1d12b2cc246ff807447b95b4202af7f430f24d9de14ad7035b92c1621bef73f82221e00df12552958f27736e73f550e6aa5768dfac931945ff2d41a'; export const ParsedUnsignedTransaction = { operations: [ diff --git a/modules/sdk-coin-icp/test/unit/icp.ts b/modules/sdk-coin-icp/test/unit/icp.ts index a328a397fa..7dc8ca8a86 100644 --- a/modules/sdk-coin-icp/test/unit/icp.ts +++ b/modules/sdk-coin-icp/test/unit/icp.ts @@ -11,12 +11,12 @@ import should from 'should'; nock.enableNetConnect(); const bitgo: TestBitGoAPI = TestBitGo.decorate(BitGoAPI, { env: 'test' }); -bitgo.safeRegister('ticp', Ticp.createInstance); +bitgo.safeRegister('icp', Icp.createInstance); describe('Internet computer', function () { let bitgo; let basecoin; - const factory = getBuilderFactory('ticp'); + const factory = getBuilderFactory('icp'); let txBuilder: any; before(async function () { @@ -24,7 +24,7 @@ describe('Internet computer', function () { bitgo.safeRegister('icp', Icp.createInstance); bitgo.safeRegister('ticp', Ticp.createInstance); bitgo.initializeTestVars(); - basecoin = bitgo.coin('ticp'); + basecoin = bitgo.coin('icp'); txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.Accounts.account1.address, testData.Accounts.account1.publicKey); diff --git a/modules/sdk-core/src/bitgo/environments.ts b/modules/sdk-core/src/bitgo/environments.ts index 95ea427d32..58798c9486 100644 --- a/modules/sdk-core/src/bitgo/environments.ts +++ b/modules/sdk-core/src/bitgo/environments.ts @@ -363,7 +363,7 @@ const testnetBase: EnvironmentTemplate = { flrExplorerBaseUrl: 'https://coston2-explorer.flare.network', xdcExplorerBaseUrl: 'https://api.etherscan.io/v2', sgbExplorerBaseUrl: 'https://coston-explorer.flare.network', - icpNodeUrl: 'https://exchanges.testnet.dfinity.network', + icpNodeUrl: 'https://ic0.app', monExplorerBaseUrl: 'https://api.etherscan.io/v2', worldExplorerBaseUrl: 'https://sepolia.worldscan.org/', somniaExplorerBaseUrl: 'https://shannon-explorer.somnia.network/',