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
81 changes: 81 additions & 0 deletions docs/swaps-bridge-faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
## MetaMask Swaps & Bridge – FAQ

Quick links
- PM Guide: `docs/swaps-bridge-pm-guide.md`
- Bridge Controller: `packages/bridge-controller/src/bridge-controller.ts`
- Bridge Status Controller: `packages/bridge-status-controller/src/bridge-status-controller.ts`
- Fetch (quotes/tokens/prices): `packages/bridge-controller/src/utils/fetch.ts`
- Status fetch/backoff: `packages/bridge-status-controller/src/utils/bridge-status.ts`
- Feature flags: `packages/bridge-controller/src/utils/feature-flags.ts`
- Selectors (sorting/metadata): `packages/bridge-controller/src/selectors.ts`
- Balance/allowance: `packages/bridge-controller/src/utils/balance.ts`
- Chain/token utils & constants: `packages/bridge-controller/src/utils/bridge.ts`, `packages/bridge-controller/src/constants/bridge.ts`, `packages/bridge-controller/src/constants/swaps.ts`

1) Where do quotes come from?
- Bridge API `GET /getQuote` (built in `fetchBridgeQuotes` in `utils/fetch.ts`).

2) Are API calls proxied or direct?
- Quotes/Tokens/Status: via Bridge API (`https://bridge.api.cx.metamask.io`).
- Prices: direct to Price API (`https://price.api.cx.metamask.io`).

3) How often do quotes refresh? Can we override per chain?
- Default 30s (`REFRESH_INTERVAL_MS` in `constants/bridge.ts`).
- Per-chain override via feature flags (`utils/feature-flags.ts` + `selectors.ts`).

4) When does quote polling stop?
- After `maxRefreshCount` (default 5) or when `insufficientBal` is true (`bridge-controller.ts`).

5) Where are feature flags stored and which keys are used?
- Source: `RemoteFeatureFlagController` state.
- Mobile uses `bridgeConfigV2`; Extension uses `bridgeConfig` (`utils/feature-flags.ts`).

6) What’s cached and for how long?
- Tokens: 10 minutes (`fetchBridgeTokens`, `cacheOptions`).
- Quotes: no cache (fresh; `cacheRefreshTime: 0`).
- Prices: 30 seconds per currency (`fetchAssetPrices`).

7) Which RPC methods do we use?
- Native balance: `eth_getBalance` via ethers `provider.getBalance`.
- ERC-20 balance: `eth_call` to `balanceOf` via ethers `Contract` (`utils/balance.ts`).
- ERC-20 allowance (USDT reset logic): `allowance(owner, spender)` (`bridge-controller.ts` + `bridge-status-controller/utils/transaction.ts`).

8) How is L1 gas fee (OP/Base) added to quotes?
- After fetching quotes, we call `TransactionController.getLayer1GasFee` for approval/trade and append `l1GasFeesInHexWei` when all quotes are on OP/Base (`bridge-controller.ts`).

9) How is status polled and retried?
- Status endpoint: Bridge API `GET /getTxStatus` (`bridge-status/utils/bridge-status.ts`).
- Exponential backoff: base interval * 2^(attempts-1), using `REFRESH_INTERVAL_MS` as base.

10) How do non‑EVM flows work (Solana, BTC, Tron)?
- Via Snaps using `SnapController:handleRequest`.
- Fee computation: unified `computeFee` (`bridge-controller/src/utils/snaps.ts`).
- Rent exemption (Solana): `getMinimumBalanceForRentExemption` (`utils/snaps.ts`).
- Non‑EVM tx submission handled in `bridge-status-controller.ts` and stored in history.

11) Mobile vs Extension differences to be aware of
- `X-Client-Id` header: `mobile` vs `extension` (`constants/bridge.ts`).
- Feature flags field: Mobile `bridgeConfigV2`, Extension `bridgeConfig`.
- Mobile hardware wallets: require approval gating and a small delay (`bridge-status-controller/utils/transaction.ts`).

12) Where are the base URLs defined?
- `BRIDGE_PROD_API_BASE_URL`, `BRIDGE_DEV_API_BASE_URL` in `constants/bridge.ts`.
- SWAPS v2 base (reference): `constants/swaps.ts`.

