Skip to content

Commit

Permalink
feat(abstract-eth): support token flush directly from forwarderV4 con…
Browse files Browse the repository at this point in the history
…tract

Changes:
If forwarder version 4 is passed to transaction
builder, it will call flushToken method directly
 on forwarder contract

TICKET: WP-1652
  • Loading branch information
sreerajs committed Apr 4, 2024
1 parent 1892106 commit 3222656
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 21 deletions.
4 changes: 2 additions & 2 deletions modules/abstract-eth/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
break;
case TransactionType.FlushTokens:
this.setContract(transactionJson.to);
const { forwarderAddress, tokenAddress } = decodeFlushTokensData(transactionJson.data);
const { forwarderAddress, tokenAddress } = decodeFlushTokensData(transactionJson);
this.forwarderAddress(forwarderAddress);
this.tokenAddress(tokenAddress);
break;
Expand Down Expand Up @@ -720,7 +720,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
* @returns {TxData} The Ethereum transaction data
*/
private buildFlushTokensTransaction(): TxData {
return this.buildBase(flushTokensData(this._forwarderAddress, this._tokenAddress));
return this.buildBase(flushTokensData(this._forwarderAddress, this._tokenAddress, this._forwarderVersion));
}

/**
Expand Down
60 changes: 42 additions & 18 deletions modules/abstract-eth/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import {
defaultForwarderVersion,
createV4ForwarderTypes,
v4CreateForwarderMethodId,
flushTokensTypesv4,
flushForwarderTokensMethodIdV4,
} from './walletUtil';
import { EthTransactionData } from './types';

Expand Down Expand Up @@ -178,10 +180,20 @@ export function sendMultiSigTokenData(
* @param forwarderAddress The forwarder address to flush
* @param tokenAddress The token address to flush from
*/
export function flushTokensData(forwarderAddress: string, tokenAddress: string): string {
const params = [forwarderAddress, tokenAddress];
const method = EthereumAbi.methodID('flushForwarderTokens', flushTokensTypes);
const args = EthereumAbi.rawEncode(flushTokensTypes, params);
export function flushTokensData(forwarderAddress: string, tokenAddress: string, forwarderVersion: number): string {
let params: string[];
let method: Uint8Array;
let args: Uint8Array;

if (forwarderVersion >= 4) {
params = [tokenAddress];
method = EthereumAbi.methodID('flushTokens', flushTokensTypesv4);
args = EthereumAbi.rawEncode(flushTokensTypesv4, params);
} else {
params = [forwarderAddress, tokenAddress];
method = EthereumAbi.methodID('flushForwarderTokens', flushTokensTypes);
args = EthereumAbi.rawEncode(flushTokensTypes, params);
}
return addHexPrefix(Buffer.concat([method, args]).toString('hex'));
}

Expand Down Expand Up @@ -430,23 +442,34 @@ export function decodeNativeTransferData(data: string): NativeTransferData {
/**
* Decode the given ABI-encoded flush tokens data and return parsed fields
*
* @param data The data to decode
* @param txJson The txJson with data to decode
* @returns parsed transfer data
*/
export function decodeFlushTokensData(data: string): FlushTokensData {
if (!data.startsWith(flushForwarderTokensMethodId)) {
throw new BuildTransactionError(`Invalid transfer bytecode: ${data}`);
export function decodeFlushTokensData(txJson: TxData): FlushTokensData {
if (txJson.data.startsWith(flushForwarderTokensMethodId)) {
const [forwarderAddress, tokenAddress] = getRawDecoded(
flushTokensTypes,
getBufferedByteCode(flushForwarderTokensMethodId, txJson.data)
);
return {
forwarderAddress: addHexPrefix(forwarderAddress as string),
tokenAddress: addHexPrefix(tokenAddress as string),
};
} else if (txJson.data.startsWith(flushForwarderTokensMethodIdV4)) {
const [tokenAddress] = getRawDecoded(
flushTokensTypesv4,
getBufferedByteCode(flushForwarderTokensMethodIdV4, txJson.data)
);
if (!txJson.to) {
throw new BuildTransactionError(`Missing to address: ${txJson}`);
}
return {
forwarderAddress: txJson.to,
tokenAddress: addHexPrefix(tokenAddress as string),
};
} else {
throw new BuildTransactionError(`Invalid transfer bytecode: ${txJson.data}`);
}

const [forwarderAddress, tokenAddress] = getRawDecoded(
flushTokensTypes,
getBufferedByteCode(flushForwarderTokensMethodId, data)
);

return {
forwarderAddress: addHexPrefix(forwarderAddress as string),
tokenAddress: addHexPrefix(tokenAddress as string),
};
}

/**
Expand Down Expand Up @@ -484,6 +507,7 @@ const transactionTypesMap = {
[v4CreateForwarderMethodId]: TransactionType.AddressInitialization,
[sendMultisigMethodId]: TransactionType.Send,
[flushForwarderTokensMethodId]: TransactionType.FlushTokens,
[flushForwarderTokensMethodIdV4]: TransactionType.FlushTokens,
[flushCoinsMethodId]: TransactionType.FlushCoins,
[sendMultisigTokenMethodId]: TransactionType.Send,
[LockMethodId]: TransactionType.StakingLock,
Expand Down
2 changes: 2 additions & 0 deletions modules/abstract-eth/src/lib/walletUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const createForwarderMethodId = '0xa68a76cc';
export const walletInitializationFirstBytes = '0x60606040';
export const recoveryWalletInitializationFirstBytes = '0x60c06040';
export const flushForwarderTokensMethodId = '0x2da03409';
export const flushForwarderTokensMethodIdV4 = '0x3ef13367';
export const flushCoinsMethodId = '0x6b9f96ea';

export const ERC721SafeTransferTypeMethodId = '0xb88d4fde';
Expand All @@ -19,6 +20,7 @@ export const defaultWalletVersion = 0;
export const walletSimpleConstructor = ['address[]'];
export const createV1WalletTypes = ['address[]', 'bytes32'];
export const flushTokensTypes = ['address', 'address'];
export const flushTokensTypesv4 = ['address'];
export const flushCoinsTypes = [];

export const sendMultiSigTypes = ['address', 'uint', 'bytes', 'uint', 'uint', 'bytes'];
Expand Down
35 changes: 34 additions & 1 deletion modules/sdk-coin-eth/test/unit/transactionBuilder/flushTokens.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import should from 'should';
import { TransactionType } from '@bitgo/sdk-core';
import { ETHTransactionType, Fee, flushForwarderTokensMethodId, KeyPair, Transaction } from '../../../src';
import {
ETHTransactionType,
Fee,
flushForwarderTokensMethodId,
flushForwarderTokensMethodIdV4,
KeyPair,
Transaction,
} from '../../../src';
import { getBuilder } from '../getBuilder';

describe('Eth Transaction builder flush tokens', function () {
Expand All @@ -15,6 +22,7 @@ describe('Eth Transaction builder flush tokens', function () {
counter?: number;
fee?: Fee;
key?: KeyPair;
forwarderVersion?: number;
}

const buildTransaction = async function (details: FlushTokensDetails): Promise<Transaction> {
Expand Down Expand Up @@ -45,6 +53,10 @@ describe('Eth Transaction builder flush tokens', function () {
txBuilder.sign({ key: details.key.getKeys().prv });
}

if (details.forwarderVersion !== undefined) {
txBuilder.forwarderVersion(details.forwarderVersion);
}

return await txBuilder.build();
};

Expand Down Expand Up @@ -117,6 +129,27 @@ describe('Eth Transaction builder flush tokens', function () {
txJson.data.should.startWith(flushForwarderTokensMethodId);
});

it('a wallet flush forwarder transaction with forwarder Version 4', async () => {
const tx = await buildTransaction({
fee: {
fee: '10',
gasLimit: '1000',
},
counter: 0,
forwarderAddress: '0x53b8e91bb3b8f618b5f01004ef108f134f219573',
tokenAddress: '0xbcf935d206ca32929e1b887a07ed240f0d8ccd22',
contractAddress: '0x8f977e912ef500548a0c3be6ddde9899f1199b81',
forwarderVersion: 4,
});

tx.type.should.equal(TransactionType.FlushTokens);
const txJson = tx.toJson();
txJson.gasLimit.should.equal('1000');
txJson._type.should.equals(ETHTransactionType.LEGACY);
should.equal(txJson.nonce, 0);
txJson.data.should.startWith(flushForwarderTokensMethodIdV4);
});

it('an unsigned flush transaction from serialized', async () => {
const tx = await buildTransaction({
fee: {
Expand Down

0 comments on commit 3222656

Please sign in to comment.