PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-ton/test/unit
Просмотр файла: ton.ts
import { TestBitGo } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';
import { Ton, TonParseTransactionOptions, Tton } from '../../src';
import * as sinon from 'sinon';
import assert from 'assert';
import * as testData from '../resources/ton';
import { EDDSAMethods, TransactionExplanation } from '@bitgo/sdk-core';
import should from 'should';
import utils from '../../src/lib/utils';
import Tonweb from 'tonweb';
describe('TON:', function () {
let basecoin;
const bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' });
bitgo.safeRegister('ton', Ton.createInstance);
bitgo.safeRegister('tton', Tton.createInstance);
bitgo.initializeTestVars();
const txPrebuildList = [
{
txHex: Buffer.from(testData.signedSendTransaction.tx, 'base64').toString('hex'),
txInfo: {},
},
{
txHex: Buffer.from(testData.signedSingleNominatorWithdrawTransaction.tx, 'base64').toString('hex'),
txInfo: {},
},
];
const txPrebuildBounceableList = [
{
txHex: Buffer.from(testData.signedSendTransaction.txBounceable, 'base64').toString('hex'),
txInfo: {},
},
{
txHex: Buffer.from(testData.signedSingleNominatorWithdrawTransaction.txBounceable, 'base64').toString('hex'),
txInfo: {},
},
];
const txParamsList = [
{
recipients: [testData.signedSendTransaction.recipient],
},
{
recipients: [testData.signedSingleNominatorWithdrawTransaction.recipient],
},
];
const txParamsBounceableList = [
{
recipients: [testData.signedSendTransaction.recipientBounceable],
},
{
recipients: [testData.signedSingleNominatorWithdrawTransaction.recipientBounceable],
},
];
it('should return the right info', function () {
const ton = bitgo.coin('ton');
const tton = bitgo.coin('tton');
ton.getChain().should.equal('ton');
ton.getFamily().should.equal('ton');
ton.getFullName().should.equal('Ton');
ton.getBaseFactor().should.equal(1e9);
tton.getChain().should.equal('tton');
tton.getFamily().should.equal('ton');
tton.getFullName().should.equal('Testnet Ton');
tton.getBaseFactor().should.equal(1e9);
});
describe('Verify transaction: ', () => {
basecoin = bitgo.coin('tton');
txParamsList.forEach((_, index) => {
const txParams = txParamsList[index];
const txPrebuild = txPrebuildList[index];
const txParamsBounceable = txParamsBounceableList[index];
const txPrebuildBounceable = txPrebuildBounceableList[index];
it('should succeed to verify transaction', async function () {
const verification = {};
const isTransactionVerified = await basecoin.verifyTransaction({
txParams,
txPrebuild,
verification,
} as any);
isTransactionVerified.should.equal(true);
const isBounceableTransactionVerified = await basecoin.verifyTransaction({
txParams: txParamsBounceable,
txPrebuild: txPrebuildBounceable,
verification: {},
} as any);
isBounceableTransactionVerified.should.equal(true);
});
it('should succeed to verify transaction when recipients amount are numbers', async function () {
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
txParamsWithNumberAmounts.recipients[0].amount = 20000000;
const verification = {};
await basecoin
.verifyTransaction({
txParams: txParamsWithNumberAmounts,
txPrebuild,
verification,
} as any)
.should.rejectedWith('Tx outputs does not match with expected txParams recipients');
});
it('should succeed to verify transaction when recipients amount are strings', async function () {
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
txParamsWithNumberAmounts.recipients[0].amount = '20000000';
const verification = {};
await basecoin
.verifyTransaction({
txParams: txParamsWithNumberAmounts,
txPrebuild,
verification,
} as any)
.should.rejectedWith('Tx outputs does not match with expected txParams recipients');
});
it('should succeed to verify transaction when recipients amounts are number and amount is same', async function () {
const verification = {};
await basecoin
.verifyTransaction({
txParams,
txPrebuild,
verification,
} as any)
.should.resolvedWith(true);
});
it('should succeed to verify transaction when recipients amounts are string and amount is same', async function () {
const verification = {};
await basecoin
.verifyTransaction({
txParams,
txPrebuild,
verification,
} as any)
.should.resolvedWith(true);
});
it('should succeed to verify transaction when recipient address are non bounceable', async function () {
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
txParamsWithNumberAmounts.recipients[0].address = new Tonweb.Address(
txParamsWithNumberAmounts.recipients[0].address
).toString(true, true, false);
const verification = {};
const isVerified = await basecoin.verifyTransaction({
txParams: txParamsWithNumberAmounts,
txPrebuild,
verification,
} as any);
isVerified.should.equal(true);
});
it('should fail to verify transaction with invalid param', async function () {
const txPrebuild = {};
await basecoin
.verifyTransaction({
txParams,
txPrebuild,
} as any)
.should.rejectedWith('missing required tx prebuild property txHex');
});
});
});
describe('Explain Transaction: ', () => {
const basecoin = bitgo.coin('tton');
it('should explain a transfer transaction', async function () {
const explainedTransaction = (await basecoin.explainTransaction({
txHex: Buffer.from(testData.signedSendTransaction.tx, 'base64').toString('hex'),
})) as TransactionExplanation;
explainedTransaction.should.deepEqual({
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
id: 'tuyOkyFUMv_neV_FeNBH24Nd4cML2jUgDP4zjGkuOFI=',
outputs: [
{
address: testData.signedSendTransaction.recipient.address,
amount: testData.signedSendTransaction.recipient.amount,
},
],
outputAmount: testData.signedSendTransaction.recipient.amount,
changeOutputs: [],
changeAmount: '0',
fee: { fee: 'UNKNOWN' },
withdrawAmount: undefined,
});
});
it('should explain a non-bounceable transfer transaction', async function () {
const explainedTransaction = (await basecoin.explainTransaction({
txHex: Buffer.from(testData.signedSendTransaction.tx, 'base64').toString('hex'),
toAddressBounceable: false,
fromAddressBounceable: false,
})) as TransactionExplanation;
explainedTransaction.should.deepEqual({
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
id: 'tuyOkyFUMv_neV_FeNBH24Nd4cML2jUgDP4zjGkuOFI=',
outputs: [
{
address: testData.signedSendTransaction.recipientBounceable.address,
amount: testData.signedSendTransaction.recipientBounceable.amount,
},
],
outputAmount: testData.signedSendTransaction.recipientBounceable.amount,
changeOutputs: [],
changeAmount: '0',
fee: { fee: 'UNKNOWN' },
withdrawAmount: undefined,
});
});
it('should explain a single nominator withdraw transaction', async function () {
const explainedTransaction = (await basecoin.explainTransaction({
txHex: Buffer.from(testData.signedSingleNominatorWithdrawTransaction.tx, 'base64').toString('hex'),
})) as TransactionExplanation;
explainedTransaction.should.deepEqual({
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
id: testData.signedSingleNominatorWithdrawTransaction.txId,
outputs: [
{
address: testData.signedSingleNominatorWithdrawTransaction.recipient.address,
amount: testData.signedSingleNominatorWithdrawTransaction.recipient.amount,
},
],
outputAmount: testData.signedSingleNominatorWithdrawTransaction.recipient.amount,
changeOutputs: [],
changeAmount: '0',
fee: { fee: 'UNKNOWN' },
withdrawAmount: '932178112330000',
});
});
it('should explain a non-bounceable single nominator withdraw transaction', async function () {
const explainedTransaction = (await basecoin.explainTransaction({
txHex: Buffer.from(testData.signedSingleNominatorWithdrawTransaction.tx, 'base64').toString('hex'),
toAddressBounceable: false,
fromAddressBounceable: false,
})) as TransactionExplanation;
explainedTransaction.should.deepEqual({
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
id: testData.signedSingleNominatorWithdrawTransaction.txIdBounceable,
outputs: [
{
address: testData.signedSingleNominatorWithdrawTransaction.recipientBounceable.address,
amount: testData.signedSingleNominatorWithdrawTransaction.recipientBounceable.amount,
},
],
outputAmount: testData.signedSingleNominatorWithdrawTransaction.recipientBounceable.amount,
changeOutputs: [],
changeAmount: '0',
fee: { fee: 'UNKNOWN' },
withdrawAmount: '932178112330000',
});
});
it('should fail to explain transaction with missing params', async function () {
try {
await basecoin.explainTransaction({});
} catch (error) {
should.equal(error.message, 'Invalid transaction');
}
});
it('should fail to explain transaction with invalid params', async function () {
try {
await basecoin.explainTransaction({ txHex: 'randomString' });
} catch (error) {
should.equal(error.message, 'Invalid transaction');
}
});
});
describe('Parse Transactions: ', () => {
const basecoin = bitgo.coin('tton');
const transactionsList = [testData.signedSendTransaction.tx, testData.signedSingleNominatorWithdrawTransaction.tx];
const transactionInputsResponseList = [
[
{
address: 'EQCSBjR3fUOL98WTw2F_IT4BrcqjZJWVLWUSz5WQDpaL9Jpl',
amount: '10000000',
},
],
[
{
address: 'EQAbJug-k-tufWMjEC1RKSM0iiJTDUcYkC7zWANHrkT55Fol',
amount: '123400000',
},
],
];
const transactionInputsResponseBounceableList = [
[
{
address: 'UQCSBjR3fUOL98WTw2F_IT4BrcqjZJWVLWUSz5WQDpaL9Meg',
amount: '10000000',
},
],
[
{
address: 'UQAbJug-k-tufWMjEC1RKSM0iiJTDUcYkC7zWANHrkT55Afg',
amount: '123400000',
},
],
];
const transactionOutputsResponseList = [
[
{
address: 'EQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBXwtG',
amount: '10000000',
},
],
[
{
address: 'EQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBXwtG',
amount: '123400000',
},
],
];
const transactionOutputsResponseBounceableList = [
[
{
address: 'UQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBX1aD',
amount: '10000000',
},
],
[
{
address: 'UQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBX1aD',
amount: '123400000',
},
],
];
transactionsList.forEach((_, index) => {
const transaction = transactionsList[index];
const transactionInputsResponse = transactionInputsResponseList[index];
const transactionInputsResponseBounceable = transactionInputsResponseBounceableList[index];
const transactionOutputsResponse = transactionOutputsResponseList[index];
const transactionOutputsResponseBounceable = transactionOutputsResponseBounceableList[index];
it('should parse a TON transaction', async function () {
const parsedTransaction = await basecoin.parseTransaction({
txHex: Buffer.from(transaction, 'base64').toString('hex'),
});
parsedTransaction.should.deepEqual({
inputs: transactionInputsResponse,
outputs: transactionOutputsResponse,
});
});
it('should parse a non-bounceable TON transaction', async function () {
const parsedTransaction = await basecoin.parseTransaction({
txHex: Buffer.from(transaction, 'base64').toString('hex'),
toAddressBounceable: false,
fromAddressBounceable: false,
} as TonParseTransactionOptions);
parsedTransaction.should.deepEqual({
inputs: transactionInputsResponseBounceable,
outputs: transactionOutputsResponseBounceable,
});
});
it('should fail to parse a TON transaction when explainTransaction response is undefined', async function () {
const stub = sinon.stub(Ton.prototype, 'explainTransaction');
stub.resolves(undefined);
await basecoin.parseTransaction({ txHex: transaction }).should.be.rejectedWith('invalid raw transaction');
stub.restore();
});
});
});
describe('Address Validation', () => {
const basecoin = bitgo.coin('tton');
let keychains;
let commonKeychain;
before(function () {
commonKeychain =
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781';
keychains = [
{
id: '6424c353eaf78d000766e95949868468',
source: 'user',
type: 'tss',
commonKeychain:
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
encryptedPrv:
'{"iv":"cZd5i7L4RxtwrALW2rK7UA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"5zgoH1Bd3Fw=","ct":"9vVlnXFRtrM9FVEo+d2chbGHlM9lFZemueBuAs3BIkPo33Fo7jzwwNK/kIWkEyg+NmEBd5IaqAS157nvvvwzzsmMWlQdUz9qbmXNv3pg987cXFR08exS+4uhwP1YNOjJTRvRNcO9ZqHb46d4fmyJ/yC9/susCge7r/EsbaN5C3afv1dzybuq912FwaQElZLYYp5BICudFOMZ9k0UDMfKM/PMDkH7WexoGHr9GKq/bgCH2B39TZZyHKU6Uy47lXep2s6h0DrMwHOrnmiL3DZjOj88Ynvphlzxuo4eOlD2UHia2+nvIaISYs29Pr0DAvREutchvcBpExj1kWWPv7hQYrv8F0NAdatsbWl3w+xKyfiMKo1USlrwyJviypGtQtXOJyw0XPN0rv2+L5lW8BbjpzHfYYN13fJTedlGTFhhkzVtbbPAKE02kx7zCJcjYaiexdSTsrDLScYNT9/Jhdt27KpsooehwVohLfSKz4vbFfRu2MPZw3/+c/hfiJNgtz6esWbnxGrcE8U2IwPYCaK+Ghk4DcqWNIni59RI5B5kAsQOToII40qPN510uTgxBSPO7q7MHgkxdd4CqBq+ojr9j0P7oao8E5Y+CBDJrojDoCh1oCCDW9vo2dXlVcD8SIbw7U/9AfvEbA4xyE/5md1M7CIwLnWs2Ynv0YtaKoqhdS9x6FmHlMDhN/DKHinrwmowtrTT82fOkpO5g9saSmgU7Qy3gLt8t+VwdEyeFeQUKRSyci8qgqXQaZIg4+aXgaSOnlCFMtmB8ekYxEhTY5uzRfrNgS4s1QeqFBpNtUF+Ydi297pbVXnJoXAN+SVWd80GCx+yI2dpVC89k3rOWK9WeyqlnzuLJWp2RIOB9cdW8GFv/fN+QAJpYeVxOE4+nZDsKnsj8nKcg9t4Dlx1G6gLM1/Vq9YxNLbuzuRC0asUYvdMnoMvszmpm++TxndYisgNYscpZSoz7wvcazJNEPfhPVjEkd6tUUuN4GM35H0DmKCUQNT+a6B6hmHlTZvjxiyGAg5bY59hdjvJ+22QduazlEEC6LI3HrA7uK0TpplWzS1tCIFvTMUhj65DEZmNJ2+ZY9bQ4vsMf+DRR3OOG4t+DMlNfjOd3zNv3QoY95BjfWpryFwPzDq7bCP67JDsoj7j2TY5FRSrRkD77H0Ewlux2cWfjRTwcMHcdQxxuV0OP0aNjGDjybFN"}',
},
{
id: '6424c353eaf78d000766e96137d4404b',
source: 'backup',
type: 'tss',
commonKeychain:
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
encryptedPrv:
'{"iv":"vi0dPef/Rx7kG/pRySQi6Q==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"9efhQsiEvVs=","ct":"Gw6atvf6gxKzsjtl3xseipO3rAxp1mAz7Yu1ihFsi5/lf2vMZegApgZx+pyILFS9KKLHbNF3U6WgSYdrr2t4vzdLsXkH1WIxfHS+cd2C5N59yADZDnPJBT6pv/IRvaYelP0Ck3nIYQ2hSMm8op+VOWC/SzHeh7slYDqwEHTGan0Wigfvk1yRd7CCJTaEAomnc/4eFi2NY3X3gt/3opy9IAgknnwUFohn96EWpEQ0F6pbzH/Z8VF6gF+DUcrrByAxExUPnHQZiFk3YHU/vVV4FxBU/mVAE8xBsBn5ul5e5SUMPfc7TBuJWv4BByTNg9xDShF/91Yx2nbfUm5d9QmM8lpKgzzQvcK8POAPk87gRCuKnsGh5vNS0UppkHc+ocfzRQlGA6jze7QyyQO0rMj5Ly8kWjwk2vISvKYHYS1NR7VU549UIXo7NXjatunKSc3+IreoRUHIshiaLg6hl+pxCCuc0qQ43V0mdIfCjTN8gkGWLNk8R7tAGPz9jyapQPcPEGHgEz0ATIi6yMNWCsibS2eLiE1uVEJONoM4lk6FPl3Q2CHbW2MeEbqjY8hbaw18mNb2xSBH/Fwpiial+Tvi2imqgnCO4ZpO9bllKftZPcQy0stN+eGBlb5ufyflKkDSiChHYroGjEpmiFicdde48cJszF52uKNnf1q67fA9/S2FAHQab3EXojxH2Gbk+kkV2h/TYKFFZSWC3vi4e8mO+vjMUcR0AdsgPFyEIz0SCGuba3CnTLNdEuZwsauAeHkx2vUTnRgJPVgNeeuXmsVG76Sy2ggJHuals0Hj8U2Xda0qO1RuFfoCWfss9wn6HGRwPPkhSB/8oNguAqmRVGKkd8Zwt3IvrTd9fk0/rFFDJKGz7WyNHkYgUmNiGcItD12v0jx7FZ52EJzl3Av1RyJUQK18+8EYPh3SGiU9dt7VX0aF0uo6JouKhOeldUvMP+AugQz8fUclwTQsbboVg27Yxo0DyATVwThW5a56R6Qf5ZiQJluFuzs5y98rq0S5q046lE6o3vVmJpEdwjeSCJoET5CL4nTgkXyWvhm4eB8u/e66l3o0qbaSx8q9YYmT9EpRcl5TP4ThLBKETYdzVvg4exjQfektMatk5EyUpEIhZPXh5vXpJZesdfO9LJ8zTaHBsBjDPU7cdNgQMbebpataRi8A0el2/IJXl+E+olgAz5zC4i2O1Q=="}',
},
{
id: '6424c353eaf78d000766e9510b125fba',
source: 'bitgo',
type: 'tss',
commonKeychain:
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781',
verifiedVssProof: true,
isBitGo: true,
},
];
});
it('should return true when validating a well formatted address', async function () {
const address = 'UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi';
basecoin.isValidAddress(address).should.equal(true);
});
it('should return false when validating an incorrectly formatted', async function () {
const address = 'wrongaddress';
basecoin.isValidAddress(address).should.equal(false);
});
it('should return true when validating a non-bounceable address format', async function () {
const address = 'UQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBX1aD';
basecoin.isValidAddress(address).should.equal(true);
});
it('should return true when validating addresses with memoIds', async function () {
const address1 = 'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n?memoId=123';
const address2 = 'UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi?memoId=123';
basecoin.isValidAddress(address1).should.equal(true);
basecoin.isValidAddress(address2).should.equal(true);
});
it('should return true for isWalletAddress with valid address for index 4', async function () {
const newAddress = 'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n';
const index = 4;
const params = { commonKeychain, address: newAddress, index, keychains };
(await basecoin.isWalletAddress(params)).should.equal(true);
});
it('should return true for isWalletAddress with valid addressand index', async function () {
const newAddress = 'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n?memoId=4';
const index = 4;
const params = { commonKeychain, address: newAddress, index, keychains };
(await basecoin.isWalletAddress(params)).should.equal(true);
});
it('should return false for isWalletAddress with valid address for index 5 and address is for a different index', async function () {
const wrongAddressForIndex5 = 'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n';
const index = 5;
const params = { commonKeychain, address: wrongAddressForIndex5, index, keychains };
(await basecoin.isWalletAddress(params)).should.equal(false);
});
it('should throw error for isWalletAddress when keychains is missing', async function () {
const address = 'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n';
const index = 0;
const params = { commonKeychain, address, index };
await assert.rejects(async () => basecoin.isWalletAddress(params));
});
it('should throw error for isWalletAddress when new address is invalid', async function () {
const wrongAddress = 'badAddress';
const index = 0;
const params = { commonKeychain, address: wrongAddress, index };
await assert.rejects(async () => basecoin.isWalletAddress(params), {
message: `invalid address: ${wrongAddress}`,
});
});
});
describe('util class ', () => {
let commonKeychain;
let derivedPublicKey;
before(async function () {
commonKeychain =
'19bdfe2a4b498a05511381235a8892d54267807c4a3f654e310b938b8b424ff4adedbe92f4c146de641c67508a961324c8504cdf8e0c0acbb68d6104ccccd781';
const MPC = await EDDSAMethods.getInitializedMpcInstance();
derivedPublicKey = MPC.deriveUnhardened(commonKeychain, 'm/' + 0).slice(0, 64);
});
describe('getAddressFromPublicKey', function () {
it('should derive bounceable address by default', async function () {
(await utils.getAddressFromPublicKey(derivedPublicKey)).should.equal(
'EQDVeyUJOx3AnZGWLtE0l-Vxv7c7uTnD8OXtCFhaO-nvavQ5'
);
(await utils.getAddressFromPublicKey(derivedPublicKey, true)).should.equal(
'EQDVeyUJOx3AnZGWLtE0l-Vxv7c7uTnD8OXtCFhaO-nvavQ5'
);
});
it('should derive non-bounceable address when requested', async function () {
(await utils.getAddressFromPublicKey(derivedPublicKey, false)).should.equal(
'UQDVeyUJOx3AnZGWLtE0l-Vxv7c7uTnD8OXtCFhaO-nvaqn8'
);
});
it('should derive raw address when requested', async function () {
(await utils.getAddressFromPublicKey(derivedPublicKey, false, false)).should.equal(
'0:d57b25093b1dc09d91962ed13497e571bfb73bb939c3f0e5ed08585a3be9ef6a'
);
});
});
describe('getAddress', function () {
it('should return address as per bounceable flag', function () {
should.equal(
utils.getAddress('UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi', false),
'UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi'
);
should.equal(
utils.getAddress('UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi', true),
'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n'
);
should.equal(
utils.getAddress('EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n', true),
'EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n'
);
should.equal(
utils.getAddress('EQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFL7n', false),
'UQB0Hyt1bTRfI0WK_ULZyKvrvP0PPtpTQFi_jKXVXX6KFOMi'
);
});
});
it('should validate block hash', async function () {
should.equal(utils.isValidBlockId('MPuOvHdu/z+t2l82YpZtiJQk8+FVKmWuKxd6ubn09fI='), true);
should.equal(utils.isValidBlockId('MPuOvHdu/z+t2l82YpZtiJQk8+FVKmWuKxd'), false);
should.equal(utils.isValidBlockId(''), false);
});
it('should validate transaction id', async function () {
should.equal(utils.isValidTransactionId('wlTdDOAXwJp8ESRfQAEJQIn0Tci_S5oLbVKBYxDtvpk='), true);
should.equal(utils.isValidTransactionId('3sykx6Rujy7UtBwHQ/X5kLgvKE0SKLA+ABiCKi7sX8o='), true); // No url friendly txid
should.equal(utils.isValidTransactionId('3sykx6Rujy7UtBwHQ_X5kLgvKE0SKLA-ABiCKi7sX8o='), true); // Url friendly txid
should.equal(utils.isValidTransactionId('wlTdDOAXwJp8ESRfQAEJQIn0Tci_S5oLb='), false);
should.equal(utils.isValidTransactionId('wlTdDOAXwJp8ESRfQAEJQIn0Tci_S5oLbVKBYxDtafdsasdadsfvpk='), false);
should.equal(utils.isValidTransactionId(''), false);
});
it('should generate transaction id', async function () {
const id = await utils.getMessageHashFromData(
'te6cckECCgEAAkoAA7V2k4Vw1nhxr7XcCBjWcFFHKqGwCglPSRuYAkWjWiTVAIAAAPbLgva0G7ytKw/xeE9a3FHK2RM8fgOpGvTpQRseeMer3efRKsiQAADxPhnaQFZWYrlQADRqbCUIAQIDAgHgBAUAgnIAZ0UIwmknkMaW7QboTcq48FfZ1NT5oMUj2VPOBr3zPoxriab4SfV65i6Hd1J2sFsuTcIWUobcKXMcIys+JWknAhMMwSvWBhmTzwRACAkB4YgA0nCuGs8ONfa7gQMazgoo5VQ2AUEp6SNzAEi0a0SaoBAHHgnPJBQQJ/meoCwzK5/PajBSxuJK2Gkva7NmlALNDs2IMEWi4ZRfIV4VeJpKBhzhKjNlFjuz60g+aeT5cNi4CU1NGLsrey/4AAAAGAAcBgEB3wcAaGIAK2cYdYCtxtBaWJ9hi7N+HVzeMsPQxLHSqYn7bfpOcRyh3NZQAAAAAAAAAAAAAAAAAAAAsWgA0nCuGs8ONfa7gQMazgoo5VQ2AUEp6SNzAEi0a0SaoBEAFbOMOsBW42gtLE+wxdm/Dq5vGWHoYljpVMT9tv0nOI5Q7msoAAYUWGAAAB7ZcF7WhMrMVypAAJ1BnYMTiAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAG/Jh6EgTBRYQAAAAAAAAgAAAAAAA4E52AP/eAYnVhJkoII4YUrhpLfpDFt6mRKiktbFnqs+QFAWDAAacQ0=\\'
);
should.equal(id, 'TsVgNKT05cde4Q54sC+RFC7nToTrHk9ppGgE5M0jXtE=');
});
it('should deserialize a cell to get the address', function () {
const data1 = 'te6cckEBAQEAJAAAQ4AaFUTgUQ/k2i+DdAooib0wNVREZQ2z+8R9WQvvFNpUJBARyAgK';
const rawAddress1 = '0:d0aa2702887f26d17c1ba051444de981aaa223286d9fde23eac85f78a6d2a120';
should.equal(utils.getRawWalletAddressFromCell(data1), rawAddress1);
const data2 = 'te6cckEBAQEAJAAAQ5/75w034qP9T0ZXY3muM5ouvFlNoNPky2YUs+Hcxd8otjDkIOPq';
const rawAddress2 = '-1:df3869bf151fea7a32bb1bcd719cd175e2ca6d069f265b30a59f0ee62ef945b1';
should.equal(utils.getRawWalletAddressFromCell(data2), rawAddress2);
});
});
describe('Ton recover - Non-BitGo and Unsigned Sweep Transactions', function () {
let sandbox: sinon.SinonSandbox;
beforeEach(() => {
sandbox = sinon.createSandbox(); // Create a new sandbox for each test
});
afterEach(() => {
sandbox.restore(); // Restore all stubs after each test
});
it('should successfully recover funds for non-BitGo recovery', async function () {
// Define recovery parameters
const recoveryParams = {
bitgoKey:
'1baafa0d62174bf0c78f3256318613ffc44b6dd54ab1a63c2185232f92ede9dae1b2818dbeb52a8215fd56f5a5f2a9f94c079ce89e4dc3b1ce6ed6e84ce71857',
recoveryDestination: 'UQBL2idCXR4ATdQtaNa4VpofcpSxuxIgHH7_slOZfdOXSadJ',
apiKey: 'db2554641c61e60a979cc6c0053f2ec91da9b13e71d287768c93c2fb556be53b',
userKey:
'1baafa0d62174bf0c78f3256318613ffc44b6dd54ab1a63c2185232f92ede9dae1b2818dbeb52a8215fd56f5a5f2a9f94c079ce89e4dc3b1ce6ed6e84ce71857',
walletPassphrase: 'dummyPassphrase',
};
// Mock the expected result for non-BitGo recovery
const mockResult = {
serializedTx:
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8=',
scanIndex: 0,
coin: 'tton',
};
// Stub the recover function to return the mocked result
const sandbox = sinon.createSandbox();
sandbox.stub(basecoin, 'recover').resolves(mockResult);
// Call the recover function
const result = await basecoin.recover(recoveryParams);
// Validate the result
result.serializedTx.should.equal(
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8='
);
result.scanIndex.should.equal(0);
result.coin.should.equal('tton');
sandbox.restore(); // Restore the stubbed method
});
it('should return an unsigned sweep transaction if userKey and backupKey are missing', async function () {
// Define recovery parameters
const recoveryParams = {
bitgoKey:
'1baafa0d62174bf0c78f3256318613ffc44b6dd54ab1a63c2185232f92ede9dae1b2818dbeb52a8215fd56f5a5f2a9f94c079ce89e4dc3b1ce6ed6e84ce71857',
recoveryDestination: 'UQBL2idCXR4ATdQtaNa4VpofcpSxuxIgHH7_slOZfdOXSadJ',
apiKey: 'db2554641c61e60a979cc6c0053f2ec91da9b13e71d287768c93c2fb556be53b',
};
// Mock the expected result for unsigned sweep transaction
const mockUnsignedTx = {
serializedTx:
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8=',
scanIndex: 0,
coin: 'tton',
signableHex: 'dd98eb5a3700c0203237095ca1c0d5288bc0d650a9b59f7b81bac552f76137df',
derivationPath: 'm/0',
parsedTx: {
inputs: [
{
address: 'UQBL2idCXR4ATdQtaNa4VpofcpSxuxIgHH7_slOZfdOXSadJ',
valueString: '1000000000',
value: 1000000000,
},
],
outputs: [
{
address: 'UQBL2idCXR4ATdQtaNa4VpofcpSxuxIgHH7_slOZfdOXSadJ',
valueString: '999000000',
coinName: 'tton',
},
],
spendAmount: 999000000,
type: '',
},
feeInfo: {
fee: 1000000,
feeString: '1000000',
},
coinSpecific: {
commonKeychain:
'1baafa0d62174bf0c78f3256318613ffc44b6dd54ab1a63c2185232f92ede9dae1b2818dbeb52a8215fd56f5a5f2a9f94c079ce89e4dc3b1ce6ed6e84ce71857',
},
};
const mockTxRequest = {
transactions: [
{
unsignedTx: mockUnsignedTx,
signatureShares: [],
},
],
walletCoin: 'ton',
};
const mockTxRequests = {
txRequests: [mockTxRequest],
};
// Stub the recover function to return the mocked unsigned sweep transaction
const sandbox = sinon.createSandbox();
sandbox.stub(basecoin, 'recover').resolves(mockTxRequests);
// Call the recover function
const result = await basecoin.recover(recoveryParams);
// Validate the result
result.should.have.property('txRequests');
result.txRequests[0].should.have.property('transactions');
result.txRequests[0].transactions[0].should.have.property('unsignedTx');
result.txRequests[0].transactions[0].unsignedTx.should.equal(mockUnsignedTx);
});
it('should take OVC output and generate a signed sweep transaction', async function () {
// Define the parameters (mock OVC response)
const params = {
ovcResponse: {
serializedTx:
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8=',
scanIndex: 0,
lastScanIndex: 0,
},
};
// Mock the expected result for the signed sweep transaction
const mockSignedSweepTxn = {
transactions: [
{
serializedTx:
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8=',
scanIndex: 0,
},
],
lastScanIndex: 0,
};
// Stub the createBroadcastableSweepTransaction function to return the mocked result
sandbox.stub(basecoin, 'createBroadcastableSweepTransaction').resolves(mockSignedSweepTxn);
// Call the createBroadcastableSweepTransaction function
const recoveryTxn = await basecoin.createBroadcastableSweepTransaction(params);
// Validate the result
recoveryTxn.transactions[0].serializedTx.should.equal(
'te6cckEBAgEAqgAB4YgAl7ROhLo8AJuoWtGtcK00PuUpY3YkQDj9/2SnMvunLpIFbl896wlMv7fsUOc+sMHzEl8q3vX5bm6noHginPJKBRznOrO7veIpHIEpiRLbH7/eNdpSsRhvL260JP/fD0vAIU1NGLtABDqAAAAACAAcAQBoQgB/OeiNdLeaL7+O04XuujuChSGrRd7ZnFl2fCd9FXdzAyDOJYCwAAAAAAAAAAAAAAAAAFwXGt8='
);
recoveryTxn.transactions[0].scanIndex.should.equal(0);
recoveryTxn.lastScanIndex.should.equal(0);
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!