13) How do we fetch tokens for a destination network the user hasn’t imported?
- `fetchBridgeTokens` hits Bridge API `GET /getTokens?chainId=…` and caches for 10 minutes (`utils/fetch.ts`).

14) How are asset exchange rates sourced?
- Prefer existing controller state: `MultichainAssetsRatesController`, `CurrencyRateController`, `TokenRatesController`.
- If missing, fetch from Price API and store in `assetExchangeRates` (`bridge-controller.ts`).

15) How do we detect quote expiration on the client?
- Selector `selectIsQuoteExpired` compares `quotesLastFetched` vs refresh interval and whether another refresh is expected (`selectors.ts`).

16) How are quotes sorted and which is recommended?
- Default sorted by cost ascending; alternative ETA ascending.
- `selectRecommendedQuote` returns the top item (`selectors.ts`).

17) How do we validate responses and surface issues?
- Quotes and status responses validated with schemas.
- Validation failures are recorded and can be tracked/inspected (see `utils/fetch.ts` and `bridge-status/utils/bridge-status.ts`).

114 changes: 114 additions & 0 deletions docs/swaps-bridge-pm-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
## MetaMask Swaps & Bridge: PM Guide

### Table of contents
- [Executive summary](#executive-summary)
- [Where core logic lives](#where-core-logic-lives)
- [APIs called (direct vs via Bridge API)](#apis-called-direct-vs-via-bridge-api)
- [Feature flags (LaunchDarkly via RemoteFeatureFlagController)](#feature-flags-launchdarkly-via-remotefeatureflagcontroller)
- [Caching, refresh rates, and timing](#caching-refresh-rates-and-timing)
- [RPC usage (what providers/methods we call)](#rpc-usage-what-providersmethods-we-call)
- [Non‑EVM (Snaps) integration](#non-evm-snaps-integration)
- [Mobile vs Extension differences](#mobile-vs-extension-differences)
- [FAQ (quick answers)](#faq-quick-answers)

### Executive summary
- The user-facing cross-chain Swaps experience is implemented by the Bridge Controller (quotes, UX timing/metrics) and the Bridge Status Controller (tx submission + tracking).
- Quotes/tokens/status use the Bridge API; price data uses the Price API directly. Caching: tokens 10m; prices 30s; quotes no cache.
- Feature flags come from the Remote Feature Flag Controller (LaunchDarkly-backed) and control refresh rates, overrides, etc.
- EVM operations use the selected provider (eth_getBalance, ERC-20 balanceOf, contract allowance); non-EVM use Snaps via SnapController.

### Where core logic lives
- Bridge Controller (quotes, metrics, polling, exchange rates, ERC20 allowance):
- `packages/bridge-controller/src/bridge-controller.ts`
- Selectors (quote sorting/metadata): `packages/bridge-controller/src/selectors.ts`
- Fetch helpers (quotes/tokens/prices): `packages/bridge-controller/src/utils/fetch.ts`
- Feature flags helpers: `packages/bridge-controller/src/utils/feature-flags.ts`
- Balance/allowance helpers: `packages/bridge-controller/src/utils/balance.ts`
- Chain/token utilities: `packages/bridge-controller/src/utils/bridge.ts`, `.../constants/bridge.ts`, `.../constants/swaps.ts`

- Bridge Status Controller (submit txs, poll status, build history, metrics):
- `packages/bridge-status-controller/src/bridge-status-controller.ts`
- Status fetch + backoff: `packages/bridge-status-controller/src/utils/bridge-status.ts`
- Tx helpers (EVM batching/7702, non‑EVM handling): `packages/bridge-status-controller/src/utils/transaction.ts`
- Received amount calc: `packages/bridge-status-controller/src/utils/swap-received-amount.ts`

### APIs called (direct vs via Bridge API)
- Bridge API (Swaps backend):
- Quotes: `GET {BRIDGE_API}/getQuote?…`
- Built in `packages/bridge-controller/src/utils/fetch.ts` (function `fetchBridgeQuotes`)
- Tokens: `GET {BRIDGE_API}/getTokens?chainId=…`
- Built in `fetchBridgeTokens`
- Status: `GET {BRIDGE_API}/getTxStatus?…`
- Built in `packages/bridge-status-controller/src/utils/bridge-status.ts` (function `fetchBridgeTxStatus`)

- Price API (direct, not proxied):
- Spot prices: `GET https://price.api.cx.metamask.io/v3/spot-prices?assetIds=…&vsCurrency=…`
- Built in `fetchAssetPrices`

- SWAPS v2 base (reference only):
- `packages/bridge-controller/src/constants/swaps.ts` → `https://swap.api.cx.metamask.io`

Constants (env):
- `BRIDGE_PROD_API_BASE_URL` = `https://bridge.api.cx.metamask.io`
- `BRIDGE_DEV_API_BASE_URL` = `https://bridge.dev-api.cx.metamask.io`
- Client identity header: `X-Client-Id` set to `extension` or `mobile`

### Feature flags (LaunchDarkly via RemoteFeatureFlagController)
- Source: `RemoteFeatureFlagController` state
- Mobile reads `bridgeConfigV2`; Extension reads `bridgeConfig`
- Normalized by `processFeatureFlags` (`packages/bridge-controller/src/utils/feature-flags.ts`)
- Usage examples:
- Refresh interval override per chain and defaults
- Quote request overrides by feature (e.g., `FeatureId.PERPS` sorting tweak)

### Caching, refresh rates, and timing
- Quotes polling:
- Default refresh: 30s (`REFRESH_INTERVAL_MS`), overridable by flags per chain
- Stop after `maxRefreshCount` (default 5) or if `insufficientBal=true`
- State tracks: `quotesRefreshCount`, `quotesLastFetched`, `quotesInitialLoadTime`
- Cache policy per endpoint:
- Tokens: 10 minutes (via `cacheOptions`)
- Quotes: no cache (always fresh)
- Prices: 30 seconds per currency (merged across currencies)
- Status polling:
- Interval base equals `REFRESH_INTERVAL_MS`, with exponential backoff on failures: `base * 2^(attempts-1)`

### RPC usage (what providers/methods we call)
- Balances:
- Native: `eth_getBalance` via ethers `provider.getBalance`
- ERC-20: `eth_call` to `balanceOf` via ethers `Contract`
- Allowance:
- `erc20.allowance(owner, spender)` to check if USDT reset is needed
- L1 gas fees (OP/Base):
- Uses `TransactionController.getLayer1GasFee` per approval/trade tx

### Non‑EVM (Snaps) integration
- Fee estimation and rent exemption:
- `SnapController:handleRequest` with `computeFee` (unified) and Solana rent exemption call
- Tx submission for non‑EVM:
- Unified `signAndSend` style flow through Snaps; response may include `transactionId`, `signature`, or structured result
- Stored in history with `isBridgeTx` and non‑EVM hints for UI/metrics

### Mobile vs Extension differences
- Client ID header: `mobile` vs `extension`
- Feature flags field: `bridgeConfigV2` (Mobile) vs `bridgeConfig` (Extension)
- Mobile hardware wallets: require pre-approval gating and a small delay between approval/trade to improve UX

### FAQ (quick answers)
- Where do we fetch quotes from?
- Bridge API `/getQuote` (proxied Swaps backend).
- Do we hit token/price APIs directly?
- Tokens: via Bridge API. Prices: direct Price API.
- How often do quotes refresh? Can I change it per chain?
- Default 30s; per-chain overrides via feature flags.
- When do we stop refreshing quotes?
- After max refreshes (default 5) or when balance is insufficient.
- How is balance checked?
- Native via `eth_getBalance`; tokens via `balanceOf` contract call on the selected provider.
- How is L1 gas for OP/Base added?
- Post-quote, we append L1 fees via `TransactionController.getLayer1GasFee`.
- Where do status updates come from?
- Bridge API `/getTxStatus`, with exponential backoff on failures.
- How do non‑EVM flows work?
- Through Snaps: fee computation, tx submission, signatures; tracked in history and polled when needed.

Loading