Skip to content

Commit

Permalink
Merge pull request #3966 from BitGo/EA-1734-sdk
Browse files Browse the repository at this point in the history
feat(sdk-coins-sui): add more validations for custom tx
  • Loading branch information
evanzbitgo authored Oct 10, 2023
2 parents dd759c4 + 6746f27 commit e6b1b13
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 22 deletions.
16 changes: 12 additions & 4 deletions modules/sdk-coin-sui/src/lib/customTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
import { Transaction } from './transaction';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import utils from './utils';
import { BaseKey, InvalidTransactionError, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
import { BaseKey, InvalidTransactionError, Recipient, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
import { UNAVAILABLE_TEXT } from './constants';

export class CustomTransaction extends Transaction<CustomProgrammableTransaction> {
private _rawTransaction: string;
private _recipients: Recipient[];

constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
Expand Down Expand Up @@ -70,13 +71,13 @@ export class CustomTransaction extends Transaction<CustomProgrammableTransaction
return;
}

const recipients = utils.getRecipients(this._suiTransaction);
this._outputs = recipients.map((recipient, index) => ({
this._recipients = utils.getRecipients(this._suiTransaction);
this._outputs = this._recipients.map((recipient, index) => ({
address: recipient.address,
value: recipient.amount,
coin: this._coinConfig.name,
}));
const totalAmount = recipients.reduce((accumulator, current) => accumulator + Number(current.amount), 0);
const totalAmount = this._recipients.reduce((accumulator, current) => accumulator + Number(current.amount), 0);

this._inputs = [
{
Expand All @@ -94,6 +95,13 @@ export class CustomTransaction extends Transaction<CustomProgrammableTransaction
return this._rawTransaction;
}

/**
* Get the recipients of the transaction if there is any transfers.
*/
get recipients(): Recipient[] {
return this._recipients;
}

/**
* @inheritdoc
*/
Expand Down
21 changes: 15 additions & 6 deletions modules/sdk-coin-sui/src/lib/customTransactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TransactionBuilder } from './transactionBuilder';
import { CustomProgrammableTransaction, SuiTransaction, SuiTransactionType } from './iface';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { CustomTransaction } from './customTransaction';
import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core';
import { BuildTransactionError, InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import { Transaction } from './transaction';
import assert from 'assert';
import utils from './utils';
Expand Down Expand Up @@ -63,18 +63,27 @@ export class CustomTransactionBuilder extends TransactionBuilder<CustomProgramma
}

/**
* Currently custom tx only allows a combination of 3 types of SUI transactions:
* 1. SplitCoins
* 2. TransferObjects
* 3. MoveCall
* @inheritdoc
*/
validateTransaction(transaction: CustomTransaction): void {
if (!transaction.suiTransaction) {
return;
}
this.validateTransactionFields();

// Check all the transaction types are supported
this.transaction.suiTransaction.tx.transactions.forEach((tx) => {
utils.getSuiTransactionType(tx);
});
try {
this.transaction.suiTransaction.tx.transactions.forEach((tx) => {
utils.getSuiTransactionType(tx);
});
} catch (e) {
if (e instanceof InvalidTransactionError) {
throw new BuildTransactionError(e.message);
}
throw e;
}
}

/**
Expand Down
18 changes: 10 additions & 8 deletions modules/sdk-coin-sui/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,17 @@ export abstract class Transaction<T> extends BaseTransaction {
if (transactions.length == 1) {
return utils.getSuiTransactionType(transactions[0]);
}
const txType = utils.getSuiTransactionType(transactions[1]);
if (txType !== SuiTransactionType.Transfer) {
return txType;
} else {
if (transactions.every((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.Transfer)) {
return SuiTransactionType.Transfer;
}
return SuiTransactionType.CustomTx;
if (transactions.some((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.AddStake)) {
return SuiTransactionType.AddStake;
}
if (transactions.some((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.WithdrawStake)) {
return SuiTransactionType.WithdrawStake;
}
if (transactions.every((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.Transfer)) {
return SuiTransactionType.Transfer;
}

return SuiTransactionType.CustomTx;
}

static getProperGasData(k: any): GasData {
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-sui/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ export class Utils implements BaseUtils {
} else if (command.target.endsWith(MethodNames.StakingPoolSplit)) {
return SuiTransactionType.CustomTx;
} else {
throw new InvalidTransactionError(`unsupported target method`);
throw new InvalidTransactionError(`unsupported target method ${command.target}`);
}
default:
throw new InvalidTransactionError(`unsupported transaction kind`);
throw new InvalidTransactionError(`unsupported transaction kind ${command.kind}`);
}
}

Expand Down
3 changes: 3 additions & 0 deletions modules/sdk-coin-sui/test/resources/sui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ export const WITHDRAW_STAKED_SUI_WITH_AMOUNT =
export const CUSTOM_TX_STAKING_POOL_SPLIT =
'AAADAQDnyfNCcc5gOQ+acbC7SdNS9c/LQtx9TfiE5cZCw8PO0appYwAAAAAAII/bNak+aX+eIOdI7b8q8rTtxy0dQjUJPpLpLQD2eXFPAAgA8gUqAQAAAAAgW+Xuhc9YJb0H33u+ePGbyq/ULp5oX9oazyQjPNe5JaYCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDHN0YWtpbmdfcG9vbAVzcGxpdAACAQAAAQEAAQECAAABAgBsENG/EuRhDaHZLvFea8WB4dXnnbMwJOjMHgDCHwx93wEb0ml1cClfvfzXzGbD+rRcyPiwYI8Z+hPsioTmR8puuAkAAAAAAAAAIHHZ+jPpLnIY+pyKG/gpMO25We2ngmV4S4og5N0qrpE4bBDRvxLkYQ2h2S7xXmvFgeHV552zMCTozB4Awh8Mfd/oAwAAAAAAAADKmjsAAAAAAA==';

export const UNSUPPORTED_TX =
'AAADAQB8/cv/rmQ4SGTkEEwSbyfbLPq1wUyg1W6IvFtoo+8KqlC2vgAAAAAAICf6QebgEjDOJoW4sw5jWv5y/UufjHTKEBOHgJuTvJUiAQCxKLKh9y8GFhre5P0lgTbBRPtAyf0t8qUokHbou50E7NKapQAAAAAAIOQxj17At6WByUlaVwW8ARhUgNYg+4piw4zD/Y0NmkGkACD2MpmTSjDA8xA/Swgzt0TpPwDQMzvfr4FvB1MJAXguuAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMMc3Rha2luZ19wb29sEHNwbGl0X3N0YWtlZF9zdWkAAgEAAAEBAAEBAgAAAQIA07HNlidQ0GBG1SOg7xObuweDJ/1vEx0EdAuFFDmMwsEBG9JpdXApX73818xmw/q0XMj4sGCPGfoT7IqE5kfKbrgJAAAAAAAAACBx2foz6S5yGPqcihv4KTDtuVntp4JleEuKIOTdKq6RONOxzZYnUNBgRtUjoO8Tm7sHgyf9bxMdBHQLhRQ5jMLB6AMAAAAAAAAAypo7AAAAAAA=';

export const invalidRecipients: Recipient[] = [
{
address: addresses.invalidAddresses[0],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { coins } from '@bitgo/statics';
import { CustomTransaction } from '../../../src/lib/customTransaction';
import { CUSTOM_TX_STAKING_POOL_SPLIT } from '../../resources/sui';
import { CUSTOM_TX_STAKING_POOL_SPLIT, UNSUPPORTED_TX } from '../../resources/sui';
import { TransactionType } from '@bitgo/sdk-core';
import should from 'should';
import { getBuilderFactory } from '../getBuilderFactory';
Expand Down Expand Up @@ -51,6 +51,10 @@ describe('Sui Custom Transaction Builder', () => {
should.equal(explainedTx.outputAmount, '5000000000');
should.equal(explainedTx.fee.fee, '1000000000');
should.equal(explainedTx.type, TransactionType.CustomTx);

const recipients = tx.recipients;
should.equal(recipients.length, 1);
should.equal(recipients[0].address, '0x5be5ee85cf5825bd07df7bbe78f19bcaafd42e9e685fda1acf24233cd7b925a6');
});

it('should init builder from a custom tx', async function () {
Expand Down Expand Up @@ -81,5 +85,11 @@ describe('Sui Custom Transaction Builder', () => {
should.deepEqual(deserialized, tx);
deserialized.toBroadcastFormat().should.equal(rawTx);
});

it('should reject a custom tx with unsupported txn type', async function () {
should(() => factory.from(UNSUPPORTED_TX)).throwError(
'unsupported target method 0000000000000000000000000000000000000000000000000000000000000003::staking_pool::split_staked_sui'
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as testData from '../../resources/sui';
import should from 'should';
import { TransactionType } from '@bitgo/sdk-core';
import { SuiTransactionType } from '../../../src/lib/iface';
import { CUSTOM_TX_STAKING_POOL_SPLIT, recipients, STAKING_AMOUNT } from '../../resources/sui';
import { CUSTOM_TX_STAKING_POOL_SPLIT, recipients, STAKING_AMOUNT, UNSUPPORTED_TX } from '../../resources/sui';
import { KeyPair } from '../../../src/lib/keyPair';
import { GasData } from '../../../src/lib/mystenlab/types';
import { StakingTransaction, TransferTransaction } from '../../../src';
Expand Down Expand Up @@ -646,5 +646,11 @@ describe('Sui Transaction Builder', async () => {
'APGQHoYbVSyL6M7lOQL5w2YYzeeVcTMEbe0Y4jVphQA+0QHq3VEDoXVwIukkL44z+vqsekviS4gQ0ZwUPTWHFQilzaq1j4wMuCiXuFW4ojFfuoBhEiBy/K4eB5BkHZ+eZw=='
);
});

it('should fail to build if unsupported txn type', async function () {
should(() => factory.from(UNSUPPORTED_TX)).throwError(
'unsupported target method 0000000000000000000000000000000000000000000000000000000000000003::staking_pool::split_staked_sui'
);
});
});
});

0 comments on commit e6b1b13

Please sign in to comment.