Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기x402 on Stellar priced in local currency (CLP, UF, EUR, BRL…) and payable with USDC, XLM or EURC. Live on mainnet.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Charge in your currency. Receive exact USDC. Local402 is x402 on Stellar for the rest of the world: a seller prices an API route in their own currency — "1 MXN", "5 INR", "70 NGN", "0.05 EUR", "0.01 UF" — and a payer settles it with whatever they hold: USDC, XLM or EURC. The seller always receives the exact USDC amount, in one transaction, with no manual currency swap on either side. [README 설치·사용 부분 발췌] Quick start Then open. The first run writes .demo-keys.local (gitignorREADME 전문 보기 ↗
Charge in your currency. Receive exact USDC.
Local402 is x402 on Stellar for the rest of the world: a seller prices an API route
in their own currency — "1 MXN", "5 INR", "70 NGN", "0.05 EUR", "0.01 UF" — and a payer settles it with whatever
they hold: USDC, XLM or EURC. The seller always receives the exact USDC amount, in one transaction, with
no manual currency swap on either side.
| Demo video (3 min) | https://youtu.be/SNeQ8wZo3hg |
| Dashboard (testnet) | https://local402.xyz |
| Live API (Stellar mainnet) | https://local402-mainnet.vercel.app |
| Scheme spec | docs/scheme_exact_fx_stellar.md |
| npm | local402-pricing · local402-fx · local402-client · local402-server · local402-facilitator · local402-mcp |
| FxPay on mainnet | CA6Z4E55YN6LZEXBQPFCWUIMUJGAV42RLHYWWZ6SNIP4E73R2I2PUGHD |
| Real payments through it | XLM → USDC 0babefe1… and EURC → USDC bfe6a44d…, 2026-09-20 — each delivered exactly 0.0520721 USDC to the seller and refunded the unused input |
| An outside seller charging with it | kuyfi prices each scan at 500 CLP — the full paid flow on video (28 s) · testnet tx 44ef0d00… |
| Security | threat model · internal review — not audited, so mainnet settles only for the demo seller and from 0.01 USDC |
| Next 30 days | sprint plan: 1.0 packages, a deployable facilitator, an integration guide, kuyfi on 1.0 — each with its acceptance criterion |
| Reproducible build | release — wasm 69a12d89…, byte for byte what is deployed; Verified Build (SEP-55) on Stellar Lab |
| Bug found and fixed upstream | x402#3491 — mainnet rejects the fee the SDK bids; our fix, x402#3503, is merged |
x402 lets an HTTP resource charge per request and lets an AI agent pay on its own. Today, though, the
price is a dollar amount settled in a single asset. Write price: "70 NGN" and the SDK fails.
That is not how commerce outside the US works. A Mexican API bills in pesos, an Indian one in rupees, a Nigerian one in naira; a Chilean rental contract is denominated in UF, an inflation-indexed unit that changes daily. And the customer pays with whatever is in their wallet, which is usually not the seller's asset.
Local402 prices in any currency the Reflector fiat oracle publishes on mainnet (checked 2026-09-16), plus USD and the UF:
| Region | Currencies |
|---|---|
| Latin America | MXN, BRL, COP, PEN, ARS, CLP, CRC, VES |
| Africa | NGN, KES, ZAR, CDF |
| Asia | INR, JPY, CNY, KRW, PHP, HKD |
| Europe and North America | EUR, GBP, TRY, RUB, CAD |
A new currency needs no code: the day Reflector publishes it, localRoute("… XYZ") prices in it. The
demo sells one route per region (/latam in MXN, /asia in INR, /africa in NGN) next to the euro and
Chilean ones. Chile is the deepest example because of the UF, which shows that even an indexed unit
that is not a currency works the same way.
Two independent layers. Either one is useful on its own.
1. Local-currency pricing (local402-pricing, local402-server).
The price stays in the seller's currency. The Reflector on-chain oracle
converts it to USDC at request time, rounding up so the seller never receives less than the local
price. The quote travels inside the standard 402 response as extra.local402, and the requirement
itself is a plain exact USDC requirement — any existing Stellar x402 client pays it unchanged.
import { paymentMiddleware } from "@x402/express";
import { localRoute, local402Server } from "local402-server";
app.use(paymentMiddleware(
{ "GET /africa": localRoute("70 NGN", { payTo: SELLER_ADDRESS }) },
local402Server(FACILITATOR_URL),
));
The seller signs each quote with LOCAL402_QUOTE_SECRET (32+ characters, openssl rand -hex 32) and
refuses to start without it. examples/quickstart is the runnable version, and
docs/errors.md lists every error code.
2. exact-fx: pay with a different asset (local402-fx, contracts/fx-pay).
A new x402 scheme. The payer signs a call to the FxPay Soroban contract, which swaps their XLM or
EURC on Soroswap for exactly the USDC the seller asked for, delivers it,
and refunds the unused input — all atomically. If the swap cannot deliver the exact amount within the
payer's limit, the whole transaction reverts and nothing moves.
The seller never touches XLM. The facilitator pays the network fee. The payer signs once.
Stellar's x402 docs support any SEP-41
token, USDC by default, through the Coinbase facilitator (testnet) and the OpenZeppelin Relayer plugin.
Both settle x402's exact scheme: the price is an amount of one token, and the payer transfers that very
token. The x402 projects on Stellar we could find in the ecosystem directories and past hackathons
(checked 2026-09-22) — facilitators, MCP templates, paywalls, agent SDKs — build on that same scheme.
The three closest, as their own READMEs describe them (checked 2026-10-06):
| Project | What it solves | The price is written in | The payer pays with |
|---|---|---|---|
| Rail402 | Who settles: an x402 facilitator that sponsors the fee, plus a Bazaar where agents find paid APIs and MCP tools | USD, settled as USDC (price: "$0.01", scheme exact) | USDC |
| REAPP | Who may spend: AP2-style mandates and a Soroban registry that enforces an agent's budget, scope and revocation | USDC (AGENT_PLAN_PRICE_USDC) | USDC |
| x402 MCP Stellar Template | How to start: Node, Python and Go templates for paid MCP servers, with optional on-chain daily caps | USDC (x402({ price: 0.10 })) | USDC |
| Local402 | What the price is, and what the payer brings | The seller's currency, converted by Reflector per request | USDC, XLM or EURC |
They answer different questions, so they can stack rather than compete. Local402's exact option is a
plain USDC requirement, which is the kind of payment Rail402 settles and a REAPP mandate is written for.
We have not tested either combination yet. What none of the three does is let a seller write "500 CLP", or let a
payer settle it in XLM while the seller still receives exact USDC.
| x402 on Stellar today | Local402 | |
|---|---|---|
| The price is written in | an amount of the settlement token | the seller's currency — CLP, UF, NGN, EUR… — converted by Reflector at request time |
| The payer pays with | the seller's token | USDC, XLM or EURC; FxPay swaps inside the payment |
| The seller receives | that token | the exact USDC amount, or the transaction reverts |
| A stock x402 client | pays | still pays: every 402 keeps a plain exact option next to exact-fx |
Local402 does not replace a facilitator or fork x402. It is four npm packages and one contract on top of it, and the one change it needed upstream — the fee bid — went back into x402 itself.
Every case below runs on the code in this repository, not on a roadmap.
70 NGN or 50 CLP; the
buyer is charged that, and you receive USDC. You never publish a dollar figure and
never carry the exchange rate on your pricing page — local402-server is a one-liner
over any x402 route.402, quotes it against its own oracle, and pays per call with whatever asset it
holds. No subscription, no account, no human in the loop.exact-fx the buyer pays in XLM or
EURC and you still receive the exact USDC you asked for — the swap and the refund happen
inside the payment. One less "first, go get USDC" step between the buyer and the sale.GET /receipts.csv hands accounting a ledger, not a screenshot.▶ Watch it: a paid kuyfi scan, end to end on testnet (28 s video, on X)
GET /scan/…→402at 500 CLP = 0.509682 USDC → payment to the kuyfi operator →200with the scan → tx44ef0d00…c8abf42. The video is animated from the real request, response and transaction below.
kuyfi is an independent, MIT-licensed project — an OSINT scanner
that maps the attack surface of a Soroban contract. It added an optional kuyfi-server that puts a price
on each scan with local402-server: GET /scan/<contractId> answers 402 priced in Chilean pesos,
and returns the read-only scan once the payment settles. That is the first integration of Local402 by a
seller we did not write. The paywall itself is the same one line as the snippet above, a
localRoute("500 CLP", …) call; the rest of the PR is the HTTP server kuyfi did not have before.
We wrote that PR (kuyfi#1); kuyfi's maintainer reviewed and
merged it, and the payments go to the maintainer's own account.
On testnet, a client paid one scan end to end:
| The price | 500 CLP, converted by Reflector to 0.509682 USDC at request time |
| What settled | that exact USDC amount to the kuyfi operator, in one transaction; the payer paid 0 XLM, since the facilitator sponsors the fee |
| What came back | the 200 scan of FxPay's own testnet deployment — five entry points over a 4821-byte wasm |
| Transaction | 44ef0d00…c8abf42 |
For now the integration runs on testnet only. Moving it to mainnet (NETWORK=stellar:pubnet, real USDC)
is planned, and is tracked in kuyfi#2. It needs one change
on our side first: the mainnet facilitator settles only for the demo seller today (see
What it does not do), so the kuyfi operator has to be added to its payee allowlist.
So the two projects now rely on each other both ways: Local402's own contract was scanned with kuyfi (see What it does not do), and kuyfi sells those scans with Local402.
Moving kuyfi onto the 1.0 packages, with paid testnet scans as the evidence, is the acceptance test of the October sprint (D5 in the sprint plan). The plan also covers what happens if kuyfi cannot move in time.
No install, no wallet, no key. Ask the mainnet API for something and read what it answers:
# The 402 itself: the price is in naira, the requirement is in USDC
curl -si https://local402-mainnet.vercel.app/africa \
| grep -i '^payment-required' | cut -d' ' -f2 | tr -d '\r' | base64 -d
# The same through the published client, which re-derives the price from its own oracle
npx -y local402-client quote https://local402-mainnet.vercel.app/africa
npx -y local402-client quote https://local402-mainnet.vercel.app/asia --with XLM
npx -y local402-client quote https://local402-mainnet.vercel.app/uf --with EURC
To pay for real, use the Freighter button in section 08 of the dashboard, or
STELLAR_SECRET=S… npx -y local402-client pay <url> --with XLM --max "100 NGN". The --max limit can be
in any supported currency, whatever the seller's, and the client refuses anything above it.
npm install
npm run demo # creates + funds throwaway testnet accounts, starts everything
Then open http://localhost:3001. The first run writes .demo-keys.local (gitignored); nothing else
is needed — no faucet visit, no configuration.
To run the pieces separately, copy each .env.example and:
npm run facilitator # :4022 settles exact and exact-fx, pays fees
npm run api # :3001 demo seller + dashboard
npm run agent # a paying client
npm run mcp # MCP server: an agent discovers, quotes and pays
sequenceDiagram
participant P as Payer / agent
participant S as Seller (API)
participant F as Facilitator
participant N as Stellar
P->>S: GET /africa
S->>N: Reflector: 1 NGN = 0.00075233 USD
S-->>P: 402 · accepts [exact, exact-fx] · extra.local402
Note over P: Re-quotes from its OWN oracle<br/>rejects >2% overcharge<br/>bounds max_send, rejects >5% FX premium
P->>F: signed FxPay.pay(...) authorization
Note over F: 11 verification rules<br/>simulate · bid inclusion fee · fee-bump
F->>N: submit
N->>N: swap XLM on Soroswap for exactly 0.0526629 USDC
N-->>S: 0.0526629 USDC — exact
N-->>P: refund of the unused XLM
F-->>S: tx hash → receipt
S-->>P: 200 + the data
Eleven real payments on Stellar mainnet between 15 and 20 September 2026, all of them successful. The last three went through the reproducible-build deployment. The latest ones are in the receipts.
| What the seller received | The exact USDC amount the 402 asked for, in all eleven payments. |
| Network fee, paid by the facilitator | 0.0024 XLM for a USDC payment. For exact-fx, 0.081 and 0.044 XLM on a fresh deployment's first two swaps, then about 0.0055 once the contract's ledger entries are warm. Redeploying reproduced this exactly: 0.0807 and 0.0436. |
| What the swap actually spent | 0.21–0.39% above the Reflector value of the price (the three payments on 15 September). |
| The most the payer authorized | 2.29–3.17% above it. That includes the 2% slippage allowance, and whatever the swap does not use is refunded. |
| A 402 carrying a live quote | About 0.41 s, warm. |
| A full payment on testnet: 402 → quote → sign → settle → 200 | 5.6 s with XLM, 6.8 s with USDC, 9.4 s with EURC (which routes through XLM). |
packages/
pricing/ local currency → USDC. Reflector oracle, UF source, x402 DynamicPrice
fx/ the exact-fx scheme: client, resource server, facilitator, fee bidding
client/ Local402Client — pays with USDC/XLM/EURC, with its own guards. Also a CLI
server/ localRoute() and localToolPayment() — one line to price a route or an MCP tool
facilitator/ verifies and settles, pays fees, serves the x402 Bazaar catalog. Runs with npx
apps/
demo-api/ the seller: six paid routes, receipts, the dashboard
agent/ minimal paying client
mcp/ MCP server an AI agent uses to discover, quote and pay
mcp-seller/ an MCP server whose tool is itself paid per call
contracts/
fx-pay/ the Soroban contract, in Rust, with its tests
docs/
scheme_exact_fx_stellar.md the exact-fx spec, in the x402 spec format
These are the places where the obvious implementation is wrong.
Rounding is directional. quoteLocalPrice rounds up (ceilDiv). A seller asking 70 NGN must
never receive 69.999 NGN worth of USDC because of integer truncation.
Quotes are median-of-five, not last-price. Reflector's CLP feed has published single 5-minute
prints 0.65% away from their neighbours. ReflectorFiatOracle takes the median of the last five
records, so one outlier tick cannot set a price. Stale rates (>15 min by default) are rejected
outright. When the feed skipped periods, the history is cut at the gap first (recentRun): those five
records would otherwise span hours while still reporting the newest timestamp, and the median would
smooth in prices that are no longer relevant. A price backed by a single record says so, with a
#single suffix on its source — that is one raw print, exactly the tick the median exists to absorb.
Quotes are HMAC-signed and frozen. x402 rebuilds the payment requirements when the paid retry
arrives — and the oracle may have moved in between, which would make the amount the payer signed no
longer match. localPrice signs the quote it issued; any server instance holding the same secret
honours it while it is valid. This is what makes the flow correct behind a load balancer, not just on
one process.
The client re-derives the price instead of trusting it. Local402Client quotes the same local
price from its own oracle and refuses a quote more than 2% above it (maxOverchargeBps), and refuses
an FX payment whose max_send is more than 5% above the oracle value of the price
(maxFxPremiumBps). The FxPay contract address is pinned client-side and never taken from the
server's response — a malicious contract could spend up to max_send.
Be precise about what that buys: by default both sides read the same Reflector feed, so this catches
a seller who misquotes, not a feed that is wrong. Pass your own oracle to Local402Client for an
independent reading. The UF adds a second shared dependency — the SII, or its two mirrors — and a
stale UF is why a quote also carries oracleValueDate: the day the value is actually from, which the
CLP timestamp cannot tell you.
The agent's budget is reserved, not checked. SpendingBudget is debited before the request and
credited back on failure, so two concurrent payments cannot both pass a check-then-spend test and
together overrun the limit.
The facilitator has eleven verification rules, and two are redundant on purpose. The authorization
tree (rule 7) already bounds what the payer's signature permits. Rules 9 and 10 re-check the simulated
events — that the seller was paid, and that no facilitator account was drained — as defence in depth.
Since the facilitator pays the fees, a public deployment can also require an allowlisted seller and a
minimum amount, and rate-limits verify/settle separately because they cost different things.
Fee bidding, and a bug we fixed upstream. @x402/stellar 2.25 always bids the 100-stroop base
fee, which mainnet frequently rejects under Soroban surge pricing. feeBumpSigner re-wraps the fee
bump with a bid of twice the network's recent p99, capped — without touching the inner transaction or
its signatures. We reported it as x402#3491 and
fixed it in x402#3503, merged into x402 main: the
exact facilitator now takes an inclusionFeeStroops option. The workaround stays until a release
(after 2.26.0) ships it.
Parallel settlement needs separate accounts. Two settlements from one Stellar account collide on
the sequence number. ChannelPool hands each concurrent settlement its own fee-paying account, and
queues the rest until one is free. It is an in-process lock, so it only separates settlements running
in the same facilitator instance: two instances sharing one key still collide. That is why /settle
retries once on a submission_failed that never reached the ledger, and why a horizontally scaled
facilitator needs a disjoint FACILITATOR_PRIVATE_KEYS per instance.
The UF is a date, not a rate. It is defined per calendar day in Chile's timezone. UfRateSource
reads the SII (the tax service), falls back to two other sources, times out at 4 s and keeps the last
known value for up to three days — because an API that returns 402 with no price is worse than one
priced on yesterday's UF.
A paid resource is never cached. Both the client probe and the API send no-store. A cached 200
would hand out a paid response for free; a cached 402 would hand out an expired quote.
Each of these fails before any money moves, or reverts the whole transaction. The facilitator codes below
drop their invalid_exact_fx_payload_ prefix.
| Situation | What happens |
|---|---|
| The oracle rate is more than 15 minutes old, dated in the future, or zero | The server refuses to quote. No requirement is issued, so nobody pays on a bad price. |
| The seller quotes more than 2% above the payer's own oracle | Local402Client refuses before signing. |
| The swap could spend more than 5% above the oracle value | The client refuses before signing (maxFxPremiumBps). |
The payer does not hold max_send | The client says how much has to be available and how much is there, before the wallet opens. |
The pools moved and the route now needs more than max_send | FxPay reverts with ExcessiveInput. Nothing moves. |
| No pool can deliver the seller's asset | FxPay fails with NoRoute, a readable error rather than a trap. |
| The amount, recipient or asset differs from the 402 | The facilitator rejects it: wrong_amount, wrong_recipient, wrong_asset. |
The signature authorizes anything beyond pay and one transfer | wrong_auth_root, wrong_auth_subinvocation or unexpected_auth_entries. |
| The transaction would move the facilitator's own funds | moves_facilitator_funds, checked against the simulated events. |
| The deadline passes while the payer approves in the wallet | deadline_expired. The client already allows 180 s of signing time. |
| Two settlements race for one account | ChannelPool gives each its own. A submission that never reached the ledger is retried once. |
| All three UF sources are down | The last known UF is used for up to three days. After that only the UF route stops quoting; the others keep working. |
npm run demo needs nothing. A deployment that more than one process serves, or that spends real
money, needs these — each .env.example lists the rest.
| Variable | Where | Why it matters |
|---|---|---|
LOCAL402_QUOTE_SECRET | seller | Signs quotes. Without it, a paid retry that lands on another instance re-quotes and rejects a payment the payer already signed. Same value on every instance, at least 32 characters (openssl rand -hex 32). Since 1.0 the seller refuses to start without it; quoteSecret: false opts a single-instance seller out. npm run demo generates one per run. |
KV_REST_API_URL, KV_REST_API_TOKEN | seller | Receipts, the demo payment caps and the last known UF value. Without them each serverless instance keeps its own, and they disappear when it does. |
SELF_URL | seller | Where the demo agent buys from. Falls back to the Vercel project's production domain, and to the request's Host only when that is localhost, because Host is whatever the caller wrote. |
CORS_ORIGIN | seller | Dashboard origins allowed to pay from the visitor's wallet, comma-separated. |
PAY_TO_ALLOWLIST, MIN_AMOUNT | facilitator | Every settlement spends the facilitator's own fees. On stellar:pubnet it refuses to start without a seller allowlist (the demo deployment falls back to its own PAY_TO), and the minimum defaults to 100000 (0.01 USDC). |
FACILITATOR_PRIVATE_KEYS | facilitator | One key per concurrent settlement. ChannelPool separates them within an instance only, so give each instance its own keys. |
VERIFY_RATE_LIMIT, SETTLE_RATE_LIMIT, TRUST_PROXY | facilitator | Requests per client per minute (60 and 20), and how many proxies' X-Forwarded-For to believe when counting clients (0; 1 on the demo deployment, behind Vercel). |
FX_CONTRACT | both | Required on mainnet: the FxPay deployment the payer's authorization is pinned to. |
To run a facilitator of your own, for your sellers or someone else's, npx local402-facilitator is the
whole install: docs/facilitator.md has every variable and the mainnet configuration,
and the runbook covers keys, funding, top-ups and rotation.
npm test # 41 unit tests across pricing, fx and client
npm run typecheck
cd contracts && cargo test # 11 contract tests against a mock Soroswap router
The contract tests cover the cases that matter: exact delivery with a refund of the unused input,
quote agreeing with what pay actually spends, rejection when the route needs more than max_send,
routing through the hub asset when there is no direct pool, picking the cheaper of two routes, and
refusal without the payer's authorization. Two cover a router that answers with no amounts at all: the
hub route still wins if it exists, and otherwise the payer gets NoRoute rather than a trap. The last one is
a property test: 200 deterministic combinations of pools, amounts and limits, checking every time that the
seller gets exactly dest_amount, the payer spends exactly the cheaper route, FxPay keeps nothing, and a
max_send below the route moves nothing at all.
Nine TypeScript tests drive the facilitator's verification offline, over every rejection it reaches before
it touches the network: a payload for another version, scheme or network, a transaction it cannot read, a
call that is not FxPay's pay, a source or payer that is the facilitator itself, a send asset it does not
accept, an asset, amount or recipient that is not what the seller asked for, and a deadline that has
expired or reaches too far.
Four further tests build a real exact-fx payload against testnet and run it through the facilitator's
verification, including the rejections. They only run when FX_PAYER_SECRET is set, so a clean
checkout needs no funded account to go green.
The contract on mainnet is not a local build someone uploaded. Its wasm is byte for byte a GitHub release artifact, compiled in CI by stellar-expert's reusable workflow. Check it without trusting this README:
curl -fsSL -O https://github.com/DiegoPoveda01/local402/releases/download/v0.1.1_contracts_fx-pay_cli27.0.0/fx-pay_v0.1.1.wasm
sha256sum fx-pay_v0.1.1.wasm
curl -s https://api.stellar.expert/explorer/public/contract/CA6Z4E55YN6LZEXBQPFCWUIMUJGAV42RLHYWWZ6SNIP4E73R2I2PUGHD | jq -r .wasm
Both print 69a12d89059be04ac195f5acf40982d7e9ee93fd08f4df33626789d00ffccbd2. The build is reproducible in
the strict sense: two runs, from different commits and with the package version changed between them,
produced identical artifacts. Each carries a SLSA provenance attestation
signed through Sigstore and logged in Rekor. scripts/mainnet/deploy-fxpay.sh deploys the downloaded
release asset and aborts if its hash is not the one above, so a local build cannot reach mainnet by accident.
stellar.expert lists FxPay as verified, matched to commit 268f32c (tag v0.1.1) of this
repository. It did not for several weeks: its intake was dropping valid submissions until the bug was
found and fixed on their side (soroban-build-workflow#9).
Stellar Lab reads the SEP-55 attestation directly, and its contract explorer shows FxPay as
Verified Build, linked to this repository.
oracle to
Local402Client for an independent reading.exact-fx is not part of x402 yet. It is a draft. A stock x402 client pays the exact option of the
same 402, not the FX one.| Party | Relies on | Protected by |
|---|---|---|
| Seller | The oracle to price fairly, and the facilitator to submit. | The signed authorization fixes the amount, asset and recipient, and FxPay reverts unless it delivers that exact amount. |
| Payer | The seller's quote, within 2% of its own oracle, and an FxPay address it pinned itself. | It signs one bounded transfer (max_send) with a deadline, and the unused part comes back. |
| Facilitator | Nothing the payer says. | Eleven verification rules on the simulation, a refusal to act as the payer, and on mainnet an allowlisted seller and a minimum amount. |
Running on Stellar mainnet with real payments: XLM and EURC through the FxPay deployment linked
above, and USDC directly through x402 exact. Every sale leaves a receipt recording the local price, the rate and its source, the swap
premium in basis points, and the transaction hash. GET /receipts.csv hands it to accounting.
Next, in October 2026: an integration guide for sellers. Pau Koh, from the Stellar Chile community and not part of the project, will follow it from scratch and report whatever is unclear before it is final.
| When | What | Evidence that it is done |
|---|---|---|
| October 2026 sprint | 1.0 packages, a facilitator anyone can deploy, the integration guide, wider tests, and kuyfi on 1.0 | The acceptance criteria in docs/sprint-plan.md |
| When kuyfi is ready | kuyfi live on mainnet. The sprint already adds its payee to the allowlist, so this is kuyfi's call (kuyfi#2) | Mainnet tx hashes for a paid kuyfi scan |
| Next: SCF Build | An external audit of FxPay, which the Instaward does not fund. The sprint leaves the code audit-ready: security-review.md for the contract, threat-model.md for the whole payment path | A published audit report, with every finding fixed or answered |
| After the audit | Open the mainnet facilitator to any seller: drop the payee allowlist and revisit the minimum amount | A mainnet payment settled for a seller nobody added by hand |
| By the end of the SCF Build | 5 external sellers settling on mainnet, kuyfi first | Counted from FxPay's FxPaid events on mainnet |
| Ongoing | exact-fx proposed upstream to x402 | The open proposal; when it merges is up to x402 |
exact-fx is a draft scheme. It is written in the x402 spec format so it can be discussed as a
proposal, and it could equally be expressed as an assetTransferMethod of exact; what does not
change either way is the seller-facing guarantee and the facilitator's verification rules.
MIT — see LICENSE.