From 591a0d3f696c237fb12f0c54a58ccb37060e4620 Mon Sep 17 00:00:00 2001 From: dwebxr Date: Thu, 24 Sep 2026 19:12:36 +0900 Subject: [PATCH] =?UTF-8?q?refactor(payment):=20Pay/Checkout/Tip=20?= =?UTF-8?q?=E3=81=AE=E9=80=81=E4=BF=A1=E3=82=AC=E3=83=BC=E3=83=89=E3=82=92?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=A0=E5=88=A5=E3=81=AE=E7=B4=94?= =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=81=AB=E3=80=81=E6=89=8B=E6=95=B0=E6=96=99?= =?UTF-8?q?=E5=86=85=E8=A8=B3=E3=82=92=E8=A1=8C=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E3=81=AB=20(R14=E3=83=BB=E6=8E=9F=2015)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第 6 回レビュー Phase 6 の R14 (F14・F8)。3 フォームの canSubmit/paymentReady 系の式を lib/paymentFlowGuards/{pay,checkout,tip}.ts のフォーム別 policy に移す (12/11/12 宣言・ byte 単位で同一・意図的な差 = Tip の route 限定ロックと完了後の許可と動的 standard engine 待ち、 Checkout の受付中は canSubmit だけ閉じて await 後に再確認、Pay の FX 期限)。手数料の内訳行は components/PaymentBreakdownRows.tsx の行モデルと breakdownRow() で同じ Row/InfoTooltip を 同じ位置に返す (ラベル・金額・wrapper は各フォームに残す)。旧コードで採った pin: 真理値表 (harness だけ変更)・送信 43 行×3・競合 (double click / await 中の chain 切替・切断・同店 hold)・ ja/en の内訳 snapshot 144・呼び出しの省略記法と UI 出力の固定。 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HeizmagJBgL5peL5mQpxkc --- components/CheckoutForm.tsx | 207 ++- components/PaymentBreakdownRows.tsx | 31 + components/PaymentForm.tsx | 238 ++-- components/TipForm.tsx | 176 ++- lib/paymentFlowGuards/checkout.ts | 124 ++ lib/paymentFlowGuards/pay.ts | 144 +++ lib/paymentFlowGuards/tip.ts | 133 ++ lib/paymentFlowGuards/types.ts | 31 + tests/components/CheckoutForm.test.tsx | 152 +++ tests/components/PaymentForm.test.tsx | 103 ++ tests/components/TipForm.test.tsx | 85 ++ .../__snapshots__/CheckoutForm.test.tsx.snap | 121 ++ .../__snapshots__/PaymentForm.test.tsx.snap | 97 ++ .../__snapshots__/TipForm.test.tsx.snap | 73 ++ .../paymentFlowGuards.test.ts.snap | 1111 +++++++++++++++++ tests/lib/paymentFlowGuards.test.ts | 265 +++- 16 files changed, 2684 insertions(+), 407 deletions(-) create mode 100644 components/PaymentBreakdownRows.tsx create mode 100644 lib/paymentFlowGuards/checkout.ts create mode 100644 lib/paymentFlowGuards/pay.ts create mode 100644 lib/paymentFlowGuards/tip.ts create mode 100644 lib/paymentFlowGuards/types.ts create mode 100644 tests/components/__snapshots__/CheckoutForm.test.tsx.snap create mode 100644 tests/components/__snapshots__/PaymentForm.test.tsx.snap create mode 100644 tests/components/__snapshots__/TipForm.test.tsx.snap create mode 100644 tests/lib/__snapshots__/paymentFlowGuards.test.ts.snap diff --git a/components/CheckoutForm.tsx b/components/CheckoutForm.tsx index 566d1cd7..9bd275b2 100644 --- a/components/CheckoutForm.tsx +++ b/components/CheckoutForm.tsx @@ -12,10 +12,9 @@ import { useLocale, useTranslations } from 'next-intl'; import { formatUnits } from 'viem'; import { useAccount, useSwitchChain } from 'wagmi'; import { ConnectButton } from './ConnectButton'; -import { InfoTooltip } from './InfoTooltip'; +import { breakdownRow } from './PaymentBreakdownRows'; import { OnrampCta } from './OnrampCta'; import { ResultRow } from './ResultRow'; -import { Row } from './Row'; import { SmartAccountFallbackBanner } from './SmartAccountFallbackBanner'; import { RelayFallbackBanner } from './RelayFallbackBanner'; import { @@ -49,11 +48,8 @@ import { type MobileOrderFeeKind, } from '@/lib/mobileOrderFee'; import { relayErrorKey } from '@/lib/relay/relayErrorMessage'; -import { - isFallbackSafeRelayError, - isRelayIpRateLimitedError, - isRelayResponseUnknownError, -} from '@/lib/relay/relayResponseError'; +import { isFallbackSafeRelayError } from '@/lib/relay/relayResponseError'; +import { deriveCheckoutGuards } from '@/lib/paymentFlowGuards/checkout'; import { generateStatusToken } from '@/lib/orderStatusToken'; import { isOrderTokenLike } from '@/lib/orderToken'; import { useErc20BalanceAndChain } from '@/hooks/useErc20BalanceAndChain'; @@ -433,33 +429,6 @@ export function CheckoutForm({ params }: { params: CheckoutParams }) { totalCustomerOutflow, ); - // relay 送信中に preflight の切替操作と競合して route が standard へ変わっていても、 - // response-unknown / IP 制限の再送封鎖を外さないため current route とは独立に判定する。 - const relayResponseUnknown = isRelayResponseUnknownError(relay.error); - const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; - // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 - // current route が後から変わってもラッチを外さず、2 本目の UserOperation 送信を防ぐ。 - const gaslessAmbiguous = gasless.isUnknown; - // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 - // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ - // (standard は localStorage に依存しないため、この fail-closed を波及させない)。 - const gaslessStoreUnavailable = - !isStandard && !useRelay && gasless.pendingStoreUnavailable; - const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) - ? relay.error - : null; - const paymentFlowPending = standard.isRestoring || relay.isRestoring - ? true - : relayAmbiguous || gaslessAmbiguous - ? true - : isStandard - ? standard.isPending - : useRelay - ? relay.isPending - : gasless.isPending; - const flowPending = orderAdmissionPending || paymentFlowPending; - // relay は gas quote も smart account も不要なので readiness は常に満たす。 - const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; // 運営の赤字防止: merchant が 0 になるケースは送信を block (fee>0 の Phase 2 で // 主に効く。fee=0 の現状で発火するのは gasless/merchant かつ total < gas のみ)。 // gasless / customer: total < fee → merchant = 0 @@ -476,46 +445,39 @@ export function CheckoutForm({ params }: { params: CheckoutParams }) { effectiveGasAmount, }); - // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一 - // 受取人へ 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全 - // なので再試行を許す。standard の fee-error は merchant transfer が確定済なので - // main ボタンは禁止し、fee の再送は専用 retryFee ボタンのみに限定する - // (PaymentForm と同一防御)。 - const settledNoRetry = - relayAmbiguous || - gaslessAmbiguous || - !!relayIpRateLimited || - (!isStandard && !useRelay && !!gasless.data?.success) || - (useRelay && - !!relay.data && - (relay.data.success || !!relay.data.pending)) || - relay.hasActiveIntent || - standard.hasActiveIntent || - (isStandard && (!!standard.data || standard.isFeeError || standard.isUnknown)); + // 再送封鎖・flow pending・送信可否は /checkout 専用の policy (注文受付と同一店舗 hold を含む) に置く + // (R14・式は移動のみ)。admission の await 後は canSubmit でなく paymentReady を読み直す (onSubmit)。 + const { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + flowPending, + gasQuoteReady, + settledNoRetry, + paymentReady, + canSubmit, + } = deriveCheckoutGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + orderAdmissionPending, + activeQuote, + isConnected, + wrongChain, + saData, + breakdown, + insufficientBalance, + merchantUnderflow, + }); const standardUnknownTxHash = standard.isFeeUnknown ? standard.feeTxHash : standard.merchantTxHash; // 未接続時に最下部 CTA からウォレット選択セクションへ誘導するためのアンカー。 const walletSectionRef = useRef(null); - - const paymentReady = - isConnected && - !wrongChain && - (isStandard || useRelay || !!saData) && - // PaymentForm と揃える明示ガード。現状は totalWei>0 (有効 items) 不変で merchantUnderflow - // が拾うが、空 batch (merchant 受取 0) 送信を構造的にも塞ぐ defense-in-depth。 - breakdown.merchantReceives > 0n && - breakdown.customerPays > 0n && - !insufficientBalance && - !paymentFlowPending && - !relay.orderPaymentHold && - gasQuoteReady && - !merchantUnderflow && - !settledNoRetry && - // gasless 経路のみ封鎖 (standard へ切り替えれば支払える)。 - !gaslessStoreUnavailable; - const canSubmit = paymentReady && !orderAdmissionPending; const paymentReadyRef = useRef(paymentReady); paymentReadyRef.current = paymentReady; @@ -1647,73 +1609,62 @@ export function CheckoutForm({ params }: { params: CheckoutParams }) {
- + {breakdownRow({ label: t('subtotalRow'), value: fmt(totalWei) })} {/* fee=0 のとき手数料行は非表示 (Phase 1 alpha)。負担者でラベルを出し分ける: 店舗が受取から吸収した (merchantReceives < 請求額) なら「(店舗負担)」を補足、 顧客上乗せ (事前モバイルオーダーの顧客負担) なら補足なし。 */} - {breakdown.feeAmount > 0n && ( - 0n && + breakdownRow({ + label: breakdown.merchantReceives < totalWei ? t('feeRowMerchant') - : t('feeRow') - } - value={fmt(breakdown.feeAmount)} - /> - )} - {isStandard ? ( - - ) : isMobileFee ? ( - /* モバイル注文 (gasless): ネットワーク手数料は利用料 (feeRow) に含むため別行を出さない - (relayGasEquiv=0 で「0 JPYC」表示になり紛らわしい)。standard 経路は上の分岐で - 顧客がウォレットで gas を払うので従来どおり gas 行を表示する (ここには来ない)。 */ - null - ) : useRecover ? ( - } - value={fmt(relayGasEquiv)} - /> - ) : useRelay || isJpyc ? ( - // JPYC ガスレス (relay free / 非 relay sponsorship free) は無徴収。中立ラベル。 - } - value={t('gasRowRelayFree')} - /> - ) : ( - - } - value={ - gasAmount !== undefined - ? t('gasRowValue', { amount: fmt(gasAmount) }) - : t('gasRowPending') - } - /> - )} + : useRelay || isJpyc + // JPYC ガスレス (relay free / 非 relay sponsorship free) は無徴収。中立ラベル。 + ? { + label: t('gasRowFree'), + tooltip: t('gasInfoJpycRelay'), + value: t('gasRowRelayFree'), + } + : { + label: isMerchantGas ? t('gasRowMerchant') : t('gasRow'), + tooltip: isCircle + ? t('gasInfoUsdcCircle', { nativeToken }) + : isErc20Paymaster + ? t('gasInfoUsdc', { nativeToken }) + : t('gasInfoJpyc', { nativeToken }), + value: + gasAmount !== undefined + ? t('gasRowValue', { amount: fmt(gasAmount) }) + : t('gasRowPending'), + }, + )}
- + {breakdownRow({ + label: isStandard + ? t('totalRowStandard', { nativeToken }) + : isMerchantGas + ? t('totalRowMerchantGas') + : t('totalRow'), + value: fmt(totalCustomerOutflow), + strong: true, + })}

diff --git a/components/PaymentBreakdownRows.tsx b/components/PaymentBreakdownRows.tsx new file mode 100644 index 00000000..0b992d5a --- /dev/null +++ b/components/PaymentBreakdownRows.tsx @@ -0,0 +1,31 @@ +import type { Key } from 'react'; +import { InfoTooltip } from './InfoTooltip'; +import { Row } from './Row'; + +// 内訳 (明細) 1 行の明示的な row model (R14)。3 フォームで共有するのは描画だけで、金額の計算と +// 文言 (ラベル・tooltip 本文) は各フォームが自前の計算と t() で埋める。Pay の split/店主受取・ +// Checkout の小計/店舗負担/モバイル注文での gas 行の非表示・Tip のクリエイター受取/Arc の gas 文言は +// 意図的に別物なので、共通のラベル集や手数料計算はここに置かない。 +export type BreakdownRowModel = { + label: string; + value: string; + // InfoTooltip の本文。無い行は tooltip を出さない。 + tooltip?: string; + strong?: boolean; +}; + +// component ではなく関数として呼び、従来と同じ (+ labelExtra の InfoTooltip) を同じ位置に返す。 +// 要素木に型を 1 段増やさないので、各行の React の同一性と描画 DOM は抽出前と変わらない。 +export function breakdownRow(row: BreakdownRowModel, key?: Key) { + return ( + + } + value={row.value} + strong={row.strong} + /> + ); +} diff --git a/components/PaymentForm.tsx b/components/PaymentForm.tsx index 7b5cce99..d49a8c86 100644 --- a/components/PaymentForm.tsx +++ b/components/PaymentForm.tsx @@ -9,9 +9,8 @@ import { useAccount, useSwitchChain } from 'wagmi'; import { ConnectButton } from './ConnectButton'; import { PayStepStrip } from './PayStepStrip'; import { PayEmptyLanding } from './PayEmptyLanding'; -import { InfoTooltip } from './InfoTooltip'; +import { breakdownRow } from './PaymentBreakdownRows'; import { OnrampCta } from './OnrampCta'; -import { Row } from './Row'; import { SmartAccountFallbackBanner } from './SmartAccountFallbackBanner'; import { RelayFallbackBanner } from './RelayFallbackBanner'; import { AlertCircle } from 'lucide-react'; @@ -65,11 +64,8 @@ import { } from '@/lib/paymentRoute'; import { recoverFeeValue } from '@/lib/relay/recoverFee'; import { relayErrorKey } from '@/lib/relay/relayErrorMessage'; -import { - isFallbackSafeRelayError, - isRelayIpRateLimitedError, - isRelayResponseUnknownError, -} from '@/lib/relay/relayResponseError'; +import { isFallbackSafeRelayError } from '@/lib/relay/relayResponseError'; +import { derivePayGuards } from '@/lib/paymentFlowGuards/pay'; import { useErc20BalanceAndChain } from '@/hooks/useErc20BalanceAndChain'; import { type GasMode } from '@/lib/fee'; import { @@ -445,80 +441,46 @@ function PaymentDetails({ params }: { params: PayParams }) { totalCustomerOutflow, ); - // relay 送信中に preflight の切替操作と競合して route が standard へ変わっていても、 - // response-unknown / IP 制限の再送封鎖を外さないため current route とは独立に判定する。 - const relayResponseUnknown = isRelayResponseUnknownError(relay.error); - const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; - // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 - // current route が後から変わってもラッチを外さず、2 本目の UserOperation 送信を防ぐ。 - const gaslessAmbiguous = gasless.isUnknown; - // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 - // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ - // (standard は localStorage に依存しないため、この fail-closed を波及させない)。 - const gaslessStoreUnavailable = - !isStandard && !useRelay && gasless.pendingStoreUnavailable; - const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) - ? relay.error - : null; - const directFlowPending = standard.isRestoring || relay.isRestoring - ? true - : relayAmbiguous || gaslessAmbiguous - ? true - : isStandard - ? standard.isPending - : useRelay - ? relay.isPending - : gasless.isPending; - // Arc の mount scan 完了前に直接送金が開始される波及を断つ (dynamic child の読込中も)。 - const arcRecoveryScanning = params.token === 'usdc' && params.chain === 'arc' && !!address && arcScannedScope !== arcRecoveryScope; - const flowPending = directFlowPending || crossChainLocked || arcRecoveryScanning; - // relay は gas quote も smart account も不要なので readiness は常に満たす。 - const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; - // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一 - // 受取人へ 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全 - // なので再試行を許す。standard の fee-error は merchant transfer が確定済なので - // main ボタンは禁止し、fee の再送は専用 retryFee ボタンのみに限定する。 - const directSettledNoRetry = - relayAmbiguous || - gaslessAmbiguous || - !!relayIpRateLimited || - (!isStandard && !useRelay && !!gasless.data?.success) || - (useRelay && - !!relay.data && - (relay.data.success || !!relay.data.pending)) || - relay.hasActiveIntent || - standard.hasActiveIntent || - (isStandard && (!!standard.data || standard.isFeeError || standard.isUnknown)); + // 再送封鎖・flow pending・送信可否は /pay 専用の policy (FX 期限を含む) に置く (R14・式は移動のみ)。 + const { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + directFlowPending, + flowPending, + gasQuoteReady, + directSettledNoRetry, + settledNoRetry, + canSubmit, + } = derivePayGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + crossChainLocked, + crossChainResult, + params, + address, + arcScannedScope, + arcRecoveryScope, + activeQuote, + isConnected, + wrongChain, + saData, + breakdown, + insufficientBalance, + merchantUnderflow, + expired, + timeMeasured, + }); const standardUnknownTxHash = standard.isFeeUnknown ? standard.feeTxHash : standard.merchantTxHash; - const settledNoRetry = directSettledNoRetry || !!crossChainResult; // 未接続時に最下部 CTA からウォレット選択セクションへ誘導するためのアンカー。 const walletSectionRef = useRef(null); - const canSubmit = - isConnected && - !wrongChain && - (isStandard || useRelay || !!saData) && - // merchantReceives > 0 を要求 (amount 未入力だと gasless では customerPays が - // gas 分だけ正になり得るが、店舗送金額 0 の空 batch は無意味。hook 側でも - // calls.length===0 を弾くが、UI でも button を無効化して金額入力を促す)。 - breakdown.merchantReceives > 0n && - breakdown.customerPays > 0n && - !insufficientBalance && - !flowPending && - gasQuoteReady && - !merchantUnderflow && - !settledNoRetry && - // gasless 経路のみ封鎖 (standard へ切り替えれば支払える)。 - !gaslessStoreUnavailable && - // 正規 /pay UI では期限目安超過後の支払いをブロック (固定レートが陳腐化しているため)。 - // 未署名 exp のため、これは敵対的な支払者へサーバ強制できる防御ではない。 - !expired && - // exp 付き QR は now 計測 (effect 後) まで送信不可 = 計測前 (expired=false) の - // 初回フレームで既期限切れ QR が送信される窓を塞ぐ。 - (params.expiresAt === undefined || timeMeasured); - // gas congested は gasless モード固有の早期 abort。i18n された案内文に // 差し替え (standard モードは paymaster を経由しないため対象外)。 // gasQuote の失敗は Pimlico RPC エラーで生表示するとユーザに技術詳細が @@ -1142,82 +1104,72 @@ function PaymentDetails({ params }: { params: PayParams }) { {t('breakdownTitle')}

- {splitBreakdown ? ( - splitBreakdown.recipients.map((r, i) => ( - - )) - ) : ( - - )} + {splitBreakdown + ? splitBreakdown.recipients.map((r, i) => + breakdownRow( + { + label: t( + i === 0 ? 'primaryRecipientRow' : 'splitRecipientRow', + { percent: r.percent, addr: shortAddress(r.to) }, + ), + value: fmt(r.amount), + }, + r.to, + ), + ) + : breakdownRow({ + label: t('merchantRowAfterFee'), + value: fmt(breakdown.merchantReceives), + })} {/* fee=0 のとき手数料行は非表示 (Phase 1 alpha)。 */} {(splitBreakdown ? splitBreakdown.feeAmount : breakdown.feeAmount) > - 0n && ( - - )} - {isStandard ? ( - - ) : useRecover ? ( - } - value={fmt(relayGasEquiv)} - /> - ) : useRelay || isJpyc ? ( - // JPYC ガスレス (relay free / 非 relay sponsorship free) は無徴収。 - // 顧客負担ではないため中立ラベル (gasRowFree)。 - } - value={t('gasRowRelayFree')} - /> - ) : ( - - } - value={ - gasAmount !== undefined - ? t('gasRowValue', { amount: fmt(gasAmount) }) - : t('gasRowPending') - } - /> + : t('gasInfoJpyc', { nativeToken }), + value: + gasAmount !== undefined + ? t('gasRowValue', { amount: fmt(gasAmount) }) + : t('gasRowPending'), + }, )}
- + {breakdownRow({ + label: isStandard + ? t('customerStandard', { nativeToken }) + : isMerchantGas + ? t('customerMerchantGas') + : t('customerCustomerGas'), + value: fmt(totalCustomerOutflow), + strong: true, + })}
{merchantGasActive && !isStandard && (

diff --git a/components/TipForm.tsx b/components/TipForm.tsx index 4b05c9bd..0703bd16 100644 --- a/components/TipForm.tsx +++ b/components/TipForm.tsx @@ -9,9 +9,8 @@ import { formatUnits } from 'viem'; import { useAccount, useSwitchChain } from 'wagmi'; import { ConnectButton } from './ConnectButton'; import { SmartAccountFallbackBanner } from './SmartAccountFallbackBanner'; -import { InfoTooltip } from './InfoTooltip'; +import { breakdownRow } from './PaymentBreakdownRows'; import { OnrampCta } from './OnrampCta'; -import { Row } from './Row'; import type { SignReassuranceProps } from './SignReassurance'; import { PaymentSuccessOverlay, @@ -75,11 +74,8 @@ import { } from '@/lib/paymentRoute'; import { recoverFeeValue } from '@/lib/relay/recoverFee'; import { relayErrorKey } from '@/lib/relay/relayErrorMessage'; -import { - isFallbackSafeRelayError, - isRelayIpRateLimitedError, - isRelayResponseUnknownError, -} from '@/lib/relay/relayResponseError'; +import { isFallbackSafeRelayError } from '@/lib/relay/relayResponseError'; +import { deriveTipGuards } from '@/lib/paymentFlowGuards/tip'; import { DEFAULT_CHAIN_FOR_SYMBOL, deploymentForSlug } from '@/lib/tokens'; import { DEFAULT_TIP_PRESETS, @@ -327,37 +323,49 @@ export function TipForm({ totalCustomerOutflow, ); + // 再送封鎖・flow pending・送信可否は /tip 専用の policy (route 限定ロック・完了後の次のチップ) に置く + // (R14・式は移動のみ)。 + const { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + directFlowPending, + flowPending, + gasQuoteReady, + directSettledNoRetry, + settledNoRetry, + canSubmit, + } = deriveTipGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + ownsStandardAttempt, + crossChainLocked, + crossChainResult, + params, + address, + preview, + arcScannedScope, + arcRecoveryScope, + activeQuote, + isConnected, + wrongChain, + saData, + standardReady, + breakdown, + insufficientBalance, + }); + // 決済結果を mode 中立に正規化 (relay は txHash のみ・blockNumber/userOpHash 無し)。 - const relayResponseUnknown = isRelayResponseUnknownError(relay.error); - const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; - // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 - // latch 中は新しい UserOperation ではなく、保持済み hash の receipt 再照会だけを許可する。 - const gaslessAmbiguous = !isStandard && !useRelay && gasless.isUnknown; - // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 - // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ - // (relay は localStorage に依存しないため、この fail-closed を波及させない)。 - const gaslessStoreUnavailable = !isStandard && !useRelay && gasless.pendingStoreUnavailable; - const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) - ? relay.error - : null; - // relay intent は sessionStorage に保存され、同一タブの TipForm で復元される。 - // 別タブ・NativeTipForm は対象外。方法切替で未解決の送金が二重払いへ波及しないよう、 - // 復元中・曖昧応答の封鎖を current route から独立させる。 - const directFlowPending = relay.isRestoring || relayAmbiguous - ? true - : isStandard - ? standard.isRestoring || standard.isPending || standard.isUnknown - : useRelay - ? relay.isPending - : gasless.isPending || gaslessAmbiguous; const directFlowSuccess = isStandard ? ownsStandardAttempt && standard.isSuccess : useRelay ? !!(relay.data?.success && relay.data.txHash) : !!gasless.data?.success; const directFlowTxHash = isStandard ? standard.merchantTxHash : useRelay ? relay.data?.txHash : gasless.data?.txHash; - const arcRecoveryScanning = params.token === 'usdc' && params.chain === 'arc' && !!address && !preview && arcScannedScope !== arcRecoveryScope; - const flowPending = directFlowPending || crossChainLocked || arcRecoveryScanning; const flowSuccess = directFlowSuccess || !!crossChainResult; const flowTxHash = crossChainResult ? crossChainResult.mintTxHash : directFlowTxHash; const flowUserOpHash = crossChainResult @@ -373,21 +381,6 @@ export function TipForm({ ? undefined : gasless.data?.blockNumber; const restoredRelayPayment = useRelay && relay.restoredIntent != null; - - // relay は gas quote / smart account 不要なので readiness 即満たす。circle は permitAmount を含む - // activeQuote(circleQuote) 確定まで待つ (未算定で送信すると useBatchPayment が throw)。 - const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; - // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一受取人へ - // 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全なので再試行を許す - // 復元された standard 成功は今回のチップではないため、新規送信を妨げない。 - const directSettledNoRetry = - relayAmbiguous || relay.hasActiveIntent || - (isStandard && (standard.hasActiveIntent || standard.isUnknown || standard.isFeeError || (ownsStandardAttempt && standard.isSuccess))) || - (!isStandard && !useRelay && (gaslessAmbiguous || !!gasless.data?.success)) || - (useRelay && - (!!relayIpRateLimited || - (!!relay.data && (relay.data.success || !!relay.data.pending)))); - const settledNoRetry = directSettledNoRetry || !!crossChainResult; // relay 202: broadcast 済だが未確定 (success でも error でもない)。送信ボタンに「送信中」を // 出してフィードバックの空白を防ぐ (再送は settledNoRetry で既に禁止)。 const relayPending = @@ -395,23 +388,6 @@ export function TipForm({ // 未接続時に最下部 CTA からウォレット選択セクションへ誘導するためのアンカー。 const walletSectionRef = useRef(null); - const canSubmit = - !preview && - isConnected && - !wrongChain && - (isStandard ? standardReady : useRelay || !!saData) && - // creator 受取 > 0 を要求 (custom amount 未入力だと gas 分で customerPays が - // 正になり得るが、tip 額 0 の空 batch は無意味)。hook 側でも calls.length===0 - // を弾くが、UI でも button を無効化して金額入力を促す。 - breakdown.merchantReceives > 0n && - breakdown.customerPays > 0n && - !insufficientBalance && - !flowPending && - gasQuoteReady && - !settledNoRetry && - // gasless 経路のみ封鎖 (relay へ切り替えれば支払える)。 - !gaslessStoreUnavailable; - // gas congested はチェーン別の早期 abort なので、生のエラーメッセージ // (デバッグ向け詳細) ではなく i18n された案内文に差し替える。 // gasQuote の失敗も同様に i18n 化 (詳細は logger 経由で Sentry へ)。 @@ -972,55 +948,47 @@ export function TipForm({ {t('breakdownTitle')}

- + {breakdownRow({ label: t('creatorRow'), value: fmt(breakdown.merchantReceives) })} {/* fee=0 のとき手数料行は非表示 (Phase 1 alpha)。 */} - {breakdown.feeAmount > 0n && ( - - )} + {breakdown.feeAmount > 0n && + breakdownRow({ label: t('feeRow'), value: fmt(breakdown.feeAmount) })} {/* relay は gas quote 非取得。recover=固定回収額 / free=OpenPay 立替 (無料)。 非 relay は従来 paymaster quote (USDC erc20 / JPYC sponsorship)。 */} - {isStandard ? ( - } value={t('gasRowUsdcArc')} /> - ) : useRecover ? ( - } - value={fmt(relayGasEquiv)} - /> - ) : useRelay || isJpyc ? ( - // JPYC ガスレス (relay free / 非 relay sponsorship free) は無徴収。中立ラベル。 - } - value={t('gasRowRelayFree')} - /> - ) : ( - - } - value={ - gasAmount !== undefined - ? t('gasRowValue', { amount: fmt(gasAmount) }) - : t('gasRowPending') - } - /> + : t('gasInfoJpyc', { nativeToken }), + value: + gasAmount !== undefined + ? t('gasRowValue', { amount: fmt(gasAmount) }) + : t('gasRowPending'), + }, )}
- + {breakdownRow({ + label: t('customerRow'), + value: fmt(totalCustomerOutflow), + strong: true, + })}

{isStandard diff --git a/lib/paymentFlowGuards/checkout.ts b/lib/paymentFlowGuards/checkout.ts new file mode 100644 index 00000000..e45a6010 --- /dev/null +++ b/lib/paymentFlowGuards/checkout.ts @@ -0,0 +1,124 @@ +import { + isRelayIpRateLimitedError, + isRelayResponseUnknownError, +} from '@/lib/relay/relayResponseError'; +import type { + GaslessGuardState, + RelayGuardState, + SubmitReadinessInput, +} from './types'; + +// /checkout (CheckoutForm) の送信可否 policy (R14)。CheckoutForm から式を変えずに移したもの。 +// Pay / Tip と 1 つの述語にまとめない (意図的に異なる): +// - @handle 注文の admission 受付中 (orderAdmissionPending) は flowPending と canSubmit だけを閉じ、 +// paymentReady には含めない。onSubmit は admission の await 後に paymentReady (ref) を読み直して +// stale な署名を止めるため、自分の受付中フラグで自分を止めない。 +// - 同一店舗の未解決の支払い (relay.orderPaymentHold・A2c) は paymentReady ごと閉じる。 +// - cross-chain / Arc の回復 scan は持たない (Checkout は対象外)。 + +export type CheckoutGuardInput = SubmitReadinessInput & { + isStandard: boolean; + useRelay: boolean; + standard: { + isRestoring: boolean; + isPending: boolean; + isUnknown: boolean; + isFeeError: boolean; + hasActiveIntent: boolean; + data: unknown; + }; + relay: RelayGuardState & { orderPaymentHold: boolean }; + gasless: GaslessGuardState; + orderAdmissionPending: boolean; + merchantUnderflow: boolean; +}; + +export function deriveCheckoutGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + orderAdmissionPending, + activeQuote, + isConnected, + wrongChain, + saData, + breakdown, + insufficientBalance, + merchantUnderflow, +}: CheckoutGuardInput) { + // relay 送信中に preflight の切替操作と競合して route が standard へ変わっていても、 + // response-unknown / IP 制限の再送封鎖を外さないため current route とは独立に判定する。 + const relayResponseUnknown = isRelayResponseUnknownError(relay.error); + const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; + // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 + // current route が後から変わってもラッチを外さず、2 本目の UserOperation 送信を防ぐ。 + const gaslessAmbiguous = gasless.isUnknown; + // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 + // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ + // (standard は localStorage に依存しないため、この fail-closed を波及させない)。 + const gaslessStoreUnavailable = + !isStandard && !useRelay && gasless.pendingStoreUnavailable; + const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) + ? relay.error + : null; + const paymentFlowPending = standard.isRestoring || relay.isRestoring + ? true + : relayAmbiguous || gaslessAmbiguous + ? true + : isStandard + ? standard.isPending + : useRelay + ? relay.isPending + : gasless.isPending; + const flowPending = orderAdmissionPending || paymentFlowPending; + // relay は gas quote も smart account も不要なので readiness は常に満たす。 + const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; + + // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一 + // 受取人へ 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全 + // なので再試行を許す。standard の fee-error は merchant transfer が確定済なので + // main ボタンは禁止し、fee の再送は専用 retryFee ボタンのみに限定する + // (PaymentForm と同一防御)。 + const settledNoRetry = + relayAmbiguous || + gaslessAmbiguous || + !!relayIpRateLimited || + (!isStandard && !useRelay && !!gasless.data?.success) || + (useRelay && + !!relay.data && + (relay.data.success || !!relay.data.pending)) || + relay.hasActiveIntent || + standard.hasActiveIntent || + (isStandard && (!!standard.data || standard.isFeeError || standard.isUnknown)); + + const paymentReady = + isConnected && + !wrongChain && + (isStandard || useRelay || !!saData) && + // PaymentForm と揃える明示ガード。現状は totalWei>0 (有効 items) 不変で merchantUnderflow + // が拾うが、空 batch (merchant 受取 0) 送信を構造的にも塞ぐ defense-in-depth。 + breakdown.merchantReceives > 0n && + breakdown.customerPays > 0n && + !insufficientBalance && + !paymentFlowPending && + !relay.orderPaymentHold && + gasQuoteReady && + !merchantUnderflow && + !settledNoRetry && + // gasless 経路のみ封鎖 (standard へ切り替えれば支払える)。 + !gaslessStoreUnavailable; + const canSubmit = paymentReady && !orderAdmissionPending; + return { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + flowPending, + gasQuoteReady, + settledNoRetry, + paymentReady, + canSubmit, + }; +} diff --git a/lib/paymentFlowGuards/pay.ts b/lib/paymentFlowGuards/pay.ts new file mode 100644 index 00000000..6e61011d --- /dev/null +++ b/lib/paymentFlowGuards/pay.ts @@ -0,0 +1,144 @@ +import { + isRelayIpRateLimitedError, + isRelayResponseUnknownError, +} from '@/lib/relay/relayResponseError'; +import type { + GaslessGuardState, + RelayGuardState, + SubmitReadinessInput, +} from './types'; + +// /pay (PaymentForm) の送信可否 policy (R14)。PaymentForm から式を変えずに移したもの。 +// Checkout / Tip と 1 つの述語にまとめない (意図的に異なる): +// - relay / gasless の曖昧応答・復元中・IP 制限の封鎖を current route と独立に全経路へ効かせる +// (preflight の切替と競合して route が変わっても二重払いへ波及させない)。 +// - 動的 QR の FX 期限 (expired / 時刻計測前) で送信を止める (Pay だけ)。 +// - cross-chain 実行中/完了と Arc の回復 scan 完了前は直接送金を止める。 + +export type PayGuardInput = SubmitReadinessInput & { + isStandard: boolean; + useRelay: boolean; + standard: { + isRestoring: boolean; + isPending: boolean; + isUnknown: boolean; + isFeeError: boolean; + hasActiveIntent: boolean; + data: unknown; + }; + relay: RelayGuardState; + gasless: GaslessGuardState; + crossChainLocked: boolean; + crossChainResult: unknown; + params: { token: string; chain?: string; expiresAt?: number }; + address: string | undefined; + arcScannedScope: string; + arcRecoveryScope: string; + merchantUnderflow: boolean; + expired: boolean; + timeMeasured: boolean; +}; + +export function derivePayGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + crossChainLocked, + crossChainResult, + params, + address, + arcScannedScope, + arcRecoveryScope, + activeQuote, + isConnected, + wrongChain, + saData, + breakdown, + insufficientBalance, + merchantUnderflow, + expired, + timeMeasured, +}: PayGuardInput) { + // relay 送信中に preflight の切替操作と競合して route が standard へ変わっていても、 + // response-unknown / IP 制限の再送封鎖を外さないため current route とは独立に判定する。 + const relayResponseUnknown = isRelayResponseUnknownError(relay.error); + const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; + // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 + // current route が後から変わってもラッチを外さず、2 本目の UserOperation 送信を防ぐ。 + const gaslessAmbiguous = gasless.isUnknown; + // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 + // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ + // (standard は localStorage に依存しないため、この fail-closed を波及させない)。 + const gaslessStoreUnavailable = + !isStandard && !useRelay && gasless.pendingStoreUnavailable; + const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) + ? relay.error + : null; + const directFlowPending = standard.isRestoring || relay.isRestoring + ? true + : relayAmbiguous || gaslessAmbiguous + ? true + : isStandard + ? standard.isPending + : useRelay + ? relay.isPending + : gasless.isPending; + // Arc の mount scan 完了前に直接送金が開始される波及を断つ (dynamic child の読込中も)。 + const arcRecoveryScanning = params.token === 'usdc' && params.chain === 'arc' && !!address && arcScannedScope !== arcRecoveryScope; + const flowPending = directFlowPending || crossChainLocked || arcRecoveryScanning; + // relay は gas quote も smart account も不要なので readiness は常に満たす。 + const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; + // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一 + // 受取人へ 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全 + // なので再試行を許す。standard の fee-error は merchant transfer が確定済なので + // main ボタンは禁止し、fee の再送は専用 retryFee ボタンのみに限定する。 + const directSettledNoRetry = + relayAmbiguous || + gaslessAmbiguous || + !!relayIpRateLimited || + (!isStandard && !useRelay && !!gasless.data?.success) || + (useRelay && + !!relay.data && + (relay.data.success || !!relay.data.pending)) || + relay.hasActiveIntent || + standard.hasActiveIntent || + (isStandard && (!!standard.data || standard.isFeeError || standard.isUnknown)); + const settledNoRetry = directSettledNoRetry || !!crossChainResult; + + const canSubmit = + isConnected && + !wrongChain && + (isStandard || useRelay || !!saData) && + // merchantReceives > 0 を要求 (amount 未入力だと gasless では customerPays が + // gas 分だけ正になり得るが、店舗送金額 0 の空 batch は無意味。hook 側でも + // calls.length===0 を弾くが、UI でも button を無効化して金額入力を促す)。 + breakdown.merchantReceives > 0n && + breakdown.customerPays > 0n && + !insufficientBalance && + !flowPending && + gasQuoteReady && + !merchantUnderflow && + !settledNoRetry && + // gasless 経路のみ封鎖 (standard へ切り替えれば支払える)。 + !gaslessStoreUnavailable && + // 正規 /pay UI では期限目安超過後の支払いをブロック (固定レートが陳腐化しているため)。 + // 未署名 exp のため、これは敵対的な支払者へサーバ強制できる防御ではない。 + !expired && + // exp 付き QR は now 計測 (effect 後) まで送信不可 = 計測前 (expired=false) の + // 初回フレームで既期限切れ QR が送信される窓を塞ぐ。 + (params.expiresAt === undefined || timeMeasured); + return { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + directFlowPending, + flowPending, + gasQuoteReady, + directSettledNoRetry, + settledNoRetry, + canSubmit, + }; +} diff --git a/lib/paymentFlowGuards/tip.ts b/lib/paymentFlowGuards/tip.ts new file mode 100644 index 00000000..9b0a40ee --- /dev/null +++ b/lib/paymentFlowGuards/tip.ts @@ -0,0 +1,133 @@ +import { + isRelayIpRateLimitedError, + isRelayResponseUnknownError, +} from '@/lib/relay/relayResponseError'; +import type { + GaslessGuardState, + RelayGuardState, + SubmitReadinessInput, +} from './types'; + +// /tip (TipForm) の送信可否 policy (R14)。TipForm から式を変えずに移したもの。 +// Pay / Checkout と 1 つの述語にまとめない (意図的に異なる): +// - relay の復元中・曖昧応答だけは current route と独立に封鎖し (同一タブの sessionStorage 復元)、 +// standard / gasless の ambiguity・復元ロックは選択中の route に限る。 +// - 復元された standard 成功 (このフォームの送信でないもの) は次のチップを妨げない。 +// - standard (Arc) は dynamic import の engine 到達 (standardReady) を待ち、preview では送らない。 +// standard の状態は TipStandardEngine から受け取る形だけに依存する (engine を import しない)。 + +export type TipGuardInput = SubmitReadinessInput & { + isStandard: boolean; + useRelay: boolean; + standard: { + isRestoring: boolean; + isPending: boolean; + isUnknown: boolean; + isFeeError: boolean; + isSuccess: boolean; + hasActiveIntent: boolean; + }; + relay: RelayGuardState; + gasless: GaslessGuardState; + ownsStandardAttempt: boolean; + crossChainLocked: boolean; + crossChainResult: unknown; + params: { token: string; chain?: string }; + address: string | undefined; + preview: boolean; + arcScannedScope: string; + arcRecoveryScope: string; + standardReady: boolean; +}; + +export function deriveTipGuards({ + isStandard, + useRelay, + standard, + relay, + gasless, + ownsStandardAttempt, + crossChainLocked, + crossChainResult, + params, + address, + preview, + arcScannedScope, + arcRecoveryScope, + activeQuote, + isConnected, + wrongChain, + saData, + standardReady, + breakdown, + insufficientBalance, +}: TipGuardInput) { + const relayResponseUnknown = isRelayResponseUnknownError(relay.error); + const relayAmbiguous = relay.recoveryState != null || relayResponseUnknown; + // Pimlico は broadcast 後の receipt 取得失敗を relay と同じ unknown として保持する。 + // latch 中は新しい UserOperation ではなく、保持済み hash の receipt 再照会だけを許可する。 + const gaslessAmbiguous = !isStandard && !useRelay && gasless.isUnknown; + // pending record store (localStorage) が読めず未解決 UserOp の有無を判定できない状態。 + // broadcast 済みとは言い切れないので ambiguous とは別扱いにし、gasless 経路だけを塞ぐ + // (relay は localStorage に依存しないため、この fail-closed を波及させない)。 + const gaslessStoreUnavailable = !isStandard && !useRelay && gasless.pendingStoreUnavailable; + const relayIpRateLimited = isRelayIpRateLimitedError(relay.error) + ? relay.error + : null; + // relay intent は sessionStorage に保存され、同一タブの TipForm で復元される。 + // 別タブ・NativeTipForm は対象外。方法切替で未解決の送金が二重払いへ波及しないよう、 + // 復元中・曖昧応答の封鎖を current route から独立させる。 + const directFlowPending = relay.isRestoring || relayAmbiguous + ? true + : isStandard + ? standard.isRestoring || standard.isPending || standard.isUnknown + : useRelay + ? relay.isPending + : gasless.isPending || gaslessAmbiguous; + const arcRecoveryScanning = params.token === 'usdc' && params.chain === 'arc' && !!address && !preview && arcScannedScope !== arcRecoveryScope; + const flowPending = directFlowPending || crossChainLocked || arcRecoveryScanning; + + // relay は gas quote / smart account 不要なので readiness 即満たす。circle は permitAmount を含む + // activeQuote(circleQuote) 確定まで待つ (未算定で送信すると useBatchPayment が throw)。 + const gasQuoteReady = isStandard || useRelay || activeQuote.data !== undefined; + // 送金が確定 (または broadcast 済で確定しうる) 後の再送信を禁止。再送すると同一受取人へ + // 2 件目の on-chain 送金 = 二重支払いになる。revert (送金未成立) は安全なので再試行を許す + // 復元された standard 成功は今回のチップではないため、新規送信を妨げない。 + const directSettledNoRetry = + relayAmbiguous || relay.hasActiveIntent || + (isStandard && (standard.hasActiveIntent || standard.isUnknown || standard.isFeeError || (ownsStandardAttempt && standard.isSuccess))) || + (!isStandard && !useRelay && (gaslessAmbiguous || !!gasless.data?.success)) || + (useRelay && + (!!relayIpRateLimited || + (!!relay.data && (relay.data.success || !!relay.data.pending)))); + const settledNoRetry = directSettledNoRetry || !!crossChainResult; + + const canSubmit = + !preview && + isConnected && + !wrongChain && + (isStandard ? standardReady : useRelay || !!saData) && + // creator 受取 > 0 を要求 (custom amount 未入力だと gas 分で customerPays が + // 正になり得るが、tip 額 0 の空 batch は無意味)。hook 側でも calls.length===0 + // を弾くが、UI でも button を無効化して金額入力を促す。 + breakdown.merchantReceives > 0n && + breakdown.customerPays > 0n && + !insufficientBalance && + !flowPending && + gasQuoteReady && + !settledNoRetry && + // gasless 経路のみ封鎖 (relay へ切り替えれば支払える)。 + !gaslessStoreUnavailable; + return { + relayAmbiguous, + gaslessAmbiguous, + gaslessStoreUnavailable, + relayIpRateLimited, + directFlowPending, + flowPending, + gasQuoteReady, + directSettledNoRetry, + settledNoRetry, + canSubmit, + }; +} diff --git a/lib/paymentFlowGuards/types.ts b/lib/paymentFlowGuards/types.ts new file mode 100644 index 00000000..65febbef --- /dev/null +++ b/lib/paymentFlowGuards/types.ts @@ -0,0 +1,31 @@ +// Pay / Checkout / Tip の送信可否 policy が読む hook 状態の最小形 (型のみ・R14)。 +// 判定式そのものは各フォームの policy (pay.ts / checkout.ts / tip.ts) に置き、ここでは共有しない: +// 3 フォームの policy は意図的に異なる (Tip の route 限定ロック・Checkout の注文受付・Pay の FX 期限)。 + +// useJpycEip3009Payment (EIP-3009 relay) のうち policy が読む部分。 +export type RelayGuardState = { + error: unknown; + recoveryState: 'auto' | 'exhausted' | null; + isRestoring: boolean; + isPending: boolean; + hasActiveIntent: boolean; + data?: { success: boolean; pending?: boolean }; +}; + +// useBatchPayment (Pimlico / Circle の UserOperation) のうち policy が読む部分。 +export type GaslessGuardState = { + isPending: boolean; + isUnknown: boolean; + pendingStoreUnavailable: boolean; + data?: { success: boolean }; +}; + +// 3 フォーム共通の送信 readiness 入力 (値の組み合わせ方はフォームごとに違う)。 +export type SubmitReadinessInput = { + isConnected: boolean; + wrongChain: boolean; + saData: unknown; + breakdown: { merchantReceives: bigint; customerPays: bigint }; + insufficientBalance: boolean; + activeQuote: { data?: unknown }; +}; diff --git a/tests/components/CheckoutForm.test.tsx b/tests/components/CheckoutForm.test.tsx index 79a8045a..dea39109 100644 --- a/tests/components/CheckoutForm.test.tsx +++ b/tests/components/CheckoutForm.test.tsx @@ -3947,3 +3947,155 @@ describe('A2c saved-order-only notification', () => { expect(fetchSpy).not.toHaveBeenCalled(); expect(loadOrderDelivery().kind).toBe('ready'); fetchSpy.mockRestore(); }); }); + +// R14 (Phase 6): 内訳行の描画 fixture。行の抽出前に移動前のコードで捕捉し、ja/en × 負担者 × +// gas 提供者 × モバイル注文/レジ × Arc の section outerHTML を固定する (PaymentForm.test と同型・ +// 開いた InfoTooltip の id は乱数なので正規化)。 +describe('CheckoutForm — R14 内訳行 fixture (ja/en)', () => { + const RECOVER_FORWARDER = '0x1111111111111111111111111111111111111111'; + function usdcReady() { + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(500_000_000n); + setSmartAccount(true); + } + function jpycReady() { + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(10_000n * 10n ** 18n); + } + function relay(recover: boolean) { + vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); + vi.mocked(jpycForwarderFor).mockReturnValue(recover ? RECOVER_FORWARDER : null); + jpycReady(); + } + const cases: Array<[string, () => CheckoutParams]> = [ + ['usdc sponsorship / customer gas', () => { usdcReady(); setGasQuote('ready', 50_000n); return USDC_PARAMS; }], + ['usdc erc20 / customer gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdcReady(); setGasQuote('ready', 100_000n); return USDC_PARAMS; }], + ['usdc erc20 / merchant gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdcReady(); setGasQuote('ready', 100_000n); return { ...USDC_PARAMS, gas: 'merchant' }; }], + ['usdc circle / customer gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); vi.mocked(resolveUsdcGaslessProvider).mockReturnValue('circle'); usdcReady(); setCircleQuote('ready', { gasAmount: 60_000n, permitAmount: 55_060_000n }); return USDC_PARAMS; }], + ['usdc gas quote pending', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdcReady(); setGasQuote('pending'); return USDC_PARAMS; }], + ['usdc standard', () => { usdcReady(); return { ...USDC_PARAMS, mode: 'standard' }; }], + ['usdc Arc standard', () => { usdcReady(); return { ...USDC_PARAMS, chain: 'arc', mode: 'standard' }; }], + ['jpyc sponsorship (non relay)', () => { jpycReady(); setGasQuote('ready', 0n); return JPYC_PARAMS; }], + ['jpyc relay free', () => { relay(false); return JPYC_PARAMS; }], + ['jpyc relay recover / merchant gas', () => { relay(true); return { ...JPYC_PARAMS, gas: 'merchant' }; }], + ['jpyc standard', () => { jpycReady(); return { ...JPYC_PARAMS, mode: 'standard' }; }], + ['mobile storefront (relay recover, merchant pays fee)', () => { feeFlags.enableMobileOrderFee = true; relay(true); return { ...JPYC_PARAMS, feeKind: 'storefront' }; }], + ['mobile preorder (relay recover, customer pays fee)', () => { feeFlags.enableMobileOrderFee = true; relay(true); return { ...JPYC_PARAMS, feeKind: 'preorder', feePayer: 'customer' }; }], + ['mobile storefront standard', () => { feeFlags.enableMobileOrderFee = true; jpycReady(); return { ...JPYC_PARAMS, feeKind: 'storefront', mode: 'standard' }; }], + ['register standard fee', () => { feeFlags.enableRegisterFee = true; feeRate.percentBps = 100; jpycReady(); return { ...JPYC_PARAMS, mode: 'standard', feeKind: 'register' }; }], + ]; + describe.each(['ja', 'en'] as const)('%s', (locale) => { + it.each(cases)('%s', (_name, setup) => { + const params = setup(); + const { container } = render(, { locale }); + const section = container.querySelector('dl')!.closest('section')!; + expect(section.outerHTML).toMatchSnapshot('closed'); + for (const button of Array.from(section.querySelectorAll('dl button'))) fireEvent.click(button); + expect(section.outerHTML.replace(/tip-[a-z0-9]+/g, 'tip-ID')).toMatchSnapshot('tooltips open'); + }); + }); +}); + +// R14 (Phase 6): click 時の競合。policy の抽出前に移動前のコードで捕捉。@handle 注文の admission は +// await を挟むため、(1) 同一 gesture の二重 click は ref で 1 回に絞り、(2) await 後は最新の +// paymentReady (接続・chain・同一店舗 hold 等) を読み直して stale な署名を止める。 +describe('CheckoutForm — R14 admission の二重送信と await 後の再確認', () => { + afterEach(() => { vi.unstubAllGlobals(); }); + function deferredAdmission() { + const pending: Array<(response: Response) => void> = []; + const fetchSpy = vi.fn((input: RequestInfo | URL) => String(input) === '/api/order/admission' + ? new Promise((resolve) => { pending.push(resolve); }) + : Promise.resolve(Response.json({}))); + vi.stubGlobal('fetch', fetchSpy); + const admissionCalls = () => fetchSpy.mock.calls.filter(([input]) => String(input) === '/api/order/admission').length; + return { admissionCalls, admit: () => pending.shift()!(Response.json({ ok: true })) }; + } + async function settle() { + await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); }); + } + const USDC_HANDLE: CheckoutParams = { ...USDC_PARAMS, storeHandle: 'coffee_shop', feeKind: 'preorder' }; + const JPYC_HANDLE: CheckoutParams = { ...JPYC_PARAMS, storeHandle: 'coffee_shop', feeKind: 'storefront' }; + function usdcReady() { + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(200_000_000n); + setSmartAccount(true); + setGasQuote('ready', 100_000n); + } + + it('同一 gesture の二重 click (再描画前) は admission 1 回・mutate 1 回', async () => { + usdcReady(); + const { admissionCalls, admit } = deferredAdmission(); + render(); + const button = screen.getByRole('button', { name: /を支払う/ }); + act(() => { button.click(); button.click(); }); + expect(admissionCalls()).toBe(1); + // admission 待ちの再描画後は canSubmit (受付中) でボタン自体が閉じる。 + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(admissionCalls()).toBe(1); + admit(); + await settle(); + expect(mutate).toHaveBeenCalledOnce(); + }); + + it.each([false, true])('admission の await 中に chain が切り替わったら署名へ進まない (切替=%s)', async (switched) => { + usdcReady(); + const { admissionCalls, admit } = deferredAdmission(); + const view = render(); + fireEvent.click(screen.getByRole('button', { name: /を支払う/ })); + expect(admissionCalls()).toBe(1); + if (switched) setAccount({ connected: true, chainId: polygonAmoy.id }); + view.rerender(); + admit(); + await settle(); + expect(mutate).toHaveBeenCalledTimes(switched ? 0 : 1); + expect(screen.queryByText('ただいま注文を受け付けていません')).toBeNull(); + }); + + it.each([false, true])('admission の await 中に wallet が切断されたら署名へ進まない (切断=%s)', async (disconnected) => { + usdcReady(); + const { admit } = deferredAdmission(); + const view = render(); + fireEvent.click(screen.getByRole('button', { name: /を支払う/ })); + if (disconnected) setAccount({ connected: false }); + view.rerender(); + admit(); + await settle(); + expect(mutate).toHaveBeenCalledTimes(disconnected ? 0 : 1); + }); + + it.each([false, true])('admission の await 中に同一店舗の支払い hold が立ったら relay 署名へ進まない (hold=%s)', async (held) => { + vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(10_000n * 10n ** 18n); + const { admissionCalls, admit } = deferredAdmission(); + const view = render(); + fireEvent.click(screen.getByRole('button', { name: /3000 JPYC を支払う/ })); + expect(admissionCalls()).toBe(1); + const first = relayMutate; + if (held) { + const current = vi.mocked(useJpycEip3009Payment).mock.results.at(-1)!.value as ReturnType; + mockHook(useJpycEip3009Payment, { ...current, orderPaymentHold: true }); + } + view.rerender(); + admit(); + await settle(); + expect(first).toHaveBeenCalledTimes(held ? 0 : 1); + }); + + it('admission 拒否後は受付中が解け、再 click で admission をやり直す (署名はしない)', async () => { + usdcReady(); + const fetchSpy = vi.fn(async () => Response.json({ ok: false }, { status: 409 })); + vi.stubGlobal('fetch', fetchSpy); + render(); + const button = screen.getByRole('button', { name: /を支払う/ }); + fireEvent.click(button); + await settle(); + expect(await screen.findByText('ただいま注文を受け付けていません')).toBeInTheDocument(); + expect(button).toBeEnabled(); + fireEvent.click(button); + await settle(); + expect(fetchSpy).toHaveBeenCalledTimes(2); + expect(mutate).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/components/PaymentForm.test.tsx b/tests/components/PaymentForm.test.tsx index f1756d8f..3fe531ba 100644 --- a/tests/components/PaymentForm.test.tsx +++ b/tests/components/PaymentForm.test.tsx @@ -2608,3 +2608,106 @@ it.each(['notify-pending', 'terminal', 'prepared'] as const)('A2c %s order from expect(vi.mocked(useJpycEip3009Payment).mock.calls.every((args) => args[1]?.restoreOrderDelivery !== true)).toBe(true); view.unmount(); client.clear(); fetchSpy.mockRestore(); sessionStorage.clear(); }); + +// R14 (Phase 6): 内訳行の描画 fixture。行の抽出 (表示部品/row model の共有) の前に移動前の +// コードで捕捉し、ja/en × 負担者 × gas 提供者 × split の section outerHTML を固定する。 +// InfoTooltip は閉じた状態では本文を DOM に出さないため、全て開いた状態も併せて固定する +// (popover の id は乱数なので正規化)。 +describe('PaymentForm — R14 内訳行 fixture (ja/en)', () => { + const B = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + const C = '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; + function usdc(query: string) { + setURL(`to=${MERCHANT}&token=usdc&${query}`); + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(500_000_000n); + setSmartAccount(true); + } + function jpyc(query: string) { + setURL(`to=${MERCHANT}&token=jpyc&${query}`); + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(10_000n * 10n ** 18n); + } + const cases: Array<[string, () => void]> = [ + ['usdc sponsorship / customer gas', () => { usdc('amount=100'); setGasQuote('ready', 50_000n); }], + ['usdc erc20 / customer gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdc('amount=100'); setGasQuote('ready', 100_000n); }], + ['usdc erc20 / merchant gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdc('amount=100&gas=merchant'); setGasQuote('ready', 100_000n); }], + ['usdc circle / customer gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); vi.mocked(resolveUsdcGaslessProvider).mockReturnValue('circle'); usdc('amount=100'); setCircleQuote('ready', { gasAmount: 60_000n, permitAmount: 100_060_000n }); }], + ['usdc gas quote pending', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdc('amount=100'); setGasQuote('pending'); }], + ['usdc split / erc20', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdc(`amount=100&split=${B}:30,${C}:20`); setGasQuote('ready', 100_000n); }], + ['usdc split / merchant gas', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdc(`amount=100&gas=merchant&split=${B}:50`); setGasQuote('ready', 100_000n); }], + ['usdc standard', () => { usdc('amount=100&mode=standard'); }], + ['jpyc sponsorship (non relay)', () => { jpyc('amount=1000'); setGasQuote('ready', 0n); }], + ['jpyc relay free', () => { vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); jpyc('amount=300'); }], + ['jpyc relay recover', () => { vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); vi.mocked(jpycForwarderFor).mockReturnValue('0x1111111111111111111111111111111111111111'); jpyc('amount=300'); }], + ['jpyc standard', () => { jpyc('amount=300&mode=standard'); }], + ]; + const titles = { ja: '明細', en: 'Breakdown' } as const; + describe.each(['ja', 'en'] as const)('%s', (locale) => { + it.each(cases)('%s', (_name, setup) => { + setup(); + render(, { locale }); + const section = screen.getByText(titles[locale]).closest('section')!; + expect(section.outerHTML).toMatchSnapshot('closed'); + for (const button of Array.from(section.querySelectorAll('dl button'))) fireEvent.click(button); + expect(section.outerHTML.replace(/tip-[a-z0-9]+/g, 'tip-ID')).toMatchSnapshot('tooltips open'); + }); + }); +}); + +// R14 (Phase 6): click 時の二重送信。policy の抽出前に移動前のコードで捕捉。1 回目の click を +// hook が受け取った (in-flight に遷移した) 後の再描画で、同じボタンの 2 回目は mutate しない。 +describe('PaymentForm — R14 二重送信 (in-flight 後の再 click)', () => { + it('gasless: 2 回目の click は mutate しない', () => { + setURL(`to=${MERCHANT}&token=usdc&amount=10`); + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(20_000_000n); + setSmartAccount(true); + setGasQuote('ready', 0n); + const view = render(); + const button = screen.getByRole('button', { name: /10 USDC を支払う/ }); + fireEvent.click(button); + const first = mutate; + expect(first).toHaveBeenCalledOnce(); + setPayment('pending'); + view.rerender(); + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(first).toHaveBeenCalledOnce(); + expect(mutate).not.toHaveBeenCalled(); + }); + + it('relay: 署名待ちの 2 回目の click は mutate しない', () => { + vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); + setURL(`to=${MERCHANT}&token=jpyc&amount=300`); + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(10_000n * 10n ** 18n); + const view = render(); + const button = screen.getByRole('button', { name: /300 JPYC を支払う/ }); + fireEvent.click(button); + const first = relayMutate; + expect(first).toHaveBeenCalledOnce(); + setRelay('pending-flow'); + view.rerender(); + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(first).toHaveBeenCalledOnce(); + expect(relayMutate).not.toHaveBeenCalled(); + }); + + it('standard: merchant 送信中の 2 回目の click は mutate しない', () => { + setURL(`to=${MERCHANT}&token=usdc&amount=10&mode=standard`); + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(20_000_000n); + const view = render(); + const button = screen.getByRole('button', { name: /10 USDC を支払う/ }); + fireEvent.click(button); + const first = standardMutate; + expect(first).toHaveBeenCalledOnce(); + setStandardPayment('merchant-sending'); + view.rerender(); + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(first).toHaveBeenCalledOnce(); + expect(standardMutate).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/components/TipForm.test.tsx b/tests/components/TipForm.test.tsx index f2a4f5b1..2ee7cb85 100644 --- a/tests/components/TipForm.test.tsx +++ b/tests/components/TipForm.test.tsx @@ -2407,3 +2407,88 @@ it.each(['notify-pending', 'terminal', 'prepared'] as const)('A2c %s order from expect(vi.mocked(useJpycEip3009Payment).mock.calls.every((args) => args[1]?.restoreOrderDelivery !== true)).toBe(true); view.unmount(); client.clear(); fetchSpy.mockRestore(); sessionStorage.clear(); }); + +// R14 (Phase 6): 内訳行の描画 fixture。行の抽出前に移動前のコードで捕捉し、ja/en × gas 提供者 × +// Arc standard × テーマの section outerHTML を固定する (PaymentForm.test と同型・開いた +// InfoTooltip の id は乱数なので正規化)。 +describe('TipForm — R14 内訳行 fixture (ja/en)', () => { + function usdcReady() { + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(500_000_000n); + setSmartAccount(true); + } + function jpycReady() { + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(1000n * 10n ** 18n); + } + async function arcReady() { + setAccount({ connected: true, chainId: 5042002 }); + setBalance(10_000_000n); + const wagmi = await import('wagmi'); + vi.mocked(wagmi.useWriteContract).mockImplementation(() => ({ data: undefined, error: null, isPending: false, writeContract: vi.fn(), reset: vi.fn() }) as never); + vi.mocked(wagmi.useWaitForTransactionReceipt).mockImplementation(() => ({ data: undefined, error: null, isSuccess: false, isError: false, refetch: vi.fn() }) as never); + } + const cases: Array<[string, () => Promise | TipParams]> = [ + ['usdc sponsorship', () => { usdcReady(); setGasQuote('ready', 50_000n); return USDC_PARAMS; }], + ['usdc erc20', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdcReady(); setGasQuote('ready', 100_000n); return USDC_PARAMS; }], + ['usdc circle', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); vi.mocked(resolveUsdcGaslessProvider).mockReturnValue('circle'); usdcReady(); setCircleQuote('ready', { gasAmount: 60_000n, permitAmount: 1_060_000n }); return USDC_PARAMS; }], + ['usdc gas quote pending', () => { vi.mocked(resolvePaymasterMode).mockReturnValue('erc20'); usdcReady(); setGasQuote('pending'); return USDC_PARAMS; }], + ['jpyc sponsorship (non relay)', () => { jpycReady(); setGasQuote('ready', 0n); return JPYC_PARAMS; }], + ['jpyc relay free', () => { vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); jpycReady(); return JPYC_PARAMS; }], + ['jpyc relay recover', () => { vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); vi.mocked(jpycForwarderFor).mockReturnValue('0x1111111111111111111111111111111111111111'); jpycReady(); return JPYC_PARAMS; }], + ['jpyc relay free / night theme', () => { vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); jpycReady(); return { ...JPYC_PARAMS, theme: 'night' }; }], + ['usdc Arc standard', async () => { await arcReady(); return { ...USDC_PARAMS, mode: 'standard', chain: 'arc', presets: ['0.5', '1'] }; }], + ]; + const titles = { ja: '明細', en: 'Breakdown' } as const; + describe.each(['ja', 'en'] as const)('%s', (locale) => { + it.each(cases)('%s', async (_name, setup) => { + const params = await setup(); + render(, { locale }); + const section = screen.getByText(titles[locale]).closest('section')!; + expect(section.outerHTML).toMatchSnapshot('closed'); + for (const button of Array.from(section.querySelectorAll('dl button'))) fireEvent.click(button); + expect(section.outerHTML.replace(/tip-[a-z0-9]+/g, 'tip-ID')).toMatchSnapshot('tooltips open'); + // dynamic な standard engine の読込が test 後に state を更新しないよう待つ。 + await act(async () => {}); + }); + }); +}); + +// R14 (Phase 6): click 時の二重送信。policy の抽出前に移動前のコードで捕捉。1 回目の click を +// hook が受け取った (in-flight に遷移した) 後の再描画で、同じボタンの 2 回目は mutate しない。 +describe('TipForm — R14 二重送信 (in-flight 後の再 click)', () => { + it('gasless: 2 回目の click は mutate しない', () => { + setAccount({ connected: true, chainId: baseSepolia.id }); + setBalance(20_000_000n); + setSmartAccount(true); + setGasQuote('ready', 0n); + const view = render(); + const button = screen.getByRole('button', { name: /1 USDC を送る/ }); + fireEvent.click(button); + const first = mutate; + expect(first).toHaveBeenCalledOnce(); + setBatchPayment('pending'); + view.rerender(); + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(first).toHaveBeenCalledOnce(); + expect(mutate).not.toHaveBeenCalled(); + }); + + it('relay: 署名待ちの 2 回目の click は mutate しない', () => { + vi.mocked(resolveJpycGaslessProvider).mockReturnValue('eip3009-relay'); + setAccount({ connected: true, chainId: polygonAmoy.id }); + setBalance(1000n * 10n ** 18n); + const view = render(); + const button = screen.getByRole('button', { name: /100 JPYC を送る/ }); + fireEvent.click(button); + const first = relayMutate; + expect(first).toHaveBeenCalledOnce(); + setRelay('pending'); + view.rerender(); + expect(button).toBeDisabled(); + fireEvent.click(button); + expect(first).toHaveBeenCalledOnce(); + expect(relayMutate).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/components/__snapshots__/CheckoutForm.test.tsx.snap b/tests/components/__snapshots__/CheckoutForm.test.tsx.snap new file mode 100644 index 00000000..588dbf54 --- /dev/null +++ b/tests/components/__snapshots__/CheckoutForm.test.tsx.snap @@ -0,0 +1,121 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network fee
Free (covered by OpenPay)
You pay
3000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network feeFor JPYC gasless payments, OpenPay covers the network fee (gas) in full. You complete the payment in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay
3000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover / merchant gas > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Estimated network fee (merchant pays)
2 JPYC
You pay
3000 JPYC

A per-transaction OpenPay usage fee applies (1% of the payment, min 2 JPYC). The store bears the fee; the customer pays only the displayed amount (signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover / merchant gas > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Estimated network fee (merchant pays)For JPYC gasless payments, OpenPay sponsors the network fee (gas) and charges a per-transaction OpenPay usage fee (1% of the payment with a 2 JPYC minimum), deducted at settlement and split to OpenPay's designated wallet within the same transaction. The store bears this fee, and the customer pays only the displayed amount (for tips to creators, the gas-equivalent amount — about 2 JPYC; the 1% does not apply — is borne by the customer sending the tip). You need no native token — just sign the transfer in your wallet.
2 JPYC
You pay
3000 JPYC

A per-transaction OpenPay usage fee applies (1% of the payment, min 2 JPYC). The store bears the fee; the customer pays only the displayed amount (signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network fee
Free (covered by OpenPay)
You pay
3000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network feeFor JPYC gasless payments, OpenPay covers the network fee (gas) in full. You complete the payment in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay
3000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc standard > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > jpyc standard > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile preorder (relay recover, customer pays fee) > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee
90 JPYC
You pay
3090 JPYC

No native token (gas) is required. Just sign the transfer in your wallet to pay the amount shown.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile preorder (relay recover, customer pays fee) > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee
90 JPYC
You pay
3090 JPYC

No native token (gas) is required. Just sign the transfer in your wallet to pay the amount shown.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile storefront (relay recover, merchant pays fee) > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
You pay
3000 JPYC

No native token (gas) is required. Just sign the transfer in your wallet to pay the amount shown.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile storefront (relay recover, merchant pays fee) > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
You pay
3000 JPYC

No native token (gas) is required. Just sign the transfer in your wallet to pay the amount shown.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile storefront standard > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > mobile storefront standard > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > register standard fee > closed 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > register standard fee > tooltips open 1`] = `"

Items

  • チケット×13000 JPYC
Subtotal
3000 JPYC
OpenPay service fee (merchant pays)
30 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
3000 JPYC

Standard payment: approve a single transfer in your wallet. The network fee (POL) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc Arc standard > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side USDC gas)
55 USDC

Arc supports standard payments only: gas is paid in USDC, with no separate token needed.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc Arc standard > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side USDC gas)
55 USDC

Arc supports standard payments only: gas is paid in USDC, with no separate token needed.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc circle / customer gas > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee
up to 0.06 USDC
You pay
55.06 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc circle / customer gas > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network feePay the network fee in USDC via Circle Paymaster (official) — no need to hold native ETH. A Circle fee of about 10% is added on top of the gas cost. The figure shown is an upper-bound estimate; any surplus is not charged. OpenPay does not collect this fee.
up to 0.06 USDC
You pay
55.06 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / customer gas > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee
up to 0.1 USDC
You pay
55.1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / customer gas > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network feePay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay
55.1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / merchant gas > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee (merchant pays)
up to 0.1 USDC
You pay
55 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / merchant gas > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee (merchant pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay
55 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee
Fetching estimate…
You pay
55 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network feePay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
Fetching estimate…
You pay
55 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship / customer gas > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network fee
up to 0.05 USDC
You pay
55.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship / customer gas > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Estimated network feePimlico Sponsorship Paymaster covers ETH gas; you complete the payment in JPYC alone with no native (ETH) holdings required. The network fee is charged as a fixed amount based on the congestion ceiling price, so during normal periods it may exceed the actual gas (the difference is not refunded).
up to 0.05 USDC
You pay
55.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc standard > closed 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side ETH gas)
55 USDC

Standard payment: approve a single transfer in your wallet. The network fee (ETH) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > en > usdc standard > tooltips open 1`] = `"

Items

  • Tシャツ×125 USDC
  • マグ×230 USDC
Subtotal
55 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side ETH gas)
55 USDC

Standard payment: approve a single transfer in your wallet. The network fee (ETH) is requested separately by your wallet.

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
お支払い額
3000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料JPYC のガスレス決済では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。お客様は JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
お支払い額
3000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover / merchant gas > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料見積 (店主負担)
2 JPYC
お支払い額
3000 JPYC

決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を申し受けます。利用料は店舗負担・お客様は表示額のみ (署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover / merchant gas > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料見積 (店主負担)JPYC のガスレス決済では、OpenPay がネットワーク手数料 (ガス) を肩代わりし、決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を決済時に申し受けます。この利用料は決済と同じ取引内で OpenPay 指定ウォレットへ分割され、店舗が負担します (お客様は表示額のみをお支払いになり、利用料は店主の受取額から差し引かれます)。ネイティブトークンの保有は不要で、ウォレットで送金内容に署名するだけで完了します。
2 JPYC
お支払い額
3000 JPYC

決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を申し受けます。利用料は店舗負担・お客様は表示額のみ (署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
お支払い額
3000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料JPYC のガスレス決済では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。お客様は JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
お支払い額
3000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc standard > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > jpyc standard > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile preorder (relay recover, customer pays fee) > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料
90 JPYC
お支払い額
3090 JPYC

ネイティブトークン (ガス) は不要です。表示の金額を、ウォレットで送金内容に署名するだけでお支払いが完了します。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile preorder (relay recover, customer pays fee) > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料
90 JPYC
お支払い額
3090 JPYC

ネイティブトークン (ガス) は不要です。表示の金額を、ウォレットで送金内容に署名するだけでお支払いが完了します。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile storefront (relay recover, merchant pays fee) > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
お支払い額
3000 JPYC

ネイティブトークン (ガス) は不要です。表示の金額を、ウォレットで送金内容に署名するだけでお支払いが完了します。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile storefront (relay recover, merchant pays fee) > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
お支払い額
3000 JPYC

ネイティブトークン (ガス) は不要です。表示の金額を、ウォレットで送金内容に署名するだけでお支払いが完了します。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile storefront standard > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > mobile storefront standard > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > register standard fee > closed 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > register standard fee > tooltips open 1`] = `"

商品

  • チケット×13000 JPYC
小計
3000 JPYC
OpenPay 利用手数料 (店舗負担)
30 JPYC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 POL gas)
3000 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (POL) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc Arc standard > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 USDC gas)
55 USDC

Arc は通常決済のみ。ガスは USDC で支払われるため別トークン不要です。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc Arc standard > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 USDC gas)
55 USDC

Arc は通常決済のみ。ガスは USDC で支払われるため別トークン不要です。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc circle / customer gas > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積
最大 0.06 USDC
お支払い額
55.06 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc circle / customer gas > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積Circle Paymaster (公式) 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。ガス代には約 10% の Circle 手数料が上乗せされます。表示額は上限見積で、超過分は引き落とされません。OpenPay は当該手数料を徴収しません。
最大 0.06 USDC
お支払い額
55.06 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / customer gas > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積
最大 0.1 USDC
お支払い額
55.1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / customer gas > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
お支払い額
55.1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / merchant gas > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積 (店主負担)
最大 0.1 USDC
お支払い額
55 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / merchant gas > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積 (店主負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
お支払い額
55 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積
見積取得中…
お支払い額
55 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
見積取得中…
お支払い額
55 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship / customer gas > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積
最大 0.05 USDC
お支払い額
55.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship / customer gas > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料見積Pimlico Sponsorship Paymaster 経由で、運営が ETH ガスを肩代わりします。お客様は JPYC のみで完結し、ネイティブトークン (ETH) を保有する必要はありません。ネットワーク手数料は混雑時の上限価格を基準に固定額で徴収するため、平常時は実際の gas を上回ることがあります (差額は返金されません)。
最大 0.05 USDC
お支払い額
55.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません)

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc standard > closed 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 ETH gas)
55 USDC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (ETH) はウォレット側で別途請求されます。

"`; + +exports[`CheckoutForm — R14 内訳行 fixture (ja/en) > ja > usdc standard > tooltips open 1`] = `"

商品

  • Tシャツ×125 USDC
  • マグ×230 USDC
小計
55 USDC
ネットワーク手数料
ウォレットで支払い
お支払い額 (+ ウォレット側 ETH gas)
55 USDC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。ネットワーク手数料 (ETH) はウォレット側で別途請求されます。

"`; diff --git a/tests/components/__snapshots__/PaymentForm.test.tsx.snap b/tests/components/__snapshots__/PaymentForm.test.tsx.snap new file mode 100644 index 00000000..413b8455 --- /dev/null +++ b/tests/components/__snapshots__/PaymentForm.test.tsx.snap @@ -0,0 +1,97 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > closed 1`] = `"

Breakdown

Merchant receives
300 JPYC
Network fee
Free (covered by OpenPay)
You pay (bill + estimated network fee)
300 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > tooltips open 1`] = `"

Breakdown

Merchant receives
300 JPYC
Network feeFor JPYC gasless payments, OpenPay covers the network fee (gas) in full. You complete the payment in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay (bill + estimated network fee)
300 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover > closed 1`] = `"

Breakdown

Merchant receives
298 JPYC
Estimated network fee (merchant pays)
2 JPYC
You pay
300 JPYC

Merchant absorbs the network fee. You pay only the displayed amount.

A per-transaction OpenPay usage fee applies (1% of the payment, min 2 JPYC). The store bears the fee; the customer pays only the displayed amount (signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover > tooltips open 1`] = `"

Breakdown

Merchant receives
298 JPYC
Estimated network fee (merchant pays)For JPYC gasless payments, OpenPay sponsors the network fee (gas) and charges a per-transaction OpenPay usage fee (1% of the payment with a 2 JPYC minimum), deducted at settlement and split to OpenPay's designated wallet within the same transaction. The store bears this fee (the customer pays only the displayed amount, and the fee is deducted from the merchant's receipt) — see the amount shown below. You need no native token — just sign the transfer in your wallet.
2 JPYC
You pay
300 JPYC

Merchant absorbs the network fee. You pay only the displayed amount.

A per-transaction OpenPay usage fee applies (1% of the payment, min 2 JPYC). The store bears the fee; the customer pays only the displayed amount (signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > closed 1`] = `"

Breakdown

Merchant receives
1000 JPYC
Network fee
Free (covered by OpenPay)
You pay (bill + estimated network fee)
1000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > tooltips open 1`] = `"

Breakdown

Merchant receives
1000 JPYC
Network feeFor JPYC gasless payments, OpenPay covers the network fee (gas) in full. You complete the payment in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay (bill + estimated network fee)
1000 JPYC

OpenPay covers the network fee, so there is no charge to you (paid in JPYC, signature only).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc standard > closed 1`] = `"

Breakdown

Merchant receives
300 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
300 JPYC

Standard payment: approve a single transfer in your wallet. Smart Account / Pimlico Paymaster is not used.

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > jpyc standard > tooltips open 1`] = `"

Breakdown

Merchant receives
300 JPYC
Network fee
Paid separately from your wallet
You pay (+ wallet-side POL gas)
300 JPYC

Standard payment: approve a single transfer in your wallet. Smart Account / Pimlico Paymaster is not used.

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc circle / customer gas > closed 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)
up to 0.06 USDC
You pay (bill + estimated network fee)
100.06 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc circle / customer gas > tooltips open 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)Pay the network fee in USDC via Circle Paymaster (official) — no need to hold native ETH. A Circle fee of about 10% is added on top of the gas cost. The figure shown is an upper-bound estimate; any surplus is not charged. OpenPay does not collect this fee.
up to 0.06 USDC
You pay (bill + estimated network fee)
100.06 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / customer gas > closed 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)
up to 0.1 USDC
You pay (bill + estimated network fee)
100.1 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / customer gas > tooltips open 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay (bill + estimated network fee)
100.1 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / merchant gas > closed 1`] = `"

Breakdown

Merchant receives
99.9 USDC
Estimated network fee (merchant pays)
up to 0.1 USDC
You pay
100 USDC

Merchant absorbs the network fee. You pay only the displayed amount.

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 / merchant gas > tooltips open 1`] = `"

Breakdown

Merchant receives
99.9 USDC
Estimated network fee (merchant pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay
100 USDC

Merchant absorbs the network fee. You pay only the displayed amount.

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > closed 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)
Fetching estimate…
You pay (bill + estimated network fee)
100 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > tooltips open 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
Fetching estimate…
You pay (bill + estimated network fee)
100 USDC

Upper-bound estimate. Actual gas is at or below this amount (paid in USDC).

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc split / erc20 > closed 1`] = `"

Breakdown

Primary (50%) 0x1111…1111
50 USDC
Recipient (30%) 0xaAaA…aaAa
30 USDC
Recipient (20%) 0xbBbB…BBbB
20 USDC
Estimated network fee (customer pays)
up to 0.1 USDC
You pay (bill + estimated network fee)
100.1 USDC

Gas is covered by OpenPay (Pimlico Sponsorship Paymaster). 3 transfers are batched in a single UserOperation.

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc split / erc20 > tooltips open 1`] = `"

Breakdown

Primary (50%) 0x1111…1111
50 USDC
Recipient (30%) 0xaAaA…aaAa
30 USDC
Recipient (20%) 0xbBbB…BBbB
20 USDC
Estimated network fee (customer pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay (bill + estimated network fee)
100.1 USDC

Gas is covered by OpenPay (Pimlico Sponsorship Paymaster). 3 transfers are batched in a single UserOperation.

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc split / merchant gas > closed 1`] = `"

Breakdown

Primary (50%) 0x1111…1111
49.95 USDC
Recipient (50%) 0xaAaA…aaAa
49.95 USDC
Estimated network fee (merchant pays)
up to 0.1 USDC
You pay
100 USDC

Merchant absorbs the network fee. You pay only the displayed amount.

Gas is covered by OpenPay (Pimlico Sponsorship Paymaster). 2 transfers are batched in a single UserOperation.

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc split / merchant gas > tooltips open 1`] = `"

Breakdown

Primary (50%) 0x1111…1111
49.95 USDC
Recipient (50%) 0xaAaA…aaAa
49.95 USDC
Estimated network fee (merchant pays)Pay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay
100 USDC

Merchant absorbs the network fee. You pay only the displayed amount.

Gas is covered by OpenPay (Pimlico Sponsorship Paymaster). 2 transfers are batched in a single UserOperation.

Review gas approvals ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship / customer gas > closed 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)
up to 0.05 USDC
You pay (bill + estimated network fee)
100.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded (paid in JPYC).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship / customer gas > tooltips open 1`] = `"

Breakdown

Merchant receives
100 USDC
Estimated network fee (customer pays)Pimlico Sponsorship Paymaster covers ETH gas; you complete the payment in JPYC alone with no native (ETH) holdings required. The network fee is charged as a fixed amount based on the congestion ceiling price, so during normal periods it may exceed the actual gas (the difference is not refunded).
up to 0.05 USDC
You pay (bill + estimated network fee)
100.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded (paid in JPYC).

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc standard > closed 1`] = `"

Breakdown

Merchant receives
100 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side ETH gas)
100 USDC

Standard payment: approve a single transfer in your wallet. Smart Account / Pimlico Paymaster is not used.

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > en > usdc standard > tooltips open 1`] = `"

Breakdown

Merchant receives
100 USDC
Network fee
Paid separately from your wallet
You pay (+ wallet-side ETH gas)
100 USDC

Standard payment: approve a single transfer in your wallet. Smart Account / Pimlico Paymaster is not used.

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > closed 1`] = `"

明細

店主受取
300 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
顧客支払額 (請求金額 + ネットワーク手数料見積)
300 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > tooltips open 1`] = `"

明細

店主受取
300 JPYC
ネットワーク手数料JPYC のガスレス決済では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。お客様は JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
顧客支払額 (請求金額 + ネットワーク手数料見積)
300 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover > closed 1`] = `"

明細

店主受取
298 JPYC
ネットワーク手数料見積 (店主負担)
2 JPYC
顧客支払額
300 JPYC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を申し受けます。利用料は店舗負担・お客様は表示額のみ (署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover > tooltips open 1`] = `"

明細

店主受取
298 JPYC
ネットワーク手数料見積 (店主負担)JPYC のガスレス決済では、OpenPay がネットワーク手数料 (ガス) を肩代わりし、決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を決済時に申し受けます。この利用料は決済と同じ取引内で OpenPay 指定ウォレットへ分割され、この利用料は店舗が負担します (お客様は表示額のみをお支払いになり、利用料は店主の受取額から差し引かれます・実額は下の表示をご確認ください)。ネイティブトークンの保有は不要で、ウォレットで送金内容に署名するだけで完了します。
2 JPYC
顧客支払額
300 JPYC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

決済 1 件ごとに OpenPay 利用料 (決済額の 1%・最低 2 JPYC) を申し受けます。利用料は店舗負担・お客様は表示額のみ (署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > closed 1`] = `"

明細

店主受取
1000 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
顧客支払額 (請求金額 + ネットワーク手数料見積)
1000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > tooltips open 1`] = `"

明細

店主受取
1000 JPYC
ネットワーク手数料JPYC のガスレス決済では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。お客様は JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
顧客支払額 (請求金額 + ネットワーク手数料見積)
1000 JPYC

ネットワーク手数料は OpenPay が負担するため、お客様の負担はありません (JPYC でお支払い・署名のみ)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc standard > closed 1`] = `"

明細

店主受取
300 JPYC
ネットワーク手数料
ウォレットで支払い
顧客支払額 (+ ウォレット側 POL gas)
300 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。Smart Account / Pimlico Paymaster は経由しません。

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > jpyc standard > tooltips open 1`] = `"

明細

店主受取
300 JPYC
ネットワーク手数料
ウォレットで支払い
顧客支払額 (+ ウォレット側 POL gas)
300 JPYC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。Smart Account / Pimlico Paymaster は経由しません。

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc circle / customer gas > closed 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)
最大 0.06 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.06 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc circle / customer gas > tooltips open 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)Circle Paymaster (公式) 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。ガス代には約 10% の Circle 手数料が上乗せされます。表示額は上限見積で、超過分は引き落とされません。OpenPay は当該手数料を徴収しません。
最大 0.06 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.06 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / customer gas > closed 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)
最大 0.1 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.1 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / customer gas > tooltips open 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.1 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / merchant gas > closed 1`] = `"

明細

店主受取
99.9 USDC
ネットワーク手数料見積 (店主負担)
最大 0.1 USDC
顧客支払額
100 USDC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 / merchant gas > tooltips open 1`] = `"

明細

店主受取
99.9 USDC
ネットワーク手数料見積 (店主負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
顧客支払額
100 USDC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > closed 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)
見積取得中…
顧客支払額 (請求金額 + ネットワーク手数料見積)
100 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > tooltips open 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
見積取得中…
顧客支払額 (請求金額 + ネットワーク手数料見積)
100 USDC

最大表示。実費はこれ以下に収まります (USDC でお支払い)

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc split / erc20 > closed 1`] = `"

明細

主受取人 (50%) 0x1111…1111
50 USDC
受取人 (30%) 0xaAaA…aaAa
30 USDC
受取人 (20%) 0xbBbB…BBbB
20 USDC
ネットワーク手数料見積 (顧客負担)
最大 0.1 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.1 USDC

ネットワーク手数料は OpenPay が肩代わり (Pimlico Sponsorship Paymaster)。3 件の transfer を 1 つの UserOperation でバッチ送信します。

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc split / erc20 > tooltips open 1`] = `"

明細

主受取人 (50%) 0x1111…1111
50 USDC
受取人 (30%) 0xaAaA…aaAa
30 USDC
受取人 (20%) 0xbBbB…BBbB
20 USDC
ネットワーク手数料見積 (顧客負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.1 USDC

ネットワーク手数料は OpenPay が肩代わり (Pimlico Sponsorship Paymaster)。3 件の transfer を 1 つの UserOperation でバッチ送信します。

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc split / merchant gas > closed 1`] = `"

明細

主受取人 (50%) 0x1111…1111
49.95 USDC
受取人 (50%) 0xaAaA…aaAa
49.95 USDC
ネットワーク手数料見積 (店主負担)
最大 0.1 USDC
顧客支払額
100 USDC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

ネットワーク手数料は OpenPay が肩代わり (Pimlico Sponsorship Paymaster)。2 件の transfer を 1 つの UserOperation でバッチ送信します。

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc split / merchant gas > tooltips open 1`] = `"

明細

主受取人 (50%) 0x1111…1111
49.95 USDC
受取人 (50%) 0xaAaA…aaAa
49.95 USDC
ネットワーク手数料見積 (店主負担)Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
顧客支払額
100 USDC

この QR は店主がネットワーク手数料を負担します。顧客は請求金額のみ支払います。

ネットワーク手数料は OpenPay が肩代わり (Pimlico Sponsorship Paymaster)。2 件の transfer を 1 つの UserOperation でバッチ送信します。

ガス代承認の状況を確認 ↗

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship / customer gas > closed 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)
最大 0.05 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません・JPYC でお支払い)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship / customer gas > tooltips open 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料見積 (顧客負担)Pimlico Sponsorship Paymaster 経由で、運営が ETH ガスを肩代わりします。お客様は JPYC のみで完結し、ネイティブトークン (ETH) を保有する必要はありません。ネットワーク手数料は混雑時の上限価格を基準に固定額で徴収するため、平常時は実際の gas を上回ることがあります (差額は返金されません)。
最大 0.05 USDC
顧客支払額 (請求金額 + ネットワーク手数料見積)
100.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません・JPYC でお支払い)

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc standard > closed 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料
ウォレットで支払い
顧客支払額 (+ ウォレット側 ETH gas)
100 USDC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。Smart Account / Pimlico Paymaster は経由しません。

"`; + +exports[`PaymentForm — R14 内訳行 fixture (ja/en) > ja > usdc standard > tooltips open 1`] = `"

明細

店主受取
100 USDC
ネットワーク手数料
ウォレットで支払い
顧客支払額 (+ ウォレット側 ETH gas)
100 USDC

通常決済(ガスあり): ウォレットで transfer を 1 件承認します。Smart Account / Pimlico Paymaster は経由しません。

"`; diff --git a/tests/components/__snapshots__/TipForm.test.tsx.snap b/tests/components/__snapshots__/TipForm.test.tsx.snap new file mode 100644 index 00000000..0d355d7c --- /dev/null +++ b/tests/components/__snapshots__/TipForm.test.tsx.snap @@ -0,0 +1,73 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free / night theme > closed 1`] = `"

Breakdown

Creator receives
100 JPYC
Network fee
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free / night theme > tooltips open 1`] = `"

Breakdown

Creator receives
100 JPYC
Network feeFor JPYC gasless tips, OpenPay covers the network fee (gas) in full. Fans complete the tip in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > closed 1`] = `"

Breakdown

Creator receives
100 JPYC
Network fee
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay free > tooltips open 1`] = `"

Breakdown

Creator receives
100 JPYC
Network feeFor JPYC gasless tips, OpenPay covers the network fee (gas) in full. Fans complete the tip in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover > closed 1`] = `"

Breakdown

Creator receives
100 JPYC
Estimated network fee
2 JPYC
You pay
102 JPYC

A per-transaction, gas-equivalent OpenPay usage fee applies (about 2 JPYC; the 1% does not apply to tips). The customer sending the tip bears it (signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc relay recover > tooltips open 1`] = `"

Breakdown

Creator receives
100 JPYC
Estimated network feeFor JPYC gasless tips, OpenPay sponsors the network fee (gas) and charges a per-transaction, gas-equivalent OpenPay usage fee (about 2 JPYC per transaction; the 1% of the amount does not apply to tips), deducted at settlement and split to OpenPay's designated wallet within the same transaction. The customer sending the tip (the fan) bears this fee, added on top of the tip. No native token required — just sign the transfer in your wallet.
2 JPYC
You pay
102 JPYC

A per-transaction, gas-equivalent OpenPay usage fee applies (about 2 JPYC; the 1% does not apply to tips). The customer sending the tip bears it (signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > closed 1`] = `"

Breakdown

Creator receives
100 JPYC
Network fee
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > jpyc sponsorship (non relay) > tooltips open 1`] = `"

Breakdown

Creator receives
100 JPYC
Network feeFor JPYC gasless tips, OpenPay covers the network fee (gas) in full. Fans complete the tip in JPYC alone — no native token holdings required. Just sign the transfer in your wallet.
Free (covered by OpenPay)
You pay
100 JPYC

OpenPay covers the network fee, so there is no charge to the fan (paid in JPYC, signature only).

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc Arc standard > closed 1`] = `"

Breakdown

Creator receives
0.5 USDC
Estimated network fee
Paid in your wallet
You pay
0.5 USDC

Your wallet charges gas separately in USDC. OpenPay collects nothing.

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc Arc standard > tooltips open 1`] = `"

Breakdown

Creator receives
0.5 USDC
Estimated network feeYour wallet charges gas separately in USDC. OpenPay collects nothing.
Paid in your wallet
You pay
0.5 USDC

Your wallet charges gas separately in USDC. OpenPay collects nothing.

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc circle > closed 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network fee
up to 0.06 USDC
You pay
1.06 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc circle > tooltips open 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network feePay the network fee in USDC via Circle Paymaster (official) — no need to hold native ETH. A Circle fee of about 10% is added on top of the gas cost. The figure shown is an upper-bound estimate; any surplus is not charged. OpenPay does not collect this fee.
up to 0.06 USDC
You pay
1.06 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 > closed 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network fee
up to 0.1 USDC
You pay
1.1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc erc20 > tooltips open 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network feePay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
up to 0.1 USDC
You pay
1.1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > closed 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network fee
Fetching estimate…
You pay
1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc gas quote pending > tooltips open 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network feePay the network fee in USDC via Pimlico ERC20 Paymaster — no need to hold native ETH. The figure shown is a worst-case estimate; any surplus over the actual cost is not charged.
Fetching estimate…
You pay
1 USDC

Upper-bound estimate. Actual gas is at or below this amount.

Review gas approvals ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship > closed 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network fee
up to 0.05 USDC
You pay
1.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded.

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > en > usdc sponsorship > tooltips open 1`] = `"

Breakdown

Creator receives
1 USDC
Estimated network feePimlico Sponsorship Paymaster covers ETH gas; you complete the payment in JPYC alone with no native (ETH) holdings required. The network fee is charged as a fixed amount based on the congestion ceiling price, so during normal periods it may exceed the actual gas (the difference is not refunded).
up to 0.05 USDC
You pay
1.05 USDC

The network fee is charged at a fixed upper-bound (congestion ceiling) price; any surplus is not refunded.

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free / night theme > closed 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free / night theme > tooltips open 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料JPYC のガスレス送金では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。ファンは JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > closed 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay free > tooltips open 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料JPYC のガスレス送金では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。ファンは JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover > closed 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料見積
2 JPYC
あなたの支払額
102 JPYC

送金 1 件ごとにガス相当額の OpenPay 利用料 (約 2 JPYC・チップに 1% は適用しません) を申し受けます。チップをお送りになるお客様のご負担です (署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc relay recover > tooltips open 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料見積JPYC のガスレス送金では、OpenPay がネットワーク手数料 (ガス) を肩代わりし、送金 1 件ごとにガス相当額の OpenPay 利用料 (1 件あたり 約 2 JPYC。チップには決済額の 1% は適用しません) を申し受けます。この利用料は送金と同じ取引内で OpenPay 指定ウォレットへ分割され、チップをお送りになるお客様 (ファン) がチップ額に上乗せしてご負担いただきます。ネイティブトークンの保有は不要で、ウォレットで送金内容に署名するだけで完了します。
2 JPYC
あなたの支払額
102 JPYC

送金 1 件ごとにガス相当額の OpenPay 利用料 (約 2 JPYC・チップに 1% は適用しません) を申し受けます。チップをお送りになるお客様のご負担です (署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > closed 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > jpyc sponsorship (non relay) > tooltips open 1`] = `"

明細

クリエイター受取
100 JPYC
ネットワーク手数料JPYC のガスレス送金では、ネットワーク手数料 (ガス) を OpenPay が全額負担します。ファンは JPYC のみで完結し、ネイティブトークンの保有は不要です。ウォレットで送金内容に署名するだけで完了します。
無料 (OpenPay が負担)
あなたの支払額
100 JPYC

ネットワーク手数料は OpenPay が負担するため、ファンの負担はありません (JPYC で送金・署名のみ)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc Arc standard > closed 1`] = `"

明細

クリエイター受取
0.5 USDC
ネットワーク手数料見積
ウォレットで支払い
あなたの支払額
0.5 USDC

ガスは USDC でウォレットが別途請求します。OpenPay の徴収はありません。

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc Arc standard > tooltips open 1`] = `"

明細

クリエイター受取
0.5 USDC
ネットワーク手数料見積ガスは USDC でウォレットが別途請求します。OpenPay の徴収はありません。
ウォレットで支払い
あなたの支払額
0.5 USDC

ガスは USDC でウォレットが別途請求します。OpenPay の徴収はありません。

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc circle > closed 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積
最大 0.06 USDC
あなたの支払額
1.06 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc circle > tooltips open 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積Circle Paymaster (公式) 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。ガス代には約 10% の Circle 手数料が上乗せされます。表示額は上限見積で、超過分は引き落とされません。OpenPay は当該手数料を徴収しません。
最大 0.06 USDC
あなたの支払額
1.06 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 > closed 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積
最大 0.1 USDC
あなたの支払額
1.1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc erc20 > tooltips open 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
最大 0.1 USDC
あなたの支払額
1.1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > closed 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積
見積取得中…
あなたの支払額
1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc gas quote pending > tooltips open 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積Pimlico ERC20 Paymaster 経由で、ネイティブトークン (ETH) を保有することなく USDC のままガスをお支払いいただけます。表示額は worst-case 見積で、実費がそれ以下の場合は超過分は引き落とされません。
見積取得中…
あなたの支払額
1 USDC

最大表示。実費はこれ以下に収まります

ガス代承認の状況を確認 ↗

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship > closed 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積
最大 0.05 USDC
あなたの支払額
1.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません)

"`; + +exports[`TipForm — R14 内訳行 fixture (ja/en) > ja > usdc sponsorship > tooltips open 1`] = `"

明細

クリエイター受取
1 USDC
ネットワーク手数料見積Pimlico Sponsorship Paymaster 経由で、運営が ETH ガスを肩代わりします。お客様は JPYC のみで完結し、ネイティブトークン (ETH) を保有する必要はありません。ネットワーク手数料は混雑時の上限価格を基準に固定額で徴収するため、平常時は実際の gas を上回ることがあります (差額は返金されません)。
最大 0.05 USDC
あなたの支払額
1.05 USDC

ネットワーク手数料は混雑時上限の価格で固定徴収します (余剰は返金されません)

"`; diff --git a/tests/lib/__snapshots__/paymentFlowGuards.test.ts.snap b/tests/lib/__snapshots__/paymentFlowGuards.test.ts.snap new file mode 100644 index 00000000..5cf2b585 --- /dev/null +++ b/tests/lib/__snapshots__/paymentFlowGuards.test.ts.snap @@ -0,0 +1,1111 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checkout UI outputs > pins the fields the form renders from 1`] = ` +{ + "circle | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "circle | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "circle | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=0", + "circle | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "circle | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "circle | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "pimlico | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "pimlico | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=0", + "pimlico | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "pimlico | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "pimlico | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-free | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-free | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-free | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-free | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-recover | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-recover | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "relay-recover | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "relay-recover | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "standard | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "standard | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 gasQuoteReady=1", + "standard | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", + "standard | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 gasQuoteReady=1", +} +`; + +exports[`pay UI outputs > pins the fields the form renders from 1`] = ` +{ + "circle | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=0 directSettledNoRetry=0", + "circle | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=0 directSettledNoRetry=0", + "pimlico | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", +} +`; + +exports[`tip UI outputs > pins the fields the form renders from 1`] = ` +{ + "circle | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=0 directSettledNoRetry=0", + "circle | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "circle | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "circle | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=0 directSettledNoRetry=0", + "pimlico | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=1 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "pimlico | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "pimlico | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-free | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-free | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "relay-recover | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "relay-recover | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | Arc recovery scan": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | cross-chain settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | gasless unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | idle": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | order admission": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay IP limit": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay IP limit with active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay active intent without ambiguity": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay auto recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay broadcast pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay exhausted recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay exhausted recovery with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay rejected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay response unknown before recovery state": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay response unknown with active intent": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | relay reverted": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | relay storage restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard owned success": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | flow | standard pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard restored success permits a new tip": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | flow | standard unknown": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | FX QR after time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | FX QR before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | FX QR expired": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | Tip preview": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | Tip standard engine not loaded": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | all ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | customer pays zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: cross-chain executing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: gasless in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: relay intent persisted before response": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | duplicate submit: relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | duplicate submit: standard in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | gas quote pending": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | insufficient balance": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | merchant receives zero": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | merchant underflow": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: gasless pending while relay IP limited": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=1 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: gasless store unavailable and relay signing": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: gasless unknown, relay selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: relay broadcast pending and standard fee error": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | mixed: relay settled, other route selected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: standard pending while relay restoring": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | mixed: standard unknown and gasless settled": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | no FX expiry before time is measured": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then chain switched": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then same-merchant hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | order admission in flight then wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: Arc recovery scan not finished": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: cross-chain result": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: gasless pending store unreadable": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: gasless unresolved UserOperation": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: relay intent in auto recovery": "relayAmbiguous=1 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: relay restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: standard active intent": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | restored: standard restoring on mount": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=1 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: standard success from another attempt": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | restored: standard success owned by this form": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=1", + "standard | submit | same-merchant order payment hold": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | smart account not ready": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | wallet disconnected": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", + "standard | submit | wrong chain": "relayAmbiguous=0 gaslessAmbiguous=0 relayIpRateLimited=0 directFlowPending=0 gasQuoteReady=1 directSettledNoRetry=0", +} +`; diff --git a/tests/lib/paymentFlowGuards.test.ts b/tests/lib/paymentFlowGuards.test.ts index 596ee9e8..439a0d62 100644 --- a/tests/lib/paymentFlowGuards.test.ts +++ b/tests/lib/paymentFlowGuards.test.ts @@ -1,9 +1,10 @@ import { readFileSync } from 'node:fs'; import ts from 'typescript'; import { describe, expect, it } from 'vitest'; +import { deriveCheckoutGuards, type CheckoutGuardInput } from '@/lib/paymentFlowGuards/checkout'; +import { derivePayGuards, type PayGuardInput } from '@/lib/paymentFlowGuards/pay'; +import { deriveTipGuards, type TipGuardInput } from '@/lib/paymentFlowGuards/tip'; import { - isRelayIpRateLimitedError, - isRelayResponseUnknownError, RelayIpRateLimitedError, RelayResponseUnknownError, } from '@/lib/relay/relayResponseError'; @@ -18,10 +19,27 @@ type HookState = { isFeeError?: boolean; hasActiveIntent?: boolean; pendingStoreUnavailable?: boolean; + orderPaymentHold?: boolean; recoveryState?: 'auto' | 'exhausted' | null; data?: { success: boolean; pending?: boolean }; error?: Error | null; }; +// submit readiness の入力 (flow 状態以外)。既定は「全て満たす」で、行ごとに 1 つずつ崩す。 +type Readiness = { + isConnected: boolean; + wrongChain: boolean; + saData: object | undefined; + merchantReceives: bigint; + customerPays: bigint; + insufficientBalance: boolean; + merchantUnderflow: boolean; + quoteReady: boolean; + expired: boolean; + timeMeasured: boolean; + expiresAt: number | undefined; + standardReady: boolean; + preview: boolean; +}; type State = { standard?: HookState; relay?: HookState; @@ -31,57 +49,149 @@ type State = { crossChainResult?: object; orderAdmissionPending?: boolean; arcScannedScope?: string; + ready?: Partial; }; type Guards = { flowPending: boolean; settledNoRetry: boolean; gaslessStoreUnavailable: boolean }; +type Submit = { canSubmit: boolean; paymentReady?: boolean }; +// フォームが banner・エラー表示・ボタン文言に使う policy の出力 (R14 review N1)。Checkout は +// directFlowPending / directSettledNoRetry を持たない。relayIpRateLimited は有無だけを見る。 +const UI_FIELDS = { + pay: ['relayAmbiguous', 'gaslessAmbiguous', 'relayIpRateLimited', 'directFlowPending', 'gasQuoteReady', 'directSettledNoRetry'], + checkout: ['relayAmbiguous', 'gaslessAmbiguous', 'relayIpRateLimited', 'gasQuoteReady'], + tip: ['relayAmbiguous', 'gaslessAmbiguous', 'relayIpRateLimited', 'directFlowPending', 'gasQuoteReady', 'directSettledNoRetry'], +} as const; +type Evaluated = Guards & Submit & { ui: Record }; +const POLICY_FN = { pay: 'derivePayGuards', checkout: 'deriveCheckoutGuards', tip: 'deriveTipGuards' } as const; -// Characterize the actual inline expressions without refactoring the money path or -// copying its logic into a test-only implementation. Only named pure declarations -// are evaluated: no React render, hook, wallet, storage or network side effects. -function readDerivePaymentFlowGuards(policy: Policy) { +function uiOf(policy: Policy, result: Record): Record { + return Object.fromEntries(UI_FIELDS[policy].map((name) => [ + name, + name === 'relayIpRateLimited' ? result[name] != null : result[name] === true, + ])); +} + +const READY: Readiness = { + isConnected: true, + wrongChain: false, + saData: {}, + merchantReceives: 1n, + customerPays: 1n, + insufficientBalance: false, + merchantUnderflow: false, + quoteReady: true, + expired: false, + timeMeasured: true, + expiresAt: undefined, + standardReady: true, + preview: false, +}; + +// R14 で 3 フォームの判定式は lib/paymentFlowGuards/{pay,checkout,tip}.ts へ式を変えずに移った +// (移動前は component の inline 宣言を parse して評価していた・真理値表は移動前と同一)。ここでは +// 各フォームの policy を直接呼ぶ。React の render・hook・wallet・storage・network は介さない。 +// component 側が policy を呼び、判定を再び inline 化していないことも併せて固定する。 +function readPolicy(policy: Policy): (input: ReturnType) => Evaluated { const component = { pay: 'PaymentForm', checkout: 'CheckoutForm', tip: 'TipForm' }[policy]; const filename = `components/${component}.tsx`; const source = ts.createSourceFile(filename, readFileSync(filename, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); - const names = [ - 'relayResponseUnknown', 'relayAmbiguous', 'gaslessAmbiguous', - 'gaslessStoreUnavailable', 'relayIpRateLimited', - policy === 'checkout' ? 'paymentFlowPending' : 'directFlowPending', - ...(policy === 'checkout' ? [] : ['arcRecoveryScanning', 'directSettledNoRetry']), - 'flowPending', 'settledNoRetry', - ]; - const declarations = new Map(); + const inline: string[] = []; + const calls: ts.CallExpression[] = []; function visit(node: ts.Node) { - if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && names.includes(node.name.text)) { - expect(node.initializer, node.name.text).toBeDefined(); - expect(declarations.has(node.name.text), node.name.text).toBe(false); - declarations.set(node.name.text, `const ${node.name.text} = ${node.initializer!.getText(source)};`); + if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) + && ['flowPending', 'settledNoRetry', 'gaslessStoreUnavailable', 'canSubmit', 'paymentReady'].includes(node.name.text)) { + inline.push(node.name.text); + } + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === POLICY_FN[policy]) { + calls.push(node); } ts.forEachChild(node, visit); } visit(source); - expect([...declarations.keys()].sort()).toEqual([...names].sort()); - const evaluate = new Function('input', 'isRelayResponseUnknownError', 'isRelayIpRateLimitedError', ` - const { isStandard, useRelay, standard, relay, gasless, ownsStandardAttempt, - crossChainLocked, crossChainResult, orderAdmissionPending, params, address, - preview, arcScannedScope, arcRecoveryScope } = input; - ${[...declarations.values()].join('\n')} - return { flowPending, settledNoRetry, gaslessStoreUnavailable }; - `) as (input: object, unknown: typeof isRelayResponseUnknownError, limited: typeof isRelayIpRateLimitedError) => Guards; - return (route: Route, state: State = {}) => evaluate({ + expect(inline).toEqual([]); + expectShorthandCallSite(policy, source, calls); + return (input) => callPolicy(policy, input); +} + +// 呼び出し側の配線も固定する (R14 review S1): 引数は component の同名変数をそのまま渡す shorthand +// だけにし (`standardReady: true` のような固定値・別名の差し込みを禁止)、渡す名前の集合は policy の +// 引数と一致させる。移動前は inline 式が component の変数を直接読んでいたのと同じ束縛になる。 +function expectShorthandCallSite(policy: Policy, source: ts.SourceFile, calls: ts.CallExpression[]) { + expect(calls).toHaveLength(1); + const [arg] = calls[0].arguments; + if (!arg || !ts.isObjectLiteralExpression(arg)) throw new Error(`${POLICY_FN[policy]} must take one object literal`); + expect(arg.properties.filter((p) => !ts.isShorthandPropertyAssignment(p)).map((p) => p.getText(source))).toEqual([]); + const policyFile = `lib/paymentFlowGuards/${policy}.ts`; + const policySource = ts.createSourceFile(policyFile, readFileSync(policyFile, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + const fn = policySource.statements.find((node): node is ts.FunctionDeclaration => + ts.isFunctionDeclaration(node) && node.name?.text === POLICY_FN[policy]); + const pattern = fn?.parameters[0]?.name; + if (!pattern || !ts.isObjectBindingPattern(pattern)) throw new Error(`${POLICY_FN[policy]} must destructure its input`); + const parameters = pattern.elements.map((element) => element.name.getText(policySource)); + expect(arg.properties.map((p) => p.name!.getText(source)).sort()).toEqual(parameters.sort()); +} + +// input は policy ごとの型に `satisfies` で照合する (R14 review N2): policy 側の入力の形が変わると +// buildInput が tsc で落ち、名前ずれで undefined が黙って流れ込まない。 +function callPolicy(policy: Policy, input: ReturnType): Evaluated { + if (policy === 'pay') { + const result = derivePayGuards(input satisfies PayGuardInput); + const { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit } = result; + return { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit, ui: uiOf(policy, result) }; + } + if (policy === 'checkout') { + const result = deriveCheckoutGuards(input satisfies CheckoutGuardInput); + const { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit, paymentReady } = result; + return { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit, paymentReady, ui: uiOf(policy, result) }; + } + const result = deriveTipGuards(input satisfies TipGuardInput); + const { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit } = result; + return { flowPending, settledNoRetry, gaslessStoreUnavailable, canSubmit, ui: uiOf(policy, result) }; +} + +function buildInput(route: Route, state: State = {}) { + const ready = { ...READY, ...state.ready }; + return { isStandard: route === 'standard', useRelay: route === 'relay-free' || route === 'relay-recover', - standard: { isRestoring: false, isPending: false, isUnknown: false, hasActiveIntent: false, ...state.standard }, - relay: { isRestoring: false, isPending: false, hasActiveIntent: false, ...state.relay }, + standard: { isRestoring: false, isPending: false, isUnknown: false, isFeeError: false, isSuccess: false, hasActiveIntent: false, data: undefined, ...state.standard }, + relay: { error: null, recoveryState: null, isRestoring: false, isPending: false, hasActiveIntent: false, orderPaymentHold: false, ...state.relay }, gasless: { isPending: false, isUnknown: false, pendingStoreUnavailable: false, ...state.gasless }, ownsStandardAttempt: state.ownsStandardAttempt ?? false, crossChainLocked: state.crossChainLocked ?? false, crossChainResult: state.crossChainResult, orderAdmissionPending: state.orderAdmissionPending ?? false, - params: { token: 'usdc', chain: 'arc' }, + params: { token: 'usdc', chain: 'arc', expiresAt: ready.expiresAt }, address: '0x1111111111111111111111111111111111111111', - preview: false, + preview: ready.preview, arcScannedScope: state.arcScannedScope ?? 'scanned', arcRecoveryScope: 'scanned', - }, isRelayResponseUnknownError, isRelayIpRateLimitedError); + isConnected: ready.isConnected, + wrongChain: ready.wrongChain, + saData: ready.saData, + breakdown: { merchantReceives: ready.merchantReceives, customerPays: ready.customerPays }, + insufficientBalance: ready.insufficientBalance, + merchantUnderflow: ready.merchantUnderflow, + activeQuote: { data: ready.quoteReady ? {} : undefined }, + expired: ready.expired, + timeMeasured: ready.timeMeasured, + standardReady: ready.standardReady, + }; +} + +function readDerivePaymentFlowGuards(policy: Policy) { + const evaluate = readPolicy(policy); + return (route: Route, state: State = {}): Guards => { + const { flowPending, settledNoRetry, gaslessStoreUnavailable } = evaluate(buildInput(route, state)); + return { flowPending, settledNoRetry, gaslessStoreUnavailable }; + }; +} + +function readDeriveSubmit(policy: Policy) { + const evaluate = readPolicy(policy); + return (route: Route, state: State = {}): Submit => { + const { canSubmit, paymentReady } = evaluate(buildInput(route, state)); + return policy === 'checkout' ? { canSubmit, paymentReady } : { canSubmit }; + }; } // Columns are standard / relay (free + recover) / gasless (Pimlico + Circle). @@ -147,3 +257,94 @@ describe.each(['pay', 'checkout', 'tip'] as const)('%s payment flow guard truth }); }); }); + +// R14 (Phase 6) で policy を外へ移す前に、移動前のコードで捕捉した submit 可否の真理値表。 +// 列は上と同じ standard / relay / gasless。1 = 送信可、0 = 不可。pay の値を既定とし、 +// フォーム固有の差 (Tip の route 限定ロック・Checkout の注文受付と同一店舗 hold・Pay の FX 期限) +// だけを tip / checkout 列で上書きする。checkoutReady は Checkout の paymentReady +// (admission の await 後に onSubmit が読む readiness) で、既定は checkout 列と同じ。 +type SubmitRow = { name: string; state: State; pay: string; tip?: string; checkout?: string; checkoutReady?: string }; +const submitRows: SubmitRow[] = [ + // readiness: 1 つずつ崩す + { name: 'all ready', state: {}, pay: '111' }, + { name: 'wallet disconnected', state: { ready: { isConnected: false } }, pay: '000' }, + { name: 'wrong chain', state: { ready: { wrongChain: true } }, pay: '000' }, + { name: 'smart account not ready', state: { ready: { saData: undefined } }, pay: '110' }, + { name: 'Tip standard engine not loaded', state: { ready: { standardReady: false } }, pay: '111', tip: '011' }, + { name: 'merchant receives zero', state: { ready: { merchantReceives: 0n } }, pay: '000' }, + { name: 'customer pays zero', state: { ready: { customerPays: 0n } }, pay: '000' }, + { name: 'insufficient balance', state: { ready: { insufficientBalance: true } }, pay: '000' }, + { name: 'gas quote pending', state: { ready: { quoteReady: false } }, pay: '110' }, + { name: 'merchant underflow', state: { ready: { merchantUnderflow: true } }, pay: '000', tip: '111' }, + { name: 'FX QR expired', state: { ready: { expired: true } }, pay: '000', tip: '111', checkout: '111' }, + { name: 'FX QR before time is measured', state: { ready: { expiresAt: 1_800_000_000_000, timeMeasured: false } }, pay: '000', tip: '111', checkout: '111' }, + { name: 'FX QR after time is measured', state: { ready: { expiresAt: 1_800_000_000_000 } }, pay: '111' }, + { name: 'no FX expiry before time is measured', state: { ready: { timeMeasured: false } }, pay: '111' }, + { name: 'Tip preview', state: { ready: { preview: true } }, pay: '111', tip: '000' }, + { name: 'same-merchant order payment hold', state: { relay: { orderPaymentHold: true } }, pay: '111', checkout: '000' }, + // post-await admission: 受付中は canSubmit だけが閉じ、await 後に読む paymentReady は開いたまま + { name: 'order admission in flight', state: { orderAdmissionPending: true }, pay: '111', checkout: '000', checkoutReady: '111' }, + { name: 'order admission in flight then wallet disconnected', state: { orderAdmissionPending: true, ready: { isConnected: false } }, pay: '000' }, + { name: 'order admission in flight then chain switched', state: { orderAdmissionPending: true, ready: { wrongChain: true } }, pay: '000' }, + { name: 'order admission in flight then same-merchant hold', state: { orderAdmissionPending: true, relay: { orderPaymentHold: true } }, pay: '111', checkout: '000' }, + // duplicate submit: 1 回目の click を hook が受け取った直後の状態 + { name: 'duplicate submit: standard in flight', state: { standard: { isPending: true } }, pay: '011' }, + { name: 'duplicate submit: relay signing', state: { relay: { isPending: true } }, pay: '101' }, + { name: 'duplicate submit: relay intent persisted before response', state: { relay: { isPending: true, hasActiveIntent: true } }, pay: '000' }, + { name: 'duplicate submit: gasless in flight', state: { gasless: { isPending: true } }, pay: '110' }, + { name: 'duplicate submit: cross-chain executing', state: { crossChainLocked: true }, pay: '000', checkout: '111' }, + // mixed pending state: 別経路の hook に未解決の状態が残ったまま route が変わった + { name: 'mixed: relay settled, other route selected', state: { relay: { data: { success: true } } }, pay: '101' }, + { name: 'mixed: standard pending while relay restoring', state: { standard: { isPending: true }, relay: { isRestoring: true } }, pay: '000' }, + { name: 'mixed: gasless pending while relay IP limited', state: { gasless: { isPending: true }, relay: { error: new RelayIpRateLimitedError(45) } }, pay: '000', tip: '100' }, + { name: 'mixed: standard unknown and gasless settled', state: { standard: { isUnknown: true }, gasless: { data: { success: true } } }, pay: '010' }, + { name: 'mixed: gasless unknown, relay selected', state: { gasless: { isUnknown: true } }, pay: '000', tip: '110' }, + { name: 'mixed: relay broadcast pending and standard fee error', state: { relay: { data: { success: false, pending: true } }, standard: { isFeeError: true } }, pay: '001' }, + { name: 'mixed: gasless store unavailable and relay signing', state: { gasless: { pendingStoreUnavailable: true }, relay: { isPending: true } }, pay: '100' }, + // restored state: 同一タブの storage から復元された状態 + { name: 'restored: relay restoring on mount', state: { relay: { isRestoring: true } }, pay: '000' }, + { name: 'restored: relay intent in auto recovery', state: { relay: { hasActiveIntent: true, recoveryState: 'auto' } }, pay: '000' }, + { name: 'restored: standard restoring on mount', state: { standard: { isRestoring: true } }, pay: '000', tip: '011' }, + { name: 'restored: standard active intent', state: { standard: { hasActiveIntent: true } }, pay: '000', tip: '011' }, + { name: 'restored: standard success from another attempt', state: { standard: { isSuccess: true, data: { success: true } } }, pay: '011', tip: '111' }, + { name: 'restored: standard success owned by this form', state: { standard: { isSuccess: true, data: { success: true } }, ownsStandardAttempt: true }, pay: '011' }, + { name: 'restored: gasless unresolved UserOperation', state: { gasless: { isUnknown: true } }, pay: '000', tip: '110' }, + { name: 'restored: gasless pending store unreadable', state: { gasless: { pendingStoreUnavailable: true } }, pay: '110' }, + { name: 'restored: cross-chain result', state: { crossChainResult: {} }, pay: '000', checkout: '111' }, + { name: 'restored: Arc recovery scan not finished', state: { arcScannedScope: 'unscanned' }, pay: '000', checkout: '111' }, +]; + +describe.each(['pay', 'checkout', 'tip'] as const)('%s submit readiness truth table', (policy) => { + const derive = readDeriveSubmit(policy); + describe.each(['standard', 'relay-free', 'relay-recover', 'pimlico', 'circle'] as const)('%s', (route) => { + const column = route === 'standard' ? 0 : route.startsWith('relay') ? 1 : 2; + it.each(submitRows)('$name', (row) => { + const expected = (row[policy] ?? row.pay)[column] === '1'; + const actual = derive(route, row.state); + if (policy === 'checkout') { + const ready = (row.checkoutReady ?? row.checkout ?? row.pay)[column] === '1'; + expect(actual).toEqual({ canSubmit: expected, paymentReady: ready }); + } else { + expect(actual).toEqual({ canSubmit: expected }); + } + }); + }); +}); + +// R14 review N1: フォームが表示に使う policy の出力 (Tip の route 限定の gaslessAmbiguous 等) を、 +// 上の 2 表の全行 × 全 route で固定する。snapshot は移動前のコード (776e6c85 の inline 宣言) で捕捉した。 +describe.each(['pay', 'checkout', 'tip'] as const)('%s UI outputs', (policy) => { + it('pins the fields the form renders from', () => { + const evaluate = readPolicy(policy); + const table: Record = {}; + for (const route of ['standard', 'relay-free', 'relay-recover', 'pimlico', 'circle'] as const) { + for (const [kind, list] of [['flow', rows], ['submit', submitRows]] as const) { + for (const row of list) { + const { ui } = evaluate(buildInput(route, row.state)); + table[`${route} | ${kind} | ${row.name}`] = Object.entries(ui).map(([name, value]) => `${name}=${value ? 1 : 0}`).join(' '); + } + } + } + expect(table).toMatchSnapshot(); + }); +});