Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/abstract-eth/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class Transaction extends BaseTransaction {
if (txData.id) {
this._id = txData.id;
}
this._type = classifyTransaction(txData.data);
this._type = classifyTransaction(txData.data, this._coinConfig.name);

// reset arrays to empty to ensure that they are only set with one set of fresh values
this._inputs = [];
Expand Down
2 changes: 1 addition & 1 deletion modules/abstract-eth/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
* @param {boolean} isFirstSigner if the transaction is being signed by the first signer
*/
protected loadBuilderInput(transactionJson: TxData, isFirstSigner?: boolean): void {
const decodedType = classifyTransaction(transactionJson.data);
const decodedType = classifyTransaction(transactionJson.data, this._coinConfig.name);
this.type(decodedType);
this.counter(transactionJson.nonce);
this.value(transactionJson.value);
Expand Down
19 changes: 16 additions & 3 deletions modules/abstract-eth/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,22 +664,35 @@ export function decodeFlushTokensData(data: string, to?: string): FlushTokensDat
* @param {string} data The data to classify the transaction with
* @returns {TransactionType} The classified transaction type
*/
export function classifyTransaction(data: string): TransactionType {
export function classifyTransaction(data: string, coinName?: string): TransactionType {
if (data.length < 10) {
// contract calls must have at least 4 bytes (method id) and '0x'
// if it doesn't have enough data to be a contract call it must be a single sig send
return TransactionType.SingleSigSend;
}

// TODO(STLX-1970): validate if we are going to constraint to some methods allowed
let transactionType = transactionTypesMap[data.slice(0, 10).toLowerCase()];
if (transactionType === undefined) {
const methodId = data.slice(0, 10).toLowerCase();
const isCeloStaking =
CELO_STAKING_METHOD_IDS.has(methodId) && coinName && (coinName === 'celo' || coinName === 'tcelo');
let transactionType = transactionTypesMap[methodId];

if ((!isCeloStaking && CELO_STAKING_METHOD_IDS.has(methodId)) || transactionType === undefined) {
transactionType = TransactionType.ContractCall;
}

return transactionType;
}

const CELO_STAKING_METHOD_IDS = new Set([
LockMethodId,
VoteMethodId,
ActivateMethodId,
UnvoteMethodId,
UnlockMethodId,
WithdrawMethodId,
]);

/**
* A transaction types map according to the starting part of the encoded data
*/
Expand Down
18 changes: 18 additions & 0 deletions modules/abstract-eth/test/unit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
flushERC1155TokensData,
decodeFlushERC721TokensData,
decodeFlushERC1155TokensData,
classifyTransaction,
} from '../../src/lib/utils';
import { TransactionType } from '@bitgo/sdk-core';

describe('Abstract ETH Utils', () => {
describe('ERC721 Flush Functions', () => {
Expand Down Expand Up @@ -228,4 +230,20 @@ describe('Abstract ETH Utils', () => {
decoded1155.tokenAddress.toLowerCase().should.equal(tokenAddressChecksum.toLowerCase());
});
});

describe('classifyTransaction', () => {
describe('CELO Staking Method ID Collision', () => {
const WITHDRAW_DATA = '0x2e1a7d4d0000000000000000000000000000000000000000000000000000000005f5e100';

it('should classify as StakingWithdraw on CELO chains', () => {
classifyTransaction(WITHDRAW_DATA, 'celo').should.equal(TransactionType.StakingWithdraw);
classifyTransaction(WITHDRAW_DATA, 'tcelo').should.equal(TransactionType.StakingWithdraw);
});

it('should classify as ContractCall on non-CELO chains', () => {
classifyTransaction(WITHDRAW_DATA, 'eth').should.equal(TransactionType.ContractCall);
classifyTransaction(WITHDRAW_DATA).should.equal(TransactionType.ContractCall);
});
});
});
});