@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 +301 -0
- package/demo/.env.example +11 -0
- package/demo/agent.ts +75 -0
- package/demo/server.ts +158 -0
- package/package.json +48 -0
- package/specs/draft-stableyard-charge-00.md +607 -0
- package/src/client.ts +143 -0
- package/src/index.ts +3 -0
- package/src/method.ts +37 -0
- package/src/server.ts +87 -0
- package/src/stableyard-api.ts +275 -0
- package/tsconfig.json +17 -0
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
|
+
}
|