Skip to content

Commit 16b178d

Browse files
authored
Merge pull request #6786 from BitGo/COIN-5352-aptos-sdk-contract-changes
feat: enable smart contract calls aptos
2 parents c20245f + 704f6f9 commit 16b178d

File tree

14 files changed

+1337
-1
lines changed

14 files changed

+1337
-1
lines changed

modules/bitgo/test/v2/unit/wallet.ts

Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5709,4 +5709,388 @@ describe('V2 Wallet:', function () {
57095709
});
57105710
});
57115711
});
5712+
5713+
describe('Aptos Custom transaction Flow', function () {
5714+
const sandbox = sinon.createSandbox();
5715+
5716+
// Set up test data and wallets
5717+
const tapt = bitgo.coin('tapt');
5718+
const aptosWalletData = {
5719+
id: '5b34252f1bf349930e34020a00000001',
5720+
coin: 'tapt',
5721+
keys: [
5722+
'598f606cd8fc24710d2ebad89dce86c3',
5723+
'598f606cc8e43aef09fcb785221d9dd3',
5724+
'5935d59cf660764331bafcade1855fd8',
5725+
],
5726+
coinSpecific: {},
5727+
multisigType: 'tss',
5728+
};
5729+
5730+
const tssAptosWallet = new Wallet(bitgo, tapt, aptosWalletData);
5731+
const custodialTssAptosWallet = new Wallet(bitgo, tapt, {
5732+
...aptosWalletData,
5733+
type: 'custodial',
5734+
});
5735+
5736+
const reqId = new RequestTracer();
5737+
5738+
const aptosTxRequest: TxRequest = {
5739+
txRequestId: 'aptos-tx-id',
5740+
transactions: [],
5741+
intent: {
5742+
intentType: 'customTx',
5743+
},
5744+
date: new Date().toISOString(),
5745+
latest: true,
5746+
state: 'pendingUserSignature',
5747+
userId: 'userId',
5748+
walletType: 'hot',
5749+
policiesChecked: false,
5750+
version: 1,
5751+
walletId: 'walletId',
5752+
unsignedTxs: [
5753+
{
5754+
serializedTxHex: 'aptos123abcd',
5755+
signableHex: 'aptosdeadbeef',
5756+
feeInfo: {
5757+
fee: 1000,
5758+
feeString: '1000',
5759+
},
5760+
derivationPath: 'm/0',
5761+
},
5762+
],
5763+
};
5764+
5765+
afterEach(function () {
5766+
sandbox.verifyAndRestore();
5767+
});
5768+
5769+
const testCustomSmartContractCall = {
5770+
moduleName: '0x1::coin',
5771+
functionName: 'transfer',
5772+
typeArguments: ['0x1::aptos_coin::AptosCoin'],
5773+
functionArguments: ['0x742d35Cc6634C0532925a3b8D400E65AAD801c0b', '1000000'],
5774+
};
5775+
5776+
const testAptosRecipients = [
5777+
{
5778+
address: '0x742d35Cc6634C0532925a3b8D400E65AAD801c0b',
5779+
amount: '1000000',
5780+
},
5781+
];
5782+
5783+
describe('prebuildTransaction with aptosCustomTransaction type', function () {
5784+
it('should call prebuildTxWithIntent with correct parameters for custom smart contract call', async function () {
5785+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5786+
prebuildTxWithIntent.resolves(aptosTxRequest);
5787+
prebuildTxWithIntent.calledOnceWithExactly({
5788+
reqId,
5789+
intentType: 'customTx',
5790+
aptosCustomTransactionParams: testCustomSmartContractCall,
5791+
recipients: testAptosRecipients,
5792+
});
5793+
5794+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
5795+
reqId,
5796+
type: 'customTx',
5797+
aptosCustomTransactionParams: testCustomSmartContractCall,
5798+
recipients: testAptosRecipients,
5799+
});
5800+
5801+
txPrebuild.should.deepEqual({
5802+
walletId: tssAptosWallet.id(),
5803+
wallet: tssAptosWallet,
5804+
txRequestId: 'aptos-tx-id',
5805+
txHex: 'aptos123abcd',
5806+
buildParams: {
5807+
type: 'customTx',
5808+
aptosCustomTransactionParams: testCustomSmartContractCall,
5809+
recipients: testAptosRecipients,
5810+
},
5811+
feeInfo: {
5812+
fee: 1000,
5813+
feeString: '1000',
5814+
},
5815+
});
5816+
});
5817+
5818+
it('should handle aptosCustomTransaction with empty recipients', async function () {
5819+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5820+
prebuildTxWithIntent.resolves(aptosTxRequest);
5821+
prebuildTxWithIntent.calledOnceWithExactly({
5822+
reqId,
5823+
intentType: 'customTx',
5824+
aptosCustomTransactionParams: testCustomSmartContractCall,
5825+
recipients: [],
5826+
});
5827+
5828+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
5829+
reqId,
5830+
type: 'customTx',
5831+
aptosCustomTransactionParams: testCustomSmartContractCall,
5832+
});
5833+
5834+
txPrebuild.should.deepEqual({
5835+
walletId: tssAptosWallet.id(),
5836+
wallet: tssAptosWallet,
5837+
txRequestId: 'aptos-tx-id',
5838+
txHex: 'aptos123abcd',
5839+
buildParams: {
5840+
type: 'customTx',
5841+
aptosCustomTransactionParams: testCustomSmartContractCall,
5842+
},
5843+
feeInfo: {
5844+
fee: 1000,
5845+
feeString: '1000',
5846+
},
5847+
});
5848+
});
5849+
5850+
it('should handle custom smart contract call with type arguments', async function () {
5851+
const accountCreationCall = {
5852+
moduleName: '0x1::aptos_account',
5853+
functionName: 'create_account',
5854+
typeArguments: [],
5855+
functionArguments: ['0x123def456abc789fed012345abcdef67890'],
5856+
};
5857+
5858+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5859+
prebuildTxWithIntent.resolves(aptosTxRequest);
5860+
prebuildTxWithIntent.calledOnceWithExactly({
5861+
reqId,
5862+
intentType: 'customTx',
5863+
aptosCustomTransactionParams: accountCreationCall,
5864+
recipients: testAptosRecipients,
5865+
});
5866+
5867+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
5868+
reqId,
5869+
type: 'customTx',
5870+
aptosCustomTransactionParams: accountCreationCall,
5871+
recipients: testAptosRecipients,
5872+
});
5873+
5874+
txPrebuild.should.deepEqual({
5875+
walletId: tssAptosWallet.id(),
5876+
wallet: tssAptosWallet,
5877+
txRequestId: 'aptos-tx-id',
5878+
txHex: 'aptos123abcd',
5879+
buildParams: {
5880+
type: 'customTx',
5881+
aptosCustomTransactionParams: accountCreationCall,
5882+
recipients: testAptosRecipients,
5883+
},
5884+
feeInfo: {
5885+
fee: 1000,
5886+
feeString: '1000',
5887+
},
5888+
});
5889+
});
5890+
5891+
it('should handle aptosCustomTransaction with ABI information', async function () {
5892+
const customCallWithAbi = {
5893+
...testCustomSmartContractCall,
5894+
abi: {
5895+
name: 'transfer',
5896+
visibility: 'public',
5897+
is_entry: true,
5898+
generic_type_params: [],
5899+
params: ['address', 'u64'],
5900+
return: [],
5901+
},
5902+
};
5903+
5904+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5905+
prebuildTxWithIntent.resolves(aptosTxRequest);
5906+
prebuildTxWithIntent.calledOnceWithExactly({
5907+
reqId,
5908+
intentType: 'customTx',
5909+
aptosCustomTransactionParams: customCallWithAbi,
5910+
recipients: testAptosRecipients,
5911+
});
5912+
5913+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
5914+
reqId,
5915+
type: 'customTx',
5916+
aptosCustomTransactionParams: customCallWithAbi,
5917+
recipients: testAptosRecipients,
5918+
});
5919+
5920+
txPrebuild.should.deepEqual({
5921+
walletId: tssAptosWallet.id(),
5922+
wallet: tssAptosWallet,
5923+
txRequestId: 'aptos-tx-id',
5924+
txHex: 'aptos123abcd',
5925+
buildParams: {
5926+
type: 'customTx',
5927+
aptosCustomTransactionParams: customCallWithAbi,
5928+
recipients: testAptosRecipients,
5929+
},
5930+
feeInfo: {
5931+
fee: 1000,
5932+
feeString: '1000',
5933+
},
5934+
});
5935+
});
5936+
5937+
it('should handle aptosCustomTransaction with pending approval ID', async function () {
5938+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5939+
prebuildTxWithIntent.resolves({
5940+
...aptosTxRequest,
5941+
state: 'pendingApproval',
5942+
pendingApprovalId: 'aptos-approval-id',
5943+
});
5944+
prebuildTxWithIntent.calledOnceWithExactly({
5945+
reqId,
5946+
intentType: 'customTx',
5947+
aptosCustomTransactionParams: testCustomSmartContractCall,
5948+
recipients: testAptosRecipients,
5949+
});
5950+
5951+
const txPrebuild = await custodialTssAptosWallet.prebuildTransaction({
5952+
reqId,
5953+
type: 'customTx',
5954+
aptosCustomTransactionParams: testCustomSmartContractCall,
5955+
recipients: testAptosRecipients,
5956+
});
5957+
5958+
txPrebuild.should.deepEqual({
5959+
walletId: custodialTssAptosWallet.id(),
5960+
wallet: custodialTssAptosWallet,
5961+
txRequestId: 'aptos-tx-id',
5962+
txHex: 'aptos123abcd',
5963+
pendingApprovalId: 'aptos-approval-id',
5964+
buildParams: {
5965+
type: 'customTx',
5966+
aptosCustomTransactionParams: testCustomSmartContractCall,
5967+
recipients: testAptosRecipients,
5968+
},
5969+
feeInfo: {
5970+
fee: 1000,
5971+
feeString: '1000',
5972+
},
5973+
});
5974+
});
5975+
5976+
it('should throw error for missing aptosCustomTransactionParams', async function () {
5977+
await tssAptosWallet
5978+
.prebuildTransaction({
5979+
reqId,
5980+
type: 'customTx',
5981+
recipients: testAptosRecipients,
5982+
})
5983+
.should.be.rejectedWith(`'aptosCustomTransactionParams' is a required parameter for customTx intent`);
5984+
});
5985+
5986+
it('should support aptosCustomTransaction for cold wallets', async function () {
5987+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
5988+
aptosTxRequest.walletType = 'cold';
5989+
prebuildTxWithIntent.resolves(aptosTxRequest);
5990+
prebuildTxWithIntent.calledOnceWithExactly({
5991+
reqId,
5992+
intentType: 'customTx',
5993+
aptosCustomTransactionParams: testCustomSmartContractCall,
5994+
recipients: testAptosRecipients,
5995+
});
5996+
5997+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
5998+
reqId,
5999+
type: 'customTx',
6000+
aptosCustomTransactionParams: testCustomSmartContractCall,
6001+
recipients: testAptosRecipients,
6002+
});
6003+
6004+
txPrebuild.should.deepEqual({
6005+
walletId: tssAptosWallet.id(),
6006+
wallet: tssAptosWallet,
6007+
txRequestId: 'aptos-tx-id',
6008+
txHex: 'aptos123abcd',
6009+
buildParams: {
6010+
type: 'customTx',
6011+
aptosCustomTransactionParams: testCustomSmartContractCall,
6012+
recipients: testAptosRecipients,
6013+
},
6014+
feeInfo: {
6015+
fee: 1000,
6016+
feeString: '1000',
6017+
},
6018+
});
6019+
});
6020+
6021+
it('should build a standard Aptos transfer without breaking custom functionality', async function () {
6022+
const standardTransferRequest = {
6023+
...aptosTxRequest,
6024+
intent: {
6025+
intentType: 'payment',
6026+
},
6027+
};
6028+
6029+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
6030+
prebuildTxWithIntent.resolves(standardTransferRequest);
6031+
prebuildTxWithIntent.calledOnceWithExactly({
6032+
reqId,
6033+
recipients: testAptosRecipients,
6034+
intentType: 'payment',
6035+
});
6036+
6037+
const txPrebuild = await tssAptosWallet.prebuildTransaction({
6038+
reqId,
6039+
recipients: testAptosRecipients,
6040+
type: 'transfer',
6041+
});
6042+
6043+
txPrebuild.should.deepEqual({
6044+
walletId: tssAptosWallet.id(),
6045+
wallet: tssAptosWallet,
6046+
txRequestId: 'aptos-tx-id',
6047+
txHex: 'aptos123abcd',
6048+
buildParams: {
6049+
recipients: testAptosRecipients,
6050+
type: 'transfer',
6051+
},
6052+
feeInfo: {
6053+
fee: 1000,
6054+
feeString: '1000',
6055+
},
6056+
});
6057+
});
6058+
6059+
it('should validate aptosCustomTransactionParams structure for smart contract calls', async function () {
6060+
const invalidParams = {
6061+
moduleName: '0x1::coin',
6062+
// Missing required functionName
6063+
typeArguments: ['0x1::aptos_coin::AptosCoin'],
6064+
functionArguments: ['0x742d35Cc6634C0532925a3b8D400E65AAD801c0b', '1000000'],
6065+
} as any; // Type assertion to test invalid params
6066+
6067+
const prebuildTxWithIntent = sandbox.stub(TssUtils.prototype, 'prebuildTxWithIntent');
6068+
prebuildTxWithIntent.rejects(new Error('Function name is required and must be a non-empty string'));
6069+
6070+
try {
6071+
await tssAptosWallet.prebuildTransaction({
6072+
reqId,
6073+
type: 'customTx',
6074+
aptosCustomTransactionParams: invalidParams,
6075+
recipients: testAptosRecipients,
6076+
});
6077+
throw new Error('Expected promise to be rejected');
6078+
} catch (error) {
6079+
error.message.should.equal('Function name is required and must be a non-empty string');
6080+
}
6081+
6082+
prebuildTxWithIntent.should.have.been.calledOnceWith({
6083+
reqId,
6084+
intentType: 'customTx',
6085+
sequenceId: undefined,
6086+
comment: undefined,
6087+
solInstructions: undefined,
6088+
aptosCustomTransactionParams: invalidParams,
6089+
recipients: testAptosRecipients,
6090+
nonce: undefined,
6091+
feeOptions: undefined,
6092+
});
6093+
});
6094+
});
6095+
});
57126096
});

0 commit comments

Comments
 (0)