@stableyard/mppx-stableyard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,301 @@
1
+ # mppx-stableyard
2
+
3
+ A new payment method for [MPP](https://mpp.dev) that lets any agent pay from any chain, and any merchant receive on any chain — powered by [Stableyard](https://stableyard.fi).
4
+
5
+ ```
6
+ tempo() → Tempo chain only
7
+ stripe() → cards only
8
+ stableyard() → Base, Polygon, Arbitrum, Ethereum, Solana (+ Tempo, Movement soon) + fiat to bank
9
+ ```
10
+
11
+ ## The Problem
12
+
13
+ MPP launched with two payment methods:
14
+ - `tempo()` — works only on Tempo chain
15
+ - `stripe()` — works only with cards
16
+
17
+ If an agent has USDC on Base and the server only accepts `tempo()`, the agent can't pay. Wrong chain. Stuck.
18
+
19
+ ## The Solution
20
+
21
+ `stableyard()` — a new payment method that plugs into mppx:
22
+
23
+ ```typescript
24
+ import { stableyard } from 'mppx-stableyard/server'
25
+
26
+ const mppx = Mppx.create({
27
+ methods: [
28
+ tempo.charge({ ... }), // Tempo chain
29
+ stableyard({ // ANY chain
30
+ apiKey: 'sy_secret_...',
31
+ destination: 'merchant@stableyard',
32
+ }),
33
+ ],
34
+ })
35
+ ```
36
+
37
+ One line added. The server now accepts payments from Base, Arbitrum, Polygon, Ethereum, and Solana — with Tempo and Movement coming soon.
38
+
39
+ ## Supported Chains
40
+
41
+ | Chain | Chain ID | sourceChain value | Provider | Settlement Time | Status |
42
+ |-------|----------|-------------------|----------|-----------------|--------|
43
+ | Base | 8453 | `base` | direct_transfer | ~1s | Live |
44
+ | Polygon | 137 | `polygon` | gasyard | ~15s | Live |
45
+ | Arbitrum | 42161 | `arbitrum` | gasyard | ~15s | Live |
46
+ | Ethereum | 1 | `ethereum` | gasyard | ~15s | Live |
47
+ | Solana | 103 | `solana` | near_intents | ~22s | Live |
48
+ | Tempo | 4217 | `tempo` | relay | ~15s | Coming soon |
49
+ | Movement | 2 | `movement` | gasyard_v2 | ~3s | Coming soon |
50
+
51
+ Agent specifies the chain when creating the client:
52
+
53
+ ```typescript
54
+ stableyard({ apiKey: '...', chain: 'base' }) // pay from Base
55
+ stableyard({ apiKey: '...', chain: 'polygon' }) // pay from Polygon
56
+ stableyard({ apiKey: '...', chain: 'solana' }) // pay from Solana
57
+ stableyard({ apiKey: '...', chain: 'tempo' }) // pay from Tempo (coming soon)
58
+ stableyard({ apiKey: '...', chain: 'movement' }) // pay from Movement (coming soon)
59
+ ```
60
+
61
+ Merchant receives on their preferred chain regardless of where the agent paid from. Configure once in the Stableyard dashboard.
62
+
63
+ ## How It Works
64
+
65
+ ```
66
+ Agent calls API
67
+
68
+ 402 Payment Required
69
+ WWW-Authenticate: Payment method="stableyard"
70
+ { amount: "100000", currency: "USDC", destination: "merchant@stableyard" }
71
+
72
+ Agent (mppx-stableyard client):
73
+ 1. POST /v2/sessions { amount, destination, sourceChain: "base" }
74
+ → Gets deposit address on Base (inline, one API call)
75
+ 2. Sends 0.10 USDC to deposit address
76
+ 3. POST /submit-tx { txHash }
77
+ → Stableyard detects payment
78
+ 4. Polls → settled
79
+
80
+ Agent retries with credential { sessionId }
81
+
82
+ Server calls POST /v2/sessions/:id/verify
83
+ → { verified: true }
84
+
85
+ 200 OK + data + Payment-Receipt
86
+ ```
87
+
88
+ ### Cross-Chain Example (Polygon → Base)
89
+
90
+ Agent has USDC on Polygon. Merchant wants settlement on Base.
91
+
92
+ ```
93
+ Agent: POST /v2/sessions { destination: "merchant@stableyard", sourceChain: "polygon" }
94
+ → Returns gasyard gateway calldata (approve + deposit txs)
95
+ → Agent executes gateway transactions on Polygon
96
+ → Stableyard routes Polygon → Base via solver network
97
+ → Merchant receives USDC on Base in ~15 seconds
98
+ ```
99
+
100
+ ### Same-Chain Example (Base → Base)
101
+
102
+ ```
103
+ Agent: POST /v2/sessions { destination: "merchant@stableyard", sourceChain: "base" }
104
+ → Returns direct_transfer deposit address
105
+ → Agent sends USDC to address on Base
106
+ → Settled in ~1 second
107
+ ```
108
+
109
+ ## Proved with Real Money
110
+
111
+ ```
112
+ Session: ses_b6afc57b153e2ae1f8fb1025
113
+ Tx: 0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9
114
+ Chain: Base (mainnet)
115
+ Amount: 0.10 USDC
116
+ Submit tx: → status: pending
117
+ Poll: → settled in 9 seconds
118
+ Verify: → { verified: true }
119
+ Basescan: https://basescan.org/tx/0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9
120
+ ```
121
+
122
+ ## Install
123
+
124
+ ```bash
125
+ npm i mppx-stableyard mppx
126
+ ```
127
+
128
+ ## Server
129
+
130
+ Accept MPP payments via Stableyard. Settle to any chain or fiat.
131
+
132
+ ```typescript
133
+ import { Mppx, tempo } from 'mppx/server'
134
+ import { stableyard } from 'mppx-stableyard/server'
135
+
136
+ const mppx = Mppx.create({
137
+ methods: [
138
+ tempo.charge({ currency: USDC, recipient: '0x...' }),
139
+ stableyard({
140
+ apiKey: 'sy_secret_...',
141
+ destination: 'merchant@stableyard',
142
+ }),
143
+ ],
144
+ })
145
+
146
+ app.get('/api/data', async (req) => {
147
+ const result = await mppx['stableyard/charge']({
148
+ amount: '100000',
149
+ description: 'Premium data',
150
+ })(req)
151
+
152
+ if (result.status === 402) return result.challenge
153
+ return result.withReceipt(Response.json({ data: '...' }))
154
+ })
155
+ ```
156
+
157
+ The 402 response includes a `WWW-Authenticate: Payment method="stableyard"` challenge. The server verifies payment via Stableyard's `/v2/sessions/:id/verify` endpoint.
158
+
159
+ ## Client
160
+
161
+ Agent automatically handles 402 → pay via Stableyard → retry.
162
+
163
+ ```typescript
164
+ import { Mppx } from 'mppx/client'
165
+ import { stableyard } from 'mppx-stableyard/client'
166
+
167
+ Mppx.create({
168
+ methods: [
169
+ stableyard({
170
+ apiKey: 'sy_secret_...',
171
+ chain: 'base', // or 'polygon', 'arbitrum', 'ethereum', 'solana'
172
+ sendPayment: async (deposit) => {
173
+ // Send USDC to deposit address
174
+ return await sendERC20(deposit.address, deposit.amount.raw)
175
+ },
176
+ }),
177
+ ],
178
+ })
179
+
180
+ // fetch auto-handles 402 → pay via Stableyard → retry
181
+ const res = await fetch('https://api.example.com/data')
182
+ ```
183
+
184
+ ## What stableyard() Adds to MPP
185
+
186
+ | Feature | tempo() | stableyard() |
187
+ |---------|---------|-------------|
188
+ | Tempo chain | Yes | Coming soon |
189
+ | Base | | Yes |
190
+ | Polygon | | Yes |
191
+ | Arbitrum | | Yes |
192
+ | Ethereum | | Yes |
193
+ | Solana | | Yes |
194
+ | Movement | | Coming soon |
195
+ | Fiat settlement (bank) | | Yes |
196
+ | Named identity (@stableyard) | | Yes |
197
+ | Gasless payments (vault) | | Yes |
198
+ | Merchant dashboard | | Yes |
199
+ | Cross-chain routing | | Yes |
200
+
201
+ ## Architecture
202
+
203
+ ```
204
+ MPP Protocol
205
+ |
206
+ +-------------+-------------+
207
+ | | |
208
+ tempo() stripe() stableyard()
209
+ Tempo chain Cards Any chain -> Any chain
210
+ Powered by Stableyard
211
+ |
212
+ |-- Base (direct_transfer, ~1s)
213
+ |-- Polygon (gasyard, ~15s)
214
+ |-- Arbitrum (gasyard, ~15s)
215
+ |-- Ethereum (gasyard, ~15s)
216
+ |-- Solana (near_intents, ~22s)
217
+ |-- Tempo (relay, coming soon)
218
+ |-- Movement (gasyard_v2, coming soon)
219
+ |
220
+ |-- Gasless vault payments (EIP-712)
221
+ |-- Fiat settlement (KYC)
222
+ +-- x402+ backward compatible
223
+ ```
224
+
225
+ Stableyard sits alongside Tempo and Stripe as a payment rail in MPP. Not competing — extending.
226
+
227
+ ## What's Inside
228
+
229
+ ```
230
+ mppx-stableyard/
231
+ src/method.ts — Method.from() definition (stableyard/charge)
232
+ src/server.ts — Method.toServer() — generates 402 challenges, verifies via Stableyard API
233
+ src/client.ts — Method.toClient() — creates session, gets deposit addr, pays, polls
234
+ src/stableyard-api.ts — Stableyard API client (sessions, quote, verify, submit-tx, poll)
235
+ specs/ — draft-stableyard-charge-00.md (IETF-style protocol spec)
236
+ demo/server.ts — Working MPP server with tempo() + stableyard() methods
237
+ demo/agent.ts — Agent that auto-pays via Stableyard
238
+ ```
239
+
240
+ ## Configuration
241
+
242
+ ### Server
243
+
244
+ ```typescript
245
+ stableyard({
246
+ apiKey: 'sy_secret_...', // Stableyard API key
247
+ destination: 'merchant@stableyard', // Where payments settle
248
+ currency: 'USDC', // Default: 'USDC'
249
+ decimals: 6, // Default: 6
250
+ verifyTimeoutMs: 30000, // Default: 30s
251
+ })
252
+ ```
253
+
254
+ ### Client
255
+
256
+ ```typescript
257
+ stableyard({
258
+ apiKey: 'sy_secret_...', // Stableyard API key
259
+ chain: 'base', // Agent's payment chain
260
+ settlementTimeoutMs: 60000, // Default: 60s
261
+ sendPayment: async (deposit) => { // Your wallet logic
262
+ return txHash
263
+ },
264
+ })
265
+ ```
266
+
267
+ ## Try It
268
+
269
+ ```bash
270
+ git clone https://github.com/stableyardfi/mppx-stableyard
271
+ cd mppx-stableyard
272
+ npm install
273
+
274
+ # Start server
275
+ cp demo/.env.example .env
276
+ npx tsx demo/server.ts
277
+
278
+ # Test 402 response
279
+ curl -D- http://localhost:3000/api/market-data
280
+
281
+ # See the MPP challenge with method="stableyard"
282
+ ```
283
+
284
+ ## Also Built
285
+
286
+ - **Protocol spec**: `draft-stableyard-charge-00.md` — IETF-style spec following Lightning/Tempo format
287
+ - **Service registry PR**: Adding Stableyard to [MPP service directory](https://github.com/tempoxyz/mpp) alongside Alchemy, OpenAI, fal.ai
288
+ - **x402+ compatibility**: Same Stableyard backend powers both [x402+](https://github.com/stableyardfi/x402plus) and mppx-stableyard
289
+
290
+ ## Links
291
+
292
+ - [Stableyard](https://stableyard.fi) — Cross-chain stablecoin payments
293
+ - [MPP](https://mpp.dev) — Machine Payments Protocol
294
+ - [mppx](https://github.com/wevm/mppx) — TypeScript SDK for MPP
295
+ - [x402+](https://github.com/stableyardfi/x402plus) — Stableyard's x402 protocol
296
+ - [Stableyard API](https://api.stableyard.fi/sdk.json) — OpenAPI spec
297
+ - [Basescan proof](https://basescan.org/tx/0xbeaf32d41f8f7573e02653a79e02bf56a73ae667dca203acb9e5c181116914a9) — Real money settlement tx
298
+
299
+ ## License
300
+
301
+ MIT
@@ -0,0 +1,11 @@
1
+ # Stableyard API credentials
2
+ STABLEYARD_API_KEY=sy_test_secret_9b64d9a0e65a9acd99081a6e2360410db53a52e778ac4b51
3
+ STABLEYARD_DESTINATION=test@stableyard
4
+
5
+ # MPP server config
6
+ MPP_SECRET_KEY=somerandomstring
7
+ TEMPO_RECIPIENT=0xB2903d2f1fbe8B4475AfED1DA9a4DE1bA547Bb65
8
+
9
+ # Agent config
10
+ CHAIN=base
11
+ SERVER_URL=http://localhost:3000
package/demo/agent.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Demo MPP agent that pays via Stableyard.
3
+ *
4
+ * This agent calls the demo server's paid endpoints and automatically
5
+ * handles 402 → pay via Stableyard → retry.
6
+ *
7
+ * Usage:
8
+ * STABLEYARD_API_KEY=sy_secret_... tsx demo/agent.ts
9
+ */
10
+
11
+ import 'dotenv/config'
12
+ import { Mppx } from 'mppx/client'
13
+ import { stableyard } from '../src/client.ts'
14
+
15
+ const stableyardApiKey = process.env.STABLEYARD_API_KEY
16
+
17
+ if (!stableyardApiKey) {
18
+ console.error('Missing STABLEYARD_API_KEY env var')
19
+ process.exit(1)
20
+ }
21
+
22
+ const SERVER_URL = process.env.SERVER_URL ?? 'http://localhost:3000'
23
+
24
+ // ─── Setup MPP client with Stableyard method ────────────────────────────────
25
+
26
+ const mppx = Mppx.create({
27
+ methods: [
28
+ stableyard({
29
+ apiKey: stableyardApiKey,
30
+ chain: process.env.CHAIN ?? 'base',
31
+ }),
32
+ ],
33
+ polyfill: false,
34
+ })
35
+
36
+ // ─── Agent logic ────────────────────────────────────────────────────────────
37
+
38
+ async function callEndpoint(path: string, description: string) {
39
+ console.log(`\n→ Calling ${path} (${description})...`)
40
+
41
+ try {
42
+ const res = await mppx.fetch(`${SERVER_URL}${path}`)
43
+
44
+ if (res.ok) {
45
+ const data = await res.json()
46
+ console.log(`← 200 OK ✓`)
47
+ console.log(` Data:`, JSON.stringify(data, null, 2).split('\n').map(l => ` ${l}`).join('\n'))
48
+ } else {
49
+ console.log(`← ${res.status} ${res.statusText}`)
50
+ }
51
+ } catch (err) {
52
+ console.error(`← Error: ${err instanceof Error ? err.message : err}`)
53
+ }
54
+ }
55
+
56
+ async function main() {
57
+ console.log(`
58
+ ╔══════════════════════════════════════════════════════════════╗
59
+ ║ mppx-stableyard Demo Agent ║
60
+ ╠══════════════════════════════════════════════════════════════╣
61
+ ║ Server: ${SERVER_URL.padEnd(50)}║
62
+ ║ Chain: ${(process.env.CHAIN ?? 'base').padEnd(51)}║
63
+ ║ Method: stableyard (cross-chain via Stableyard) ║
64
+ ╚══════════════════════════════════════════════════════════════╝
65
+ `)
66
+
67
+ // Call each paid endpoint
68
+ await callEndpoint('/api/market-data', 'Market data — $0.01')
69
+ await callEndpoint('/api/analysis', 'AI analysis — $0.05')
70
+ await callEndpoint('/api/premium-image', 'Premium image — $0.10')
71
+
72
+ console.log('\n✓ All endpoints called. Check Stableyard dashboard for settlement details.')
73
+ }
74
+
75
+ main().catch(console.error)
package/demo/server.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Demo MPP server using both tempo() and stableyard() payment methods.
3
+ *
4
+ * Shows how adding stableyard() gives merchants cross-chain settlement
5
+ * with zero code changes to the agent side.
6
+ *
7
+ * Usage:
8
+ * STABLEYARD_API_KEY=sy_secret_... STABLEYARD_DESTINATION=merchant@stableyard tsx demo/server.ts
9
+ */
10
+
11
+ import 'dotenv/config'
12
+ import { Hono } from 'hono'
13
+ import { serve } from '@hono/node-server'
14
+ import { Mppx, tempo } from 'mppx/server'
15
+ import { stableyard } from '../src/server.ts'
16
+
17
+ const TEMPO_CURRENCY = '0x20c0000000000000000000000000000000000000' // pathUSD
18
+ const TEMPO_RECIPIENT = process.env.TEMPO_RECIPIENT ?? '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
19
+
20
+ const stableyardApiKey = process.env.STABLEYARD_API_KEY
21
+ const stableyardDestination = process.env.STABLEYARD_DESTINATION
22
+
23
+ if (!stableyardApiKey || !stableyardDestination) {
24
+ console.error('Missing STABLEYARD_API_KEY or STABLEYARD_DESTINATION env vars')
25
+ process.exit(1)
26
+ }
27
+
28
+ // ─── Create MPP handler with BOTH methods ───────────────────────────────────
29
+
30
+ const mppx = Mppx.create({
31
+ methods: [
32
+ tempo.charge({
33
+ currency: TEMPO_CURRENCY,
34
+ recipient: TEMPO_RECIPIENT,
35
+ }),
36
+ stableyard({
37
+ apiKey: stableyardApiKey,
38
+ destination: stableyardDestination,
39
+ }),
40
+ ],
41
+ secretKey: process.env.MPP_SECRET_KEY ?? 'demo-secret-key',
42
+ })
43
+
44
+ // ─── App ────────────────────────────────────────────────────────────────────
45
+
46
+ const app = new Hono()
47
+
48
+ // Health check
49
+ app.get('/health', (c) => c.json({ ok: true, methods: ['tempo', 'stableyard'] }))
50
+
51
+ // ─── Paid endpoint: Market Data ($0.01) ─────────────────────────────────────
52
+
53
+ app.get('/api/market-data', async (c) => {
54
+ // Use stableyard method only for clean demo
55
+ const result = await mppx['stableyard/charge']({
56
+ amount: '100000',
57
+ description: 'Market data feed',
58
+ })(c.req.raw)
59
+
60
+ if (result.status === 402) return result.challenge
61
+
62
+ return result.withReceipt(
63
+ Response.json({
64
+ data: {
65
+ btc: { price: 98450.23, change24h: 2.1 },
66
+ eth: { price: 3842.67, change24h: -0.5 },
67
+ sol: { price: 187.42, change24h: 4.3 },
68
+ },
69
+ timestamp: new Date().toISOString(),
70
+ source: 'stableyard-demo',
71
+ }),
72
+ )
73
+ })
74
+
75
+ // ─── Paid endpoint: AI Analysis ($0.05) ─────────────────────────────────────
76
+
77
+ app.get('/api/analysis', async (c) => {
78
+ const result = await mppx['stableyard/charge']({
79
+ amount: '500000',
80
+ description: 'AI-powered market analysis',
81
+ })(c.req.raw)
82
+
83
+ if (result.status === 402) return result.challenge
84
+
85
+ return result.withReceipt(
86
+ Response.json({
87
+ analysis: {
88
+ summary: 'BTC showing bullish momentum with strong support at $96k. ETH consolidating ahead of upcoming network upgrade.',
89
+ sentiment: 'bullish',
90
+ confidence: 0.78,
91
+ signals: ['RSI oversold bounce', 'Volume spike on support', 'Whale accumulation detected'],
92
+ },
93
+ timestamp: new Date().toISOString(),
94
+ }),
95
+ )
96
+ })
97
+
98
+ // ─── Paid endpoint: Premium Image ($0.10) ───────────────────────────────────
99
+
100
+ app.get('/api/premium-image', async (c) => {
101
+ const result = await mppx['stableyard/charge']({
102
+ amount: '1000000',
103
+ description: 'Premium stock photo',
104
+ })(c.req.raw)
105
+
106
+ if (result.status === 402) return result.challenge
107
+
108
+ const imageRes = await fetch('https://picsum.photos/1024/1024')
109
+ return result.withReceipt(
110
+ Response.json({
111
+ imageUrl: imageRes.url,
112
+ resolution: '1024x1024',
113
+ license: 'commercial',
114
+ }),
115
+ )
116
+ })
117
+
118
+ // ─── Multi-method endpoint: shows both tempo + stableyard ───────────────────
119
+
120
+ app.get('/api/multi', async (c) => {
121
+ const result = await Mppx.compose(
122
+ mppx['tempo/charge']({ amount: '10000' }),
123
+ mppx['stableyard/charge']({ amount: '10000', description: 'Multi-method demo' }),
124
+ )(c.req.raw)
125
+
126
+ if (result.status === 402) return result.challenge
127
+
128
+ return result.withReceipt(
129
+ Response.json({ message: 'Paid via tempo OR stableyard!', timestamp: new Date().toISOString() }),
130
+ )
131
+ })
132
+
133
+ // ─── Start ──────────────────────────────────────────────────────────────────
134
+
135
+ const port = Number(process.env.PORT ?? 3000)
136
+
137
+ serve({ fetch: app.fetch, port }, () => {
138
+ console.log(`
139
+ ╔══════════════════════════════════════════════════════════════╗
140
+ ║ mppx-stableyard Demo Server ║
141
+ ╠══════════════════════════════════════════════════════════════╣
142
+ ║ ║
143
+ ║ URL: http://localhost:${String(port).padEnd(43)}║
144
+ ║ Destination: ${(stableyardDestination ?? '').padEnd(43)}║
145
+ ║ ║
146
+ ║ Payment Methods: ║
147
+ ║ • tempo → Tempo chain only ║
148
+ ║ • stableyard → 7 chains + Solana + Movement + fiat ║
149
+ ║ ║
150
+ ║ Endpoints: ║
151
+ ║ GET /api/market-data $0.10 (stableyard only) ║
152
+ ║ GET /api/analysis $0.50 (stableyard only) ║
153
+ ║ GET /api/premium-image $1.00 (stableyard only) ║
154
+ ║ GET /api/multi $0.10 (tempo + stableyard) ║
155
+ ║ ║
156
+ ╚══════════════════════════════════════════════════════════════╝
157
+ `)
158
+ })
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@stableyard/mppx-stableyard",
3
+ "version": "0.1.0",
4
+ "description": "Stableyard payment method for the Machine Payments Protocol (MPP). Any chain in, any chain out, fiat settlement.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ },
11
+ "./server": {
12
+ "types": "./dist/server.d.ts",
13
+ "default": "./dist/server.js"
14
+ },
15
+ "./client": {
16
+ "types": "./dist/client.d.ts",
17
+ "default": "./dist/client.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "demo:server": "tsx demo/server.ts",
23
+ "demo:agent": "tsx demo/agent.ts"
24
+ },
25
+ "keywords": [
26
+ "mpp",
27
+ "mppx",
28
+ "stableyard",
29
+ "machine-payments",
30
+ "cross-chain",
31
+ "stablecoin",
32
+ "402",
33
+ "payment-protocol",
34
+ "agent-payments"
35
+ ],
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "mppx": "^0.4.7",
39
+ "zod": "^4.3.6"
40
+ },
41
+ "devDependencies": {
42
+ "typescript": "^5.7.0",
43
+ "tsx": "^4.19.0",
44
+ "hono": "^4.7.0",
45
+ "@hono/node-server": "^1.13.0",
46
+ "dotenv": "^16.4.0"
47
+ }
48
+ }