Skip to content

Commit c773fdf

Browse files
runway-github[bot]matthewwalsh0joaoloureirop
authored
chore(runway): cherry-pick feat: gas station support for metamask pay deposits (#23078)
- feat: cp-7.60.0 gas station support for metamask pay deposits (#23033) ## **Description** Support EIP-7702 gas station when depositing with MetaMask Pay. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: [#6150](https://github.com/MetaMask/MetaMask-planning/issues/6150) ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds gas-fee token support to pay balance/fee checks, delegates getGasFeeTokens, updates transaction-pay-controller to v10, and removes "no gas" token disabling logic. > > - **Confirmations > Alerts**: > - Handle `fees.isSourceGasFeeToken` in `useInsufficientPayTokenBalanceAlert` to include source network fees in totals when using a gas-fee token, and skip native-fee insufficiency in that case. > - **Utils**: > - Simplify `getAvailableTokens` by removing native-gas dependency and disabling logic; no longer sets `disabled`/`disabledMessage`. > - **Engine Messenger**: > - Delegate `TransactionController:getGasFeeTokens` to Transaction Pay controller messenger. > - **Dependencies**: > - Bump `@metamask/transaction-pay-controller` to `^10.0.0`. > - **Tests**: > - Update alert tests to use persistent mocks and add cases for gas-fee token behavior; remove test asserting disabled token when no native gas. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit eac80ae. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> [b5472e1](b5472e1) Co-authored-by: Matthew Walsh <matthew.walsh@consensys.net> Co-authored-by: João Loureiro <175489935+joaoloureirop@users.noreply.github.com>
1 parent c2cea4a commit c773fdf

File tree

7 files changed

+87
-57
lines changed

7 files changed

+87
-57
lines changed

app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.test.ts

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
8787
});
8888

