feat: MoonPay fiat on-ramp with tracked buy rows and automatic Agglayer bridge to Miden - #704
Draft
0xnullifier wants to merge 4 commits into
Draft
feat: MoonPay fiat on-ramp with tracked buy rows and automatic Agglayer bridge to Miden#7040xnullifier wants to merge 4 commits into
0xnullifier wants to merge 4 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a fiat on-ramp (Buy) to the wallet: a MoonPay Buy widget embedded as a plain signed-URL iframe, purchases tracked as first-class
buyactivity rows, and the delivered token bridged automatically from the account's derived EVM address to the user's Miden account over Agglayer.How it works
Widget (
/buy) —src/lib/fiat-ramp/moonpay.tsbuilds the widget URL (sandboxbuy-sandbox.moonpay.comforpk_test_keys, prod otherwise) with the derived EVM address pre-filled; MoonPay requires HMAC-signing such URLs, so the signature comes from a local sign server (../moonpay-sign-server.mjs, holds the sk). No SDK, no remote script — MV3-safe.Two extension-specific unblockers:
credentiallesson the iframe (extension pages run COEPrequire-corp; MoonPay sends no CORP header).moonpay-frame-rules.ts, installed at SW start) stripping MoonPay'sframe-ancestors *CSP for extension-initiated MoonPay sub_frames only — per spec*never matcheschrome-extension://origins, so every extension is otherwise blocked regardless of dashboard allowlisting.Purchase tracking — each
/buymount mints a session uuid (localStorage slot) carried as MoonPay'sexternalTransactionId. The app-rootBuyBridgeManager(20s tick, outlives the/buyscreen) polls the sign server's/tx-statusproxy (sk-only endpoint); acompletedpurchase inserts a tracking-onlyBuyTransactionDexie row (bornCompleted,extraInputs.bridgeProgressowns live state — same pattern asbridged-receive).Auto-bridge — once the delivered token shows a balance on the derived address, the watcher gasses it via a local paymaster (
../miden-paymaster, drips 0.0005 Sepolia ETH), then broadcastsapprove+bridgeAsseton the lxly bridge (vault-signed via the restoredlib/agglayer/vault-evm.ts, addressed to the Miden account viamidenAddrToEvmAddr). Lifecycle:not-initiated → initiated → processed | failed, with delivery reconciled against the bridge indexer (reconcileBuyBridges, mirroringreconcileAgglayerBridgedReceives, wired into the AllHistory poll). The Agglayer note auto-consumes on arrival like any other.UI — dedicated Activity row ("Buy / Via MoonPay → Miden", progress-driven status chip) and a HistoryDetails "Buy Details" card (route, bridged amount, Etherscan-linked EVM tx hash, error on failure) with live reload while non-terminal.
Misc — all vault-signed EVM paths moved off viem's default Sepolia RPC (thirdweb, 429s under light write traffic) to PublicNode.
Local pieces (not in this repo)
../moonpay-sign-server.mjs— URL signing +/tx-statusproxy + webhook receiver (dev aid).../miden-paymaster— minimal Sepolia gas drip server. Interim: the target architecture is EIP-7702 sponsorship (relayer pays gas, EOA delegates to a smart-account implementation); the drip unblocks the end-to-end flow now.Testing
pk_test_keys (EUR→USDC, Banxa unavailable on the sandbox key per Onramper/MoonPay sandbox constraints; MoonPay test card).0x699cFE…4A17, Sepolia) on the derived address: paymaster drip → approve →bridgeAssetbroadcast → indexer-confirmedprocessed.yarn lint,tsc --noEmit, and the chrome extension build are green.🤖 Generated with Claude Code