mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
9ade5d0eb1
* TransactionUtils: show contractInteraction if value ... exists when the transaction comes from a contract * TransactionUtils: add contractInteraction test * clean: restore empty line
378 lines
13 KiB
JavaScript
378 lines
13 KiB
JavaScript
import EthQuery from 'ethjs-query';
|
|
import { createTestProviderTools } from '../../test/stub/provider';
|
|
import { TransactionType } from '../constants/transaction';
|
|
import {
|
|
determineTransactionType,
|
|
isEIP1559Transaction,
|
|
isLegacyTransaction,
|
|
parseStandardTokenTransactionData,
|
|
} from './transaction.utils';
|
|
|
|
describe('Transaction.utils', function () {
|
|
describe('parseStandardTokenTransactionData', () => {
|
|
it('should return token data', () => {
|
|
const tokenData = parseStandardTokenTransactionData(
|
|
'0xa9059cbb00000000000000000000000050a9d56c2b8ba9a5c7f2c08c3d26e0499f23a7060000000000000000000000000000000000000000000000000000000000004e20',
|
|
);
|
|
expect(tokenData).toStrictEqual(expect.anything());
|
|
const { name, args } = tokenData;
|
|
expect(name).toStrictEqual(TransactionType.tokenMethodTransfer);
|
|
const to = args._to;
|
|
const value = args._value.toString();
|
|
expect(to).toStrictEqual('0x50A9D56C2B8BA9A5c7f2C08C3d26E0499F23a706');
|
|
expect(value).toStrictEqual('20000');
|
|
});
|
|
|
|
it('should not throw errors when called without arguments', () => {
|
|
expect(() => parseStandardTokenTransactionData()).not.toThrow();
|
|
});
|
|
});
|
|
describe('isEIP1559Transaction', function () {
|
|
it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => {
|
|
expect(
|
|
isEIP1559Transaction({
|
|
txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: '0x1' },
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('should return false if either maxFeePerGas and maxPriorityFeePerGas are non-hex strings', () => {
|
|
expect(
|
|
isEIP1559Transaction({
|
|
txParams: { maxFeePerGas: 0, maxPriorityFeePerGas: '0x1' },
|
|
}),
|
|
).toBe(false);
|
|
expect(
|
|
isEIP1559Transaction({
|
|
txParams: { maxFeePerGas: '0x1', maxPriorityFeePerGas: 'fail' },
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it('should return false if either maxFeePerGas or maxPriorityFeePerGas are not supplied', () => {
|
|
expect(
|
|
isEIP1559Transaction({
|
|
txParams: { maxPriorityFeePerGas: '0x1' },
|
|
}),
|
|
).toBe(false);
|
|
expect(
|
|
isEIP1559Transaction({
|
|
txParams: { maxFeePerGas: '0x1' },
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isLegacyTransaction', function () {
|
|
it('should return true if no gas related fields are supplied', () => {
|
|
expect(
|
|
isLegacyTransaction({
|
|
txParams: {},
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('should return true if gasPrice is solely provided', () => {
|
|
expect(
|
|
isLegacyTransaction({
|
|
txParams: { gasPrice: '0x1' },
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('should return false if gasPrice is not a hex string', () => {
|
|
expect(
|
|
isLegacyTransaction({
|
|
txParams: { gasPrice: 100 },
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it('should return false if either maxFeePerGas or maxPriorityFeePerGas are supplied', () => {
|
|
expect(
|
|
isLegacyTransaction({
|
|
txParams: {
|
|
maxFeePerGas: '0x1',
|
|
},
|
|
}),
|
|
).toBe(false);
|
|
|
|
expect(
|
|
isLegacyTransaction({
|
|
txParams: {
|
|
maxPriorityFeePerGas: 'any data',
|
|
},
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('determineTransactionType', function () {
|
|
const genericProvider = createTestProviderTools().provider;
|
|
const query = new EthQuery(genericProvider);
|
|
|
|
it('should return a simple send type when to is truthy and is not a contract address', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0x',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0xabc',
|
|
data: '',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.simpleSend,
|
|
getCodeResponse: null,
|
|
});
|
|
});
|
|
|
|
it('should return a token transfer type when the recipient is a contract, there is no value passed, and data is for the respective method call', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xab',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.tokenMethodTransfer,
|
|
getCodeResponse: '0xab',
|
|
});
|
|
});
|
|
|
|
it(
|
|
'should NOT return a token transfer type and instead return contract interaction' +
|
|
' when the recipient is a contract, the data matches the respective method call, but there is a value passed',
|
|
async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xab',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const resultWithEmptyValue = await determineTransactionType(
|
|
{
|
|
value: '0x0',
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(resultWithEmptyValue).toMatchObject({
|
|
type: TransactionType.tokenMethodTransfer,
|
|
getCodeResponse: '0xab',
|
|
});
|
|
|
|
const resultWithValue = await determineTransactionType(
|
|
{
|
|
value: '0x12345',
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(resultWithValue).toMatchObject({
|
|
type: TransactionType.contractInteraction,
|
|
getCodeResponse: '0xab',
|
|
});
|
|
},
|
|
);
|
|
|
|
it('should NOT return a token transfer type when the recipient is not a contract but the data matches the respective method call', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0x',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.simpleSend,
|
|
getCodeResponse: '0x',
|
|
});
|
|
});
|
|
|
|
it('should return a token approve type when when the recipient is a contract and data is for the respective method call', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xab',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.tokenMethodApprove,
|
|
getCodeResponse: '0xab',
|
|
});
|
|
});
|
|
|
|
it('should return a contract deployment type when to is falsy and there is data', async function () {
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '',
|
|
data: '0xabd',
|
|
},
|
|
query,
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.deployContract,
|
|
getCodeResponse: undefined,
|
|
});
|
|
});
|
|
|
|
it('should return a simple send type with a 0x getCodeResponse when there is data and but the to address is not a contract address', async function () {
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xabd',
|
|
},
|
|
query,
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.simpleSend,
|
|
getCodeResponse: '0x',
|
|
});
|
|
});
|
|
|
|
it('should return a simple send type with a null getCodeResponse when to is truthy and there is data and but getCode returns an error', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: null,
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '0xabd',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.simpleSend,
|
|
getCodeResponse: null,
|
|
});
|
|
});
|
|
|
|
it('should return a contract interaction type with the correct getCodeResponse when to is truthy and there is data and it is not a token transaction', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xa',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: 'abd',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.contractInteraction,
|
|
getCodeResponse: '0x0a',
|
|
});
|
|
});
|
|
|
|
it('should return a contract interaction type with the correct getCodeResponse when to is a contract address and data is falsy', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xa',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
data: '',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.contractInteraction,
|
|
getCodeResponse: '0x0a',
|
|
});
|
|
});
|
|
|
|
it('should return contractInteraction for send with approve', async function () {
|
|
const _providerResultStub = {
|
|
// 1 gwei
|
|
eth_gasPrice: '0x0de0b6b3a7640000',
|
|
// by default, all accounts are external accounts (not contracts)
|
|
eth_getCode: '0xa',
|
|
};
|
|
const _provider = createTestProviderTools({
|
|
scaffold: _providerResultStub,
|
|
}).provider;
|
|
|
|
const result = await determineTransactionType(
|
|
{
|
|
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
|
value: '0x5af3107a4000',
|
|
data: '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005',
|
|
},
|
|
new EthQuery(_provider),
|
|
);
|
|
expect(result).toMatchObject({
|
|
type: TransactionType.contractInteraction,
|
|
getCodeResponse: '0x0a',
|
|
});
|
|
});
|
|
});
|
|
});
|