feat(transaction-pay): add TokenPay strategy with Across provider + metrics#7806
feat(transaction-pay): add TokenPay strategy with Across provider + metrics#7806pedronfigueiredo wants to merge 1 commit intomainfrom
Conversation
packages/transaction-pay-controller/src/strategy/token-pay/TokenPayStrategy.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/across/across-submit.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/across/across-submit.ts
Show resolved
Hide resolved
5428c48 to
6e9ba0c
Compare
…etrics - Introduce TokenPay strategy with provider adapter interface and registry - Add Across provider (quotes + submit) and Relay adapter wrapper - Implement Across quote normalization (fees, dust, durations, fiat) and actions API payloads for delegated calls - Add feature flags for tokenPay providers and Across API config - Add Across submit flow (approvals + swap tx), intent completion, and confirmation waits - Gate unsupported cases (same-chain, perps deposits) and block type‑4 authorizationList until Across supports it - Add Across quote latency metrics and execution latency recording in metamaskPay metadata - Add/extend unit tests for Across quotes/submit/supports and publish hook metrics
6e9ba0c to
047509a
Compare
packages/transaction-pay-controller/src/strategy/across/across-submit.ts
Show resolved
Hide resolved
packages/transaction-pay-controller/src/strategy/across/across-quotes.ts
Show resolved
Hide resolved
| import { getRelayQuotes } from './relay-quotes'; | ||
| import { submitRelayQuotes } from './relay-submit'; | ||
|
|
||
| export class RelayProvider implements TokenPayProvider<RelayQuote> { |
There was a problem hiding this comment.
For the sake of modularity and risk, can we refactor Relay in a dedicated PR and leave as is here?
Would let us have a working control in the clients when testing also.
| /** | ||
| * Deposit funds for Across quote. | ||
| */ | ||
| acrossDeposit = 'acrossDeposit', |
There was a problem hiding this comment.
We actually have a pending task to make relayDeposit more granular, so perpsRelayDeposit and predictRelayDeposit, so should do the same for Across also.
| totalFiat?: string; | ||
|
|
||
| /** Total time spent executing the MetaMask Pay flow, in milliseconds. */ | ||
| executionLatencyMs?: number; |
|
|
||
| - `tokenPay.providerOrder` controls priority (default: `[primaryProvider, 'relay', 'across']`). | ||
| - Each provider can be enabled/disabled via `tokenPay.providers.<id>.enabled`. | ||
| - Providers may also implement capability gating in `supports(...)` (e.g., Across rejects same-chain swaps). |
There was a problem hiding this comment.
This sounds like a good mechanism, but can we abstract to PayStrategy in general so we can accommodate fiat in future also and any strategy?
That way, we could isolate this into it's own dedicated PR for risk and easier review?
| export enum TransactionPayStrategy { | ||
| Bridge = 'bridge', | ||
| Relay = 'relay', | ||
| TokenPay = 'tokenPay', |
There was a problem hiding this comment.
I was assuming this would just be an abstract internal class to remove duplication.
Ideally the client could specify multiple preferences in priority order, and we pick the first that is supported or throw?
Thereby combining the new fallback mechanism and getStrategy?
| continue; | ||
| } | ||
|
|
||
| if (provider.id === 'across' && !config.providers.across.enabled) { |
There was a problem hiding this comment.
Is this breaking the abstraction if it knows about the implementations?
| return { transactionHash }; | ||
| } | ||
|
|
||
| async function executeSingleQuote( |
There was a problem hiding this comment.
I understood the core benefit of the TokenPayStrategy was to remove duplication between the Relay and Across strategies.
But if we're just using it for the fallback logic, would it not be simpler to incorporate that into PayStrategy directly (as mentioned in another comment)?
Then we could create some common token strategy utils to add the origin transactions for example given the duplication?
| return Math.ceil(estimatedGas * gasBuffer); | ||
| } catch (error) { | ||
| log('Gas estimate failed, using fallback', { error }); | ||
| return 900000; |
There was a problem hiding this comment.
We have some feature flags for this too, so another benefit of a common util.
| }; | ||
| } | ||
|
|
||
| function buildDelegationAction(delegation: { |
There was a problem hiding this comment.
This is all done in the client via a constructor callback, so we can decouple delegations from this controller.
| } | ||
|
|
||
| const relayer = quote.fees?.relayerTotal?.amountUsd ?? '0'; | ||
| const app = quote.fees?.app?.amountUsd ?? '0'; |
There was a problem hiding this comment.
I'm worried this isn't the total price impact for the user, but just the Across specific fee, does this definitely include the total impact of the bridge provider itself?
Have we confirmed for example, that the quote results in the requested fee minus the this fee only?
There was a problem hiding this comment.
Good point, I adjusted the provider fee to be the impact just like in the case of relay.
Explanation
This PR introduces a TokenPay strategy that routes to provider adapters (Relay + Across), adds an Across provider end‑to‑end, and tracks quote/execution latency and costs. It also enforces current Across limitations (same‑chain swaps, perps deposits, and type‑4 authorization lists) with explicit gating and TODOs.
Key changes
References
Checklist
Note
Medium Risk
Adds a new payment-routing strategy and Across submission path that constructs and submits approval/swap transactions and updates transaction metadata; bugs here could lead to failed payments or incorrect tracking. Feature-flag gating reduces blast radius, but the new provider integration touches transaction creation, gas estimation, and state updates.
Overview
Adds a new TokenPay strategy that routes MetaMask Pay quote/execution through provider adapters (currently Relay and a new Across provider), with provider ordering/enabling controlled via
confirmations_pay.tokenPayfeature flags.Introduces an end-to-end Across integration: fetches
/swap/approvalquotes (GET/POST with optional action payloads), normalizes fees/amounts/dust and records quote latency, then submits approval+swap transactions (batched when needed) while trackingrequiredTransactionIdsand marking intent completion.Adds MetaMask Pay execution latency tracking by writing
metamaskPay.executionLatencyMsduring publish-hook execution, preserves existingmetamaskPayfields when updating quote-derived metadata, and extends transaction-controller types withTransactionType.acrossDepositand the new metadata field.Written by Cursor Bugbot for commit 047509a. This will update automatically on new commits. Configure here.