11 KiB
Request Network Integration — Constraints and Design Implications
Date: 2026-05-27 Status: Active concerns; 2026-05-28 probe confirmed RN webhook delivery but exposed Amanat confirmation handling gaps Owners: Backend payments (Amanat), product
This document captures payment-flow issues that surfaced while integrating Request Network (RN) into the Amanat escrow stack. Each one is either a show-stopper or a non-trivial architectural constraint. Listed in priority order.
1. RN hosted UI does not support Rabby -- mitigated by Amanat in-house checkout
Problem
RN's hosted payment page (the pay.request.network/?token=… UI returned by /v2/secure-payments) does not detect / connect to Rabby. A meaningful slice of Amanat's user base pays from Rabby. Sending them to a screen that won't even let them connect is a hard block.
Mitigation (implemented core path)
Skip the RN-hosted UI. Amanat still calls /v2/secure-payments, stores the Request Network identifiers, and exposes an in-house checkout block. The frontend builds the same RN-compatible on-chain action from the buyer's wallet, so Rabby/MetaMask users stay inside the Amanat flow.
So the new flow becomes:
- Backend calls RN
/v2/secure-payments(same as today) and stores therequestIds[0]+ destination wallet + amount + token on ourPaymentdoc. - We render our own checkout screen that:
- Shows the buyer the wallet address to pay to (the destination resolved from the merchant reference / chain / token).
- Lets the buyer connect any wallet — Rabby, MetaMask, OKX, Phantom-bridged, WalletConnect.
- Builds the two RN-compatible transactions client-side: token
approve(proxy, amount), thentransferFromWithReferenceAndFee(...)on RN's ERC20FeeProxy.
- RN's webhook tells us when the proxy event lands; Request Network search/status APIs remain the polling fallback.
Why this is acceptable
- RN's value to us at that point is the settlement bookkeeping, not the UI. We use them as "did this address receive the expected amount before timeout?" — the wallet UX stays in our control.
- Buyer never sees a third-party brand mid-checkout, which is a UX win regardless of Rabby.
Remaining work
- Keep the RN hosted URL exposed as an escape hatch.
- Continue hardening timer/persistence/telemetry around the in-house checkout.
- Treat durable webhook ingress as a production gate, because the main Express app should not be the only landing zone for callback evidence.
2. RN's multi-chain routing forces an expensive LiFi bridge
Problem
When we configure a destination route (e.g. BSC + USDC), RN's hosted UI still lets the buyer pick any chain where they hold funds (e.g. ARB). To honor that, RN routes the buyer's funds through LiFi, which charges bridging fees that someone has to pay, and it's not clearly disclosed who.
The visible costs:
- Buyer over-pays vs. nominal invoice amount (bad UX).
- Or we eat the spread (bad margin).
- Or seller gets less than they expected (worst — they'll dispute).
- Plus settlement latency goes from seconds to minutes-hours depending on the bridge.
Mitigation (partially implemented)
Take the chain choice away from RN's UI and bring it into ours, gated by what the seller will accept.
Two-step UX:
- At offer creation (seller side): seller specifies which chain(s) they accept payouts on. We persist this as
acceptedChains: [bsc, arb, base, …]on the offer / merchant configuration. - At checkout (buyer side, before any RN call): we show the buyer the seller's accepted chains. Buyer picks one. Then we call RN with that exact chain pinned as the destination. No LiFi bridge — same-chain transfer.
Side benefit
This composes cleanly with #1 (own checkout screen): we already render the wallet picker, so seller-accepted chain selection can happen before wallet connection. The chain/token registry and admin networks page exist; seller-side accepted-chain policy remains a separate product/data-model task.
Open
- We need a per-seller/per-offer config table for accepted chains. Today the global merchant reference is still the fallback, while derived destination work handles recipient variation.
- Does RN's API support creating a secure-payment that rejects off-chain payments rather than auto-bridging? Or do we have to enforce this purely on our side by never offering the cross-chain option to the buyer? Confirm with RN docs/support.
3. Sanctioned-funds risk — single escrow wallet poisons the entire platform
Problem
Today the entire escrow stack receives funds into one (or a handful of) wallets — REQUEST_NETWORK_MERCHANT_REFERENCE resolves to a single destination address. If a buyer pays with funds tied to a sanctioned source / mixer / known-bad address:
- That destination wallet gets tagged non-compliant by Chainalysis / TRM / Elliptic.
- Downstream exchanges and OTC desks won't accept transfers from it.
- One bad buyer can effectively brick the entire platform's settlement layer.
This is a show-stopper for going live at scale. Same class of issue we already considered around SHKeeper.
Mitigation (implemented core path; operational probe pending)
Per-(buyer, merchant)-pair ephemeral wallets. Each new escrow gets a freshly-generated address that only ever receives that one transaction. If those funds turn out to be dirty:
- Only that wallet is tainted.
- We never sweep it into our main treasury (or sweep only after the payment passes screening).
- Risk is siloed to the individual escrow, not platform-wide.
What this requires (architectural work)
- Wallet abstraction layer -- implemented in
backend/src/services/payment/wallets/derivedDestinations.tsusing xpub-only derivation. - Address book / registry -- implemented in
DerivedDestination, keyed by(buyerId, sellerOfferId, chainId). - Sweep job -- implemented with build-only/hot-key signer abstraction; production must keep build-only and move execution to Trezor/Safe.
- Key custody policy -- still the important missing operational layer. See PRD - Decentralized Custody and Smart-Contract Escrow Roadmap.
Critical open question
Does RN support creating a secure-payment with a destination wallet we specify per request at production volume, rather than a static merchant reference? The backend/frontend support the shape, but the live divergent-destination probe remains the operational proof point. If RN cannot support this reliably, fallback options are:
- Pre-register a large pool of addresses with RN and rotate through them, or
- Bypass RN's destination model and go full self-host (which is most of issue #4).
Action: run the two-paid-intent divergent-destination probe and confirm with RN support whether this usage is supported on the same API key at expected volume.
4. RN reduced to a notification service -- viable, partially validated
Problem statement
If we adopt #1 (own checkout UI), #2 (own chain selection), and #3 (own ephemeral wallets), RN's role in the flow collapses to:
"Tell me when wallet X receives Y tokens (or doesn't, before timeout)."
Which is a notification primitive, not a payment platform. We'd be paying for a feature we're using maybe 5% of.
Why this might still be worth it
- We get RN's chain watchers + reorg handling + webhook reliability for free.
- We don't have to run our own indexer on n chains.
- Their screening (if they do any) is one more compliance layer.
Why this might NOT be worth it
- Pricing built around hosted-UI usage, not API-only. May not be cost-effective at API-only volumes.
- We're outsourcing the one thing RN is good at (settlement) and keeping the parts they don't help with (UX, wallet generation, compliance).
- Alternative: do the same with our own chain watcher (Alchemy webhooks / Tenderly / Goldsky) and skip RN entirely.
What still needs testing before we commit at scale
- Webhook reliability at our volume. What's RN's SLA for "address received funds → webhook delivered"? P50? P99?
- Custom destination support. See open question in #3.
- Per-API-key rate limits. If we end up calling
/v2/secure-paymentsonce per escrow, do we hit ceilings? - Pricing for the notification-only flow — is there a tier, or is it the same as the full-stack price?
- What happens when the payment arrives from a transaction WE built (not theirs)? The 2026-05-28 in-house checkout probe proved the basic path for a real BSC USDC payment; this still needs repeated paid probes across tokens/chains and webhook durability coverage.
Until webhook durability, destination divergence, pricing, and SLA are confirmed, treat RN as useful but not irreplaceable infrastructure.
5. Webhook durability remains P0 before production rollout
What the 2026-05-28 probe proved
The dev test transaction 0x3a23febd9abd43d7e0851c1ea86c4ceaf08c11098852cb0425fa074e9c88350b succeeded on BSC. RN then called POST /api/payment/request-network/webhook on dev.amn.gg four times from 34.34.233.192. Amanat returned 404 because backend correlation looked up the wrong reference shape; the Payment record held RN request/payment-reference values that the handler did not search.
Design implication
Do not treat the main Express app as the only webhook landing zone, and do not treat a signed provider callback as enough to credit escrow.
Required mitigation and status
- Correlation repair: implemented for the in-house checkout path; keep smoke coverage around every persisted RN reference shape.
- Callback repair: implemented enough for the successful paid dev probe; keep polling/backoff hardening on the checkout roadmap.
- Transaction Safety Provider: implemented for tx hash, confirmations, transfer match, and AML placeholder; real AML provider remains Task #10.
- Durable ingress: not started. Put a Cloudflare Worker in front of RN webhooks. The Worker stores raw delivery evidence durably, forwards to the backend, and supports replay. It is not the trust oracle; the backend still verifies, deduplicates, and applies safety/ledger transitions.
Cross-cutting next actions
| # | Action | Blocker / Owner |
|---|---|---|
| 1 | Run the live divergent-destination probe: two paid intents to two derived addresses | Backend payments |
| 2 | Confirm /v2/secure-payments per-request destination usage with RN support and pricing |
Product / RN account manager |
| 3 | Confirm RN doesn't auto-bridge when buyer pays on the destination chain natively | Backend payments |
| 4 | Get RN's webhook P99 latency + delivery guarantees in writing | Product / RN account manager |
| 5 | Move sweep/release/refund custody to Trezor/Safe, not backend hot keys | Backend + ops |
| 6 | Spec the seller-side accepted-chains config | Backend + frontend |
| 7 | Build Cloudflare Worker durable webhook ingress + replay | Backend / platform |
| 8 | Add AML/sanctions adapter behind Transaction Safety Provider | Compliance / backend |
Actions 1-4 are information-gathering and should run in parallel before deeper RN commitment. Actions 5, 7, and 8 are production-safety work regardless of whether Amanat keeps RN long-term or replaces it with a direct chain watcher.