# 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:

1. Backend calls RN `/v2/secure-payments` (same as today) and stores the `requestIds[0]` + destination wallet + amount + token on our `Payment` doc.
2. **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)`, then `transferFromWithReferenceAndFee(...)` on RN's ERC20FeeProxy.
3. 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:

1. **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.
2. **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)

1. **Wallet abstraction layer** -- implemented in `backend/src/services/payment/wallets/derivedDestinations.ts` using xpub-only derivation.
2. **Address book / registry** -- implemented in `DerivedDestination`, keyed by `(buyerId, sellerOfferId, chainId)`.
3. **Sweep job** -- implemented with build-only/hot-key signer abstraction; production must keep build-only and move execution to Trezor/Safe.
4. **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

1. **Webhook reliability at our volume.** What's RN's SLA for "address received funds → webhook delivered"? P50? P99?
2. **Custom destination support.** See open question in #3.
3. **Per-API-key rate limits.** If we end up calling `/v2/secure-payments` once per escrow, do we hit ceilings?
4. **Pricing for the notification-only flow** — is there a tier, or is it the same as the full-stack price?
5. **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

1. **Correlation repair:** implemented for the in-house checkout path; keep smoke coverage around every persisted RN reference shape.
2. **Callback repair:** implemented enough for the successful paid dev probe; keep polling/backoff hardening on the checkout roadmap.
3. **Transaction Safety Provider:** implemented for tx hash, confirmations, transfer match, and AML placeholder; real AML provider remains Task #10.
4. **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.
