@stableyard/mppx-stableyard
v0.1.0
Published
Stableyard payment method for the Machine Payments Protocol (MPP). Any chain in, any chain out, fiat settlement.
Downloads
98
Maintainers
Readme
mppx-stableyard
A new payment method for MPP that lets any agent pay from any chain, and any merchant receive on any chain — powered by Stableyard.
tempo() → Tempo chain only
stripe() → cards only
stableyard() → Base, Polygon, Arbitrum, Ethereum, Solana (+ Tempo, Movement soon) + fiat to bankThe Problem
MPP launched with two payment methods:
tempo()— works only on Tempo chainstripe()— works only with cards
If an agent has USDC on Base and the server only accepts tempo(), the agent can't pay. Wrong chain. Stuck.
The Solution
stableyard() — a new payment method that plugs into mppx:
import { stableyard } from 'mppx-stableyard/server'
const mppx = Mppx.create({
methods: [
tempo.charge({ ... }), // Tempo chain
stableyard({ // ANY chain
apiKey: 'sy_secret_...',
destination: 'merchant@stableyard',
}),
],
})One line added. The server now accepts payments from Base, Arbitrum, Polygon, Ethereum, and Solana — with Tempo and Movement coming soon.
Supported Chains
| Chain | Chain ID | sourceChain value | Provider | Settlement Time | Status |
|-------|----------|-------------------|----------|-----------------|--------|
| Base | 8453 | base | direct_transfer | ~1s | Live |
| Polygon | 137 | polygon | gasyard | ~15s | Live |
| Arbitrum | 42161 | arbitrum | gasyard | ~15s | Live |
| Ethereum | 1 | ethereum | gasyard | ~15s | Live |
| Solana | 103 | solana | near_intents | ~22s | Live |
| Tempo | 4217 | tempo | relay | ~15s | Coming soon |
| Movement | 2 | movement | gasyard_v2 | ~3s | Coming soon |
Agent specifies the chain when creating the client:
stableyard({ apiKey: '...', chain: 'base' }) // pay from Base
stableyard({ apiKey: '...', chain: 'polygon' }) // pay from Polygon
stableyard({ apiKey: '...', chain: 'solana' }) // pay from Solana
stableyard({ apiKey: '...', chain: 'tempo' }) // pay from Tempo (coming soon)
stableyard({ apiKey: '...', chain: 'movement' }) // pay from Movement (coming soon)Merchant receives on their preferred chain regardless of where the agent paid from. Configure once in the Stableyard dashboard.
How It Works
Agent calls API
↓
402 Payment Required
WWW-Authenticate: Payment method="stableyard"
{ amount: "100000", currency: "USDC", destination: "merchant@stableyard" }
↓
Agent (mppx-stableyard client):
1. POST /v2/sessions { amount, destination, sourceChain: "base" }
→ Gets deposit address on Base (inline, one API call)
2. Sends 0.10 USDC to deposit address
3. POST /submit-tx { txHash }
→ Stableyard detects payment
4. Polls → settled
↓
Agent retries with credential { sessionId }
↓
Server calls POST /v2/sessions/:id/verify
→ { verified: true }
↓
200 OK + data + Payment-ReceiptCross-Chain Example (Polygon → Base)
Agent has USDC on Polygon. Merchant wants settlement on Base.
Agent: POST /v2/sessions { destination: "merchant@stableyard", sourceChain: "polygon" }
→ Returns gasyard gateway calldata (approve + deposit txs)
→ Agent executes gateway transactions on Polygon
→ Stableyard routes Polygon → Base via solver network
→ Merchant receives USDC on Base in ~15 secondsSame-Chain Example (Base → Base)
Agent: POST /v2/sessions { destination: "merchant@stableyard", sourceChain: "base" }
→ Returns direct_transfer deposit address
→ Agent sends USDC to address on Base
→ Settled in ~1 secondProved with Real Money
Session: ses_b6afc57b153e2ae1f8fb1025
Tx: 0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9
Chain: Base (mainnet)
Amount: 0.10 USDC
Submit tx: → status: pending
Poll: → settled in 9 seconds
Verify: → { verified: true }
Basescan: https://basescan.org/tx/0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9Install
npm i mppx-stableyard mppxServer
Accept MPP payments via Stableyard. Settle to any chain or fiat.
import { Mppx, tempo } from 'mppx/server'
import { stableyard } from 'mppx-stableyard/server'
const mppx = Mppx.create({
methods: [
tempo.charge({ currency: USDC, recipient: '0x...' }),
stableyard({
apiKey: 'sy_secret_...',
destination: 'merchant@stableyard',
}),
],
})
app.get('/api/data', async (req) => {
const result = await mppx['stableyard/charge']({
amount: '100000',
description: 'Premium data',
})(req)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
})The 402 response includes a WWW-Authenticate: Payment method="stableyard" challenge. The server verifies payment via Stableyard's /v2/sessions/:id/verify endpoint.
Client
Agent automatically handles 402 → pay via Stableyard → retry.
import { Mppx } from 'mppx/client'
import { stableyard } from 'mppx-stableyard/client'
Mppx.create({
methods: [
stableyard({
apiKey: 'sy_secret_...',
chain: 'base', // or 'polygon', 'arbitrum', 'ethereum', 'solana'
sendPayment: async (deposit) => {
// Send USDC to deposit address
return await sendERC20(deposit.address, deposit.amount.raw)
},
}),
],
})
// fetch auto-handles 402 → pay via Stableyard → retry
const res = await fetch('https://api.example.com/data')What stableyard() Adds to MPP
| Feature | tempo() | stableyard() | |---------|---------|-------------| | Tempo chain | Yes | Coming soon | | Base | | Yes | | Polygon | | Yes | | Arbitrum | | Yes | | Ethereum | | Yes | | Solana | | Yes | | Movement | | Coming soon | | Fiat settlement (bank) | | Yes | | Named identity (@stableyard) | | Yes | | Gasless payments (vault) | | Yes | | Merchant dashboard | | Yes | | Cross-chain routing | | Yes |
Architecture
MPP Protocol
|
+-------------+-------------+
| | |
tempo() stripe() stableyard()
Tempo chain Cards Any chain -> Any chain
Powered by Stableyard
|
|-- Base (direct_transfer, ~1s)
|-- Polygon (gasyard, ~15s)
|-- Arbitrum (gasyard, ~15s)
|-- Ethereum (gasyard, ~15s)
|-- Solana (near_intents, ~22s)
|-- Tempo (relay, coming soon)
|-- Movement (gasyard_v2, coming soon)
|
|-- Gasless vault payments (EIP-712)
|-- Fiat settlement (KYC)
+-- x402+ backward compatibleStableyard sits alongside Tempo and Stripe as a payment rail in MPP. Not competing — extending.
What's Inside
mppx-stableyard/
src/method.ts — Method.from() definition (stableyard/charge)
src/server.ts — Method.toServer() — generates 402 challenges, verifies via Stableyard API
src/client.ts — Method.toClient() — creates session, gets deposit addr, pays, polls
src/stableyard-api.ts — Stableyard API client (sessions, quote, verify, submit-tx, poll)
specs/ — draft-stableyard-charge-00.md (IETF-style protocol spec)
demo/server.ts — Working MPP server with tempo() + stableyard() methods
demo/agent.ts — Agent that auto-pays via StableyardConfiguration
Server
stableyard({
apiKey: 'sy_secret_...', // Stableyard API key
destination: 'merchant@stableyard', // Where payments settle
currency: 'USDC', // Default: 'USDC'
decimals: 6, // Default: 6
verifyTimeoutMs: 30000, // Default: 30s
})Client
stableyard({
apiKey: 'sy_secret_...', // Stableyard API key
chain: 'base', // Agent's payment chain
settlementTimeoutMs: 60000, // Default: 60s
sendPayment: async (deposit) => { // Your wallet logic
return txHash
},
})Try It
git clone https://github.com/stableyardfi/mppx-stableyard
cd mppx-stableyard
npm install
# Start server
cp demo/.env.example .env
npx tsx demo/server.ts
# Test 402 response
curl -D- http://localhost:3000/api/market-data
# See the MPP challenge with method="stableyard"Also Built
- Protocol spec:
draft-stableyard-charge-00.md— IETF-style spec following Lightning/Tempo format - Service registry PR: Adding Stableyard to MPP service directory alongside Alchemy, OpenAI, fal.ai
- x402+ compatibility: Same Stableyard backend powers both x402+ and mppx-stableyard
Links
- Stableyard — Cross-chain stablecoin payments
- MPP — Machine Payments Protocol
- mppx — TypeScript SDK for MPP
- x402+ — Stableyard's x402 protocol
- Stableyard API — OpenAPI spec
- Basescan proof — Real money settlement tx
License
MIT
