@metamask-previews/transaction-pay-controller 28.0.2-preview-4454eb0 → 28.0.2-preview-6616ef8e1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/dist/strategy/fiat/fiat-direct-musd.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-direct-musd.js +25 -26
- package/dist/strategy/fiat/fiat-direct-musd.js.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.js +5 -17
- package/dist/strategy/fiat/fiat-quotes.js.map +1 -1
- package/dist/strategy/fiat/utils.d.ts +8 -33
- package/dist/strategy/fiat/utils.d.ts.map +1 -1
- package/dist/strategy/fiat/utils.js +11 -60
- package/dist/strategy/fiat/utils.js.map +1 -1
- package/dist/strategy/relay/constants.d.ts +1 -0
- package/dist/strategy/relay/constants.d.ts.map +1 -1
- package/dist/strategy/relay/constants.js +1 -0
- package/dist/strategy/relay/constants.js.map +1 -1
- package/dist/strategy/relay/{relay-max-gas-station.d.ts → relay-max.d.ts} +15 -1
- package/dist/strategy/relay/relay-max.d.ts.map +1 -0
- package/dist/strategy/relay/{relay-max-gas-station.js → relay-max.js} +144 -2
- package/dist/strategy/relay/relay-max.js.map +1 -0
- package/dist/strategy/relay/relay-quotes.d.ts.map +1 -1
- package/dist/strategy/relay/relay-quotes.js +40 -10
- package/dist/strategy/relay/relay-quotes.js.map +1 -1
- package/dist/strategy/relay/relay-submit-execute.d.ts +1 -0
- package/dist/strategy/relay/relay-submit-execute.d.ts.map +1 -1
- package/dist/strategy/relay/relay-submit-execute.js +3 -3
- package/dist/strategy/relay/relay-submit-execute.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/feature-flags.d.ts +25 -1
- package/dist/utils/feature-flags.d.ts.map +1 -1
- package/dist/utils/feature-flags.js +35 -1
- package/dist/utils/feature-flags.js.map +1 -1
- package/dist/utils/quotes.d.ts.map +1 -1
- package/dist/utils/quotes.js +14 -0
- package/dist/utils/quotes.js.map +1 -1
- package/package.json +1 -1
- package/dist/strategy/relay/relay-max-gas-station.d.ts.map +0 -1
- package/dist/strategy/relay/relay-max-gas-station.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Quote as RampsQuote,\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger.js';\nimport type { TransactionPayControllerMessenger } from '../../types.js';\nimport {\n getFiatAssetPerTransactionType,\n getFiatEnabledTypes,\n} from '../../utils/feature-flags.js';\nimport { buildCaipAssetType, getTokenInfo } from '../../utils/token.js';\nimport { getTransferredAmountFromTxHash } from '../../utils/transaction.js';\nimport type { RelayQuote } from '../relay/types.js';\nimport type { TransactionPayFiatAsset } from './constants.js';\nimport { DEFAULT_FIAT_CURRENCY, FIAT_ENABLED_TYPES } from './constants.js';\n\nconst log = createModuleLogger(projectLogger, 'fiat-utils');\n\nexport function deriveFiatAssetForFiatPayment(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayFiatAsset {\n const enabledTypes = getFiatEnabledTypes(messenger);\n const txType = resolveTransactionType(transaction, enabledTypes);\n\n return getFiatAssetPerTransactionType(messenger, txType);\n}\n\n/**\n * Resolves the effective transaction type for fiat strategy purposes.\n *\n * For non-batch transactions returns the transaction's own type.\n * For batch transactions returns the first nested transaction type\n * that appears in the enabled types list, or the batch type itself\n * if no nested type matches.\n *\n * @param transaction - The transaction metadata to inspect.\n * @param enabledTypes - Transaction types eligible for fiat payment.\n * @returns The resolved transaction type, or `undefined`.\n */\nexport function resolveTransactionType(\n transaction: TransactionMeta,\n enabledTypes: TransactionType[],\n): TransactionType | undefined {\n if (transaction.type !== TransactionType.batch) {\n return transaction.type;\n }\n\n const nestedType = transaction.nestedTransactions?.find(\n (tx) => tx.type && enabledTypes.includes(tx.type),\n )?.type;\n\n return nestedType ?? transaction.type;\n}\n\n/**\n * Checks whether a transaction is a Money Account deposit.\n *\n * Handles both direct `moneyAccountDeposit` transactions and EIP-7702\n * batch transactions that contain a `moneyAccountDeposit` nested call.\n * Uses {@link resolveTransactionType} with `FIAT_ENABLED_TYPES` to\n * correctly skip non-deposit nested types (e.g. `tokenMethodApprove`).\n *\n * @param transaction - The transaction metadata to inspect.\n * @returns `true` if the transaction is a Money Account deposit.\n */\nexport function isMoneyAccountDepositTransaction(\n transaction: TransactionMeta,\n): boolean {\n return (\n resolveTransactionType(transaction, FIAT_ENABLED_TYPES) ===\n TransactionType.moneyAccountDeposit\n );\n}\n\n/**\n * Fetches the first matching Ramps quote for a fiat asset and payment method.\n *\n * @param options - Quote options.\n * @param options.adjustedAmount - Fiat amount sent to Ramps.\n * @param options.errorMessage - Error thrown when no matching quote is returned.\n * @param options.fiatAsset - Fiat asset to buy.\n * @param options.fiatPaymentMethod - Selected fiat payment method.\n * @param options.messenger - Controller messenger.\n * @param options.walletAddress - Wallet address that receives the on-ramped asset.\n * @returns The first matching Ramps quote.\n */\nexport async function getRampsQuote({\n adjustedAmount,\n errorMessage = 'No matching ramps quote found for selected provider',\n fiatAsset,\n fiatPaymentMethod,\n messenger,\n walletAddress,\n}: {\n adjustedAmount: number;\n errorMessage?: string;\n fiatAsset: TransactionPayFiatAsset;\n fiatPaymentMethod: string;\n messenger: TransactionPayControllerMessenger;\n walletAddress: string;\n}): Promise<RampsQuote> {\n const quotes = await messenger.call('RampsController:getQuotes', {\n amount: adjustedAmount,\n assetId: buildCaipAssetType(fiatAsset.chainId, fiatAsset.address),\n autoSelectProvider: true,\n fiat: DEFAULT_FIAT_CURRENCY,\n paymentMethods: [fiatPaymentMethod],\n restrictToKnownOrNativeProviders: true,\n walletAddress,\n });\n\n log('Fetched ramps quotes', {\n quotesCount: quotes.success?.length ?? 0,\n });\n\n const quote = quotes.success?.[0];\n\n if (!quote) {\n throw new Error(errorMessage);\n }\n\n return quote;\n}\n\n/**\n * Validates that a completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nexport function validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = buildCaipAssetType(\n expectedAsset.chainId,\n expectedAsset.address,\n ).toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Result from {@link resolveSourceAmountRaw}.\n */\nexport type ResolvedSourceAmount = {\n /** Raw (atomic) source amount as a decimal string. */\n amountRaw: string;\n /**\n * Block number of the ramps settlement transaction as a 0x-prefixed hex\n * string. Populated when `order.txHash` is present and the on-chain receipt\n * was successfully fetched (ERC-20 only). Use this as the `fromBlock` for\n * CHOMP idempotency log queries — it reuses the receipt already fetched for\n * the amount and requires no additional network request.\n */\n fromBlock: Hex | undefined;\n};\n\n/**\n * Resolves the raw source amount for a completed fiat order.\n *\n * Attempts to read the actual transferred amount from the on-chain transaction\n * identified by `order.txHash`. If the on-chain read fails or returns\n * no amount, falls back to computing the amount from `order.cryptoAmount`.\n *\n * Also returns the receipt `blockNumber` from the ramps tx when available, so\n * callers can use it as a CHOMP idempotency baseline without any extra request.\n *\n * @param options - The resolution options.\n * @param options.messenger - Controller messenger for network access.\n * @param options.order - The completed on-ramp order.\n * @param options.fiatAsset - The fiat asset describing the expected token.\n * @param options.walletAddress - Recipient wallet address for on-chain lookup.\n * @returns The raw (atomic) source amount and optional receipt block number.\n */\nexport async function resolveSourceAmountRaw({\n messenger,\n order,\n fiatAsset,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n order: RampsOrder;\n fiatAsset: TransactionPayFiatAsset;\n walletAddress: Hex;\n}): Promise<ResolvedSourceAmount> {\n if (order.txHash) {\n try {\n const { amountRaw: onChainAmount, blockNumber } =\n await getTransferredAmountFromTxHash({\n messenger,\n txHash: order.txHash,\n chainId: fiatAsset.chainId,\n tokenAddress: fiatAsset.address,\n walletAddress,\n });\n\n if (onChainAmount) {\n log('Resolved source amount from on-chain transaction', {\n txHash: order.txHash,\n onChainAmount,\n blockNumber,\n });\n return { amountRaw: onChainAmount, fromBlock: blockNumber };\n }\n } catch (error) {\n log(\n 'Failed to read on-chain amount, falling back to order.cryptoAmount',\n { txHash: order.txHash, error },\n );\n }\n }\n\n const tokenInfo = getTokenInfo(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!tokenInfo) {\n throw new Error(\n `Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`,\n );\n }\n\n const amountRaw = getRawSourceAmountFromOrderCryptoAmount({\n cryptoAmount: order.cryptoAmount,\n decimals: tokenInfo.decimals,\n });\n\n return { amountRaw, fromBlock: undefined };\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nexport function getRawSourceAmountFromOrderCryptoAmount({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the relay exchange rate hasn't drifted significantly between\n * the original quoting phase and the post-settlement discovery quote.\n *\n * Compares the USD output/input ratio from both quotes. This normalises for\n * different source amounts (quoting phase uses a theoretical amount, discovery\n * uses the actual settled amount) so the comparison reflects genuine rate\n * movement rather than amount differences.\n *\n * @param options - The validation options.\n * @param options.originalQuote - Relay quote from the original quoting phase.\n * @param options.discoveryQuote - Relay quote from the post-settlement discovery.\n * @param options.maxRateDriftPercent - Maximum allowed rate drift percentage.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nexport function validateRelayRateDrift({\n originalQuote,\n discoveryQuote,\n maxRateDriftPercent,\n transactionId,\n}: {\n originalQuote: RelayQuote;\n discoveryQuote: RelayQuote;\n maxRateDriftPercent: number;\n transactionId: string;\n}): void {\n const originalIn = new BigNumber(originalQuote.details.currencyIn.amountUsd);\n const originalOut = new BigNumber(\n originalQuote.details.currencyOut.amountUsd,\n );\n const discoveryIn = new BigNumber(\n discoveryQuote.details.currencyIn.amountUsd,\n );\n const discoveryOut = new BigNumber(\n discoveryQuote.details.currencyOut.amountUsd,\n );\n\n if (\n !originalIn.gt(0) ||\n !originalOut.gt(0) ||\n !discoveryIn.gt(0) ||\n !discoveryOut.gt(0)\n ) {\n return;\n }\n\n const originalRate = originalOut.dividedBy(originalIn);\n const discoveryRate = discoveryOut.dividedBy(discoveryIn);\n\n const driftPercent = originalRate\n .minus(discoveryRate)\n .dividedBy(originalRate)\n .multipliedBy(100);\n\n log('Relay rate drift check', {\n originalRate: originalRate.toFixed(6),\n discoveryRate: discoveryRate.toFixed(6),\n driftPercent: driftPercent.toFixed(2),\n maxRateDriftPercent,\n transactionId,\n });\n\n if (driftPercent.gt(maxRateDriftPercent)) {\n throw new Error(\n `Relay rate drift too high for transaction ` +\n `${driftPercent.toFixed(2)}% exceeds ${maxRateDriftPercent}% max`,\n );\n }\n}\n\n/**\n * Extracts the provider code from a ramps provider string.\n *\n * Accepts the canonical provider code (e.g. `transak-native`) and, for\n * backwards compatibility, the legacy path form (e.g. `/providers/transak-native`).\n *\n * @param provider - Canonical provider code, or legacy provider path.\n * @returns The provider code, or `null` if the format is invalid.\n */\nexport function extractProviderCode(\n provider: string | undefined,\n): string | null {\n if (!provider) {\n return null;\n }\n\n const parts = provider.split('/').filter(Boolean);\n\n if (parts[0] === 'providers') {\n return parts[1] ?? null;\n }\n\n return parts.length === 1 ? parts[0] : null;\n}\n\n/**\n * Provider codes that identify Transak's native (non-aggregator) integration.\n * Matches the codes emitted by `TransakService` for production and staging.\n */\nconst NATIVE_TRANSAK_PROVIDER_CODES = [\n 'transak-native',\n 'transak-native-staging',\n];\n\n/**\n * When the resolved ramps provider is Transak Native, fetches the native\n * Transak buy quote and returns its total fee so the estimate matches what\n * Transak Native actually charges.\n *\n * The native lookup (`/api/v2/lookup/quotes`) is an unauthenticated, API-key\n * only call, so it is safe to run at estimate time before the user signs in to\n * Transak. Returns `null` when the provider is not Transak Native, or when the\n * native lookup fails or returns an unusable fee, so the caller falls back to\n * the aggregator quote's fee.\n *\n * @param options - Native fee options.\n * @param options.adjustedAmount - Fiat amount (USD) sent to ramps.\n * @param options.fiatAsset - Fiat asset being bought.\n * @param options.fiatPaymentMethod - Selected fiat payment method.\n * @param options.fiatQuote - The resolved aggregator quote (used to detect native).\n * @param options.isFeeExcludedFromFiat - Whether Transak adds its fee on top of\n * the fiat amount (`true`) or carves it out of the amount (`false`). Must mirror\n * the eventual checkout mode so the estimate equals the charge.\n * @param options.messenger - Controller messenger.\n * @returns The native total fee as a BigNumber, or `null` to use the aggregator fee.\n */\nexport async function getNativeTransakRampsFee({\n adjustedAmount,\n fiatAsset,\n fiatPaymentMethod,\n fiatQuote,\n isFeeExcludedFromFiat,\n messenger,\n}: {\n adjustedAmount: number;\n fiatAsset: TransactionPayFiatAsset;\n fiatPaymentMethod: string;\n fiatQuote: RampsQuote;\n isFeeExcludedFromFiat: boolean;\n messenger: TransactionPayControllerMessenger;\n}): Promise<BigNumber | null> {\n const providerCode = extractProviderCode(fiatQuote.provider);\n\n if (!providerCode || !NATIVE_TRANSAK_PROVIDER_CODES.includes(providerCode)) {\n return null;\n }\n\n try {\n const assetId = buildCaipAssetType(fiatAsset.chainId, fiatAsset.address);\n const network = assetId.split('/')[0];\n\n const nativeQuote = await messenger.call(\n 'TransakService:getBuyQuote',\n DEFAULT_FIAT_CURRENCY,\n assetId,\n network,\n fiatPaymentMethod,\n String(adjustedAmount),\n isFeeExcludedFromFiat,\n );\n\n const fee = new BigNumber(nativeQuote.totalFee ?? NaN);\n\n if (!fee.isFinite() || fee.isLessThan(0)) {\n log(\n 'Native Transak quote returned an unusable fee; using aggregator fee',\n {\n totalFee: nativeQuote.totalFee,\n },\n );\n return null;\n }\n\n return fee;\n } catch (error) {\n log('Native Transak fee fetch failed; using aggregator fee', { error });\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Quote as RampsQuote,\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger.js';\nimport type { TransactionPayControllerMessenger } from '../../types.js';\nimport {\n getFiatAssetPerTransactionType,\n getFiatEnabledTypes,\n} from '../../utils/feature-flags.js';\nimport { buildCaipAssetType, getTokenInfo } from '../../utils/token.js';\nimport { getTransferredAmountFromTxHash } from '../../utils/transaction.js';\nimport type { RelayQuote } from '../relay/types.js';\nimport type { TransactionPayFiatAsset } from './constants.js';\nimport { DEFAULT_FIAT_CURRENCY, FIAT_ENABLED_TYPES } from './constants.js';\n\nconst log = createModuleLogger(projectLogger, 'fiat-utils');\n\nexport function deriveFiatAssetForFiatPayment(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayFiatAsset {\n const enabledTypes = getFiatEnabledTypes(messenger);\n const txType = resolveTransactionType(transaction, enabledTypes);\n\n return getFiatAssetPerTransactionType(messenger, txType);\n}\n\n/**\n * Resolves the effective transaction type for fiat strategy purposes.\n *\n * For non-batch transactions returns the transaction's own type.\n * For batch transactions returns the first nested transaction type\n * that appears in the enabled types list, or the batch type itself\n * if no nested type matches.\n *\n * @param transaction - The transaction metadata to inspect.\n * @param enabledTypes - Transaction types eligible for fiat payment.\n * @returns The resolved transaction type, or `undefined`.\n */\nexport function resolveTransactionType(\n transaction: TransactionMeta,\n enabledTypes: TransactionType[],\n): TransactionType | undefined {\n if (transaction.type !== TransactionType.batch) {\n return transaction.type;\n }\n\n const nestedType = transaction.nestedTransactions?.find(\n (tx) => tx.type && enabledTypes.includes(tx.type),\n )?.type;\n\n return nestedType ?? transaction.type;\n}\n\n/**\n * Checks whether a transaction is a Money Account deposit.\n *\n * Handles both direct `moneyAccountDeposit` transactions and EIP-7702\n * batch transactions that contain a `moneyAccountDeposit` nested call.\n * Uses {@link resolveTransactionType} with `FIAT_ENABLED_TYPES` to\n * correctly skip non-deposit nested types (e.g. `tokenMethodApprove`).\n *\n * @param transaction - The transaction metadata to inspect.\n * @returns `true` if the transaction is a Money Account deposit.\n */\nexport function isMoneyAccountDepositTransaction(\n transaction: TransactionMeta,\n): boolean {\n return (\n resolveTransactionType(transaction, FIAT_ENABLED_TYPES) ===\n TransactionType.moneyAccountDeposit\n );\n}\n\n/**\n * Fetches the best matching Ramps quote for a fiat asset and payment method,\n * with provider fees reconciled to what the resolved provider actually charges.\n *\n * The `RampsController:getQuoteWithFees` action owns the provider check and the\n * native buy-quote lookup, so a Transak Native quote already carries the native\n * fee in its `providerFee`/`networkFee`/`totalFees` fields. MM Pay deposits are\n * fee-on-top, so the quote is requested with `isFeeExcludedFromFiat: true`.\n *\n * @param options - Quote options.\n * @param options.adjustedAmount - Fiat amount sent to Ramps.\n * @param options.errorMessage - Error thrown when no matching quote is returned.\n * @param options.fiatAsset - Fiat asset to buy.\n * @param options.fiatPaymentMethod - Selected fiat payment method.\n * @param options.messenger - Controller messenger.\n * @param options.walletAddress - Wallet address that receives the on-ramped asset.\n * @returns The best matching Ramps quote with reconciled fees.\n */\nexport async function getRampsQuote({\n adjustedAmount,\n errorMessage = 'No matching ramps quote found for selected provider',\n fiatAsset,\n fiatPaymentMethod,\n messenger,\n walletAddress,\n}: {\n adjustedAmount: number;\n errorMessage?: string;\n fiatAsset: TransactionPayFiatAsset;\n fiatPaymentMethod: string;\n messenger: TransactionPayControllerMessenger;\n walletAddress: string;\n}): Promise<RampsQuote> {\n const quote = await messenger.call('RampsController:getQuoteWithFees', {\n amount: adjustedAmount,\n assetId: buildCaipAssetType(fiatAsset.chainId, fiatAsset.address),\n autoSelectProvider: true,\n fiat: DEFAULT_FIAT_CURRENCY,\n isFeeExcludedFromFiat: true,\n paymentMethods: [fiatPaymentMethod],\n restrictToKnownOrNativeProviders: true,\n walletAddress,\n });\n\n log('Fetched ramps quote', { hasQuote: Boolean(quote) });\n\n if (!quote) {\n throw new Error(errorMessage);\n }\n\n return quote;\n}\n\n/**\n * Validates that a completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nexport function validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = buildCaipAssetType(\n expectedAsset.chainId,\n expectedAsset.address,\n ).toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Result from {@link resolveSourceAmountRaw}.\n */\nexport type ResolvedSourceAmount = {\n /** Raw (atomic) source amount as a decimal string. */\n amountRaw: string;\n /**\n * Block number of the ramps settlement transaction as a 0x-prefixed hex\n * string. Populated when `order.txHash` is present and the on-chain receipt\n * was successfully fetched (ERC-20 only). Use this as the `fromBlock` for\n * CHOMP idempotency log queries — it reuses the receipt already fetched for\n * the amount and requires no additional network request.\n */\n fromBlock: Hex | undefined;\n};\n\n/**\n * Resolves the raw source amount for a completed fiat order.\n *\n * Attempts to read the actual transferred amount from the on-chain transaction\n * identified by `order.txHash`. If the on-chain read fails or returns\n * no amount, falls back to computing the amount from `order.cryptoAmount`.\n *\n * Also returns the receipt `blockNumber` from the ramps tx when available, so\n * callers can use it as a CHOMP idempotency baseline without any extra request.\n *\n * @param options - The resolution options.\n * @param options.messenger - Controller messenger for network access.\n * @param options.order - The completed on-ramp order.\n * @param options.fiatAsset - The fiat asset describing the expected token.\n * @param options.walletAddress - Recipient wallet address for on-chain lookup.\n * @returns The raw (atomic) source amount and optional receipt block number.\n */\nexport async function resolveSourceAmountRaw({\n messenger,\n order,\n fiatAsset,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n order: RampsOrder;\n fiatAsset: TransactionPayFiatAsset;\n walletAddress: Hex;\n}): Promise<ResolvedSourceAmount> {\n if (order.txHash) {\n try {\n const { amountRaw: onChainAmount, blockNumber } =\n await getTransferredAmountFromTxHash({\n messenger,\n txHash: order.txHash,\n chainId: fiatAsset.chainId,\n tokenAddress: fiatAsset.address,\n walletAddress,\n });\n\n if (onChainAmount) {\n log('Resolved source amount from on-chain transaction', {\n txHash: order.txHash,\n onChainAmount,\n blockNumber,\n });\n return { amountRaw: onChainAmount, fromBlock: blockNumber };\n }\n } catch (error) {\n log(\n 'Failed to read on-chain amount, falling back to order.cryptoAmount',\n { txHash: order.txHash, error },\n );\n }\n }\n\n const tokenInfo = getTokenInfo(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!tokenInfo) {\n throw new Error(\n `Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`,\n );\n }\n\n const amountRaw = getRawSourceAmountFromOrderCryptoAmount({\n cryptoAmount: order.cryptoAmount,\n decimals: tokenInfo.decimals,\n });\n\n return { amountRaw, fromBlock: undefined };\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nexport function getRawSourceAmountFromOrderCryptoAmount({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the relay exchange rate hasn't drifted significantly between\n * the original quoting phase and the post-settlement discovery quote.\n *\n * Compares the USD output/input ratio from both quotes. This normalises for\n * different source amounts (quoting phase uses a theoretical amount, discovery\n * uses the actual settled amount) so the comparison reflects genuine rate\n * movement rather than amount differences.\n *\n * @param options - The validation options.\n * @param options.originalQuote - Relay quote from the original quoting phase.\n * @param options.discoveryQuote - Relay quote from the post-settlement discovery.\n * @param options.maxRateDriftPercent - Maximum allowed rate drift percentage.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nexport function validateRelayRateDrift({\n originalQuote,\n discoveryQuote,\n maxRateDriftPercent,\n transactionId,\n}: {\n originalQuote: RelayQuote;\n discoveryQuote: RelayQuote;\n maxRateDriftPercent: number;\n transactionId: string;\n}): void {\n const originalIn = new BigNumber(originalQuote.details.currencyIn.amountUsd);\n const originalOut = new BigNumber(\n originalQuote.details.currencyOut.amountUsd,\n );\n const discoveryIn = new BigNumber(\n discoveryQuote.details.currencyIn.amountUsd,\n );\n const discoveryOut = new BigNumber(\n discoveryQuote.details.currencyOut.amountUsd,\n );\n\n if (\n !originalIn.gt(0) ||\n !originalOut.gt(0) ||\n !discoveryIn.gt(0) ||\n !discoveryOut.gt(0)\n ) {\n return;\n }\n\n const originalRate = originalOut.dividedBy(originalIn);\n const discoveryRate = discoveryOut.dividedBy(discoveryIn);\n\n const driftPercent = originalRate\n .minus(discoveryRate)\n .dividedBy(originalRate)\n .multipliedBy(100);\n\n log('Relay rate drift check', {\n originalRate: originalRate.toFixed(6),\n discoveryRate: discoveryRate.toFixed(6),\n driftPercent: driftPercent.toFixed(2),\n maxRateDriftPercent,\n transactionId,\n });\n\n if (driftPercent.gt(maxRateDriftPercent)) {\n throw new Error(\n `Relay rate drift too high for transaction ` +\n `${driftPercent.toFixed(2)}% exceeds ${maxRateDriftPercent}% max`,\n );\n }\n}\n\n/**\n * Extracts the provider code from a ramps provider string.\n *\n * Accepts the canonical provider code (e.g. `transak-native`) and, for\n * backwards compatibility, the legacy path form (e.g. `/providers/transak-native`).\n *\n * @param provider - Canonical provider code, or legacy provider path.\n * @returns The provider code, or `null` if the format is invalid.\n */\nexport function extractProviderCode(\n provider: string | undefined,\n): string | null {\n if (!provider) {\n return null;\n }\n\n const parts = provider.split('/').filter(Boolean);\n\n if (parts[0] === 'providers') {\n return parts[1] ?? null;\n }\n\n return parts.length === 1 ? parts[0] : null;\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { TransactionType } from '@metamask/transaction-controller';
|
|
|
2
2
|
import type { Hex } from '@metamask/utils';
|
|
3
3
|
import type { RelayStatus } from './types.js';
|
|
4
4
|
export declare const FALLBACK_HASH: Hex;
|
|
5
|
+
export declare const ATOMIC_PROMOTION_FAILURE_PREFIX = "Atomic promotion failed: ";
|
|
5
6
|
export declare const RELAY_URL_BASE = "https://api.relay.link";
|
|
6
7
|
export declare const RELAY_AUTHORIZE_URL = "https://api.relay.link/authorize";
|
|
7
8
|
export declare const RELAY_EXECUTE_URL = "https://api.relay.link/execute";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,aAAa,EAAY,GAAG,CAAC;AAE1C,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,mBAAmB,qCAAgC,CAAC;AACjE,eAAO,MAAM,iBAAiB,mCAA8B,CAAC;AAC7D,eAAO,MAAM,eAAe,iCAA4B,CAAC;AACzD,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AACtE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,oBAAoB,qCAAiC,CAAC;AACnE,eAAO,MAAM,wBAAwB,yCAAqC,CAAC;AAC3E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAI/C,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAM/C,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAS/D,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,aAAa,EAAY,GAAG,CAAC;AAE1C,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAE3E,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,mBAAmB,qCAAgC,CAAC;AACjE,eAAO,MAAM,iBAAiB,mCAA8B,CAAC;AAC7D,eAAO,MAAM,eAAe,iCAA4B,CAAC;AACzD,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AACtE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,oBAAoB,qCAAiC,CAAC;AACnE,eAAO,MAAM,wBAAwB,yCAAqC,CAAC;AAC3E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAI/C,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAM/C,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAS/D,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TransactionType } from '@metamask/transaction-controller';
|
|
2
2
|
import { PERPS_DEPOSIT_TYPES } from '../../constants.js';
|
|
3
3
|
export const FALLBACK_HASH = '0x0';
|
|
4
|
+
export const ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';
|
|
4
5
|
export const RELAY_URL_BASE = 'https://api.relay.link';
|
|
5
6
|
export const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;
|
|
6
7
|
export const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,cAAc,YAAY,CAAC;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,cAAc,UAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,cAAc,QAAQ,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,oBAAoB,OAAO,CAAC;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,oBAAoB,WAAW,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,QAAQ;IACR,UAAU;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,YAAY;IACZ,SAAS;IACT,WAAW;IACX,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,GAAG,MAAM,CAAC,WAAW,CACnB,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI;QACJ,eAAe,CAAC,iBAAiB;KAClC,CAAC,CACH;CACF,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { PERPS_DEPOSIT_TYPES } from '../../constants.js';\nimport type { RelayStatus } from './types.js';\n\nexport const FALLBACK_HASH = '0x0' as Hex;\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;\nexport const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;\nexport const RELAY_QUOTE_URL = `${RELAY_URL_BASE}/quote`;\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const HYPERLIQUID_URL_BASE = 'https://api.hyperliquid.xyz';\nexport const HYPERLIQUID_INFO_URL = `${HYPERLIQUID_URL_BASE}/info`;\nexport const HYPERLIQUID_EXCHANGE_URL = `${HYPERLIQUID_URL_BASE}/exchange`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_FAILURE_STATUSES: RelayStatus[] = [\n 'failure',\n 'refund',\n 'refunded',\n];\n\nexport const RELAY_PENDING_STATUSES: RelayStatus[] = [\n 'delayed',\n 'depositing',\n 'pending',\n 'submitted',\n 'waiting',\n];\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.musdConversion]: TransactionType.musdRelayDeposit,\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n ...Object.fromEntries(\n PERPS_DEPOSIT_TYPES.map((type) => [\n type,\n TransactionType.perpsRelayDeposit,\n ]),\n ),\n};\n"]}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,+BAA+B,GAAG,2BAA2B,CAAC;AAE3E,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,cAAc,YAAY,CAAC;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,cAAc,UAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,cAAc,QAAQ,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,oBAAoB,OAAO,CAAC;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,oBAAoB,WAAW,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,QAAQ;IACR,UAAU;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,YAAY;IACZ,SAAS;IACT,WAAW;IACX,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,GAAG,MAAM,CAAC,WAAW,CACnB,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI;QACJ,eAAe,CAAC,iBAAiB;KAClC,CAAC,CACH;CACF,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { PERPS_DEPOSIT_TYPES } from '../../constants.js';\nimport type { RelayStatus } from './types.js';\n\nexport const FALLBACK_HASH = '0x0' as Hex;\n\nexport const ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;\nexport const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;\nexport const RELAY_QUOTE_URL = `${RELAY_URL_BASE}/quote`;\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const HYPERLIQUID_URL_BASE = 'https://api.hyperliquid.xyz';\nexport const HYPERLIQUID_INFO_URL = `${HYPERLIQUID_URL_BASE}/info`;\nexport const HYPERLIQUID_EXCHANGE_URL = `${HYPERLIQUID_URL_BASE}/exchange`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_FAILURE_STATUSES: RelayStatus[] = [\n 'failure',\n 'refund',\n 'refunded',\n];\n\nexport const RELAY_PENDING_STATUSES: RelayStatus[] = [\n 'delayed',\n 'depositing',\n 'pending',\n 'submitted',\n 'waiting',\n];\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.musdConversion]: TransactionType.musdRelayDeposit,\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n ...Object.fromEntries(\n PERPS_DEPOSIT_TYPES.map((type) => [\n type,\n TransactionType.perpsRelayDeposit,\n ]),\n ),\n};\n"]}
|
|
@@ -18,5 +18,19 @@ type GetSingleQuoteFn = (request: QuoteRequest, fullRequest: PayStrategyGetQuote
|
|
|
18
18
|
* @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.
|
|
19
19
|
*/
|
|
20
20
|
export declare function getRelayMaxGasStationQuote(request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest, getSingleQuote: GetSingleQuoteFn): Promise<TransactionPayQuote<RelayQuote>>;
|
|
21
|
+
export declare function maybePromoteSubsidizedMaxMoneyAccountQuote({ discoveryQuote, fullRequest, getSingleQuote, request, }: {
|
|
22
|
+
discoveryQuote: TransactionPayQuote<RelayQuote>;
|
|
23
|
+
fullRequest: PayStrategyGetQuotesRequest;
|
|
24
|
+
getSingleQuote: GetSingleQuoteFn;
|
|
25
|
+
request: QuoteRequest;
|
|
26
|
+
}): Promise<TransactionPayQuote<RelayQuote>>;
|
|
27
|
+
export declare function throwAtomicPromotionFailed(error: unknown): never;
|
|
28
|
+
export declare function isPromotedSubsidizedMaxMoneyAccountQuote(quote: TransactionPayQuote<RelayQuote>): boolean;
|
|
29
|
+
export declare function maybeDemoteUnsubsidizedAtomicQuote({ fullRequest, getSingleQuote, quote, request, }: {
|
|
30
|
+
fullRequest: PayStrategyGetQuotesRequest;
|
|
31
|
+
getSingleQuote: GetSingleQuoteFn;
|
|
32
|
+
quote: TransactionPayQuote<RelayQuote>;
|
|
33
|
+
request: QuoteRequest;
|
|
34
|
+
}): Promise<TransactionPayQuote<RelayQuote>>;
|
|
21
35
|
export {};
|
|
22
|
-
//# sourceMappingURL=relay-max
|
|
36
|
+
//# sourceMappingURL=relay-max.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-max.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EAEZ,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAexB,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;AAuBnE,KAAK,gBAAgB,GAAG,CACtB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,2BAA2B,KACrC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;AAS9C;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,2BAA2B,EACxC,cAAc,EAAE,gBAAgB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAgJ1C;AA2PD,wBAAsB,0CAA0C,CAAC,EAC/D,cAAc,EACd,WAAW,EACX,cAAc,EACd,OAAO,GACR,EAAE;IACD,cAAc,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAChD,WAAW,EAAE,2BAA2B,CAAC;IACzC,cAAc,EAAE,gBAAgB,CAAC;IACjC,OAAO,EAAE,YAAY,CAAC;CACvB,GAAG,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAwD3C;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAOhE;AAED,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,mBAAmB,CAAC,UAAU,CAAC,GACrC,OAAO,CAMT;AAuHD,wBAAsB,kCAAkC,CAAC,EACvD,WAAW,EACX,cAAc,EACd,KAAK,EACL,OAAO,GACR,EAAE;IACD,WAAW,EAAE,2BAA2B,CAAC;IACzC,cAAc,EAAE,gBAAgB,CAAC;IACjC,KAAK,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACvC,OAAO,EAAE,YAAY,CAAC;CACvB,GAAG,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAgB3C"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { toHex } from '@metamask/controller-utils';
|
|
2
|
+
import { TransactionType } from '@metamask/transaction-controller';
|
|
1
3
|
import { createModuleLogger } from '@metamask/utils';
|
|
2
4
|
import { BigNumber } from 'bignumber.js';
|
|
3
5
|
import { projectLogger } from '../../logger.js';
|
|
6
|
+
import { prefixError } from '../../utils/error-prefix.js';
|
|
7
|
+
import { isAtomicMaxPromotionEnabled } from '../../utils/feature-flags.js';
|
|
4
8
|
import { getGasStationEligibility, getGasStationCostInSourceTokenRaw, } from '../../utils/gas-station.js';
|
|
5
9
|
import { getNativeToken, getTokenBalance, getTokenInfo, } from '../../utils/token.js';
|
|
6
|
-
|
|
10
|
+
import { QuoteError } from '../../utils/validation.js';
|
|
11
|
+
import { ATOMIC_PROMOTION_FAILURE_PREFIX } from './constants.js';
|
|
12
|
+
import { isSubsidizedRelayQuote } from './relay-submit-execute.js';
|
|
13
|
+
const log = createModuleLogger(projectLogger, 'relay-max');
|
|
7
14
|
const PROBE_AMOUNT_PERCENTAGE = 0.25;
|
|
8
15
|
var GasCostEstimateSource;
|
|
9
16
|
(function (GasCostEstimateSource) {
|
|
@@ -239,4 +246,139 @@ function markQuoteAsMaxGasStation(quote) {
|
|
|
239
246
|
isMaxGasStation: true,
|
|
240
247
|
};
|
|
241
248
|
}
|
|
242
|
-
|
|
249
|
+
export async function maybePromoteSubsidizedMaxMoneyAccountQuote({ discoveryQuote, fullRequest, getSingleQuote, request, }) {
|
|
250
|
+
if (!shouldAttemptAtomicPromotion(request, fullRequest.transaction, discoveryQuote, fullRequest.messenger)) {
|
|
251
|
+
return discoveryQuote;
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const targetAmount = validatePositiveIntegerString(discoveryQuote.original.details.currencyOut.amount);
|
|
255
|
+
const transactionClone = cloneTransactionForPromotion(fullRequest.transaction);
|
|
256
|
+
const promotionTransaction = await applyAmountDataUpdates({
|
|
257
|
+
amount: targetAmount,
|
|
258
|
+
messenger: fullRequest.messenger,
|
|
259
|
+
transaction: transactionClone,
|
|
260
|
+
});
|
|
261
|
+
const promotedQuote = await getSingleQuote({
|
|
262
|
+
...request,
|
|
263
|
+
atomic: true,
|
|
264
|
+
isMaxAmount: false,
|
|
265
|
+
targetAmountMinimum: targetAmount,
|
|
266
|
+
}, {
|
|
267
|
+
...fullRequest,
|
|
268
|
+
transaction: promotionTransaction,
|
|
269
|
+
});
|
|
270
|
+
assertAtomicPromotionIsValid({
|
|
271
|
+
discoveryQuote,
|
|
272
|
+
promotedQuote,
|
|
273
|
+
targetAmount,
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
...promotedQuote,
|
|
277
|
+
request: {
|
|
278
|
+
...promotedQuote.request,
|
|
279
|
+
atomic: true,
|
|
280
|
+
isMaxAmount: true,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
return throwAtomicPromotionFailed(error);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
export function throwAtomicPromotionFailed(error) {
|
|
289
|
+
const prefixed = prefixError(error, ATOMIC_PROMOTION_FAILURE_PREFIX);
|
|
290
|
+
throw new QuoteError({
|
|
291
|
+
detail: [prefixed.message],
|
|
292
|
+
message: prefixed.message,
|
|
293
|
+
reason: 'no-quotes',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
export function isPromotedSubsidizedMaxMoneyAccountQuote(quote) {
|
|
297
|
+
return (quote.request.isMaxAmount === true &&
|
|
298
|
+
quote.request.atomic === true &&
|
|
299
|
+
isSubsidizedRelayQuote(quote.original));
|
|
300
|
+
}
|
|
301
|
+
function shouldAttemptAtomicPromotion(request, transaction, discoveryQuote, messenger) {
|
|
302
|
+
return (isAtomicMaxPromotionEnabled(messenger, transaction) &&
|
|
303
|
+
request.isMaxAmount === true &&
|
|
304
|
+
request.isPostQuote !== true &&
|
|
305
|
+
request.atomic !== true &&
|
|
306
|
+
isSubsidizedRelayQuote(discoveryQuote.original));
|
|
307
|
+
}
|
|
308
|
+
function validatePositiveIntegerString(value) {
|
|
309
|
+
const amount = new BigNumber(value);
|
|
310
|
+
if (!amount.isFinite() || !amount.isInteger() || !amount.isGreaterThan(0)) {
|
|
311
|
+
throw new Error(`Invalid target amount: ${value}`);
|
|
312
|
+
}
|
|
313
|
+
return amount.toFixed(0);
|
|
314
|
+
}
|
|
315
|
+
function cloneTransactionForPromotion(transaction) {
|
|
316
|
+
return {
|
|
317
|
+
...transaction,
|
|
318
|
+
nestedTransactions: transaction.nestedTransactions?.map((nestedTransaction) => ({
|
|
319
|
+
...nestedTransaction,
|
|
320
|
+
})),
|
|
321
|
+
requiredAssets: transaction.requiredAssets?.map((requiredAsset) => ({
|
|
322
|
+
...requiredAsset,
|
|
323
|
+
})),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
async function applyAmountDataUpdates({ amount, messenger, transaction, }) {
|
|
327
|
+
const { updates } = await messenger.call('TransactionPayController:getAmountData', {
|
|
328
|
+
amount,
|
|
329
|
+
transaction,
|
|
330
|
+
});
|
|
331
|
+
if (!updates.length) {
|
|
332
|
+
throw new Error('getAmountData returned no updates for atomic promotion');
|
|
333
|
+
}
|
|
334
|
+
const nestedTransactions = transaction.nestedTransactions?.map((nestedTransaction) => ({
|
|
335
|
+
...nestedTransaction,
|
|
336
|
+
}));
|
|
337
|
+
if (!nestedTransactions?.length) {
|
|
338
|
+
throw new Error('Missing nested transactions for atomic promotion');
|
|
339
|
+
}
|
|
340
|
+
for (const { nestedTransactionIndex, data } of updates) {
|
|
341
|
+
if (!nestedTransactions[nestedTransactionIndex]) {
|
|
342
|
+
throw new Error('getAmountData returned an unusable nested transaction update');
|
|
343
|
+
}
|
|
344
|
+
nestedTransactions[nestedTransactionIndex].data = data;
|
|
345
|
+
}
|
|
346
|
+
const requiredAssets = transaction.requiredAssets?.map((requiredAsset) => ({
|
|
347
|
+
...requiredAsset,
|
|
348
|
+
}));
|
|
349
|
+
if (!requiredAssets?.[0]) {
|
|
350
|
+
throw new Error('Missing required assets for atomic promotion');
|
|
351
|
+
}
|
|
352
|
+
requiredAssets[0].amount = toHex(BigInt(amount));
|
|
353
|
+
return {
|
|
354
|
+
...transaction,
|
|
355
|
+
nestedTransactions,
|
|
356
|
+
requiredAssets,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function assertAtomicPromotionIsValid({ discoveryQuote, promotedQuote, targetAmount, }) {
|
|
360
|
+
if (!isSubsidizedRelayQuote(promotedQuote.original)) {
|
|
361
|
+
throw new Error('Promoted quote lost subsidy');
|
|
362
|
+
}
|
|
363
|
+
if (discoveryQuote.original.details.currencyOut.amount !== targetAmount) {
|
|
364
|
+
throw new Error('Discovery quote target amount changed before promotion');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
export async function maybeDemoteUnsubsidizedAtomicQuote({ fullRequest, getSingleQuote, quote, request, }) {
|
|
368
|
+
if (!shouldAttemptAtomicDemotion(request, fullRequest.transaction, fullRequest.messenger)) {
|
|
369
|
+
return quote;
|
|
370
|
+
}
|
|
371
|
+
if (isSubsidizedRelayQuote(quote.original)) {
|
|
372
|
+
return quote;
|
|
373
|
+
}
|
|
374
|
+
return getSingleQuote({ ...request, atomic: false }, fullRequest);
|
|
375
|
+
}
|
|
376
|
+
function shouldAttemptAtomicDemotion(request, transaction, messenger) {
|
|
377
|
+
return (isAtomicMaxPromotionEnabled(messenger, transaction) &&
|
|
378
|
+
request.isMaxAmount !== true &&
|
|
379
|
+
request.isPostQuote !== true &&
|
|
380
|
+
request.atomic !== false &&
|
|
381
|
+
transaction.type !== TransactionType.perpsDepositAndOrder &&
|
|
382
|
+
transaction.type !== TransactionType.predictDepositAndOrder);
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=relay-max.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-max.js","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger.js';\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types.js';\nimport { prefixError } from '../../utils/error-prefix.js';\nimport { isAtomicMaxPromotionEnabled } from '../../utils/feature-flags.js';\nimport {\n getGasStationEligibility,\n getGasStationCostInSourceTokenRaw,\n} from '../../utils/gas-station.js';\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenInfo,\n} from '../../utils/token.js';\nimport { QuoteError } from '../../utils/validation.js';\nimport { ATOMIC_PROMOTION_FAILURE_PREFIX } from './constants.js';\nimport { isSubsidizedRelayQuote } from './relay-submit-execute.js';\nimport type { RelayQuote, RelayTransactionStep } from './types.js';\n\nconst log = createModuleLogger(projectLogger, 'relay-max');\n\nconst PROBE_AMOUNT_PERCENTAGE = 0.25;\n\nenum GasCostEstimateSource {\n GasStation = 'gas-station',\n Probe = 'probe',\n Quote = 'quote',\n}\n\ntype GasCostEstimate = {\n amount: BigNumber;\n source: GasCostEstimateSource;\n};\n\ntype NativeBalanceCheckResult = {\n hasEnoughNativeBalance: boolean;\n nativeBalance?: string;\n nativeGasCostRaw?: string;\n};\n\ntype GetSingleQuoteFn = (\n request: QuoteRequest,\n fullRequest: PayStrategyGetQuotesRequest,\n) => Promise<TransactionPayQuote<RelayQuote>>;\n\ntype MaxAmountQuoteContext = {\n fullRequest: PayStrategyGetQuotesRequest;\n getSingleQuote: GetSingleQuoteFn;\n messenger: TransactionPayControllerMessenger;\n request: QuoteRequest;\n};\n\n/**\n * Returns a Relay max-amount quote using a two-phase gas-station fallback.\n *\n * It first requests a standard max quote (phase 1), then when needed estimates\n * gas in source-token units (directly or via a probe quote), requests an\n * adjusted max quote (phase 2), and accepts phase 2 only if validation passes\n * (source gas fee token selected, gas-limit checks, affordability).\n *\n * If any step fails or validation is unsafe, it safely falls back to phase 1.\n * Successful phase-2 quotes are tagged with `metamask.isMaxGasStation = true`.\n *\n * @param request - Relay quote request for a max-amount flow.\n * @param fullRequest - Full quote request context including messenger and transaction.\n * @param getSingleQuote - Quote fetcher used for phase-1, phase-2, and probe quotes.\n * @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.\n */\nexport async function getRelayMaxGasStationQuote(\n request: QuoteRequest,\n fullRequest: PayStrategyGetQuotesRequest,\n getSingleQuote: GetSingleQuoteFn,\n): Promise<TransactionPayQuote<RelayQuote>> {\n const { messenger } = fullRequest;\n const { sourceChainId, sourceTokenAmount } = request;\n const context: MaxAmountQuoteContext = {\n fullRequest,\n getSingleQuote,\n messenger,\n request,\n };\n\n const phase1Quote = await getSingleQuote(request, fullRequest);\n\n if (phase1Quote.original.metamask?.isExecute) {\n return phase1Quote;\n }\n\n const nativeBalanceCheck = checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(\n phase1Quote,\n messenger,\n request,\n );\n\n if (nativeBalanceCheck.hasEnoughNativeBalance) {\n return fallbackToPhase1(\n phase1Quote,\n 'Native balance is sufficient for gas',\n {\n nativeBalance: nativeBalanceCheck.nativeBalance,\n nativeGasCost: nativeBalanceCheck.nativeGasCostRaw,\n },\n );\n }\n\n const gasStationEligibility = getGasStationEligibility(\n messenger,\n sourceChainId,\n );\n\n if (!gasStationEligibility.isEligible) {\n return fallbackToPhase1(\n phase1Quote,\n 'Gas station is disabled or unsupported',\n );\n }\n\n const sourceAmountBN = new BigNumber(sourceTokenAmount);\n const probeCost = await getProbeGasCostInSourceTokenRaw(context);\n\n if (!probeCost) {\n return fallbackToPhase1(\n phase1Quote,\n 'Unable to estimate gas-station source token cost',\n );\n }\n\n const initialGasEstimate: GasCostEstimate = {\n amount: probeCost,\n source: GasCostEstimateSource.Probe,\n };\n\n const adjustedSourceAmount = getAdjustedSourceAmount(\n sourceAmountBN,\n initialGasEstimate.amount,\n );\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n return fallbackToPhase1(\n phase1Quote,\n 'Insufficient balance for gas station after max adjustment',\n );\n }\n\n const phase2Quote = await getAdjustedPhase2Quote(\n adjustedSourceAmount,\n initialGasEstimate,\n context,\n );\n\n if (!phase2Quote) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted phase-2 quote request failed',\n );\n }\n\n if (!phase2Quote.fees.isSourceGasFeeToken) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote did not return source gas fee token pricing',\n );\n }\n\n const phase1GasLimits = phase1Quote.original.metamask.gasLimits ?? [];\n const phase2GasLimits = phase2Quote.original.metamask.gasLimits ?? [];\n\n if (!hasMatchingGasLimitShape(phase1GasLimits, phase2GasLimits)) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote gas limit shape changed between phases',\n {\n phase1GasLimits,\n phase2GasLimits,\n },\n );\n }\n\n const phase1TotalGasLimit = getTotalGasLimit(phase1GasLimits);\n const phase2TotalGasLimit = getTotalGasLimit(phase2GasLimits);\n\n if (phase2TotalGasLimit > phase1TotalGasLimit) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote total gas limit increased between phases',\n {\n phase1TotalGasLimit,\n phase2TotalGasLimit,\n },\n );\n }\n\n const validationGasEstimate = new BigNumber(\n phase2Quote.fees.sourceNetwork.max.raw,\n );\n\n if (\n !isAdjustedAmountAffordable(\n adjustedSourceAmount,\n validationGasEstimate,\n sourceAmountBN,\n )\n ) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote fails affordability validation',\n {\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n validationGasCost: validationGasEstimate.toString(10),\n },\n );\n }\n\n markQuoteAsMaxGasStation(phase2Quote);\n\n return phase2Quote;\n}\n\nfunction checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): NativeBalanceCheckResult {\n if (quote.fees.isSourceGasFeeToken) {\n return { hasEnoughNativeBalance: false };\n }\n\n const nativeGasCostRaw = quote.fees.sourceNetwork.max.raw;\n const nativeBalance = getTokenBalance(\n messenger,\n request.from,\n request.sourceChainId,\n getNativeToken(request.sourceChainId),\n );\n\n return {\n hasEnoughNativeBalance: new BigNumber(nativeBalance).isGreaterThanOrEqualTo(\n nativeGasCostRaw,\n ),\n nativeBalance,\n nativeGasCostRaw,\n };\n}\n\nfunction getAdjustedSourceAmount(\n sourceAmount: BigNumber,\n estimatedGasCost: BigNumber,\n): BigNumber {\n return sourceAmount\n .minus(estimatedGasCost)\n .integerValue(BigNumber.ROUND_DOWN);\n}\n\nfunction isAdjustedAmountAffordable(\n adjustedSourceAmount: BigNumber,\n gasCost: BigNumber,\n originalSourceAmount: BigNumber,\n): boolean {\n return adjustedSourceAmount\n .plus(gasCost)\n .isLessThanOrEqualTo(originalSourceAmount);\n}\n\nasync function getAdjustedPhase2Quote(\n adjustedSourceAmount: BigNumber,\n initialGasEstimate: GasCostEstimate,\n context: MaxAmountQuoteContext,\n): Promise<TransactionPayQuote<RelayQuote> | undefined> {\n const { fullRequest, getSingleQuote, request } = context;\n\n log('Requesting adjusted max quote', {\n adjustedAmount: adjustedSourceAmount.toString(10),\n gasCostInSourceToken: initialGasEstimate.amount.toString(10),\n gasEstimateSource: initialGasEstimate.source,\n originalAmount: request.sourceTokenAmount,\n });\n\n try {\n return await getSingleQuote(\n {\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(\n 0,\n BigNumber.ROUND_DOWN,\n ),\n },\n fullRequest,\n );\n } catch (error) {\n log('Adjusted quote request failed, falling back to phase-1 quote', {\n error,\n });\n return undefined;\n }\n}\n\nasync function getGasCostFromQuoteOrGasStation(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): Promise<GasCostEstimate | undefined> {\n const gasCost = quote.fees.sourceNetwork.max;\n\n if (quote.fees.isSourceGasFeeToken) {\n log('Gas cost already in source token units', { raw: gasCost.raw });\n return {\n amount: new BigNumber(gasCost.raw),\n source: GasCostEstimateSource.Quote,\n };\n }\n\n const firstTxStep = quote.original.steps.find(\n (step): step is RelayTransactionStep => step.kind === 'transaction',\n );\n const firstStepData = firstTxStep?.items[0]?.data;\n\n if (!firstStepData) {\n return undefined;\n }\n\n const totalItemCount = quote.original.steps.reduce(\n (count, step) => count + step.items.length,\n 0,\n );\n\n const totalGasEstimate = (quote.original.metamask.gasLimits ?? []).reduce(\n (acc, gasLimit) => acc + gasLimit,\n 0,\n );\n\n const gasStationCost = await getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request: {\n from: request.from,\n sourceChainId: request.sourceChainId,\n sourceTokenAddress: request.sourceTokenAddress,\n },\n totalGasEstimate,\n totalItemCount,\n });\n\n if (!gasStationCost) {\n return undefined;\n }\n\n return {\n amount: new BigNumber(gasStationCost.raw),\n source: GasCostEstimateSource.GasStation,\n };\n}\n\nasync function getProbeGasCostInSourceTokenRaw(\n context: MaxAmountQuoteContext,\n): Promise<BigNumber | undefined> {\n const { fullRequest, getSingleQuote, messenger, request } = context;\n\n const sourceTokenInfo = getTokenInfo(\n messenger,\n request.sourceTokenAddress,\n request.sourceChainId,\n );\n\n if (!sourceTokenInfo) {\n return undefined;\n }\n\n const probeAmount = getProbeSourceAmountRaw(\n request.sourceTokenAmount,\n sourceTokenInfo.decimals,\n );\n\n if (!probeAmount || probeAmount === request.sourceTokenAmount) {\n return undefined;\n }\n\n log('Requesting probe quote for gas station estimation', {\n originalSourceAmount: request.sourceTokenAmount,\n probeAmount,\n });\n\n let probeQuote: TransactionPayQuote<RelayQuote> | undefined;\n\n try {\n probeQuote = await getSingleQuote(\n {\n ...request,\n sourceTokenAmount: probeAmount,\n },\n fullRequest,\n );\n } catch (error) {\n log('Probe quote request failed', { error });\n return undefined;\n }\n\n if (!probeQuote) {\n return undefined;\n }\n\n const probeEstimate = await getGasCostFromQuoteOrGasStation(\n probeQuote,\n messenger,\n request,\n );\n\n return probeEstimate?.amount;\n}\n\nfunction getProbeSourceAmountRaw(\n sourceAmountRaw: string,\n sourceDecimals: number,\n): string | undefined {\n const sourceAmount = new BigNumber(sourceAmountRaw);\n\n if (sourceAmount.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n const probeRawAmount = sourceAmount\n .multipliedBy(PROBE_AMOUNT_PERCENTAGE)\n .integerValue(BigNumber.ROUND_FLOOR);\n\n // Minimum probe size: ~0.01 token for tokens with >=2 decimals,\n // otherwise one raw unit for low-decimal tokens.\n const minimumProbeRaw = new BigNumber(1).shiftedBy(\n Math.max(sourceDecimals - 2, 0),\n );\n\n const probeRaw = BigNumber.minimum(\n sourceAmount,\n BigNumber.maximum(probeRawAmount, minimumProbeRaw),\n ).integerValue(BigNumber.ROUND_FLOOR);\n\n if (probeRaw.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n return probeRaw.toFixed(0);\n}\n\nfunction fallbackToPhase1(\n phase1Quote: TransactionPayQuote<RelayQuote>,\n message: string,\n paramsToLog?: Record<string, unknown>,\n): TransactionPayQuote<RelayQuote> {\n log(message, paramsToLog);\n return phase1Quote;\n}\n\nfunction hasMatchingGasLimitShape(phase1: number[], phase2: number[]): boolean {\n return phase1.length === phase2.length;\n}\n\nfunction getTotalGasLimit(gasLimits: number[]): number {\n return gasLimits.reduce((total, gasLimit) => total + gasLimit, 0);\n}\n\nfunction markQuoteAsMaxGasStation(\n quote: TransactionPayQuote<RelayQuote>,\n): void {\n quote.original.metamask = {\n ...quote.original.metamask,\n isMaxGasStation: true,\n };\n}\n\nexport async function maybePromoteSubsidizedMaxMoneyAccountQuote({\n discoveryQuote,\n fullRequest,\n getSingleQuote,\n request,\n}: {\n discoveryQuote: TransactionPayQuote<RelayQuote>;\n fullRequest: PayStrategyGetQuotesRequest;\n getSingleQuote: GetSingleQuoteFn;\n request: QuoteRequest;\n}): Promise<TransactionPayQuote<RelayQuote>> {\n if (\n !shouldAttemptAtomicPromotion(\n request,\n fullRequest.transaction,\n discoveryQuote,\n fullRequest.messenger,\n )\n ) {\n return discoveryQuote;\n }\n\n try {\n const targetAmount = validatePositiveIntegerString(\n discoveryQuote.original.details.currencyOut.amount,\n );\n\n const transactionClone = cloneTransactionForPromotion(\n fullRequest.transaction,\n );\n const promotionTransaction = await applyAmountDataUpdates({\n amount: targetAmount,\n messenger: fullRequest.messenger,\n transaction: transactionClone,\n });\n\n const promotedQuote = await getSingleQuote(\n {\n ...request,\n atomic: true,\n isMaxAmount: false,\n targetAmountMinimum: targetAmount,\n },\n {\n ...fullRequest,\n transaction: promotionTransaction,\n },\n );\n\n assertAtomicPromotionIsValid({\n discoveryQuote,\n promotedQuote,\n targetAmount,\n });\n\n return {\n ...promotedQuote,\n request: {\n ...promotedQuote.request,\n atomic: true,\n isMaxAmount: true,\n },\n };\n } catch (error) {\n return throwAtomicPromotionFailed(error);\n }\n}\n\nexport function throwAtomicPromotionFailed(error: unknown): never {\n const prefixed = prefixError(error, ATOMIC_PROMOTION_FAILURE_PREFIX);\n throw new QuoteError({\n detail: [prefixed.message],\n message: prefixed.message,\n reason: 'no-quotes',\n });\n}\n\nexport function isPromotedSubsidizedMaxMoneyAccountQuote(\n quote: TransactionPayQuote<RelayQuote>,\n): boolean {\n return (\n quote.request.isMaxAmount === true &&\n quote.request.atomic === true &&\n isSubsidizedRelayQuote(quote.original)\n );\n}\n\nfunction shouldAttemptAtomicPromotion(\n request: QuoteRequest,\n transaction: TransactionMeta,\n discoveryQuote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n): boolean {\n return (\n isAtomicMaxPromotionEnabled(messenger, transaction) &&\n request.isMaxAmount === true &&\n request.isPostQuote !== true &&\n request.atomic !== true &&\n isSubsidizedRelayQuote(discoveryQuote.original)\n );\n}\n\nfunction validatePositiveIntegerString(value: string): string {\n const amount = new BigNumber(value);\n\n if (!amount.isFinite() || !amount.isInteger() || !amount.isGreaterThan(0)) {\n throw new Error(`Invalid target amount: ${value}`);\n }\n\n return amount.toFixed(0);\n}\n\nfunction cloneTransactionForPromotion(\n transaction: TransactionMeta,\n): TransactionMeta {\n return {\n ...transaction,\n nestedTransactions: transaction.nestedTransactions?.map(\n (nestedTransaction) => ({\n ...nestedTransaction,\n }),\n ),\n requiredAssets: transaction.requiredAssets?.map((requiredAsset) => ({\n ...requiredAsset,\n })),\n };\n}\n\nasync function applyAmountDataUpdates({\n amount,\n messenger,\n transaction,\n}: {\n amount: string;\n messenger: TransactionPayControllerMessenger;\n transaction: TransactionMeta;\n}): Promise<TransactionMeta> {\n const { updates } = await messenger.call(\n 'TransactionPayController:getAmountData',\n {\n amount,\n transaction,\n },\n );\n\n if (!updates.length) {\n throw new Error('getAmountData returned no updates for atomic promotion');\n }\n\n const nestedTransactions = transaction.nestedTransactions?.map(\n (nestedTransaction) => ({\n ...nestedTransaction,\n }),\n );\n\n if (!nestedTransactions?.length) {\n throw new Error('Missing nested transactions for atomic promotion');\n }\n\n for (const { nestedTransactionIndex, data } of updates) {\n if (!nestedTransactions[nestedTransactionIndex]) {\n throw new Error(\n 'getAmountData returned an unusable nested transaction update',\n );\n }\n\n nestedTransactions[nestedTransactionIndex].data = data;\n }\n\n const requiredAssets = transaction.requiredAssets?.map((requiredAsset) => ({\n ...requiredAsset,\n }));\n\n if (!requiredAssets?.[0]) {\n throw new Error('Missing required assets for atomic promotion');\n }\n\n requiredAssets[0].amount = toHex(BigInt(amount));\n\n return {\n ...transaction,\n nestedTransactions,\n requiredAssets,\n };\n}\n\nfunction assertAtomicPromotionIsValid({\n discoveryQuote,\n promotedQuote,\n targetAmount,\n}: {\n discoveryQuote: TransactionPayQuote<RelayQuote>;\n promotedQuote: TransactionPayQuote<RelayQuote>;\n targetAmount: string;\n}): void {\n if (!isSubsidizedRelayQuote(promotedQuote.original)) {\n throw new Error('Promoted quote lost subsidy');\n }\n\n if (discoveryQuote.original.details.currencyOut.amount !== targetAmount) {\n throw new Error('Discovery quote target amount changed before promotion');\n }\n}\n\nexport async function maybeDemoteUnsubsidizedAtomicQuote({\n fullRequest,\n getSingleQuote,\n quote,\n request,\n}: {\n fullRequest: PayStrategyGetQuotesRequest;\n getSingleQuote: GetSingleQuoteFn;\n quote: TransactionPayQuote<RelayQuote>;\n request: QuoteRequest;\n}): Promise<TransactionPayQuote<RelayQuote>> {\n if (\n !shouldAttemptAtomicDemotion(\n request,\n fullRequest.transaction,\n fullRequest.messenger,\n )\n ) {\n return quote;\n }\n\n if (isSubsidizedRelayQuote(quote.original)) {\n return quote;\n }\n\n return getSingleQuote({ ...request, atomic: false }, fullRequest);\n}\n\nfunction shouldAttemptAtomicDemotion(\n request: QuoteRequest,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): boolean {\n return (\n isAtomicMaxPromotionEnabled(messenger, transaction) &&\n request.isMaxAmount !== true &&\n request.isPostQuote !== true &&\n request.atomic !== false &&\n transaction.type !== TransactionType.perpsDepositAndOrder &&\n transaction.type !== TransactionType.predictDepositAndOrder\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-quotes.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-quotes.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAGV,2BAA2B,EAG3B,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"relay-quotes.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-quotes.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAGV,2BAA2B,EAG3B,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAwCxB,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,YAAY,CAAC;AAuBpB;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,CAAC,CA0E5C"}
|
|
@@ -17,7 +17,7 @@ import { TOKEN_TRANSFER_FOUR_BYTE } from './constants.js';
|
|
|
17
17
|
import { applyHyperliquidActivationFee } from './hyperliquid-activation.js';
|
|
18
18
|
import { applyPolymarketDepositWalletOverrides, getPredictWithdrawSafeAddress, isPredictWithdraw, } from './polymarket/withdraw.js';
|
|
19
19
|
import { fetchRelayQuote } from './relay-api.js';
|
|
20
|
-
import { getRelayMaxGasStationQuote } from './relay-max
|
|
20
|
+
import { getRelayMaxGasStationQuote, isPromotedSubsidizedMaxMoneyAccountQuote, maybeDemoteUnsubsidizedAtomicQuote, maybePromoteSubsidizedMaxMoneyAccountQuote, throwAtomicPromotionFailed, } from './relay-max.js';
|
|
21
21
|
import { validateRelayQuotes } from './relay-validation.js';
|
|
22
22
|
const log = createModuleLogger(projectLogger, 'relay-strategy');
|
|
23
23
|
// Buffer applied to the gas cost when reserving native tokens for gas in
|
|
@@ -35,6 +35,7 @@ const ZERO_AMOUNT = { fiat: '0', human: '0', raw: '0', usd: '0' };
|
|
|
35
35
|
export async function getRelayQuotes(request) {
|
|
36
36
|
const { requests } = request;
|
|
37
37
|
log('Fetching quotes', requests);
|
|
38
|
+
let quotes = [];
|
|
38
39
|
try {
|
|
39
40
|
const normalizedRequests = await Promise.all(requests
|
|
40
41
|
.filter((singleRequest) => {
|
|
@@ -47,13 +48,30 @@ export async function getRelayQuotes(request) {
|
|
|
47
48
|
.map((singleRequest) => normalizeRequest(singleRequest, request.transaction))
|
|
48
49
|
.map((normalizedRequest) => applyHyperliquidActivationFee(normalizedRequest, request.messenger, request.transaction, request.signal)));
|
|
49
50
|
log('Normalized requests', normalizedRequests);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
quotes = await Promise.all(normalizedRequests.map((singleRequest) => getQuoteWithMaxAmountHandling(singleRequest, request)));
|
|
52
|
+
const promotedQuotes = quotes.filter(isPromotedSubsidizedMaxMoneyAccountQuote);
|
|
53
|
+
const otherQuotes = quotes.filter((quote) => !isPromotedSubsidizedMaxMoneyAccountQuote(quote));
|
|
54
|
+
if (otherQuotes.length > 0) {
|
|
55
|
+
await validateRelayQuotes({
|
|
56
|
+
messenger: request.messenger,
|
|
57
|
+
quotes: otherQuotes,
|
|
58
|
+
signal: request.signal,
|
|
59
|
+
transaction: request.transaction,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (promotedQuotes.length > 0) {
|
|
63
|
+
try {
|
|
64
|
+
await validateRelayQuotes({
|
|
65
|
+
messenger: request.messenger,
|
|
66
|
+
quotes: promotedQuotes,
|
|
67
|
+
signal: request.signal,
|
|
68
|
+
transaction: request.transaction,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throwAtomicPromotionFailed(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
57
75
|
return quotes;
|
|
58
76
|
}
|
|
59
77
|
catch (error) {
|
|
@@ -64,9 +82,21 @@ export async function getRelayQuotes(request) {
|
|
|
64
82
|
async function getQuoteWithMaxAmountHandling(request, fullRequest) {
|
|
65
83
|
const { isMaxAmount } = request;
|
|
66
84
|
if (!isMaxAmount) {
|
|
67
|
-
|
|
85
|
+
const quote = await getQuoteWithPostQuoteGasHandling(request, fullRequest);
|
|
86
|
+
return maybeDemoteUnsubsidizedAtomicQuote({
|
|
87
|
+
fullRequest,
|
|
88
|
+
getSingleQuote,
|
|
89
|
+
quote,
|
|
90
|
+
request,
|
|
91
|
+
});
|
|
68
92
|
}
|
|
69
|
-
|
|
93
|
+
const discoveryQuote = await getRelayMaxGasStationQuote(request, fullRequest, getSingleQuote);
|
|
94
|
+
return maybePromoteSubsidizedMaxMoneyAccountQuote({
|
|
95
|
+
discoveryQuote,
|
|
96
|
+
fullRequest,
|
|
97
|
+
getSingleQuote,
|
|
98
|
+
request,
|
|
99
|
+
});
|
|
70
100
|
}
|
|
71
101
|
/**
|
|
72
102
|
* For post-quote flows, fetch an initial quote to compute gas cost in source
|