PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-trx/test/unit
Просмотр файла: trx.ts
import assert from 'node:assert';
import { afterEach, before, describe, it, mock } from 'node:test';
import { BitGoAPI } from '@bitgo/sdk-api';
import { TestBitGoAPI, TestBitGo } from '@bitgo/sdk-test';
import * as _ from 'lodash';
import { Trx, Ttrx, Utils } from '../../src';
import { signTxOptions, mockTx } from '../fixtures';
import {
baseAddressBalance,
SampleRawTokenSendTxn,
receiveAddressBalance,
TestRecoverData,
creationTransaction,
} from '../resources';
describe('TRON:', function () {
const bitgo: TestBitGoAPI = TestBitGo.decorate(BitGoAPI, { env: 'test' });
bitgo.initializeTestVars();
bitgo.safeRegister('trx', Trx.createInstance);
bitgo.safeRegister('ttrx', Ttrx.createInstance);
let basecoin;
before(function () {
basecoin = bitgo.coin('ttrx');
});
it('should instantiate the coin', function () {
const basecoin = bitgo.coin('trx');
assert.ok(basecoin instanceof Trx);
});
it('explain a txHex', async function () {
const txHex = JSON.stringify(mockTx);
const explainParams = {
txHex,
feeInfo: { fee: 1 },
txID: mockTx.txID,
};
const explanation = await basecoin.explainTransaction(explainParams);
const toAddress = Utils.getBase58AddressFromHex(mockTx.raw_data.contract[0].parameter.value.to_address);
assert.equal(explanation.id, mockTx.txID);
assert.equal(explanation.outputs.length, 1);
assert.equal(explanation.outputs[0].amount, '10');
assert.equal(explanation.outputs[0].address, toAddress);
assert.equal(explanation.outputAmount, '10');
assert.equal(explanation.changeAmount, '0');
assert.equal(explanation.changeOutputs.length, 0);
assert.equal(explanation.fee.fee, 1);
assert.equal(explanation.expiration, mockTx.raw_data.expiration);
assert.equal(explanation.timestamp, mockTx.raw_data.timestamp);
});
it('should check valid addresses', function () {
const badAddresses = [
'',
null,
'xxxx',
'YZ09fd-',
'412C2BA4A9FF6C53207DC5B686BFECF75EA7B805772',
'412C2BA4A9FF6C53207DC5B686BFECF75EA7B80',
'TBChwKYNaTo4a4N68Me1qEiiKsRDspXqLLZ',
'0x96be113992bdc3be24c11f6017085b605d253649',
'0x341qg3922b1',
'41E0C0F581D7D02D40826C1C6CBEE71F625D6344D0',
'412C2BA4A9FF6C53207DC5B686BFECF75EA7B80577',
'418840E6C55B9ADA326D211D818C34A994AECED808',
'412A2B9F7641D0750C1E822D0E49EF765C8106524B',
'41A614F803B6FD780986A42C78EC9C7F77E6DED13C',
'418840E6C55B9ADA326D211D818C34A994AECED808',
];
const goodAddresses = ['TBChwKYNaTo4a4N68Me1qEiiKsRDspXqLp', 'TPcf5jtYUhCN1X14tN577zF4NepbDZbxT7'];
badAddresses.map((addr) => {
assert.equal(basecoin.isValidAddress(addr), false);
});
goodAddresses.map((addr) => {
assert.equal(basecoin.isValidAddress(addr), true);
});
});
it('should throw if the params object is missing parameters', async function () {
const explainParams = {
feeInfo: { fee: 1 },
txID: mockTx.txID,
txHex: null,
};
await assert.rejects(basecoin.explainTransaction(explainParams), {
message: 'missing explain tx parameters',
});
});
it('explain an half-signed/fully signed transaction', async function () {
const txHex = JSON.stringify(mockTx);
const explainParams = {
halfSigned: { txHex },
feeInfo: { fee: 1 },
txID: mockTx.txID,
};
const explanation = await basecoin.explainTransaction(explainParams);
const toAddress = Utils.getBase58AddressFromHex(mockTx.raw_data.contract[0].parameter.value.to_address);
assert.equal(explanation.id, mockTx.txID);
assert.equal(explanation.outputs.length, 1);
assert.equal(explanation.outputs[0].amount, '10');
assert.equal(explanation.outputs[0].address, toAddress);
assert.equal(explanation.outputAmount, '10');
assert.equal(explanation.changeAmount, '0');
assert.equal(explanation.changeOutputs.length, 0);
assert.equal(explanation.fee.fee, 1);
assert.equal(explanation.expiration, mockTx.raw_data.expiration);
assert.equal(explanation.timestamp, mockTx.raw_data.timestamp);
});
it('should sign a half signed tx', async function () {
const tx = await basecoin.signTransaction(signTxOptions);
const unsignedTxJson = JSON.parse(signTxOptions.txPrebuild.txHex);
const signedTxJson = JSON.parse(tx.halfSigned.txHex);
assert.equal(signedTxJson.txID, unsignedTxJson.txID);
assert.equal(signedTxJson.raw_data_hex, unsignedTxJson.raw_data_hex);
assert.deepStrictEqual(JSON.stringify(signedTxJson.raw_data), JSON.stringify(unsignedTxJson.raw_data));
assert.equal(signedTxJson.signature.length, 1);
assert.equal(
signedTxJson.signature[0],
'0a9944316924ec7fba4895f1ea1e7cc95f9e2b828ae268a48a8dbeddef40c6f5e127170a95aed9f3f5425b13058d0cb6ef1f5c2213190e482e87043691f22e6800'
);
});
it('should sign with an Xprv a half signed tx', async function () {
const p = {
prv: 'xprv9s21ZrQH143K2sg2Cukk5XqLQdrYnMCDah3y1FFVy6Hz9bQfqMSfmUiHPVHKhcUyft3N1emE5FudJVxgFm5N12MAg5o7DTPsDATTkwNgr73',
txPrebuild: {
txHex: signTxOptions.txPrebuild.txHex,
},
};
const tx = await basecoin.signTransaction(p);
const unsignedTxJson = JSON.parse(signTxOptions.txPrebuild.txHex);
const signedTxJson = JSON.parse(tx.halfSigned.txHex);
assert.equal(signedTxJson.txID, unsignedTxJson.txID);
assert.equal(signedTxJson.raw_data_hex, unsignedTxJson.raw_data_hex);
assert.deepStrictEqual(JSON.stringify(signedTxJson.raw_data), JSON.stringify(unsignedTxJson.raw_data));
assert.equal(signedTxJson.signature.length, 1);
assert.equal(
signedTxJson.signature[0],
'65e56f53a458c6f82d1ef39b2cf5be685a906ad22bb02699f907fcb72ef26f1e91cfc2b6a43bf5432faa0b63bdc5aebf1dc2f49a675d28d23fd7e038b3358b0600'
);
});
it('should add feeLimit to recipient', async function () {
const feeLimit = 100;
const buildParams = {
recipients: [{ data: 'test' }],
feeLimit,
};
basecoin.getExtraPrebuildParams(buildParams);
assert.equal((buildParams as any).recipients[0].feeLimit, feeLimit);
});
it('should`t add any new field', async function () {
const buildParams = {
recipients: [{ data: 'test' }],
};
const unmodifiedBuildParams = _.cloneDeep(buildParams);
await basecoin.getExtraPrebuildParams(buildParams);
assert.deepStrictEqual(buildParams, unmodifiedBuildParams);
});
describe('Keypairs:', () => {
it('should generate a keypair from random seed', function () {
const keyPair = basecoin.generateKeyPair();
assert.ok(Object.prototype.hasOwnProperty.call(keyPair, 'pub'));
assert.ok(Object.prototype.hasOwnProperty.call(keyPair, 'prv'));
assert.equal(basecoin.isValidPub(keyPair.pub), true);
});
it('should generate a keypair from a seed', function () {
const seedText =
'80350b4208d381fbfe2276a326603049fe500731c46d3c9936b5ce036b51377f24bab7dd0c2af7f107416ef858ff79b0670c72406dad064e72bb17fc0a9038bb';
const seed = Buffer.from(seedText, 'hex');
const keyPair = basecoin.generateKeyPair(seed);
assert.equal(
keyPair.pub,
'xpub661MyMwAqRbcFAwqvSGbk35kJf7CQqdN1w4CMUBBTqH5e3ivjU6D8ugv9hRSgRbRenC4w3ahXdLVahwjgjXhSuQKMdNdn55Y9TNSagBktws'
);
assert.equal(
keyPair.prv,
'xprv9s21ZrQH143K2gsNpQjbNu91kdGi1NuWei8bZ5mZuVk6mFPnBvmxb7NSJQdbZW3FGpK3Ycn7jorAXcEzMvviGtbyBz5tBrjfnWyQp3g75FK'
);
});
});
describe('Build Unsigned Sweep', () => {
afterEach(() => {
mock.reset();
});
it('should recover trx from base address to recovery address', async function () {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.baseAddress) {
return Promise.resolve(baseAddressBalance(3000000));
}
return undefined;
});
const baseAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.baseAddress);
const destinationHex = Utils.getHexAddressFromBase58Address(TestRecoverData.recoveryDestination);
mock.method(Trx.prototype as any, 'getBuildTransaction', (...args) => {
if (args.length > 0 && args[0] === destinationHex && args[1] === baseAddrHex && args[2] === 900000) {
return Promise.resolve(creationTransaction(baseAddrHex, destinationHex, 900000));
}
return undefined;
});
const res = await basecoin.recover({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
recoveryDestination: TestRecoverData.recoveryDestination,
});
assert.notEqual(res.length, 0);
assert.ok(Object.prototype.hasOwnProperty.call(res, 'txHex'));
assert.ok(Object.prototype.hasOwnProperty.call(res, 'feeInfo'));
const rawData = JSON.parse(res.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData, 'contract'));
const value = rawData.contract[0].parameter.value;
assert.equal(value.amount, 900000);
assert.equal(Utils.getBase58AddressFromHex(value.owner_address), TestRecoverData.baseAddress);
assert.equal(Utils.getBase58AddressFromHex(value.to_address), TestRecoverData.recoveryDestination);
});
it('should recover trx from receive address to base address', async function () {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.baseAddress) {
return Promise.resolve(baseAddressBalance(2000000));
}
if (args.length > 0 && args[0] === TestRecoverData.firstReceiveAddress) {
return Promise.resolve(receiveAddressBalance(102100000, TestRecoverData.firstReceiveAddress));
}
return undefined;
});
const firstReceiveAddressHex = Utils.getHexAddressFromBase58Address(TestRecoverData.firstReceiveAddress);
const baseAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.baseAddress);
mock.method(Trx.prototype as any, 'getBuildTransaction', (...args) => {
if (args.length > 0 && args[0] === baseAddrHex && args[1] === firstReceiveAddressHex && args[2] === 100000000) {
return Promise.resolve(creationTransaction(firstReceiveAddressHex, baseAddrHex, 100000000));
}
return undefined;
});
const res = await basecoin.recover({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
recoveryDestination: TestRecoverData.recoveryDestination,
});
assert.notEqual(res.length, 0);
assert.ok(Object.prototype.hasOwnProperty.call(res, 'txHex'));
assert.ok(Object.prototype.hasOwnProperty.call(res, 'feeInfo'));
const rawData = JSON.parse(res.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData, 'contract'));
const value = rawData.contract[0].parameter.value;
assert.equal(value.amount, 100000000);
assert.equal(Utils.getBase58AddressFromHex(value.owner_address), TestRecoverData.firstReceiveAddress);
assert.equal(Utils.getBase58AddressFromHex(value.to_address), TestRecoverData.baseAddress);
});
it('should recover token from base address to recovery address', async function () {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.baseAddress) {
return Promise.resolve(
baseAddressBalance(100000000, [
{
TSdZwNqpHofzP6BsBKGQUWdBeJphLmF6id: '1000000000',
},
{
TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs: '1100000000',
},
])
);
}
return undefined;
});
mock.method(Trx.prototype as any, 'getTriggerSmartContractTransaction', (...args) => {
return Promise.resolve(SampleRawTokenSendTxn);
});
const res = await basecoin.recover({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
tokenContractAddress: 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs',
recoveryDestination: TestRecoverData.recoveryDestination,
});
assert.notEqual(res.length, 0);
assert.equal(res.recoveryAmount, 1100000000);
assert.equal(res.feeInfo.fee, '100000000');
const expirationDuration = res.tx.raw_data.expiration - res.tx.raw_data.timestamp;
assert.ok(expirationDuration >= 86400000);
assert.equal(res.addressInfo, undefined);
const rawData = JSON.parse(res.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData, 'contract'));
const value = rawData.contract[0].parameter.value;
assert.equal(Utils.getBase58AddressFromHex(value.owner_address), TestRecoverData.baseAddress);
assert.equal(Utils.getBase58AddressFromHex(value.contract_address), 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs');
});
it('should throw if trx balance at base address is not sufficient to cover token send', async function () {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.baseAddress) {
return Promise.resolve(
baseAddressBalance(1000000, [
{
TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs: '1100000000',
},
])
);
}
return undefined;
});
mock.method(Trx.prototype as any, 'getTriggerSmartContractTransaction', (...args) => {
return Promise.resolve(SampleRawTokenSendTxn);
});
await assert.rejects(
basecoin.recover({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
tokenContractAddress: 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs',
recoveryDestination: TestRecoverData.recoveryDestination,
}),
{
message:
"Amount of funds to recover 1000000 is less than 100000000 and wouldn't be able to fund a trc20 send",
}
);
});
});
describe('Build Unsigned Consolidation Recoveries', () => {
afterEach(() => {
mock.reset();
});
it('should throw if startingScanIndex is not ge to 1', async () => {
await assert.rejects(
basecoin.recoverConsolidations({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
startingScanIndex: -1,
}),
{
message:
'Invalid starting or ending index to scan for addresses. startingScanIndex: -1, endingScanIndex: 19.',
}
);
});
it('should throw if scan factor is too high', async () => {
await assert.rejects(
basecoin.recoverConsolidations({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
startingScanIndex: 1,
endingScanIndex: 300,
}),
{
message:
'Invalid starting or ending index to scan for addresses. startingScanIndex: 1, endingScanIndex: 300.',
}
);
});
it('should build consolidate recoveries', async () => {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.firstReceiveAddress) {
return Promise.resolve(receiveAddressBalance(102100000, TestRecoverData.firstReceiveAddress));
}
if (args.length > 0 && args[0] === TestRecoverData.secondReceiveAddress) {
return Promise.resolve(receiveAddressBalance(50000000, TestRecoverData.secondReceiveAddress));
}
return undefined;
});
mock.method(Trx.prototype as any, 'getBuildTransaction', (...args) => {
if (args.length > 0 && args[0] === baseAddrHex && args[1] === firstReceiveAddrHex && args[2] === 100000000) {
return Promise.resolve(creationTransaction(firstReceiveAddrHex, baseAddrHex, 100000000));
}
if (args.length > 0 && args[0] === baseAddrHex && args[1] === secondReceiveAddrHex && args[2] === 47900000) {
return Promise.resolve(creationTransaction(secondReceiveAddrHex, baseAddrHex, 47900000));
}
return undefined;
});
const firstReceiveAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.firstReceiveAddress);
const secondReceiveAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.secondReceiveAddress);
const baseAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.baseAddress);
const res = await basecoin.recoverConsolidations({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
startingScanIndex: 1,
endingScanIndex: 3,
});
assert.notEqual(res.length, 0);
assert.ok(Object.prototype.hasOwnProperty.call(res, 'transactions'));
assert.equal(res.transactions.length, 2);
const txn1 = res.transactions[0];
const rawData1 = JSON.parse(txn1.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData1, 'contract'));
const value1 = rawData1.contract[0].parameter.value;
assert.equal(value1.amount, 100000000);
assert.equal(Utils.getBase58AddressFromHex(value1.owner_address), TestRecoverData.firstReceiveAddress);
assert.equal(Utils.getBase58AddressFromHex(value1.to_address), TestRecoverData.baseAddress);
const txn2 = res.transactions[1];
const rawData2 = JSON.parse(txn2.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData2, 'contract'));
const value2 = rawData2.contract[0].parameter.value;
assert.equal(value2.amount, 47900000);
assert.equal(Utils.getBase58AddressFromHex(value2.owner_address), TestRecoverData.secondReceiveAddress);
assert.equal(Utils.getBase58AddressFromHex(value2.to_address), TestRecoverData.baseAddress);
});
it('should build consolidate token recoveries', async () => {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.firstReceiveAddress) {
return Promise.resolve(
receiveAddressBalance(202100000, TestRecoverData.firstReceiveAddress, [
{
TSdZwNqpHofzP6BsBKGQUWdBeJphLmF6id: '1100000000',
},
])
);
}
if (args.length > 0 && args[0] === TestRecoverData.secondReceiveAddress) {
return Promise.resolve(receiveAddressBalance(500, TestRecoverData.secondReceiveAddress));
}
return undefined;
});
mock.method(Trx.prototype as any, 'getBuildTransaction', (...args) => {
if (args.length > 0 && args[0] === baseAddrHex && args[1] === firstReceiveAddrHex && args[2] === 1100000000) {
return Promise.resolve(creationTransaction(firstReceiveAddrHex, baseAddrHex, 1100000000));
}
return undefined;
});
const firstReceiveAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.firstReceiveAddress);
const baseAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.baseAddress);
const res = await basecoin.recoverConsolidations({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
tokenContractAddress: 'TSdZwNqpHofzP6BsBKGQUWdBeJphLmF6id',
startingScanIndex: 1,
endingScanIndex: 3,
});
assert.notEqual(res.length, 0);
assert.ok(Object.prototype.hasOwnProperty.call(res, 'transactions'));
assert.equal(res.transactions.length, 1);
const txn = res.transactions[0];
const rawData = JSON.parse(txn.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData, 'contract'));
const value = rawData.contract[0].parameter.value;
assert.equal(
value.data,
'a9059cbb000000000000000000000000c25420255c2c5a2dd54ef69f92ef261e6bd4216a000000000000000000000000000000000000000000000000000000004190ab00'
);
assert.equal(Utils.getBase58AddressFromHex(value.owner_address), TestRecoverData.firstReceiveAddress);
assert.equal(Utils.getBase58AddressFromHex(value.contract_address), 'TSdZwNqpHofzP6BsBKGQUWdBeJphLmF6id');
});
it('should skip building consolidate transaction if balance is lower than reserved fee', async () => {
mock.method(Trx.prototype as any, 'getAccountBalancesFromNode', (...args) => {
if (args.length > 0 && args[0] === TestRecoverData.firstReceiveAddress) {
return Promise.resolve(receiveAddressBalance(102100000, TestRecoverData.firstReceiveAddress));
}
if (args.length > 0 && args[0] === TestRecoverData.secondReceiveAddress) {
return Promise.resolve(receiveAddressBalance(2000000, TestRecoverData.secondReceiveAddress));
}
return undefined;
});
mock.method(Trx.prototype as any, 'getBuildTransaction', (...args) => {
if (args.length > 0 && args[0] === baseAddrHex && args[1] === firstReceiveAddrHex && args[2] === 100000000) {
return Promise.resolve(creationTransaction(firstReceiveAddrHex, baseAddrHex, 100000000));
}
return undefined;
});
const firstReceiveAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.firstReceiveAddress);
const baseAddrHex = Utils.getHexAddressFromBase58Address(TestRecoverData.baseAddress);
const res = await basecoin.recoverConsolidations({
userKey: TestRecoverData.userKey,
backupKey: TestRecoverData.backupKey,
bitgoKey: TestRecoverData.bitgoKey,
startingScanIndex: 1,
endingScanIndex: 3,
});
assert.notEqual(res.length, 0);
assert.ok(Object.prototype.hasOwnProperty.call(res, 'transactions'));
assert.equal(res.transactions.length, 1);
const txn1 = res.transactions[0];
const rawData1 = JSON.parse(txn1.txHex).raw_data;
assert.ok(Object.prototype.hasOwnProperty.call(rawData1, 'contract'));
const value1 = rawData1.contract[0].parameter.value;
assert.equal(value1.amount, 100000000);
assert.equal(Utils.getBase58AddressFromHex(value1.owner_address), TestRecoverData.firstReceiveAddress);
assert.equal(Utils.getBase58AddressFromHex(value1.to_address), TestRecoverData.baseAddress);
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!