The merchant names one price in their own currency. The payer brings whatever they hold. Both numbers are exact.
A programmable clearing layer for humans, applications and autonomous agents. Merchants price in their local currency and settle in a stablecoin; the payer brings any supported asset. Every settlement below is a transaction you can open on a block explorer.
A coffee roaster in Jakarta prices a bag at IDR 50,000 and wants to sell beyond Indonesia. A customer in Berlin wants to pay in EURC. An agent wants to pay for one API call and has never heard of an account.
To sell to either of them today, the merchant has to become an infrastructure team:
accept crypto → pick a chain, hold a wallet, fund it with gas
→ watch a rate that moves while the customer decides
→ swap, hope the pool is deep enough, eat the slippage
→ reconcile a balance nobody can explain
One clearing layer between the two. The merchant's number is locked first, and everything else is derived from it.
merchant prices IDR 50,000, in their own currency
payer holds ETH, EURC, USDC, or another configured asset
Mayarin locks the merchant's stablecoin settlement minimum
merchant receives exactly the configured settlement amount
The merchant never sees a chain, a gas token, or a slippage setting. An autonomous agent is a payer class, not a product line — it reaches the same clearing engine, ledger and merchant as a person at a checkout, and is given one thing to sign.
1 INTENT the merchant states a price and a settlement asset
2 LOCK the quote engine freezes the settlement minimum
3 PAY contract call, deposit transfer, or one signed x402 authorization
4 CONFIRM the receipt is read back off the chain and matched
5 RECORD clearing transition + balanced ledger postings, one DB transaction
↓
merchant paid ──► exactly the invoice, or the payment does not advance
A submitted transaction is never proof of payment, and a webhook only wakes the engine — the chain is the truth.
Ports and adapters: a domain package never imports a concrete adapter.
apps/api/src/container.ts is the only file that knows which implementations run.
flowchart TB
Payer["Human checkout · Application (SDK) · AI agent (x402)"]
subgraph Edge["apps/* — Hono + Zod"]
API["Payment Intents · Commerce · x402 · Webhooks"]
Worker["chain-worker — watcher, indexer, executor"]
end
subgraph Core["packages/core/* — pure domain, zero I/O"]
Intent["payment-intent"] --> Quote["quote + liquidity"] --> Clearing["clearing"] --> Ledger["ledger"]
Chain["chain"] -- recordAssetReceived --> Clearing
end
subgraph Adapters["packages/providers/* + packages/db"]
EVM["evm · viem"]
Swap["uniswap · 0x · lifi"]
Oracle["pyth · chainlink"]
DB["Drizzle + Postgres"]
end
Payer --> Edge --> Core
Core -. ports .-> Adapters
Three execution paths, chosen per payment:
| Path | The payer does | Advances on |
|---|---|---|
| Contract | Calls PaymentRouter with a signed order |
A PaymentCompleted log past confirmation depth |
| Deposit-match | Sends a transfer to a unique per-intent address | A confirmed deposit matched to the intent |
| x402 | Signs one EIP-3009 authorization | The receipt read back and matched |
Full detail: Architecture · Clearing Engine · Agent payments.
Any Mayarin-gated endpoint becomes payable per call by an agent that has never registered, holds no
API key, and never sees a checkout page. It gets a 402 with machine-readable terms, signs one
EIP-3009 authorization for an exact amount, and gets the
resource.
agent calls endpoint → 402 + terms
agent signs → one EIP-3009 authorization, exact amount
Mayarin settles → receipt read back and matched, ledger posted
agent gets → the resource
An agent that does not hold the merchant's asset still pays. The swap is priced backwards from the invoice — exact-output, so the merchant gets exactly the invoice or the swap reverts:
authorization payer → operator the agent's asset, exactly what it signed for
swap operator → pool exact-output, bounded by the authorization
pool → merchant exactly the invoice
surplus what the pool did not need, owed back to the payer
Current boundaries: exact scheme only, EVM only, and the broadcaster pays gas. Full detail in
docs/x402.md.
Testnet only. Mainnet is deliberately unprovisioned.
Arbitrum Sepolia and Robinhood Chain Testnet (an Arbitrum Orbit L2) share one set of addresses:
| Contract | Address |
|---|---|
PaymentRouter |
0xEe7c5B5a9eeAf667A6EFb217A8a77534C873f7a9 |
EscrowFactoryV2 |
0x309bDD9aF097BF0F3FaafF718c5C6c9A7f511B18 |
DepositForwarderFactory |
0x4b96e74b963efa7ab7d7497854fc87220bd092c4 |
PayoutBatcher |
0xfad1c3ec064ae52676ce874c2ba4d644ff2ad0b9 |
Other chains:
| Chain | PaymentRouter |
DepositForwarderFactory |
|---|---|---|
| Base Sepolia | 0xee7c5b5a… |
0x69c72C51… |
| Ethereum Sepolia | 0xE54E800b… |
0xCa83514c… |
| Arc Testnet | 0xee7c5b5a… |
0x04cd74e7… |
EscrowFactoryV2 and PayoutBatcher sit at the same addresses on these three chains too.
| Price in your own currency | IDR, USD, EUR and more — settle in a stablecoin |
| Payer brings any supported asset | exact-output swaps priced backwards from the invoice |
| Money is never a float | bigint minor units end to end, double-entry ledger |
| Agents pay per call | x402 — no account, no API key, one signature |
| Commerce out of the box | catalog, payment links, invoices, hosted checkout, dashboard, SDK |
| Buyer protection | on-chain escrow: bonded disputes, an arbiter, auto-release |
| Recurring and bulk | subscriptions, and batch payouts in one transaction |
| Anyone can check | every settlement is a transaction on a public block explorer |
Arbitrum Sepolia and Robinhood Chain Testnet run the full contract set above. Settlement is chosen per chain: USDC on Arbitrum, USDG on Robinhood Chain, PYUSD where the merchant settles in it.
Arc is the merchant's settlement rail. Its gas token and the merchant's settlement asset are the same thing, USDC, so a merchant never has to hold a second token to get paid.
Multi-step settlement with a condition. An agent holding EURC pays a merchant who is owed USDC,
and signs only once. Three things then happen on chain: the authorization is settled, an
exact-output swap runs, and the merchant is paid. If the pool cannot deliver the full invoice, the
swap reverts, so the merchant gets the exact amount or nothing moves. Authorization
0xf08c141d…,
swap
0x9f4bf25b….
Agent decisions tied to real signals. The demo agent (bun run demo:agent) prints each
decision it makes, and the reason for it:
| Decision | Signal |
|---|---|
| Which rail to pay | Requested chain, then the merchant's first compatible configured rail |
| Whether to sign | The quote is checked against an oracle and refused if it has drifted |
| How much to spend | A spend ceiling, checked before signing, so nothing over it is signed |
Circle products
| Used | How |
|---|---|
| Arc testnet | PaymentRouter, DepositForwarderFactory, x402 settlement |
| USDC, EURC | USDC is what merchants settle in; EURC is a payer asset for cross-asset payments |
| Agent Stack (Circle Wallets) | A contract-account wallet pays, and USDC verifies it through EIP-1271 |
Not used: App Kits, Nanopayments, Paymaster, CCTP, Gateway, StableFX. The payer pays no gas because Mayarin's operator broadcasts the signed authorization. That is not Paymaster.
Cross-asset x402: an agent holding EURC pays a merchant settled in USDC, through an exact-output swap
priced backwards from the invoice — authorization
0x254b93ce…,
swap 0xb1436735….
Requires Bun 1.4+, Node 22.12+, and Docker.
git clone https://github.com/playriglabs/mayarin.git
cd mayarin
bun run setup # install, write .env, start Postgres, migrate
bun run dev:all # core API + chain worker + dashboard + checkoutimport { createMayarin } from "@mayarin/sdk";
const mayarin = createMayarin({
baseUrl: "https://api-testnet.mayarin.xyz",
secretKey: process.env.MAYARIN_SECRET_KEY ?? "",
});
const link = await mayarin.commerce.paymentLinks.create(
{
kind: "fixed",
merchant: {
id: "merchant_123",
name: "Toko Melati",
city: "Jakarta",
countryCode: "ID",
},
amount: { amount: "50000.00", asset: "IDR" },
},
{ idempotencyKey: "order-4711" },
);Commands, conventions and tests: AGENT.md. Design record: docs/.
API reference: docs.mayarin.xyz.
mayarin.xyz — shipped and running on testnet: clearing core, double-entry ledger, contract and deposit execution, oracle-guarded quotes, commerce and hosted checkout, merchant dashboard, SDK, agent payments, buyer-protection escrow, subscriptions and batch payouts.
Not done, and not pretended otherwise:
- Fiat rails (QRIS, bank transfer) and the off-ramp are later phases.
- Mainnet is planned, not provisioned.
- Testnet pool depth is not market depth; the oracle deviation guard is widened on testnet.
Apache License 2.0 — copyright 2026 Playrig Labs. Solidity sources under
packages/contracts/payment-router stay MIT.