Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/sdk-coin-icp/src/icp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ export class Icp extends BaseCoin {
return 'ecdsa';
}

allowsAccountConsolidations(): boolean {
return true; // platform enforces V2-only (CECHO-2443)
}

/** @inheritDoc **/
getHashFunction(): Hash {
return createHash('sha256');
Expand Down
54 changes: 53 additions & 1 deletion modules/sdk-coin-icp/test/unit/icp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';
import { Ecdsa } from '@bitgo/sdk-core';
import utils from '../../src/lib/utils';
import { getBuilderFactory } from './getBuilderFactory';

Expand Down Expand Up @@ -430,15 +431,22 @@ describe('Internet computer', function () {
});
});

describe('Wallet VersionKey 2+', () => {
describe('V2 non-memoId wallets', () => {
let keychains;
let derivedAddressAtIndex1: string;

before(function () {
keychains = [
{ commonKeychain: addressVerificationData.commonKeychain },
{ commonKeychain: addressVerificationData.commonKeychain },
{ commonKeychain: addressVerificationData.commonKeychain },
];
// Mirror addressVerification.ts:88-97: secp256k1 path m/{index}, first 33 bytes.
derivedAddressAtIndex1 = utils.getAddressFromPublicKey(
Buffer.from(new Ecdsa().deriveUnhardened(addressVerificationData.commonKeychain, 'm/1'), 'hex')
.subarray(0, 33)
.toString('hex')
);
});

it('should verify a valid TSS root address (index 0)', async function () {
Expand All @@ -454,6 +462,50 @@ describe('Internet computer', function () {
result.should.equal(true);
});

it('should verify a derived address at index 1', async function () {
const params = {
address: derivedAddressAtIndex1,
rootAddress: addressVerificationData.rootAddress,
keychains: keychains,
index: 1,
walletVersion: 2,
};

const result = await basecoin.isWalletAddress(params);
result.should.equal(true);
});

it('should reject when derived index-1 address is verified at index 0', async function () {
const params = {
address: derivedAddressAtIndex1,
rootAddress: addressVerificationData.rootAddress,
keychains: keychains,
index: 0,
walletVersion: 2,
};

await basecoin
.isWalletAddress(params)
.should.be.rejectedWith(
`address validation failure: address ${derivedAddressAtIndex1} is not a wallet address`
);
});

it('should reject memo-suffixed addresses for V2 wallets', async function () {
const addressWithMemo = `${addressVerificationData.rootAddress}?memoId=5`;
const params = {
address: addressWithMemo,
rootAddress: addressVerificationData.rootAddress,
keychains: keychains,
index: 0,
walletVersion: 2,
};

await basecoin
.isWalletAddress(params)
.should.be.rejectedWith(`address validation failure: address ${addressWithMemo} is not a wallet address`);
});

it('should throw error for invalid TSS address', async function () {
const invalidAddress = testData.Accounts.account2.address;

Expand Down
16 changes: 16 additions & 0 deletions modules/sdk-coin-icp/test/unit/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ describe('ICP Transaction', () => {
payloadsData.payloads.length.should.equal(1);
});

it('should preserve memo suffix on receiver address when building a transfer', async () => {
const txn = new Transaction(config);
txn.icpTransactionData = testData.IcpTransactionData;
const factory = getBuilderFactory('ticp');
const txBuilder = factory.getTransferBuilder();
txBuilder.initBuilder(txn);
txBuilder.sender(testData.Accounts.account1.address, testData.Accounts.account1.publicKey);
txBuilder.receiverId(testData.Accounts.account6.address);
txBuilder.amount('10');
txBuilder.memo(Number(testData.MetaDataWithMemo.memo));
await txBuilder.build();

const icpTransaction = txBuilder.transaction.icpTransaction;
should.equal(icpTransaction.operations[1].account.address, testData.Accounts.account6.address);
});

it('build a json transaction from raw hex', async () => {
await tx.fromRawTransaction(serializedTxHex);
const json = tx.toJson();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ describe('ICP Transaction Builder with default memo as 0', async () => {
should.equal(icpTransaction.metadata.memo, DEFAULT_MEMO);
should.equal(icpTransaction.operations[0].account.address, testData.Accounts.account1.address);
should.equal(icpTransaction.operations[1].account.address, testData.Accounts.account2.address);
should.equal(icpTransaction.operations[2].account.address, testData.Accounts.account1.address);
should.equal(icpTransaction.operations[0].amount.value, '-10');
should.equal(icpTransaction.operations[1].amount.value, '10');
should.equal(icpTransaction.operations[2].amount.value, '-10000');
Expand Down
Loading