PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-trx/test/unit/transactionBuilder
Просмотр файла: contractCallBuilder.ts
import { describe, it } from 'node:test';
import assert from 'assert';
import { TransactionType } from '@bitgo/sdk-core';
import { coins, TronNetwork } from '@bitgo/statics';
import BigNumber from 'bignumber.js';
import { Transaction, WrappedBuilder } from '../../../src';
import { getBuilder } from '../../../src/lib/builder';
import {
PARTICIPANTS,
CONTRACTS,
MINT_CONFIRM_DATA,
BLOCK_HASH,
FEE_LIMIT,
BLOCK_NUMBER,
EXPIRATION,
TX_CONTRACT,
} from '../../resources';
describe('Trx Contract call Builder', () => {
const initTxBuilder = () => {
const builder = (getBuilder('ttrx') as WrappedBuilder).getContractCallBuilder();
builder
.source({ address: PARTICIPANTS.custodian.address })
.to({ address: CONTRACTS.factory })
.block({ number: BLOCK_NUMBER, hash: BLOCK_HASH })
.fee({ feeLimit: FEE_LIMIT });
return builder;
};
describe('Contract Call builder', () => {
describe('should build', () => {
describe('non serialized transactions', () => {
it('a signed contract call transaction', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA).sign({ key: PARTICIPANTS.custodian.pk });
const tx = await txBuilder.build();
tx.toJson();
});
});
describe('serialized transactions', () => {
it('a transaction signed multiple times', async () => {
const timestamp = Date.now();
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(timestamp);
txBuilder.expiration(timestamp + EXPIRATION);
const tx = await txBuilder.build();
let txJson = tx.toJson();
let rawData = txJson.raw_data;
assert.deepStrictEqual(rawData.contract, TX_CONTRACT);
assert.equal(txJson.signature.length, 0);
const txBuilder2 = getBuilder('ttrx').from(tx.toJson());
txBuilder2.sign({ key: PARTICIPANTS.custodian.pk });
const tx2 = await txBuilder2.build();
txJson = tx2.toJson();
rawData = txJson.raw_data;
assert.deepStrictEqual(rawData.contract, TX_CONTRACT);
assert.equal(txJson.signature.length, 1);
const txBuilder3 = getBuilder('ttrx').from(tx2.toJson());
txBuilder3.sign({ key: PARTICIPANTS.from.pk });
const tx3 = await txBuilder3.build();
txJson = tx3.toJson();
rawData = txJson.raw_data;
assert.deepStrictEqual(rawData.contract, TX_CONTRACT);
assert.equal(txJson.signature.length, 2);
const txBuilder4 = getBuilder('ttrx').from(tx3.toJson());
txBuilder4.sign({ key: PARTICIPANTS.multisig.pk });
const tx4 = await txBuilder4.build();
assert.equal(tx4.inputs.length, 1);
assert.equal(tx4.inputs[0].address, PARTICIPANTS.custodian.address);
if (tx4.inputs[0].contractAddress) {
assert.equal(tx4.inputs[0].contractAddress, CONTRACTS.factory);
}
if (tx4.inputs[0].data) {
assert.equal(tx4.inputs[0].data, 'K/kLqhJzFAw+G1dWskLMiM18TdimG/hctcHdX1C6YeBmtToV');
}
assert.equal(tx4.inputs[0].value, '0');
assert.equal(tx4.outputs[0].value, '0');
assert.equal(tx4.outputs[0].address, PARTICIPANTS.custodian.address);
txJson = tx4.toJson();
rawData = txJson.raw_data;
assert.deepStrictEqual(rawData.contract, TX_CONTRACT);
assert.equal(txJson.signature.length, 3);
assert.equal(rawData.fee_limit, FEE_LIMIT);
assert.equal(rawData.expiration, timestamp + EXPIRATION);
assert.equal(rawData.timestamp, timestamp);
});
it('an unsigned transaction from a string and from a JSON', async () => {
const timestamp = Date.now();
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(timestamp);
txBuilder.expiration(timestamp + 40000);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
txBuilder2.sign({ key: PARTICIPANTS.custodian.pk });
const tx2 = await txBuilder2.build();
const txBuilder3 = getBuilder('ttrx').from(tx.toJson());
txBuilder3.sign({ key: PARTICIPANTS.custodian.pk });
const tx3 = await txBuilder3.build();
assert.deepStrictEqual(tx2, tx3);
});
it('an unsigned transaction with extended duration', async () => {
const timestamp = Date.now();
const expiration = timestamp + EXPIRATION;
const extension = 60000;
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(timestamp);
txBuilder.expiration(expiration);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
txBuilder2.extendValidTo(extension);
txBuilder2.sign({ key: PARTICIPANTS.custodian.pk });
const tx2 = await txBuilder2.build();
assert.equal(tx2.inputs.length, 1);
assert.equal(tx2.inputs[0].address, PARTICIPANTS.custodian.address);
if (tx2.inputs[0].contractAddress) {
assert.equal(tx2.inputs[0].contractAddress, CONTRACTS.factory);
}
if (tx2.inputs[0].data) {
assert.equal(tx2.inputs[0].data, 'K/kLqhJzFAw+G1dWskLMiM18TdimG/hctcHdX1C6YeBmtToV');
}
assert.equal(tx2.inputs[0].value, '0');
assert.equal(tx2.outputs[0].value, '0');
assert.equal(tx2.outputs[0].address, PARTICIPANTS.custodian.address);
const txJson = tx2.toJson();
assert.equal(txJson.raw_data.expiration, expiration + extension);
});
it('a transaction with correct inputs', async () => {
const timestamp = Date.now();
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(timestamp);
txBuilder.expiration(timestamp + 40000);
const tx = (await txBuilder.build()) as Transaction;
assert.equal(tx.type, TransactionType.ContractCall);
assert.equal(tx.inputs.length, 1);
assert.equal(tx.inputs[0].address, PARTICIPANTS.custodian.address);
if (tx.inputs[0].contractAddress) {
assert.equal(tx.inputs[0].contractAddress, CONTRACTS.factory);
}
if (tx.inputs[0].data) {
assert.equal(tx.inputs[0].data, 'K/kLqhJzFAw+G1dWskLMiM18TdimG/hctcHdX1C6YeBmtToV');
}
assert.equal(tx.inputs[0].value, '0');
assert.equal(tx.outputs[0].value, '0');
assert.equal(tx.outputs[0].address, PARTICIPANTS.custodian.address);
});
});
});
describe('should fail to build', () => {
it('a transaction with wrong data', async () => {
const txBuilder = initTxBuilder();
assert.throws(
() => {
txBuilder.data('addMintRequest()');
},
(e: any) => e.message === 'addMintRequest() is not a valid hex string.'
);
});
it('a transaction with duplicate signatures', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.sign({ key: PARTICIPANTS.custodian.pk });
assert.throws(
() => {
txBuilder.sign({ key: PARTICIPANTS.custodian.pk });
},
(e: any) => e.message === 'Duplicated key'
);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
txBuilder2.sign({ key: PARTICIPANTS.custodian.pk });
assert.rejects(txBuilder2.build(), {
message: 'Transaction signing did not return an additional signature.',
});
});
it('an invalid raw transaction', () => {
assert.throws(
() => {
getBuilder('ttrx').from('an invalid raw transaction');
},
(e: any) => e.message === 'There was error in parsing the JSON string'
);
});
});
});
describe('Should validate ', () => {
it('a valid expiration', async () => {
const now = Date.now();
const expiration = now + EXPIRATION;
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(now);
txBuilder.expiration(expiration + 1000);
txBuilder.expiration(expiration);
const tx = await txBuilder.build();
const txJson = tx.toJson();
assert.equal(txJson.raw_data.expiration, expiration);
});
it('an expiration greater than one year', async () => {
const now = Date.now();
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(now);
assert.throws(
() => {
txBuilder.expiration(now + 31536000001);
},
(e: any) => e.message === 'Expiration must not be greater than one year'
);
});
it('an expiration less than the current date', async () => {
const now = Date.now();
const txBuilder = initTxBuilder();
txBuilder.timestamp(now - 2000);
txBuilder.data(MINT_CONFIRM_DATA);
assert.throws(
() => {
txBuilder.expiration(now - 1000);
},
(e: any) => e.message === 'Expiration must be greater than current time'
);
});
it('an expiration less than the timestamp', async () => {
const now = Date.now();
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.timestamp(now + 2000);
assert.throws(
() => {
txBuilder.expiration(now + 1000);
},
(e: any) => e.message === 'Expiration must be greater than timestamp'
);
});
it('an expiration set after build', async () => {
const now = Date.now();
const expiration = now + EXPIRATION;
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
await txBuilder.build();
assert.throws(
() => {
txBuilder.expiration(expiration);
},
(e: any) => e.message === 'Expiration is already set, it can only be extended'
);
});
it('an expiration set after deserializing', async () => {
const now = Date.now();
const expiration = now + EXPIRATION;
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
assert.throws(
() => {
txBuilder2.expiration(expiration);
},
(e: any) => e.message === 'Expiration is already set, it can only be extended'
);
});
it('an extension without a set expiration', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
assert.throws(
() => {
txBuilder.extendValidTo(20000);
},
(e: any) => e.message === 'There is not expiration to extend'
);
});
it('a zero millisecond extension', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
const expiration = Date.now() + EXPIRATION;
txBuilder.expiration(expiration);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
assert.throws(
() => {
txBuilder2.extendValidTo(0);
},
(e: any) => e.message === 'Value cannot be below zero'
);
});
it('an extension grater than one year', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
const expiration = Date.now() + EXPIRATION;
txBuilder.expiration(expiration);
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
assert.throws(
() => {
txBuilder2.extendValidTo(31536000001);
},
(e: any) => e.message === 'The expiration cannot be extended more than one year'
);
});
it('an extension after signing', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
txBuilder.sign({ key: PARTICIPANTS.custodian.pk });
const tx = await txBuilder.build();
const txBuilder2 = getBuilder('ttrx').from(tx.toBroadcastFormat());
assert.throws(
() => {
txBuilder2.extendValidTo(20000);
},
(e: any) => e.message === 'Cannot extend a signed transaction'
);
});
it('fee limit', async () => {
const txBuilder = initTxBuilder();
txBuilder.data(MINT_CONFIRM_DATA);
assert.throws(
() => {
txBuilder.fee({ feeLimit: 'not a number' });
},
(e: any) => e.message === 'Invalid fee limit value'
);
assert.throws(
() => {
txBuilder.fee({ feeLimit: '-15000' });
},
(e: any) => e.message === 'Invalid fee limit value'
);
assert.throws(
() => {
const tronNetwork = coins.get('ttrx').network as TronNetwork;
txBuilder.fee({ feeLimit: new BigNumber(tronNetwork.maxFeeLimit).plus(1).toString() });
},
(e: any) => e.message === 'Invalid fee limit value'
);
});
it('transaction mandatory fields', async () => {
const txBuilder = (getBuilder('ttrx') as WrappedBuilder).getContractCallBuilder();
await assert.rejects(txBuilder.build(), {
message: 'Missing parameter: data',
});
txBuilder.data(MINT_CONFIRM_DATA);
await assert.rejects(txBuilder.build(), {
message: 'Missing parameter: source',
});
txBuilder.source({ address: PARTICIPANTS.custodian.address });
await assert.rejects(txBuilder.build(), {
message: 'Missing parameter: contract address',
});
txBuilder.to({ address: CONTRACTS.factory });
await assert.rejects(txBuilder.build(), {
message: 'Missing block reference information',
});
txBuilder.block({ number: BLOCK_NUMBER, hash: BLOCK_HASH });
await assert.rejects(txBuilder.build(), {
message: 'Missing fee',
});
txBuilder.fee({ feeLimit: FEE_LIMIT });
assert.doesNotReject(() => {
return txBuilder.build();
});
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!