PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-ethlike/test/unit
Просмотр файла: ethlikeCoin.ts
import assert from 'assert';
import { BitGoAPI } from '@bitgo/sdk-api';
import { common, FullySignedTransaction, HalfSignedTransaction, TransactionType } from '@bitgo/sdk-core';
import { OfflineVaultTxInfo, TransferBuilder } from '@bitgo/abstract-eth';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { bip32 } from '@bitgo/secp256k1';
import nock from 'nock';
import { EthLikeCoin, TethLikeCoin, EthLikeTransactionBuilder } from '../../src';
import { getBuilder } from '../getBuilder';
import { baseChainCommon, getCommon } from '../resources';
import * as mockData from '../fixtures/ethlikeCoin';
nock.enableNetConnect();
const coins = [
{
name: 'hteth',
common: getCommon('hteth'),
},
{
name: 'tarbeth',
common: getCommon('tarbeth'),
},
];
describe('EthLike coin tests', function () {
let bitgo: TestBitGoAPI;
let basecoin: TethLikeCoin;
coins.forEach((coin) => {
describe(coin.name, function () {
before(function () {
const env = 'test';
bitgo = TestBitGo.decorate(BitGoAPI, { env });
bitgo.safeRegister(coin.name, TethLikeCoin.createInstance);
bitgo.initializeTestVars();
basecoin = bitgo.coin(coin.name) as TethLikeCoin;
});
after(function () {
nock.cleanAll();
});
it('should instantiate a coin', function () {
basecoin.should.be.an.instanceof(TethLikeCoin);
});
it('should reject for missing encryptedPrv for hot wallet', async function () {
const recoveryId = '0x1234567890abcdef';
nock(bitgo.microservicesUrl(`/api/recovery/v1/crosschain`)).get(`/${recoveryId}/buildtx`).reply(200, {
txHex: mockData.ccr[coin.name].txHex,
});
const walletPassphrase = TestBitGo.V2.TEST_RECOVERY_PASSCODE as string;
const params = {
recoveryId,
walletPassphrase,
common: coin.common,
};
await basecoin
.sendCrossChainRecoveryTransaction({ ...params, walletType: 'hot' })
.should.be.rejectedWith('missing encryptedPrv');
});
it('should send cross chain recovery transaction for hot wallet', async function () {
const recoveryId = '0x1234567890abcdef';
nock(bitgo.microservicesUrl(`/api/recovery/v1/crosschain`)).get(`/${recoveryId}/buildtx`).reply(200, {
txHex: mockData.ccr[coin.name].txHex,
});
nock(bitgo.microservicesUrl(`/api/recovery/v1/crosschain`)).post(`/${recoveryId}/sign`).reply(200, {
coin: coin.name,
txid: mockData.ccr[coin.name].txid,
});
const walletPassphrase = TestBitGo.V2.TEST_RECOVERY_PASSCODE as string;
const params = {
recoveryId,
walletPassphrase,
encryptedPrv: mockData.encryptedUserKey,
common: coin.common,
};
const result = await basecoin.sendCrossChainRecoveryTransaction({ ...params, walletType: 'hot' });
result.coin.should.equal(coin.name);
result.txid.should.equal(mockData.ccr[coin.name].txid);
});
it('should build txn for cross chain recovery for cold wallet', async function () {
const recoveryId = '0x1234567890abcdef';
nock(bitgo.microservicesUrl(`/api/recovery/v1/crosschain`)).get(`/${recoveryId}/buildtx`).reply(200, {
txHex: mockData.ccr[coin.name].txHex,
});
const params = {
recoveryId,
common: coin.common,
};
const result = await basecoin.sendCrossChainRecoveryTransaction({ ...params, walletType: 'cold' });
assert(result.txHex);
result.txHex.should.equal(mockData.ccr[coin.name].txHex);
});
it('should generate signature data for custodial hot wallet and sign using hsm signature', async function () {
const baseAddress = '0x702cf81e03aa310ec9481d814e3d04a20b04b505';
const destinationAddress = '0xb9f62c71d5f6949cfb211a67fb13ccf079cc760b';
const tokenContractAddress = '0xe4ab69c077896252fafbd49efd26b5d171a32410';
const txBuilder = getBuilder(coin.name, coin.common) as EthLikeTransactionBuilder;
txBuilder.contract(baseAddress);
txBuilder.contractCounter(0);
txBuilder.fee({
fee: '100000',
gasLimit: '21000',
});
const transferBuilder = txBuilder.transfer() as TransferBuilder;
transferBuilder
.coin(coin.name)
.amount('100000000')
.contractSequenceId(100)
.expirationTime(1744049633)
.to(destinationAddress)
.tokenContractAddress(tokenContractAddress);
const signatureData = transferBuilder.getSignatureData();
assert.strictEqual(signatureData.toString('hex'), mockData.custodialHot[coin.name].signatureData);
// Set HSM Signature
transferBuilder.setSignature(mockData.custodialHot[coin.name].signature);
const tx = await txBuilder.build();
const txHex = tx.toBroadcastFormat();
assert.strictEqual(txHex, mockData.custodialHot[coin.name].signedTxHex);
});
});
});
});
describe('EthLikeCoin', function () {
let bitgo: TestBitGoAPI;
const coinName = 'tbaseeth';
let basecoin: TethLikeCoin;
before(function () {
const bitgoKeyXprv =
'xprv9s21ZrQH143K3tpWBHWe31sLoXNRQ9AvRYJgitkKxQ4ATFQMwvr7hHNqYRUnS7PsjzB7aK1VxqHLuNQjj1sckJ2Jwo2qxmsvejwECSpFMfC';
const bitgoKey = bip32.fromBase58(bitgoKeyXprv);
if (!bitgoKey.privateKey) {
throw new Error('no privateKey');
}
const bitgoXpub = bitgoKey.neutered().toBase58();
const env = 'test';
bitgo = TestBitGo.decorate(BitGoAPI, { env });
common.Environments[env].hsmXpub = bitgoXpub;
bitgo.safeRegister('baseeth', EthLikeCoin.createInstance);
bitgo.safeRegister('tbaseeth', TethLikeCoin.createInstance);
bitgo.initializeTestVars();
basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
});
after(function () {
nock.cleanAll();
});
it('should instantiate a coin', function () {
let coin = bitgo.coin('tbaseeth');
coin.should.be.an.instanceof(TethLikeCoin);
coin = bitgo.coin('baseeth');
coin.should.be.an.instanceof(EthLikeCoin);
});
it('should build unsigned transaction', async function () {
const expireTime = Math.floor(new Date().getTime() / 1000);
const txBuilder = getBuilder(coinName, baseChainCommon) as EthLikeTransactionBuilder;
txBuilder.type(TransactionType.Send);
txBuilder.fee({
fee: '1000000000',
gasLimit: '100000',
});
txBuilder.counter(1);
txBuilder.contract('0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4');
const transferBuilder = txBuilder.transfer();
transferBuilder
.coin(coinName)
.expirationTime(expireTime)
.amount('1000000000000000000')
.to('0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4')
.contractSequenceId(1);
const tx = await txBuilder.build();
const txJson = tx.toJson();
txJson.gasLimit.should.equal('100000');
txJson.gasPrice.should.equal('1000000000');
txJson.chainId.should.equal('0x14a34');
});
it('should sign a transaction', async function () {
const account_1 = {
address: '0x8Ce59c2d1702844F8EdED451AA103961bC37B4e8',
owner_1: '4ee089aceabf3ddbf748db79b1066c33b7d3ea1ab3eb7e325121bba2bff2f5ca',
owner_2: '5c7e4efff7304d4dfff6d5f1591844ec6f2adfa6a47e9fece6a3c1a4d755f1e3',
owner_3: '4421ab25dd91e1a3180d03d57c323a7886dcc313d3b3a4b4256a5791572bf597',
};
const expireTime = Math.floor(new Date().getTime() / 1000);
const txBuilder = getBuilder(coinName, baseChainCommon) as EthLikeTransactionBuilder;
txBuilder.type(TransactionType.Send);
txBuilder.fee({
fee: '1000000000',
gasLimit: '100000',
});
txBuilder.counter(1);
txBuilder.contract(account_1.address);
const transferBuilder = txBuilder.transfer();
transferBuilder
.coin(coinName)
.expirationTime(expireTime)
.amount('1000000000000000000')
.to('0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4')
.contractSequenceId(1);
const unsignedTx = await txBuilder.build();
const unsignedTxHex = unsignedTx.toBroadcastFormat();
const halfSignedTx = (await basecoin.signTransaction({
txPrebuild: {
txHex: unsignedTxHex,
},
prv: account_1.owner_1,
common: baseChainCommon,
})) as HalfSignedTransaction;
transferBuilder.key(account_1.owner_1);
const halfSignedTxBuilder = await txBuilder.build();
const halfSignedTxHexBuilder = halfSignedTxBuilder.toBroadcastFormat();
halfSignedTxHexBuilder.should.equal(halfSignedTx.halfSigned.txHex);
// Sign with the second key
const fullSignedTxn = (await basecoin.signTransaction({
txPrebuild: {
halfSigned: {
txHex: halfSignedTxHexBuilder,
expireTime: expireTime,
contractSequenceId: 1,
signature: '',
},
},
prv: account_1.owner_2,
common: baseChainCommon,
isLastSignature: true,
})) as FullySignedTransaction;
assert(fullSignedTxn.txHex);
});
describe('explainTransaction', function () {
const txHex = mockData.ccr[coinName].txHex;
const feeInfo = {
fee: '1000000000',
gasLimit: '100000',
};
it('should explain transaction when common is provided', async function () {
const explanation = await basecoin.explainTransaction({
txHex,
feeInfo,
common: baseChainCommon,
});
explanation.should.have.property('id');
explanation.should.have.property('outputs');
explanation.should.have.property('outputAmount');
explanation.should.have.property('changeOutputs');
explanation.should.have.property('changeAmount');
explanation.should.have.property('fee');
explanation.fee.should.equal(feeInfo);
explanation.outputs.should.be.an.Array();
});
it('should fail to explain transaction when common is not provided', async function () {
await basecoin
.explainTransaction({
txHex,
feeInfo,
})
.should.be.rejectedWith('Common must be provided for EthLikeTransactionBuilder');
});
});
describe('Recovery', function () {
const baseUrl = 'https://api-sepolia.basescan.org/';
const userXpub =
'xpub661MyMwAqRbcEeTc8789MK5PUGEYiPG4F4V17n2Rd2LoTATA1XoCnJT5FAYAShQxSxtFjpo5NHmcWwTp2LiWGBMwpUcAA3HywhxivgYfq7q';
const backupXpub =
'xpub661MyMwAqRbcFZX15xpZf4ERCGHiVSJm8r5C4yh1yXV2GrdZCUPYo4WQr6tN9oUywKXsgSHo7Risf9r22GH5joVD2hEEEhqnSCvK8qy11wW';
it('should generate an unsigned sweep transaction', async function () {
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const backupKeyAddress = '0x4f2c4830cc37f2785c646f89ded8a919219fa0e9';
nock(baseUrl)
.get('/api')
.twice()
.query(mockData.getTxListRequest(backupKeyAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(backupKeyAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const baseCoin = bitgo.coin('tbaseeth') as TethLikeCoin;
const transaction = (await baseCoin.recover({
userKey: userXpub,
backupKey: backupXpub,
walletContractAddress: walletContractAddress,
recoveryDestination: TestBitGo.V2.TEST_ERC20_TOKEN_RECIPIENT as string,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction.txHex);
assert(transaction.contractSequenceId);
assert.strictEqual(transaction.gasLimit, '500000');
});
});
describe('Evm Based Cross Chain Recovery transaction:', function () {
const baseUrl = 'https://api-sepolia.basescan.org/';
const userXpub =
'xpub661MyMwAqRbcEeTc8789MK5PUGEYiPG4F4V17n2Rd2LoTATA1XoCnJT5FAYAShQxSxtFjpo5NHmcWwTp2LiWGBMwpUcAA3HywhxivgYfq7q';
it('should generate an unsigned recovery txn for cold wallet', async function () {
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: userXpub,
backupKey: '',
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('userKey');
transaction.should.have.property('coin');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
});
it('should generate an unsigned recovery txn for custody wallet', async function () {
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: '',
backupKey: '',
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('coin');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
});
it('should generate an unsigned recovery txn for hot wallet', async function () {
const userKey =
'{"iv":"VFZ3jvXhxo1Z+Yaf2MtZnA==","v":1,"iter":10000,"ks":256,"ts":64,"mode"\n' +
':"ccm","adata":"","cipher":"aes","salt":"p+fkHuLa/8k=","ct":"hYG7pvljLIgCjZ\n' +
'53PBlCde5KZRmlUKKHLtDMk+HJfuU46hW+x+C9WsIAO4gFPnTCvFVmQ8x7czCtcNFub5AO2otOG\n' +
'OsX4GE2gXOEmCl1TpWwwNhm7yMUjGJUpgW6ZZgXSXdDitSKi4V/hk78SGSzjFOBSPYRa6I="}\n';
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const walletPassphrase = TestBitGo.V2.TEST_RECOVERY_PASSCODE as string;
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: userKey,
backupKey: '',
walletPassphrase: walletPassphrase,
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('coin');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
transaction.should.have.property('feesUsed');
transaction.feesUsed?.gasLimit.should.equal('500000');
transaction.should.have.property('halfSigned');
transaction.halfSigned?.should.have.property('txHex');
transaction.halfSigned?.should.have.property('recipients');
});
it('should generate an unsigned recovery txn of a token for cold wallet ', async function () {
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const tokenContractAddress = '0x326c977e6efc84e512bb9c30f76e30c160ed06fb';
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getTokenBalanceRequest(tokenContractAddress, walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: userXpub,
backupKey: '',
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
tokenContractAddress: tokenContractAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('userKey');
transaction.should.have.property('coin');
transaction.coin.should.equal('tbaseeth');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
});
it('should generate an unsigned recovery txn of a token for custody wallet', async function () {
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const tokenContractAddress = '0x326c977e6efc84e512bb9c30f76e30c160ed06fb'; // unsupported token contract address
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getTokenBalanceRequest(tokenContractAddress, walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: '',
backupKey: '',
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
tokenContractAddress: tokenContractAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('coin');
transaction.coin.should.equal('tbaseeth');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
});
it('should generate an unsigned recovery txn of a token for hot wallet', async function () {
const userKey =
'{"iv":"VFZ3jvXhxo1Z+Yaf2MtZnA==","v":1,"iter":10000,"ks":256,"ts":64,"mode"\n' +
':"ccm","adata":"","cipher":"aes","salt":"p+fkHuLa/8k=","ct":"hYG7pvljLIgCjZ\n' +
'53PBlCde5KZRmlUKKHLtDMk+HJfuU46hW+x+C9WsIAO4gFPnTCvFVmQ8x7czCtcNFub5AO2otOG\n' +
'OsX4GE2gXOEmCl1TpWwwNhm7yMUjGJUpgW6ZZgXSXdDitSKi4V/hk78SGSzjFOBSPYRa6I="}\n';
const walletContractAddress = TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS as string;
const bitgoFeeAddress = '0x33a42faea3c6e87021347e51700b48aaf49aa1e7';
const destinationAddress = '0xd5adde17fed8baed3f32b84af05b8f2816f7b560';
const bitgoDestinationAddress = '0xE5986CE4490Deb67d2950562Ceb930Ddf9be7a14';
const walletPassphrase = TestBitGo.V2.TEST_RECOVERY_PASSCODE as string;
const tokenContractAddress = '0x326c977e6efc84e512bb9c30f76e30c160ed06fb'; // unsupported contract token address
const basecoin = bitgo.coin('tbaseeth') as TethLikeCoin;
nock(baseUrl)
.get('/api')
.query(mockData.getTxListRequest(bitgoFeeAddress))
.reply(200, mockData.getTxListResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getBalanceRequest(bitgoFeeAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl)
.get('/api')
.query(mockData.getTokenBalanceRequest(tokenContractAddress, walletContractAddress))
.reply(200, mockData.getBalanceResponse);
nock(baseUrl).get('/api').query(mockData.getContractCallRequest).reply(200, mockData.getContractCallResponse);
const transaction = (await basecoin.recover({
userKey: userKey,
backupKey: '',
walletPassphrase: walletPassphrase,
walletContractAddress: walletContractAddress,
bitgoFeeAddress: bitgoFeeAddress,
recoveryDestination: destinationAddress,
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
gasLimit: 500000,
bitgoDestinationAddress: bitgoDestinationAddress,
tokenContractAddress: tokenContractAddress,
common: baseChainCommon,
})) as OfflineVaultTxInfo;
assert(transaction);
transaction.should.have.property('txHex');
transaction.should.have.property('coin');
transaction.coin.should.equal('tbaseeth');
transaction.should.have.property('contractSequenceId');
transaction.should.have.property('expireTime');
transaction.should.have.property('gasLimit');
transaction.gasLimit.should.equal('500000');
transaction.should.have.property('isEvmBasedCrossChainRecovery');
transaction.isEvmBasedCrossChainRecovery?.should.equal(true);
transaction.should.have.property('walletContractAddress');
transaction.walletContractAddress.should.equal(TestBitGo.V2.TEST_ETH_WALLET_FIRST_ADDRESS);
transaction.should.have.property('recipients');
const recipient = transaction.recipients[0];
recipient.should.have.property('address');
recipient.address.should.equal(destinationAddress);
recipient.should.have.property('amount');
recipient.amount.should.equal('9999999999999999928');
transaction.should.have.property('feesUsed');
transaction.feesUsed?.gasLimit.should.equal('500000');
transaction.should.have.property('halfSigned');
transaction.halfSigned?.should.have.property('txHex');
transaction.halfSigned?.should.have.property('recipients');
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!