@altaga/x402-sui 1.0.1 → 1.0.2

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.
Files changed (3) hide show
  1. package/README.MD +69 -267
  2. package/dist/index.js +1 -0
  3. package/package.json +3 -2
package/README.MD CHANGED
@@ -2,76 +2,7 @@
2
2
 
3
3
  > Modular [x402](https://www.x402.org/) implementation for **Sui Sponsored Transactions** (gas-station style).
4
4
 
5
- This package is a small, pluggable reference SDK for the x402 "Payment Required" HTTP
6
- pattern, targeting the **Sui** blockchain. The design separates concerns into three
7
- roles — **Client**, **Resource Server**, and **Facilitator** — and exposes an
8
- HTTP middleware layer so any Express-based API can gate routes behind a 402
9
- payment challenge with a few lines of glue.
10
-
11
- The bundled `sui/exact` scheme uses Sui's *sponsored transaction* model: the
12
- buyer signs the payment intent, the facilitator co-signs as the **gas owner**, and
13
- the resource server never touches a private key.
14
-
15
- ---
16
-
17
- ## Table of Contents
18
-
19
- - [Architecture](#architecture)
20
- - [Installation](#installation)
21
- - [Project Layout](#project-layout)
22
- - [Protocol Flow](#protocol-flow)
23
- - [Quick Start](#quick-start)
24
- - [1. Resource Server (the paid API)](#1-resource-server-the-paid-api)
25
- - [2. Facilitator (the gas station)](#2-facilitator-the-gas-station)
26
- - [3. Client (the buyer)](#3-client-the-buyer)
27
- - [Module Reference](#module-reference)
28
- - [`core/client`](#coreclient)
29
- - [`core/http`](#corehttp)
30
- - [`core/server`](#coreserver)
31
- - [`core/server-http`](#coreserver-http)
32
- - [`core/facilitator`](#corefacilitator)
33
- - [`sui/exact/client`](#suiexactclient)
34
- - [`sui/exact/server`](#suiexactserver)
35
- - [`sui/exact/facilitator`](#suiexactfacilitator)
36
- - [Building](#building)
37
- - [Peer Dependencies](#peer-dependencies)
38
- - [License](#license)
39
-
40
- ---
41
-
42
- ## Architecture
43
-
44
- x402 is a request/response convention layered on top of HTTP 402 *Payment Required*:
45
-
46
- ```
47
- ┌─────────┐ ┌──────────────────┐ ┌──────────────┐
48
- │ Client │ ──GET──▶│ Resource Server │ │ Facilitator │
49
- │ │ │ (Express route) │ │ (gas owner) │
50
- │ │◀─ 402 ──│ │ │ │
51
- │ │ + x-accepts │ │ │
52
- │ │ + x-facilitator-url │ │ │
53
- │ │ │ │ │
54
- │ │── POST /sponsor ────────────────────▶ │
55
- │ │◀── sponsored tx bytes ───────────────── │
56
- │ │── GET + X-PAYMENT ──▶│ │ │
57
- │ │ │── /verify ──────▶│ │
58
- │ │ │── /settle ─────▶│ │
59
- │ │◀── 200 + x-payment-response ────────────│ │
60
- └─────────┘ └──────────────────┘ └──────────────┘
61
- ```
62
-
63
- The SDK mirrors that split into three independent core modules, each of which
64
- is scheme-agnostic. Scheme implementations (currently just `sui/exact`) are
65
- plugged in with a simple `register(schemeName:network, impl)` call — wildcards
66
- (`sui:*`, `*:*`) are supported.
67
-
68
- | Role | Module | Responsibility |
69
- |------|--------|----------------|
70
- | Client | `core/client`, `core/http` | Parse a 402 challenge, pick a supported scheme, ask the facilitator to sponsor, sign the tx, replay the request with `X-PAYMENT`. |
71
- | Resource Server | `core/server`, `core/server-http` | Issue 402 challenges for protected routes; verify and settle payments via the facilitator. |
72
- | Facilitator | `core/facilitator` | Co-sign transactions as gas owner, dry-run to verify, and submit to the chain. |
73
-
74
- ---
5
+ This package provides an SDK for the x402 "Payment Required" HTTP pattern, targeting the **Sui** blockchain. The bundled `sui/exact` scheme uses Sui's *sponsored transaction* model: the buyer signs the payment intent, the facilitator co-signs as the gas owner, and the resource server never touches a private key.
75
6
 
76
7
  ## Installation
77
8
 
@@ -85,77 +16,6 @@ You also need the peer dependencies in your project:
85
16
  npm install @mysten/sui @mysten/bcs express
86
17
  ```
87
18
 
88
- > Pre-built bundles live in [`dist/`](./dist). The build step is only required
89
- > if you're modifying the source under `core/` or `sui/`.
90
-
91
- ---
92
-
93
- ## Project Layout
94
-
95
- ```
96
- sdk/
97
- ├── core/ # Scheme-agnostic framework
98
- │ ├── client.mjs # x402Client — scheme registry & lookup
99
- │ ├── http.mjs # x402HTTPClient — parse 402, encode X-PAYMENT
100
- │ ├── server.mjs # x402ResourceServer — verify+settle via facilitator
101
- │ ├── server-http.mjs # x402HTTPResourceServer — Express middleware
102
- │ └── facilitator.mjs # x402Facilitator — sponsor / verify / settle
103
- ├── sui/
104
- │ └── exact/ # The "exact" payment scheme on Sui
105
- │ ├── client.mjs # Builds the payment PTB, signs, asks for sponsor
106
- │ ├── server.mjs # Passthrough (work happens in facilitator)
107
- │ └── facilitator.mjs # Co-signs as gas owner, dry-runs, submits
108
- ├── dist/ # esbuild output (consumed via package exports)
109
- ├── package.json
110
- └── README.MD
111
- ```
112
-
113
- `package.json` exposes each piece as a subpath export so consumers can import
114
- only what they need:
115
-
116
- ```json
117
- {
118
- "@altaga/x402-sui/core/client": "…",
119
- "@altaga/x402-sui/core/http": "…",
120
- "@altaga/x402-sui/core/server": "…",
121
- "@altaga/x402-sui/core/server-http": "…",
122
- "@altaga/x402-sui/core/facilitator": "…",
123
- "@altaga/x402-sui/sui/exact/client": "…",
124
- "@altaga/x402-sui/sui/exact/server": "…",
125
- "@altaga/x402-sui/sui/exact/facilitator":"…"
126
- }
127
- ```
128
-
129
- ---
130
-
131
- ## Protocol Flow
132
-
133
- 1. **Challenge.** The client requests a protected route. The server replies
134
- `402 Payment Required` with:
135
- - `X-Accepts`: base64-encoded JSON array of accepted `{ scheme, network, asset, payTo, maxAmountRequired, ... }` payment requirements.
136
- - `X-Facilitator-Url`: where the client should ask for gas sponsorship.
137
-
138
- 2. **Sponsor.** The client picks a supported scheme from `X-Accepts`, builds
139
- the payment transaction (a `splitCoins` + `transferObjects` PTB on Sui),
140
- and POSTs the raw tx bytes + sender address to `{facilitatorUrl}/sponsor`.
141
- The facilitator returns `sponsoredTxBytes` (with itself as `gasOwner`) and
142
- its `sponsorSignature`.
143
-
144
- 3. **Sign.** The client signs the sponsored bytes with its own keypair.
145
-
146
- 4. **Pay.** The client replays the original request with `X-PAYMENT:`
147
- (base64 JSON of `{ scheme, network, transaction, signature, amount, payTo, asset }`).
148
-
149
- 5. **Verify & Settle.** The server forwards the payload to the facilitator:
150
- - `/verify` performs a `dryRunTransactionBlock` to mathematically prove the
151
- tx would succeed.
152
- - `/settle` calls `executeTransactionBlock` with the combined
153
- `[buyerSignature, sponsorSignature]` array and returns the digest.
154
-
155
- 6. **Respond.** On success, the server attaches `X-Payment-Response` (base64
156
- JSON containing the `transactionDigest`) and hands off to the actual route
157
- handler.
158
-
159
19
  ---
160
20
 
161
21
  ## Quick Start
@@ -164,23 +24,23 @@ only what they need:
164
24
 
165
25
  ```js
166
26
  import express from "express";
167
- import { SuiClient } from "@mysten/sui/client";
168
-
169
- import { x402ResourceServer } from "@altaga/x402-sui/core/server";
170
- import { HTTPFacilitatorClient } from "@altaga/x402-sui/core/http";
171
- import { x402HTTPResourceServer } from "@altaga/x402-sui/core/server-http";
172
- import { ExactSuiServerScheme } from "@altaga/x402-sui/sui/exact/server";
27
+ import {
28
+ x402ResourceServer,
29
+ HTTPFacilitatorClient,
30
+ x402HTTPResourceServer,
31
+ ExactSuiServerScheme
32
+ } from "@altaga/x402-sui";
173
33
 
174
34
  const app = express();
175
35
 
176
- // 1. The server is configured with the URL of a trusted facilitator
36
+ // The server is configured with the URL of a trusted facilitator
177
37
  const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator.example.com" });
178
38
  const resourceServer = new x402ResourceServer(facilitator);
179
39
 
180
- // 2. Register scheme(s) you want to accept. Wildcards ("sui:*", "*:*") are OK.
40
+ // Register scheme(s) you want to accept. Wildcards ("sui:*", "*:*") are OK.
181
41
  resourceServer.register("exact:sui:mainnet", new ExactSuiServerScheme());
182
42
 
183
- // 3. Declare which routes are paid, and what they cost
43
+ // Declare which routes are paid, and what they cost
184
44
  const routes = {
185
45
  "GET /premium": {
186
46
  accepts: {
@@ -196,7 +56,7 @@ const routes = {
196
56
 
197
57
  app.use(new x402HTTPResourceServer(resourceServer, routes).middleware());
198
58
 
199
- // 4. The actual handler — req.payment is populated by the middleware
59
+ // The actual handler — req.payment is populated by the middleware
200
60
  app.get("/premium", (req, res) => {
201
61
  res.json({ ok: true, paid: req.payment });
202
62
  });
@@ -210,9 +70,7 @@ app.listen(3000);
210
70
  import express from "express";
211
71
  import { SuiClient } from "@mysten/sui/client";
212
72
  import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
213
-
214
- import { x402Facilitator } from "@altaga/x402-sui/core/facilitator";
215
- import { ExactSuiFacilitatorScheme } from "@altaga/x402-sui/sui/exact/facilitator";
73
+ import { x402Facilitator, ExactSuiFacilitatorScheme } from "@altaga/x402-sui";
216
74
 
217
75
  const client = new SuiClient({ url: "https://fullnode.mainnet.sui.io" });
218
76
  const keypair = Ed25519Keypair.fromSecretKey(process.env.FACILITATOR_SECRET);
@@ -256,21 +114,15 @@ app.post("/settle", async (req, res) => {
256
114
  }
257
115
  });
258
116
 
259
- app.listen: 4000;
117
+ app.listen(4000);
260
118
  ```
261
119
 
262
- > The facilitator keypair must hold SUI for gas. If `client.getCoins` returns
263
- > zero gas coins, sponsorship fails with `Facilitator out of gas!`.
264
-
265
120
  ### 3. Client (the buyer)
266
121
 
267
122
  ```js
268
123
  import { SuiClient } from "@mysten/sui/client";
269
124
  import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
270
-
271
- import { x402Client } from "@altaga/x402-sui/core/client";
272
- import { x402HTTPClient } from "@altaga/x402-sui/core/http";
273
- import { ExactSuiClientScheme } from "@altaga/x402-sui/sui/exact/client";
125
+ import { x402Client, x402HTTPClient, ExactSuiClientScheme } from "@altaga/x402-sui";
274
126
 
275
127
  const sui = new SuiClient({ url: "https://fullnode.mainnet.sui.io" });
276
128
  const keypair = Ed25519Keypair.fromSecretKey(process.env.BUYER_SECRET);
@@ -304,136 +156,86 @@ console.log("data:", await res.json(), "settle:", settleInfo);
304
156
 
305
157
  ---
306
158
 
307
- ## Module Reference
159
+ ## API Reference
308
160
 
309
- ### `core/client`
161
+ ### Client Setup (`x402Client` & `x402HTTPClient`)
310
162
 
311
- `x402Client` — a tiny scheme registry. `register(pattern, impl)` accepts
312
- `scheme:network`, `scheme:*`, or `*:*`; `getScheme(scheme, network)` resolves
313
- with the most specific match. Throws if nothing is registered.
314
-
315
- ### `core/http`
316
-
317
- `x402HTTPClient(coreClient)` — HTTP-specific glue on top of a core client:
318
-
319
- - `getPaymentRequiredResponse(getHeaderFn, bodyJson)` — decodes `X-Accepts`
320
- (base64 JSON) and reads `X-Facilitator-Url` (with body fallback).
321
- - `createPaymentPayload(paymentRequired)` — iterates `accepts`, hands the
322
- first one a registered scheme can fulfil, and returns a normalized
323
- `{ scheme, network, transaction, signature, amount, payTo, asset }`.
324
- - `encodePaymentSignatureHeader(payload)` — base64-encodes the payload for
325
- the `X-PAYMENT` request header.
326
- - `getPaymentSettleResponse(getHeaderFn)` — decodes `X-Payment-Response` if
327
- present.
328
-
329
- ### `core/server`
330
-
331
- `x402ResourceServer(facilitatorClient)` — the server-side brain.
332
-
333
- - `register(pattern, impl)` — same lookup rules as the client.
334
- - `initialize()` — async hook for parity with richer implementations.
335
- - `handlePayment(paymentPayload, routes)` — calls
336
- `facilitatorClient.verify(...)` then `facilitatorClient.settle(...)` and
337
- returns the settlement result.
338
-
339
- `HTTPFacilitatorClient({ url })` — the default transport, POSTing JSON to
340
- `{url}/verify` and `{url}/settle`. Roll your own if you need mTLS or auth
341
- headers.
342
-
343
- ### `core/server-http`
344
-
345
- `x402HTTPResourceServer(resourceServer, routesConfig).middleware()` returns
346
- an Express middleware that:
163
+ ```js
164
+ import { x402Client, x402HTTPClient } from "@altaga/x402-sui";
347
165
 
348
- 1. Looks up the route via `${req.method} ${req.path}`.
349
- 2. If no `X-PAYMENT` header is present, responds `402` with
350
- `X-Accepts` + `X-Facilitator-Url`.
351
- 3. If a header is present, base64-decodes it, calls
352
- `resourceServer.handlePayment(...)`, attaches `X-Payment-Response` to the
353
- outgoing response, populates `req.payment`, and calls `next()`.
354
- 4. On error, responds `400` with the error message.
166
+ const core = new x402Client();
167
+ core.register("exact:sui:mainnet", schemeImpl); // exact match
168
+ core.register("exact:sui:*", wildcardImpl); // any network
169
+ core.register("*:*", catchAllImpl); // any scheme/network
355
170
 
356
- ### `core/facilitator`
171
+ const http = new x402HTTPClient(core);
357
172
 
358
- `x402Facilitator` scheme registry + dispatch for the three facilitator
359
- operations:
173
+ // Decode a 402 response
174
+ const paymentRequired = http.getPaymentRequiredResponse(
175
+ (name) => res.headers.get(name),
176
+ null
177
+ );
360
178
 
361
- - `sponsor(schemeName, network, payload)` typically turns a raw client PTB
362
- into a sponsored transaction with the facilitator as `gasOwner`.
363
- - `verify(paymentPayload, paymentRequirements)` — must return
364
- `{ isValid: true }` or throw.
365
- - `settle(paymentPayload, paymentRequirements)` — must return
366
- `{ transactionDigest }` or throw.
179
+ // Build + sign + sponsor the payment
180
+ const payload = await http.createPaymentPayload(paymentRequired);
367
181
 
368
- ### `sui/exact/client`
182
+ // Read the settlement info from the final response
183
+ const settleInfo = http.getPaymentSettleResponse((n) => res.headers.get(n));
184
+ ```
369
185
 
370
- `ExactSuiClientScheme(client, keypair)` implements `createPaymentPayload`:
186
+ ### Server Setup (`x402ResourceServer` & `x402HTTPResourceServer`)
371
187
 
372
- 1. Fetchs the buyer's coins of `requirement.asset`; aborts if none exist.
373
- 2. Builds a PTB: `splitCoins(coin, [amount])` → `transferObjects([split], payTo)`.
374
- 3. If a `facilitatorUrl` was advertised, POSTs the raw bytes to
375
- `{facilitatorUrl}/sponsor` and receives `sponsoredTxBytes` +
376
- `sponsorSignature`.
377
- 4. Signs the (possibly sponsored) bytes with the buyer's keypair and returns
378
- `{ transaction, signature: [buyer, sponsor?] }`.
188
+ ```js
189
+ import express from "express";
190
+ import { x402ResourceServer, HTTPFacilitatorClient, x402HTTPResourceServer } from "@altaga/x402-sui";
379
191
 
380
- ### `sui/exact/server`
192
+ const app = express();
381
193
 
382
- `ExactSuiServerScheme` intentionally empty. On Sui, the heavy lifting
383
- (verification, gas sponsorship, submission) is done by the facilitator, so
384
- the server-side scheme is a passthrough. Register one for parity:
194
+ const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator.example.com" });
195
+ const resourceServer = new x402ResourceServer(facilitator);
385
196
 
386
- ```js
387
197
  resourceServer.register("exact:sui:mainnet", new ExactSuiServerScheme());
388
- ```
198
+ await resourceServer.initialize();
389
199
 
390
- ### `sui/exact/facilitator`
200
+ const routes = {
201
+ "GET /premium": { accepts: { /* ... */ } },
202
+ "POST /upload": { accepts: { /* ... */ } },
203
+ };
391
204
 
392
- `ExactSuiFacilitatorScheme(client, keypair)` — the gas station.
205
+ app.use(new x402HTTPResourceServer(resourceServer, routes).middleware());
206
+ ```
393
207
 
394
- - **`sponsor({ txBytes, sender })`** — deserializes the buyer's PTB, overrides
395
- `setSender(sender)`, `setGasOwner(self)`, picks a gas coin, sets a
396
- `setGasBudget(10_000_000)` and `setGasPrice(1000)`, calls `tx.build`, and
397
- returns `{ sponsoredTxBytes, sponsorSignature }`.
398
- - **`verify(payload, reqs)`** — `dryRunTransactionBlock` on the decoded
399
- bytes; succeeds only if `effects.status.status === "success"`.
400
- - **`settle(payload, reqs)`** — `executeTransactionBlock` with the combined
401
- signature array; returns `{ transactionDigest }` on success.
208
+ ### Facilitator Setup (`x402Facilitator`)
402
209
 
403
- ---
210
+ ```js
211
+ import { x402Facilitator } from "@altaga/x402-sui";
404
212
 
405
- ## Building
213
+ const facilitator = new x402Facilitator();
214
+ facilitator.register("exact:sui:mainnet", new ExactSuiFacilitatorScheme(sui, keypair));
406
215
 
407
- Source modules are written as `.mjs`. To rebuild the `dist/` bundles:
216
+ // Sponsor turn a raw client PTB into a gas-sponsored transaction
217
+ const { sponsoredTxBytes, sponsorSignature } = await facilitator.sponsor("exact", "sui:mainnet", { txBytes, sender });
408
218
 
409
- ```bash
410
- npm run build
411
- ```
412
-
413
- Which is shorthand for:
219
+ // Verify — must return { isValid: true } or throw
220
+ const verify = await facilitator.verify(paymentPayload, paymentRequirements);
414
221
 
415
- ```bash
416
- esbuild core/*.mjs sui/exact/*.mjs \
417
- --outdir=dist \
418
- --minify \
419
- --format=esm \
420
- --target=es2022
222
+ // Settle — must return { transactionDigest } or throw
223
+ const { transactionDigest } = await facilitator.settle(paymentPayload, paymentRequirements);
421
224
  ```
422
225
 
423
- ---
424
-
425
- ## Peer Dependencies
226
+ ### Sui Exact Scheme
426
227
 
427
- | Package | Version | Why |
428
- |---------|---------|-----|
429
- | [`@mysten/sui`](https://www.npmjs.com/package/@mysten/sui) | `^1.14.0` | `Transaction`, `SuiClient`, keypairs |
430
- | [`@mysten/bcs`](https://www.npmjs.com/package/@mysten/bcs) | `^1.2.0` | `toBase64` for serializing built PTBs |
431
- | [`express`](https://www.npmjs.com/package/express) | `^4.21.2` | Used by `x402HTTPResourceServer.middleware()` |
228
+ Provides the native implementations for the `exact` payment scheme on Sui:
432
229
 
433
- `esbuild` is only needed if you re-run the build.
230
+ ```js
231
+ import { ExactSuiClientScheme, ExactSuiServerScheme, ExactSuiFacilitatorScheme } from "@altaga/x402-sui";
434
232
 
435
- ---
233
+ // Buyer
234
+ core.register("exact:sui:mainnet", new ExactSuiClientScheme(sui, buyerKeypair));
436
235
 
437
- ## License
236
+ // Server
237
+ resourceServer.register("exact:sui:mainnet", new ExactSuiServerScheme());
438
238
 
439
- [MIT](./LICENSE) — © Altaga
239
+ // Facilitator
240
+ facilitator.register("exact:sui:mainnet", new ExactSuiFacilitatorScheme(sui, sponsorKeypair));
241
+ ```
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export*from"./core/client.mjs";export*from"./core/http.mjs";export*from"./core/facilitator.mjs";export*from"./core/server.mjs";export*from"./core/server-http.mjs";export*from"./sui/exact/client.mjs";export*from"./sui/exact/server.mjs";export*from"./sui/exact/facilitator.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@altaga/x402-sui",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Modular x402 implementation for Sui Sponsored Transactions",
5
5
  "main": "dist/core/client.js",
6
6
  "type": "module",
@@ -11,10 +11,11 @@
11
11
  "dist"
12
12
  ],
13
13
  "scripts": {
14
- "build": "esbuild core/*.mjs sui/exact/*.mjs --outdir=dist --minify --format=esm --target=es2022",
14
+ "build": "esbuild index.mjs core/*.mjs sui/exact/*.mjs --outdir=dist --minify --format=esm --target=es2022",
15
15
  "test": "echo \"Error: no test specified\" && exit 1"
16
16
  },
17
17
  "exports": {
18
+ ".": "./dist/index.js",
18
19
  "./core/client": "./dist/core/client.js",
19
20
  "./core/http": "./dist/core/http.js",
20
21
  "./core/server": "./dist/core/server.js",