Skip to content
Open
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
4 changes: 4 additions & 0 deletions packages/transaction-pay-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Extract shared `buildTokenData` utility and add `TokenListController` fallback for token lookups ([#7774](https://github.com/MetaMask/core/pull/7774))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only include public API or external changes in the changelog, so not internal function references.


## [12.0.2]

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import type { TransactionMeta } from '@metamask/transaction-controller';
import { noop } from 'lodash';

import { updatePaymentToken } from './update-payment-token';
import type { TransactionData } from '../types';
import {
getTokenBalance,
getTokenInfo,
getTokenFiatRate,
} from '../utils/token';
import type { TransactionData, TransactionPaymentToken } from '../types';
import { buildTokenData } from '../utils/token';
import { getTransaction } from '../utils/transaction';

jest.mock('../utils/token');
Expand All @@ -18,18 +14,25 @@ const CHAIN_ID_MOCK = '0x1';
const FROM_MOCK = '0x456';
const TRANSACTION_ID_MOCK = '123-456';

const PAYMENT_TOKEN_MOCK: TransactionPaymentToken = {
address: TOKEN_ADDRESS_MOCK,
balanceFiat: '2.46',
balanceHuman: '1.23',
balanceRaw: '1230000',
balanceUsd: '3.69',
chainId: CHAIN_ID_MOCK,
decimals: 6,
symbol: 'TST',
};

describe('Update Payment Token Action', () => {
const getTokenBalanceMock = jest.mocked(getTokenBalance);
const getTokenInfoMock = jest.mocked(getTokenInfo);
const getTokenFiatRateMock = jest.mocked(getTokenFiatRate);
const buildTokenDataMock = jest.mocked(buildTokenData);
const getTransactionMock = jest.mocked(getTransaction);

beforeEach(() => {
jest.resetAllMocks();

getTokenInfoMock.mockReturnValue({ decimals: 6, symbol: 'TST' });
getTokenBalanceMock.mockReturnValue('1230000');
getTokenFiatRateMock.mockReturnValue({ fiatRate: '2.0', usdRate: '3.0' });
buildTokenDataMock.mockReturnValue(PAYMENT_TOKEN_MOCK);

getTransactionMock.mockReturnValue({
id: TRANSACTION_ID_MOCK,
Expand All @@ -52,43 +55,23 @@ describe('Update Payment Token Action', () => {
},
);

expect(buildTokenDataMock).toHaveBeenCalledWith({
chainId: CHAIN_ID_MOCK,
from: FROM_MOCK,
messenger: {},
tokenAddress: TOKEN_ADDRESS_MOCK,
});

expect(updateTransactionDataMock).toHaveBeenCalledTimes(1);

const transactionDataMock = {} as TransactionData;
updateTransactionDataMock.mock.calls[0][1](transactionDataMock);

expect(transactionDataMock.paymentToken).toStrictEqual({
address: TOKEN_ADDRESS_MOCK,
balanceFiat: '2.46',
balanceHuman: '1.23',
balanceRaw: '1230000',
balanceUsd: '3.69',
chainId: CHAIN_ID_MOCK,
decimals: 6,
symbol: 'TST',
});
});

it('throws if decimals not found', () => {
getTokenInfoMock.mockReturnValue(undefined);

expect(() =>
updatePaymentToken(
{
chainId: CHAIN_ID_MOCK,
tokenAddress: TOKEN_ADDRESS_MOCK,
transactionId: TRANSACTION_ID_MOCK,
},
{
messenger: {} as never,
updateTransactionData: noop,
},
),
).toThrow('Payment token not found');
expect(transactionDataMock.paymentToken).toStrictEqual(PAYMENT_TOKEN_MOCK);
});

it('throws if token fiat rate not found', () => {
getTokenFiatRateMock.mockReturnValue(undefined);
it('throws if token data not found', () => {
buildTokenDataMock.mockReturnValue(undefined);

expect(() =>
updatePaymentToken(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { createModuleLogger } from '@metamask/utils';
import type { Hex } from '@metamask/utils';
import { BigNumber } from 'bignumber.js';

import type { TransactionPayControllerMessenger } from '..';
import { projectLogger } from '../logger';
import type {
TransactionPaymentToken,
UpdatePaymentTokenRequest,
UpdateTransactionDataCallback,
} from '../types';
import {
getTokenBalance,
getTokenFiatRate,
getTokenInfo,
} from '../utils/token';
import { buildTokenData } from '../utils/token';
import { getTransaction } from '../utils/transaction';

const log = createModuleLogger(projectLogger, 'update-payment-token');
Expand Down Expand Up @@ -42,7 +36,7 @@ export function updatePaymentToken(
throw new Error('Transaction not found');
}

const paymentToken = getPaymentToken({
const paymentToken = buildTokenData({
chainId,
from: transaction?.txParams.from as Hex,
messenger,
Expand All @@ -59,63 +53,3 @@ export function updatePaymentToken(
data.paymentToken = paymentToken;
});
}

/**
* Generate the full payment token data from a token address and chain ID.
*
* @param request - The payment token request parameters.
* @param request.chainId - The chain ID.
* @param request.from - The address to get the token balance for.
* @param request.messenger - The transaction pay controller messenger.
* @param request.tokenAddress - The token address.
* @returns The payment token or undefined if the token data could not be retrieved.
*/
function getPaymentToken({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we are moving this logic? Why is this needed for the withdrawal support?

chainId,
from,
messenger,
tokenAddress,
}: {
chainId: Hex;
from: Hex;
messenger: TransactionPayControllerMessenger;
tokenAddress: Hex;
}): TransactionPaymentToken | undefined {
const { decimals, symbol } =
getTokenInfo(messenger, tokenAddress, chainId) ?? {};

if (decimals === undefined || !symbol) {
return undefined;
}

const tokenFiatRate = getTokenFiatRate(messenger, tokenAddress, chainId);

if (tokenFiatRate === undefined) {
return undefined;
}

const balance = getTokenBalance(messenger, from, chainId, tokenAddress);
const balanceRawValue = new BigNumber(balance);
const balanceHumanValue = new BigNumber(balance).shiftedBy(-decimals);
const balanceRaw = balanceRawValue.toFixed(0);
const balanceHuman = balanceHumanValue.toString(10);

const balanceFiat = balanceHumanValue
.multipliedBy(tokenFiatRate.fiatRate)
.toString(10);

const balanceUsd = balanceHumanValue
.multipliedBy(tokenFiatRate.usdRate)
.toString(10);

return {
address: tokenAddress,
balanceFiat,
balanceHuman,
balanceRaw,
balanceUsd,
chainId,
decimals,
symbol,
};
}
11 changes: 11 additions & 0 deletions packages/transaction-pay-controller/src/tests/messenger-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { TokensControllerGetStateAction } from '@metamask/assets-controller
import type { TokenBalancesControllerGetStateAction } from '@metamask/assets-controllers';
import type { TokenRatesControllerGetStateAction } from '@metamask/assets-controllers';
import type { AccountTrackerControllerGetStateAction } from '@metamask/assets-controllers';
import type { GetTokenListState } from '@metamask/assets-controllers';
import type { BridgeStatusControllerGetStateAction } from '@metamask/bridge-status-controller';
import type {
MessengerActions,
Expand Down Expand Up @@ -93,6 +94,10 @@ export function getMessengerMock({
TokensControllerGetStateAction['handler']
> = jest.fn();

const getTokenListControllerStateMock: jest.MockedFn<
GetTokenListState['handler']
> = jest.fn();

const getTokenBalanceControllerStateMock: jest.MockedFn<
TokenBalancesControllerGetStateAction['handler']
> = jest.fn();
Expand Down Expand Up @@ -197,6 +202,11 @@ export function getMessengerMock({
getTokensControllerStateMock,
);

messenger.registerActionHandler(
'TokenListController:getState',
getTokenListControllerStateMock,
);

messenger.registerActionHandler(
'TokenBalancesController:getState',
getTokenBalanceControllerStateMock,
Expand Down Expand Up @@ -263,6 +273,7 @@ export function getMessengerMock({
getRemoteFeatureFlagControllerStateMock,
getStrategyMock,
getTokenBalanceControllerStateMock,
getTokenListControllerStateMock,
getTokenRatesControllerStateMock,
getTokensControllerStateMock,
getTransactionControllerStateMock,
Expand Down
Loading
Loading