Files
nick-doc/01 - Architecture/Request Network Integration Constraints.md
Siavash Sameni 0060b16912 docs: ship in-house RN checkout, scope 5 follow-up tasks (#7-11)
In-house Request Network checkout went fully end-to-end on dev today.
A real 0.01 USDC payment flowed through wallet connect -> approve ->
ERC20FeeProxy.transferFromWithReferenceAndFee -> RN webhook ->
TransactionSafetyProvider -> Payment.status=completed -> page success
state. Tx 0x494c77a29161b5100d8e0b1ac675f1822955d0bb3633ecdbfafb886f84f2f320.

Docs:
- New PRD: Wallet, Multichain, Confirmations, AML, Trezor
  (5 follow-ups, each sized for an independent contributor)
- Updated PRD: Request Network In-House Checkout (phases 0..3 done,
  phase 4 partial, phases 5-6 not started)
- Updated handoff: deployed versions, what is working end-to-end,
  follow-up tasks index

Taskmaster: 5 new top-level tasks (#7..#11) covering ephemeral
destination wallets, multichain proxy registry + USDC/USDT, runtime
confirmation thresholds, optional seller-paid AML screening, and
Trezor signing for admin actions. Tasks are scoped fine-grained so
each is independent enough for kimi to pick up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 15:50:24 +04:00

179 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 does not support Rabby — show-stopper for our wallet user base
### 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 (designed, not yet implemented)
Skip the RN-hosted UI. We already call `/v2/secure-payments` and receive a `securePaymentUrl`, but we also receive `requestIds` and `token` — that's everything we need to know what the merchant request is. Behind that token there is a contract on the destination chain that anyone can fulfill.
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.
### Open
- Need to confirm RN settles a payment that arrives from a *proxy transaction we built*, not from their hosted page. The 2026-05-28 probe confirms RN webhook delivery to Amanat, but the app returned `404`; repeat the probe only after the confirmation repair is deployed.
- Need a fallback for the buyer who insists on the RN hosted UI (some users will already have the link copied). Keep `securePaymentUrl` exposed as a "advanced / pay with RN" link.
---
## 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 (designed)
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 have to render the wallet picker, so adding a chain selector before the wallet step costs almost nothing.
### Open
- We need a per-seller config table for accepted chains. Today the env-level `REQUEST_NETWORK_MERCHANT_REFERENCE` hard-codes a single chain (`bsc`). Needs to become per-seller, per-offer.
- 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 (designed; needs RN feasibility check)
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** — service that on demand generates a fresh address (HD wallet derivation from a master seed kept in a hardware module / KMS) and returns it to the payment-intent flow.
2. **Address book / registry** — maps `(paymentId, chainId)` → derived address. Persists derivation path + sequence number so we can reproduce keys for sweeps later.
3. **Sweep job** — once a payment is confirmed AND has passed an on-chain screening check (Chainalysis API or similar), sweep the ephemeral wallet to the main treasury. If screening fails, the ephemeral wallet is quarantined and the payment refunded out of band.
4. **Key custody policy** — these are still our funds in custody briefly; need clear policy on who can sign sweeps, hot-key vs cold-key separation.
### Critical open question
**Does RN support creating a secure-payment with a destination wallet we specify per-request, rather than a static merchant reference?** If yes, this is straightforward — we generate a wallet, register it as the destination for one specific `/v2/secure-payments` call, done. If no (RN only allows pre-registered destinations), we have to either:
- 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: confirm with RN support whether per-request destinations are supported on the same API key.**
---
## 4. RN reduced to a notification service — viable, but not yet 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 needs testing before we commit
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)? Does the webhook still fire? Is settlement still recognized? — this is the load-bearing test for the whole strategy.
Until #5 is confirmed, the rest is just paper architecture.
---
## 5. Webhook durability and transaction safety are P0 before more paid probes
### 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
1. **Correlation repair:** lookup Request Network payments by every persisted reference shape, including `providerPaymentId`, top-level RN request id/payment reference, and nested raw RN data.
2. **Callback repair:** payment callback polling must unwrap the backend response shape, clear polling after terminal states, and avoid a 3-second loop that self-rate-limits.
3. **Transaction Safety Provider:** completion must pass configured safety checks: transaction hash present, minimum confirmations, token/recipient/amount transfer match, and future AML/sanctions provider approval.
4. **Durable ingress:** 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 | Deploy confirmation repair and repeat the dev payment probe | Backend payments |
| 2 | Test: `/v2/secure-payments` accepts a per-request destination wallet | Backend payments |
| 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 | Spec the wallet-abstraction layer (HD derivation + sweep job + key policy) | Backend, before going live |
| 6 | Spec the seller-side accepted-chains config | Backend + frontend |
| 7 | Add Cloudflare Worker durable webhook ingress to the roadmap | Backend / platform |
| 8 | Add AML/sanctions adapter behind Transaction Safety Provider | Compliance / backend |
Actions 14 are *information-gathering* and should run in parallel before any more architectural commitment to RN. Actions 56 are blocked on 13 confirming RN can actually support this shape.