8989
it('returns alert if pay token balance is less than required token amount', () => {
90-
useTransactionPayTokenMock.mockReturnValueOnce({
90+
useTransactionPayTokenMock.mockReturnValue({
9191
payToken: {
9292
...PAY_TOKEN_MOCK,
9393
balanceUsd: '1.22',
@@ -111,14 +111,14 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
111111
});
112112

113113
it('ignores required token amount if skipIfBalance', () => {
114-
useTransactionPayRequiredTokensMock.mockReturnValueOnce([
114+
useTransactionPayRequiredTokensMock.mockReturnValue([
115115
{
116116
...REQUIRED_TOKEN_MOCK,
117117
skipIfBalance: true,
118118
},
119119
]);
120120

121-
useTransactionPayTokenMock.mockReturnValueOnce({
121+
useTransactionPayTokenMock.mockReturnValue({
122122
payToken: {
123123
...PAY_TOKEN_MOCK,
124124
balanceUsd: '1.22',
@@ -134,7 +134,7 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
134134

135135
describe('for fees', () => {
136136
it('returns alert if pay token balance is less than total source amount', () => {
137-
useTransactionPayTokenMock.mockReturnValueOnce({
137+
useTransactionPayTokenMock.mockReturnValue({
138138
payToken: {
139139
...PAY_TOKEN_MOCK,
140140
balanceRaw: '999',
@@ -160,7 +160,7 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
160160
});
161161

162162
it('returns alert if pay token balance is less than source amount plus source network', () => {
163-
useTransactionPayTokenMock.mockReturnValueOnce({
163+
useTransactionPayTokenMock.mockReturnValue({
164164
payToken: {
165165
...PAY_TOKEN_MOCK,
166166
address: NATIVE_TOKEN_MOCK.address as Hex,
@@ -185,11 +185,45 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
185185
},
186186
]);
187187
});
188+
189+
it('returns alert if pay token balance is less than source amount plus source network if gas fee token', () => {
190+
useTransactionPayTokenMock.mockReturnValue({
191+
payToken: {
192+
...PAY_TOKEN_MOCK,
193+
balanceRaw: '1099',
194+
},
195+
setPayToken: jest.fn(),
196+
});
197+
198+
useTransactionPayTotalsMock.mockReturnValue({
199+
...TOTALS_MOCK,
200+
fees: {
201+
...TOTALS_MOCK.fees,
202+
isSourceGasFeeToken: true,
203+
},
204+
});
205+
206+
const { result } = runHook();
207+
208+
expect(result.current).toStrictEqual([
209+
{
210+
key: AlertKeys.InsufficientPayTokenFees,
211+
field: RowAlertKey.Amount,
212+
isBlocking: true,
213+
title: strings('alert_system.insufficient_pay_token_balance.message'),
214+
message: strings(
215+
'alert_system.insufficient_pay_token_balance_fees.message',
216+
{ amount: '$1.11' },
217+
),
218+
severity: Severity.Danger,
219+
},
220+
]);
221+
});
188222
});
189223

190224
describe('for source network fee', () => {
191225
it('returns alert if native balance is less than total source network fee', () => {
192-
useTokenWithBalanceMock.mockReturnValueOnce({
226+
useTokenWithBalanceMock.mockReturnValue({
193227
...NATIVE_TOKEN_MOCK,
194228
balanceRaw: '99',
195229
} as ReturnType<typeof useTokenWithBalance>);
@@ -212,12 +246,12 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
212246
});
213247

214248
it('returns no alert if pay token is native', () => {
215-
useTokenWithBalanceMock.mockReturnValueOnce({
249+
useTokenWithBalanceMock.mockReturnValue({
216250
...NATIVE_TOKEN_MOCK,
217251
balanceRaw: '99',
218252
} as ReturnType<typeof useTokenWithBalance>);
219253

220-
useTransactionPayTokenMock.mockReturnValueOnce({
254+
useTransactionPayTokenMock.mockReturnValue({
221255
payToken: {
222256
...PAY_TOKEN_MOCK,
223257
address: NATIVE_TOKEN_MOCK.address as Hex,
@@ -230,5 +264,33 @@ describe('useInsufficientPayTokenBalanceAlert', () => {
230264

231265
expect(result.current).toStrictEqual([]);
232266
});
267+
268+
it('returns no alert if source network is using gas fee token', () => {
269+
useTokenWithBalanceMock.mockReturnValue({
270+
...NATIVE_TOKEN_MOCK,
271+
balanceRaw: '99',
272+
} as ReturnType<typeof useTokenWithBalance>);
273+
274+
useTransactionPayTotalsMock.mockReturnValue({
275+
...TOTALS_MOCK,
276+
fees: {
277+
...TOTALS_MOCK.fees,
278+
isSourceGasFeeToken: true,
279+
},
280+
sourceAmount: TOTALS_MOCK.sourceAmount,
281+
});
282+
283+
useTransactionPayTokenMock.mockReturnValue({
284+
payToken: {
285+
...PAY_TOKEN_MOCK,
286+
balanceRaw: '1100',
287+
},
288+
setPayToken: jest.fn(),
289+
});
290+
291+
const { result } = runHook();
292+
293+
expect(result.current).toStrictEqual([]);
294+
});
233295
});
234296
});

app/components/Views/confirmations/hooks/alerts/useInsufficientPayTokenBalanceAlert.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export function useInsufficientPayTokenBalanceAlert({
2727
const totals = useTransactionPayTotals();
2828
const formatFiat = useFiatFormatter({ currency: 'usd' });
2929
const isLoading = useIsTransactionPayLoading();
30+
const isSourceGasFeeToken = totals?.fees.isSourceGasFeeToken ?? false;
3031

3132
const sourceChainId = payToken?.chainId ?? '0x0';
3233

@@ -64,20 +65,20 @@ export function useInsufficientPayTokenBalanceAlert({
6465
}
6566

6667
return new BigNumber(totals?.sourceAmount.raw ?? '0').plus(
67-
isPayTokenNative
68+
isPayTokenNative || isSourceGasFeeToken
6869
? new BigNumber(totals?.fees.sourceNetwork.max.raw ?? '0')
6970
: '0',
7071
);
71-
}, [isLoading, isPayTokenNative, totals]);
72+
}, [isLoading, isPayTokenNative, isSourceGasFeeToken, totals]);
7273

7374
const totalSourceAmountUsd = useMemo(
7475
() =>
7576
new BigNumber(totals?.sourceAmount.usd ?? '0').plus(
76-
isPayTokenNative
77+
isPayTokenNative || isSourceGasFeeToken
7778
? new BigNumber(totals?.fees.sourceNetwork.max.usd ?? '0')
7879
: '0',
7980
),
80-
[isPayTokenNative, totals],
81+
[isPayTokenNative, isSourceGasFeeToken, totals],
8182
);
8283

8384
const targetAmountUsd = useMemo(() => {
@@ -104,9 +105,11 @@ export function useInsufficientPayTokenBalanceAlert({
104105
() =>
105106
payToken &&
106107
!isPayTokenNative &&
108+
!isSourceGasFeeToken &&
107109
totalSourceNetworkFeeRaw.isGreaterThan(nativeToken?.balanceRaw ?? '0'),
108110
[
109111
isPayTokenNative,
112+
isSourceGasFeeToken,
110113
nativeToken?.balanceRaw,
111114
payToken,
112115
totalSourceNetworkFeeRaw,

app/components/Views/confirmations/utils/transaction-pay.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
TransactionPaymentToken,
2020
} from '@metamask/transaction-pay-controller';
2121
import { Hex } from '@metamask/utils';
22-
import { strings } from '../../../../../locales/i18n';
2322

2423
const CHAIN_ID_MOCK = '0x1';
2524
const TO_MOCK = '0x0987654321098765432109876543210987654321';
@@ -200,25 +199,6 @@ describe('Transaction Pay Utils', () => {
200199
expect(result).toMatchObject([tokenWithZeroBalance]);
201200
});
202201

203-
it('returns disabled token with message if no native gas', async () => {
204-
const nonNativeToken = {
205-
...TOKEN_MOCK,
206-
address: '0x234',
207-
} as AssetType;
208-
209-
const result = getAvailableTokens({
210-
tokens: [nonNativeToken] as AssetType[],
211-
});
212-
213-
expect(result).toMatchObject([
214-
{
215-
...nonNativeToken,
216-
disabled: true,
217-
disabledMessage: strings('pay_with_modal.no_gas'),
218-
},
219-
]);
220-
});
221-
222202
it('does not return token if no balance', async () => {
223203
const tokenWithZeroBalance = {
224204
...TOKEN_MOCK,

app/components/Views/confirmations/utils/transaction-pay.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import {
1111
TransactionPayRequiredToken,
1212
TransactionPaymentToken,
1313
} from '@metamask/transaction-pay-controller';
14-
import { getNativeTokenAddress } from './asset';
15-
import { strings } from '../../../../../locales/i18n';
1614
import { BigNumber } from 'bignumber.js';
1715
import { isTestNet } from '../../../../util/networks';
1816

@@ -126,22 +124,8 @@ export function getAvailableTokens({
126124
payToken?.address.toLowerCase() === token.address.toLowerCase() &&
127125
payToken?.chainId === token.chainId;
128126

129-
const nativeTokenAddress = getNativeTokenAddress(token.chainId as Hex);
130-
131-
const nativeToken = tokens.find(
132-
(t) => t.address === nativeTokenAddress && t.chainId === token.chainId,
133-
);
134-
135-
const disabled = new BigNumber(nativeToken?.balance ?? 0).isZero();
136-
137-
const disabledMessage = disabled
138-
? strings('pay_with_modal.no_gas')
139-
: undefined;
140-
141127
return {
142128
...token,
143-
disabled,
144-
disabledMessage,
145129
isSelected,
146130
};
147131
});

app/core/Engine/messengers/transaction-pay-controller-messenger/transaction-pay-controller-messenger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function getTransactionPayControllerMessenger(
3535
'TokenListController:getState',
3636
'TokenRatesController:getState',
3737
'TokensController:getState',
38+
'TransactionController:getGasFeeTokens',
3839
'TransactionController:getState',
3940
'TransactionController:updateTransaction',
4041
],

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@
286286
"@metamask/swaps-controller": "^15.0.0",
287287
"@metamask/token-search-discovery-controller": "^4.0.0",
288288
"@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch",
289-
"@metamask/transaction-pay-controller": "^9.0.0",
289+
"@metamask/transaction-pay-controller": "^10.0.0",
290290
"@metamask/tron-wallet-snap": "^1.9.1",
291291
"@metamask/utils": "^11.8.1",
292292
"@ngraveio/bc-ur": "^1.1.6",

yarn.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8947,9 +8947,9 @@ __metadata:
89478947
languageName: node
89488948
linkType: hard
89498949

8950-
"@metamask/transaction-pay-controller@npm:^9.0.0":
8951-
version: 9.0.0
8952-
resolution: "@metamask/transaction-pay-controller@npm:9.0.0"
8950+
"@metamask/transaction-pay-controller@npm:^10.0.0":
8951+
version: 10.0.0
8952+
resolution: "@metamask/transaction-pay-controller@npm:10.0.0"
89538953
dependencies:
89548954
"@ethersproject/abi": "npm:^5.7.0"
89558955
"@ethersproject/contracts": "npm:^5.7.0"
@@ -8963,14 +8963,14 @@ __metadata:
89638963
immer: "npm:^9.0.6"
89648964
lodash: "npm:^4.17.21"
89658965
peerDependencies:
8966-
"@metamask/assets-controllers": ^90.0.0
8967-
"@metamask/bridge-controller": ^62.0.0
8968-
"@metamask/bridge-status-controller": ^62.0.0
8966+
"@metamask/assets-controllers": ^91.0.0
8967+
"@metamask/bridge-controller": ^63.0.0
8968+
"@metamask/bridge-status-controller": ^63.0.0
89698969
"@metamask/gas-fee-controller": ^26.0.0
89708970
"@metamask/network-controller": ^26.0.0
89718971
"@metamask/remote-feature-flag-controller": ^2.0.0
89728972
"@metamask/transaction-controller": ^62.0.0
8973-
checksum: 10/f97b313e75b4229d4cf0213449e4505164a3e4cb276e4b01f04347341dcf01fa3e89f21ea5df2def1fd608acddf574c11b517f4d364bf4f82ad59e11922cf2e3
8973+
checksum: 10/596b50c04ee658bd16aefc8000d8cdbe2ac04e82636e9029b828c352377ca1e1af6b3c33f129ea28ba966553f513f42988e6ad50bc4edb99c59a68467fe6a1f6
89748974
languageName: node
89758975
linkType: hard
89768976

@@ -34449,7 +34449,7 @@ __metadata:
3444934449
"@metamask/test-dapp-solana": "npm:^0.3.0"
3445034450
"@metamask/token-search-discovery-controller": "npm:^4.0.0"
3445134451
"@metamask/transaction-controller": "patch:@metamask/transaction-controller@npm%3A62.0.0#~/.yarn/patches/@metamask-transaction-controller-npm-61.0.0-cccac388c7.patch"
34452-
"@metamask/transaction-pay-controller": "npm:^9.0.0"
34452+
"@metamask/transaction-pay-controller": "npm:^10.0.0"
3445334453
"@metamask/tron-wallet-snap": "npm:^1.9.1"
3445434454
"@metamask/utils": "npm:^11.8.1"
3445534455
"@ngraveio/bc-ur": "npm:^1.1.6"

0 commit comments

Comments
 (0)