s402 0.2.3 → 0.3.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/CHANGELOG.md +30 -0
- package/README.md +2 -2
- package/dist/compat.mjs +1 -0
- package/dist/errors.d.mts +2 -1
- package/dist/errors.mjs +8 -3
- package/dist/http.mjs +6 -5
- package/dist/index.d.mts +2 -94
- package/dist/scheme-D7qqwo3Q.d.mts +136 -0
- package/dist/test-utils.d.mts +74 -0
- package/dist/test-utils.mjs +132 -0
- package/dist/types.d.mts +13 -5
- package/package.json +8 -1
- package/test/conformance/vectors/requirements-decode.json +3 -3
- package/test/conformance/vectors/requirements-encode.json +3 -3
- package/test/conformance/vectors/validation-reject.json +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-04-11
|
|
9
|
+
|
|
10
|
+
This release closes the facilitator causal-binding hole identified in the April 2026 scale-fragility review, and establishes s402 as a pure chain-agnostic protocol repo (no Sui code anywhere). Chain-specific implementations now live in downstream adapter repos — the canonical Sui reference is `@sweefi/sui` in the SweeFi monorepo.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`verifySettlement` — client-side causal-binding check (S8 Facilitator Accountability).** New optional method on `s402ClientScheme`. For all client-signed schemes (`exact`, `stream`, `escrow`, `unlock` TX1), this is a **local, offline comparison**: derive the expected transaction digest from the signed BCS bytes and compare to `SettleResponse.txDigest`. No RPC call required. Closes the causal-binding hole where a malicious facilitator could substitute an unrelated-but-real transaction digest — that digest would correspond to different signed bytes the client never produced, and the check would reject it. Interface-only in this release; concrete implementations land in `@sweefi/sui` per ADR-002. See `typescript/src/scheme.ts` and `INVARIANTS.md` § S8 for the full contract and copy-paste implementation template.
|
|
15
|
+
- **`s402SettlementVerification` type** — return shape for `verifySettlement`: `{ verified, expectedDigest, actualDigest, reason? }`.
|
|
16
|
+
- **`DIGEST_MISMATCH` error code** — `retryable: false`, with a `suggestedAction` warning callers NOT to retry on mismatch. Retrying is dangerous: the signed bytes may have already landed on-chain under the *expected* digest (the facilitator may simply be lying about what it broadcast), and a fresh retry would double-pay. The correct failure mode is to mark the payment as non-settled and stop trusting the facilitator. See `typescript/src/errors.ts`.
|
|
17
|
+
- **S8. Facilitator Accountability** — first-class safety invariant alongside S1–S7. Full statement, formal proof for the `exact` scheme on Sui (by blake2b-256 collision resistance), per-scheme scope table, and a copy-paste implementation template for downstream Sui adapters now live in `INVARIANTS.md` § S8. The Allium behavioral spec is at `spec/allium/s8-facilitator-accountability.allium`.
|
|
18
|
+
- **ADR-001 — Protocol Boundaries.** Documents four decisions from the scale-fragility council: (1) facilitator trust boundary sealed by client-side digest verification, (2) receipt cardinality is a non-guarantee at the protocol layer, (3) scheme cap at five with burden-of-proof for any new scheme, (4) extension hygiene rules. See `docs/adr/001-protocol-boundaries.md`.
|
|
19
|
+
- **ADR-002 — s402 is a pure protocol repo.** Decides that this repo contains NO chain-specific code at any path — not in `typescript/src/`, not in a sibling package, not anywhere. All Sui-specific implementation moves to SweeFi. Corollary: the S7 chain-agnostic boundary is now enforced repo-wide, not just inside `src/`. See `docs/adr/002-s402-is-pure-protocol.md`.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
|
|
23
|
+
- **`mcp-server/` directory deleted.** The Sui-specific MCP server that previously shipped in this repo has been relocated to `@sweefi/mcp` (canonical implementation in the SweeFi repo) per ADR-002. **This does not affect the npm `s402` package** — `mcp-server` was a separate consumer of this package, not part of it. Users who were installing from the repo directly should migrate to `npx @sweefi/mcp`; a forthcoming `npm deprecate s402-mcp` will redirect the legacy standalone package to the new name.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **S7 scope strengthened to repo-level.** `INVARIANTS.md` § S7 scope note now reads: "chain-specific code lives in downstream implementation repos (e.g. `@sweefi/sui` for the Sui implementation) which consume this package from npm and add chain validation on top. Per ADR-002, the s402 repo itself contains NO chain-specific imports at the repo level — the protocol-pure boundary is enforced repo-wide, not just inside `src/`."
|
|
28
|
+
- **`INVARIANTS.md` Sui references rewritten as downstream pointers.** Prior revisions of the S8 proof block referenced a reference implementation at `mcp-server/src/sui-exact.ts`. That path no longer exists; the proof block now points at `sweefi/packages/sui/src/s402/exact/client.ts` as the canonical Sui adapter per ADR-002.
|
|
29
|
+
- **Demo API distribution surfaces** (`demo-api/public/index.html` served at `demo.s402-protocol.org`, and `demo-api/src/server.ts`) now reference `@sweefi/mcp` with the correct `SUI_PRIVATE_KEY` / `SUI_NETWORK` environment variables, matching SweeFi's documented `mcpServers` config shape. Outside the npm package scope but noted here for consumers browsing the monorepo.
|
|
30
|
+
|
|
31
|
+
### Compatibility
|
|
32
|
+
|
|
33
|
+
- **TypeScript type compatibility**: `verifySettlement` is optional on `s402ClientScheme`, and `DIGEST_MISMATCH` is a purely additive enum member. Existing adapter implementations compile unchanged against `^0.3.0`.
|
|
34
|
+
- **Wire format**: unchanged from v0.2.3. The 132 conformance test vectors in `test/conformance/vectors/` still pass byte-for-byte against v0.3.0.
|
|
35
|
+
- **Minor-bump rationale**: the 0.2.3 → 0.3.0 jump reflects the semantic significance of adding a new safety invariant (S8) and the repo-level architectural decisions (ADR-001/002), not a breaking wire-format change. Under semver 0.x, minor bumps are treated as breaking by `^0.x.y` ranges — consumers should expect to opt-in explicitly.
|
|
36
|
+
|
|
8
37
|
## [0.2.1] - 2026-03-02
|
|
9
38
|
|
|
10
39
|
### Added
|
|
@@ -129,6 +158,7 @@ _Version bump for npm publish after license change._
|
|
|
129
158
|
- Property-based fuzz testing via fast-check
|
|
130
159
|
- 207 tests, zero runtime dependencies
|
|
131
160
|
|
|
161
|
+
[0.3.0]: https://github.com/s402-protocol/core/compare/v0.2.3...v0.3.0
|
|
132
162
|
[0.2.1]: https://github.com/s402-protocol/core/compare/v0.2.0...v0.2.1
|
|
133
163
|
[0.2.0]: https://github.com/s402-protocol/core/compare/v0.1.8...v0.2.0
|
|
134
164
|
[0.1.8]: https://github.com/s402-protocol/core/compare/v0.1.7...v0.1.8
|
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://github.com/s402-protocol/core/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/s402)
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**Chain-agnostic HTTP 402 protocol.** Five payment schemes for AI agent commerce. Wire-compatible with x402. Zero runtime dependencies. Includes an optional compat layer (`s402/compat`) for normalizing x402 input.
|
|
7
7
|
|
|
8
|
-
s402 is
|
|
8
|
+
s402 is a chain-agnostic HTTP 402 wire format — types, HTTP encoding, scheme registry, and error handling for five payment schemes. The protocol layer contains no chain-specific logic (see [S7 invariant](./AGENTS.md)). The reference implementation on Sui uses Programmable Transaction Blocks to reduce 1,000 payments to just 2 on-chain transactions via the Prepaid scheme, cutting per-call effective gas from $0.007 to $0.000014 and making micropayments economically viable for AI agents for the first time.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
npm install s402
|
package/dist/compat.mjs
CHANGED
|
@@ -15,6 +15,7 @@ function fromX402Requirements(x402) {
|
|
|
15
15
|
if (x402.facilitatorUrl !== void 0) try {
|
|
16
16
|
const url = new URL(x402.facilitatorUrl);
|
|
17
17
|
if (url.protocol !== "https:" && url.protocol !== "http:") throw new s402Error("INVALID_PAYLOAD", `facilitatorUrl must use https:// or http://, got "${url.protocol}"`);
|
|
18
|
+
if (url.username || url.password) throw new s402Error("INVALID_PAYLOAD", "facilitatorUrl must not contain embedded credentials (user:password@)");
|
|
18
19
|
} catch (e) {
|
|
19
20
|
if (e instanceof s402Error) throw e;
|
|
20
21
|
throw new s402Error("INVALID_PAYLOAD", "facilitatorUrl is not a valid URL");
|
package/dist/errors.d.mts
CHANGED
|
@@ -23,6 +23,7 @@ declare const s402ErrorCode: {
|
|
|
23
23
|
readonly REQUIREMENTS_EXPIRED: "REQUIREMENTS_EXPIRED";
|
|
24
24
|
readonly VERIFICATION_FAILED: "VERIFICATION_FAILED";
|
|
25
25
|
readonly SETTLEMENT_FAILED: "SETTLEMENT_FAILED";
|
|
26
|
+
readonly DIGEST_MISMATCH: "DIGEST_MISMATCH";
|
|
26
27
|
};
|
|
27
28
|
type s402ErrorCodeType = (typeof s402ErrorCode)[keyof typeof s402ErrorCode];
|
|
28
29
|
interface s402ErrorInfo {
|
|
@@ -34,7 +35,7 @@ interface s402ErrorInfo {
|
|
|
34
35
|
/**
|
|
35
36
|
* Create a typed s402 error with recovery hints.
|
|
36
37
|
*
|
|
37
|
-
* @param code - One of the
|
|
38
|
+
* @param code - One of the 16 s402 error codes (e.g. 'SETTLEMENT_FAILED')
|
|
38
39
|
* @param message - Optional human-readable message (defaults to the code)
|
|
39
40
|
* @returns Error info object with code, message, retryable flag, and suggestedAction
|
|
40
41
|
*
|
package/dist/errors.mjs
CHANGED
|
@@ -22,7 +22,8 @@ const s402ErrorCode = {
|
|
|
22
22
|
SIGNATURE_INVALID: "SIGNATURE_INVALID",
|
|
23
23
|
REQUIREMENTS_EXPIRED: "REQUIREMENTS_EXPIRED",
|
|
24
24
|
VERIFICATION_FAILED: "VERIFICATION_FAILED",
|
|
25
|
-
SETTLEMENT_FAILED: "SETTLEMENT_FAILED"
|
|
25
|
+
SETTLEMENT_FAILED: "SETTLEMENT_FAILED",
|
|
26
|
+
DIGEST_MISMATCH: "DIGEST_MISMATCH"
|
|
26
27
|
};
|
|
27
28
|
/** Error recovery hints for each error code */
|
|
28
29
|
const ERROR_HINTS = {
|
|
@@ -68,7 +69,7 @@ const ERROR_HINTS = {
|
|
|
68
69
|
},
|
|
69
70
|
NETWORK_MISMATCH: {
|
|
70
71
|
retryable: false,
|
|
71
|
-
suggestedAction: "Ensure client and server are on the same
|
|
72
|
+
suggestedAction: "Ensure client and server are on the same network"
|
|
72
73
|
},
|
|
73
74
|
SIGNATURE_INVALID: {
|
|
74
75
|
retryable: false,
|
|
@@ -85,12 +86,16 @@ const ERROR_HINTS = {
|
|
|
85
86
|
SETTLEMENT_FAILED: {
|
|
86
87
|
retryable: true,
|
|
87
88
|
suggestedAction: "Transient RPC failure during settlement — retry in a few seconds"
|
|
89
|
+
},
|
|
90
|
+
DIGEST_MISMATCH: {
|
|
91
|
+
retryable: false,
|
|
92
|
+
suggestedAction: "Facilitator returned a transaction digest that does not match the signed payload. Do NOT retry — treat payment as non-settled and investigate the facilitator."
|
|
88
93
|
}
|
|
89
94
|
};
|
|
90
95
|
/**
|
|
91
96
|
* Create a typed s402 error with recovery hints.
|
|
92
97
|
*
|
|
93
|
-
* @param code - One of the
|
|
98
|
+
* @param code - One of the 16 s402 error codes (e.g. 'SETTLEMENT_FAILED')
|
|
94
99
|
* @param message - Optional human-readable message (defaults to the code)
|
|
95
100
|
* @returns Error info object with code, message, retryable flag, and suggestedAction
|
|
96
101
|
*
|
package/dist/http.mjs
CHANGED
|
@@ -109,8 +109,8 @@ const S402_SUB_OBJECT_KEYS = {
|
|
|
109
109
|
]),
|
|
110
110
|
unlock: new Set([
|
|
111
111
|
"encryptionId",
|
|
112
|
-
"
|
|
113
|
-
"
|
|
112
|
+
"encryptedContentId",
|
|
113
|
+
"encryptionServiceId"
|
|
114
114
|
]),
|
|
115
115
|
prepaid: new Set([
|
|
116
116
|
"ratePerCall",
|
|
@@ -373,8 +373,8 @@ function validateUnlockShape(value) {
|
|
|
373
373
|
assertPlainObject(value, "unlock");
|
|
374
374
|
const obj = value;
|
|
375
375
|
assertString(obj, "encryptionId", "unlock");
|
|
376
|
-
assertString(obj, "
|
|
377
|
-
assertString(obj, "
|
|
376
|
+
assertString(obj, "encryptedContentId", "unlock");
|
|
377
|
+
assertString(obj, "encryptionServiceId", "unlock");
|
|
378
378
|
}
|
|
379
379
|
/**
|
|
380
380
|
* Validate prepaid sub-object.
|
|
@@ -420,7 +420,7 @@ function validateRequirementsShape(obj) {
|
|
|
420
420
|
if (typeof record.network !== "string") missing.push("network (string)");
|
|
421
421
|
if (typeof record.asset !== "string") missing.push("asset (string)");
|
|
422
422
|
if (typeof record.amount !== "string") missing.push("amount (string)");
|
|
423
|
-
else if (!
|
|
423
|
+
else if (!isValidAmount(record.amount)) throw new s402Error("INVALID_PAYLOAD", `Invalid amount "${record.amount}": must be a non-negative integer string`);
|
|
424
424
|
if (typeof record.payTo !== "string") missing.push("payTo (string)");
|
|
425
425
|
else if (record.payTo.length === 0) throw new s402Error("INVALID_PAYLOAD", "payTo must be a non-empty string");
|
|
426
426
|
if (missing.length > 0) throw new s402Error("INVALID_PAYLOAD", `Malformed payment requirements: missing ${missing.join(", ")}`);
|
|
@@ -446,6 +446,7 @@ function validateRequirementsShape(obj) {
|
|
|
446
446
|
try {
|
|
447
447
|
const url = new URL(record.facilitatorUrl);
|
|
448
448
|
if (url.protocol !== "https:" && url.protocol !== "http:") throw new s402Error("INVALID_PAYLOAD", `facilitatorUrl must use https:// or http://, got "${url.protocol}"`);
|
|
449
|
+
if (url.username || url.password) throw new s402Error("INVALID_PAYLOAD", "facilitatorUrl must not contain embedded credentials (user:password@)");
|
|
449
450
|
} catch (e) {
|
|
450
451
|
if (e instanceof s402Error) throw e;
|
|
451
452
|
throw new s402Error("INVALID_PAYLOAD", "facilitatorUrl is not a valid URL");
|
package/dist/index.d.mts
CHANGED
|
@@ -1,101 +1,9 @@
|
|
|
1
1
|
import { createS402Error, s402Error, s402ErrorCode, s402ErrorCodeType, s402ErrorInfo } from "./errors.mjs";
|
|
2
2
|
import { S402_HEADERS, S402_VERSION, s402Discovery, s402EscrowExtra, s402EscrowPayload, s402ExactPayload, s402Mandate, s402MandateRequirements, s402PaymentPayload, s402PaymentPayloadBase, s402PaymentRequirements, s402PaymentSession, s402PrepaidExtra, s402PrepaidPayload, s402RegistryQuery, s402Scheme, s402ServiceEntry, s402SettleResponse, s402SettlementMode, s402StreamExtra, s402StreamPayload, s402UnlockExtra, s402UnlockPayload, s402VerifyResponse } from "./types.mjs";
|
|
3
3
|
import { S402_CONTENT_TYPE, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, isValidAmount, isValidU64Amount, validateRequirementsShape } from "./http.mjs";
|
|
4
|
+
import { a as s402ServerScheme, i as s402RouteConfig, n as s402DirectScheme, o as s402SettlementVerification, r as s402FacilitatorScheme, t as s402ClientScheme } from "./scheme-D7qqwo3Q.mjs";
|
|
4
5
|
import { S402_RECEIPT_HEADER, formatReceiptHeader, parseReceiptHeader, s402Receipt, s402ReceiptSigner, s402ReceiptVerifier } from "./receipts.mjs";
|
|
5
6
|
|
|
6
|
-
//#region src/scheme.d.ts
|
|
7
|
-
/** Implemented by each scheme on the client side */
|
|
8
|
-
interface s402ClientScheme {
|
|
9
|
-
/** Which scheme this implements */
|
|
10
|
-
readonly scheme: s402Scheme;
|
|
11
|
-
/** Create a signed payment payload from server requirements */
|
|
12
|
-
createPayment(requirements: s402PaymentRequirements): Promise<s402PaymentPayload>;
|
|
13
|
-
}
|
|
14
|
-
/** Implemented by each scheme on the server side */
|
|
15
|
-
interface s402ServerScheme {
|
|
16
|
-
readonly scheme: s402Scheme;
|
|
17
|
-
/** Build payment requirements from route config */
|
|
18
|
-
buildRequirements(config: s402RouteConfig): s402PaymentRequirements;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Implemented by each scheme in the facilitator.
|
|
22
|
-
*
|
|
23
|
-
* Critical: each scheme has its OWN verify logic.
|
|
24
|
-
* - Exact: signature recovery + dry-run simulation + balance check
|
|
25
|
-
* - Stream: stream creation PTB validation + deposit check
|
|
26
|
-
* - Escrow: escrow creation PTB validation + arbiter/deadline check
|
|
27
|
-
* - Unlock: escrow validation (key release is separate PTB)
|
|
28
|
-
*/
|
|
29
|
-
interface s402FacilitatorScheme {
|
|
30
|
-
readonly scheme: s402Scheme;
|
|
31
|
-
/** Verify a payment payload without broadcasting */
|
|
32
|
-
verify(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402VerifyResponse>;
|
|
33
|
-
/** Verify and broadcast the transaction */
|
|
34
|
-
settle(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* For self-sovereign agents that hold their own keys.
|
|
38
|
-
* Builds, signs, and broadcasts in one step — no facilitator needed.
|
|
39
|
-
*
|
|
40
|
-
* MUST call waitForTransaction() before returning success.
|
|
41
|
-
* Without finality confirmation, server could grant access for a
|
|
42
|
-
* transaction that gets reverted.
|
|
43
|
-
*/
|
|
44
|
-
interface s402DirectScheme {
|
|
45
|
-
readonly scheme: s402Scheme;
|
|
46
|
-
/** Build, sign, broadcast, and wait for finality */
|
|
47
|
-
settleDirectly(requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
|
|
48
|
-
}
|
|
49
|
-
/** Per-route payment configuration for the server middleware */
|
|
50
|
-
interface s402RouteConfig {
|
|
51
|
-
/** Which payment scheme(s) to accept. Always includes "exact" for x402 compat. */
|
|
52
|
-
schemes: s402Scheme[];
|
|
53
|
-
/** Amount in base units, same as wire format (e.g., "1000000") */
|
|
54
|
-
price: string;
|
|
55
|
-
/** Network identifier (e.g., "sui:mainnet", "solana:mainnet-beta") */
|
|
56
|
-
network: string;
|
|
57
|
-
/** Recipient address (chain-specific format, validated by chain adapter) */
|
|
58
|
-
payTo: string;
|
|
59
|
-
/** Asset/coin type identifier (chain-specific, e.g., Sui Move type or Solana mint address) */
|
|
60
|
-
asset: string;
|
|
61
|
-
/** Facilitator URL (optional for direct settlement) */
|
|
62
|
-
facilitatorUrl?: string;
|
|
63
|
-
/** Settlement mode preference */
|
|
64
|
-
settlementMode?: s402SettlementMode;
|
|
65
|
-
/** AP2 mandate requirements */
|
|
66
|
-
mandate?: {
|
|
67
|
-
required: boolean;
|
|
68
|
-
minPerTx?: string;
|
|
69
|
-
};
|
|
70
|
-
/** Protocol fee in basis points */
|
|
71
|
-
protocolFeeBps?: number;
|
|
72
|
-
/** Require on-chain receipt */
|
|
73
|
-
receiptRequired?: boolean;
|
|
74
|
-
stream?: {
|
|
75
|
-
ratePerSecond: string;
|
|
76
|
-
budgetCap: string;
|
|
77
|
-
minDeposit: string;
|
|
78
|
-
};
|
|
79
|
-
escrow?: {
|
|
80
|
-
seller: string;
|
|
81
|
-
arbiter?: string;
|
|
82
|
-
deadlineMs: string;
|
|
83
|
-
};
|
|
84
|
-
unlock?: {
|
|
85
|
-
encryptionId: string;
|
|
86
|
-
walrusBlobId: string;
|
|
87
|
-
encryptionPackageId: string;
|
|
88
|
-
};
|
|
89
|
-
prepaid?: {
|
|
90
|
-
ratePerCall: string;
|
|
91
|
-
maxCalls?: string;
|
|
92
|
-
minDeposit: string;
|
|
93
|
-
withdrawalDelayMs: string; /** Provider Ed25519 pubkey (hex). Enables v0.2 signed receipt mode. @since v0.2 */
|
|
94
|
-
providerPubkey?: string; /** Dispute window in ms. Required when providerPubkey is set. @since v0.2 */
|
|
95
|
-
disputeWindowMs?: string;
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
//#endregion
|
|
99
7
|
//#region src/client.d.ts
|
|
100
8
|
declare class s402Client {
|
|
101
9
|
private schemes;
|
|
@@ -277,4 +185,4 @@ declare class s402ResourceServer {
|
|
|
277
185
|
process(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
|
|
278
186
|
}
|
|
279
187
|
//#endregion
|
|
280
|
-
export { S402_CONTENT_TYPE, S402_HEADERS, S402_RECEIPT_HEADER, S402_VERSION, createS402Error, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, formatReceiptHeader, isValidAmount, isValidU64Amount, parseReceiptHeader, s402Client, type s402ClientScheme, type s402DirectScheme, type s402Discovery, s402Error, s402ErrorCode, type s402ErrorCodeType, type s402ErrorInfo, type s402EscrowExtra, type s402EscrowPayload, type s402ExactPayload, s402Facilitator, type s402FacilitatorScheme, type s402Mandate, type s402MandateRequirements, type s402PaymentPayload, type s402PaymentPayloadBase, type s402PaymentRequirements, type s402PaymentSession, type s402PrepaidExtra, type s402PrepaidPayload, type s402Receipt, type s402ReceiptSigner, type s402ReceiptVerifier, type s402RegistryQuery, s402ResourceServer, type s402RouteConfig, type s402Scheme, type s402ServerScheme, type s402ServiceEntry, type s402SettleResponse, type s402SettlementMode, type s402StreamExtra, type s402StreamPayload, type s402UnlockExtra, type s402UnlockPayload, type s402VerifyResponse, validateRequirementsShape };
|
|
188
|
+
export { S402_CONTENT_TYPE, S402_HEADERS, S402_RECEIPT_HEADER, S402_VERSION, createS402Error, decodePayloadBody, decodePaymentPayload, decodePaymentRequired, decodeRequirementsBody, decodeSettleBody, decodeSettleResponse, detectProtocol, detectTransport, encodePayloadBody, encodePaymentPayload, encodePaymentRequired, encodeRequirementsBody, encodeSettleBody, encodeSettleResponse, extractRequirementsFromResponse, formatReceiptHeader, isValidAmount, isValidU64Amount, parseReceiptHeader, s402Client, type s402ClientScheme, type s402DirectScheme, type s402Discovery, s402Error, s402ErrorCode, type s402ErrorCodeType, type s402ErrorInfo, type s402EscrowExtra, type s402EscrowPayload, type s402ExactPayload, s402Facilitator, type s402FacilitatorScheme, type s402Mandate, type s402MandateRequirements, type s402PaymentPayload, type s402PaymentPayloadBase, type s402PaymentRequirements, type s402PaymentSession, type s402PrepaidExtra, type s402PrepaidPayload, type s402Receipt, type s402ReceiptSigner, type s402ReceiptVerifier, type s402RegistryQuery, s402ResourceServer, type s402RouteConfig, type s402Scheme, type s402ServerScheme, type s402ServiceEntry, type s402SettleResponse, type s402SettlementMode, type s402SettlementVerification, type s402StreamExtra, type s402StreamPayload, type s402UnlockExtra, type s402UnlockPayload, type s402VerifyResponse, validateRequirementsShape };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { s402PaymentPayload, s402PaymentRequirements, s402Scheme, s402SettleResponse, s402SettlementMode, s402VerifyResponse } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/scheme.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Result of client-side settlement verification.
|
|
6
|
+
*
|
|
7
|
+
* See `s402ClientScheme.verifySettlement` — this is the outcome of the
|
|
8
|
+
* client independently checking that the facilitator's returned digest
|
|
9
|
+
* is causally bound to the signed payload the client actually sent.
|
|
10
|
+
*/
|
|
11
|
+
interface s402SettlementVerification {
|
|
12
|
+
/** True iff the facilitator's digest matches the one derived from the signed payload bytes. */
|
|
13
|
+
verified: boolean;
|
|
14
|
+
/** The digest the client computed locally from its own signed bytes. */
|
|
15
|
+
expectedDigest: string;
|
|
16
|
+
/** The digest the facilitator returned (copied from SettleResponse). */
|
|
17
|
+
actualDigest: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Human-readable reason when `verified` is false. Present only on mismatch,
|
|
20
|
+
* unknown-digest, or when the scheme cannot verify the settlement locally.
|
|
21
|
+
*/
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Implemented by each scheme on the client side */
|
|
25
|
+
interface s402ClientScheme {
|
|
26
|
+
/** Which scheme this implements */
|
|
27
|
+
readonly scheme: s402Scheme;
|
|
28
|
+
/** Create a signed payment payload from server requirements */
|
|
29
|
+
createPayment(requirements: s402PaymentRequirements): Promise<s402PaymentPayload>;
|
|
30
|
+
/**
|
|
31
|
+
* Verify that the facilitator's `SettleResponse` is causally bound to the
|
|
32
|
+
* signed payload the client actually sent.
|
|
33
|
+
*
|
|
34
|
+
* For schemes where the client signs the full transaction before sending
|
|
35
|
+
* (exact, stream, escrow, unlock-TX1), this is a **local, offline check**:
|
|
36
|
+
* derive the expected tx digest from the signed bytes and compare it to the
|
|
37
|
+
* digest the facilitator returned. No RPC call required. This closes the
|
|
38
|
+
* causal-binding hole identified in the April 2026 scale-fragility review
|
|
39
|
+
* (see `knowledge/scale-fragility-council-v03.md`): a malicious facilitator
|
|
40
|
+
* cannot substitute an unrelated-but-real tx digest, because that other
|
|
41
|
+
* digest would correspond to different signed bytes the client never
|
|
42
|
+
* produced.
|
|
43
|
+
*
|
|
44
|
+
* Optional for backward-compatibility. Schemes that cannot verify locally
|
|
45
|
+
* (e.g. unlock-TX2, which is facilitator-constructed) should return
|
|
46
|
+
* `{ verified: false, reason: 'scheme does not support local verification' }`
|
|
47
|
+
* and rely on other attestation mechanisms.
|
|
48
|
+
*/
|
|
49
|
+
verifySettlement?(payload: s402PaymentPayload, settleResponse: s402SettleResponse): s402SettlementVerification;
|
|
50
|
+
}
|
|
51
|
+
/** Implemented by each scheme on the server side */
|
|
52
|
+
interface s402ServerScheme {
|
|
53
|
+
readonly scheme: s402Scheme;
|
|
54
|
+
/** Build payment requirements from route config */
|
|
55
|
+
buildRequirements(config: s402RouteConfig): s402PaymentRequirements;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Implemented by each scheme in the facilitator.
|
|
59
|
+
*
|
|
60
|
+
* Critical: each scheme has its OWN verify logic.
|
|
61
|
+
* - Exact: signature recovery + dry-run simulation + balance check
|
|
62
|
+
* - Stream: stream creation PTB validation + deposit check
|
|
63
|
+
* - Escrow: escrow creation PTB validation + arbiter/deadline check
|
|
64
|
+
* - Unlock: escrow validation (key release is separate PTB)
|
|
65
|
+
*/
|
|
66
|
+
interface s402FacilitatorScheme {
|
|
67
|
+
readonly scheme: s402Scheme;
|
|
68
|
+
/** Verify a payment payload without broadcasting */
|
|
69
|
+
verify(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402VerifyResponse>;
|
|
70
|
+
/** Verify and broadcast the transaction */
|
|
71
|
+
settle(payload: s402PaymentPayload, requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* For self-sovereign agents that hold their own keys.
|
|
75
|
+
* Builds, signs, and broadcasts in one step — no facilitator needed.
|
|
76
|
+
*
|
|
77
|
+
* MUST call waitForTransaction() before returning success.
|
|
78
|
+
* Without finality confirmation, server could grant access for a
|
|
79
|
+
* transaction that gets reverted.
|
|
80
|
+
*/
|
|
81
|
+
interface s402DirectScheme {
|
|
82
|
+
readonly scheme: s402Scheme;
|
|
83
|
+
/** Build, sign, broadcast, and wait for finality */
|
|
84
|
+
settleDirectly(requirements: s402PaymentRequirements): Promise<s402SettleResponse>;
|
|
85
|
+
}
|
|
86
|
+
/** Per-route payment configuration for the server middleware */
|
|
87
|
+
interface s402RouteConfig {
|
|
88
|
+
/** Which payment scheme(s) to accept. Always includes "exact" for x402 compat. */
|
|
89
|
+
schemes: s402Scheme[];
|
|
90
|
+
/** Amount in base units, same as wire format (e.g., "1000000") */
|
|
91
|
+
price: string;
|
|
92
|
+
/** Network identifier (e.g., "sui:mainnet", "solana:mainnet-beta") */
|
|
93
|
+
network: string;
|
|
94
|
+
/** Recipient address (chain-specific format, validated by chain adapter) */
|
|
95
|
+
payTo: string;
|
|
96
|
+
/** Asset/coin type identifier (chain-specific, e.g., Sui Move type or Solana mint address) */
|
|
97
|
+
asset: string;
|
|
98
|
+
/** Facilitator URL (optional for direct settlement) */
|
|
99
|
+
facilitatorUrl?: string;
|
|
100
|
+
/** Settlement mode preference */
|
|
101
|
+
settlementMode?: s402SettlementMode;
|
|
102
|
+
/** AP2 mandate requirements */
|
|
103
|
+
mandate?: {
|
|
104
|
+
required: boolean;
|
|
105
|
+
minPerTx?: string;
|
|
106
|
+
};
|
|
107
|
+
/** Protocol fee in basis points */
|
|
108
|
+
protocolFeeBps?: number;
|
|
109
|
+
/** Require on-chain receipt */
|
|
110
|
+
receiptRequired?: boolean;
|
|
111
|
+
stream?: {
|
|
112
|
+
ratePerSecond: string;
|
|
113
|
+
budgetCap: string;
|
|
114
|
+
minDeposit: string;
|
|
115
|
+
};
|
|
116
|
+
escrow?: {
|
|
117
|
+
seller: string;
|
|
118
|
+
arbiter?: string;
|
|
119
|
+
deadlineMs: string;
|
|
120
|
+
};
|
|
121
|
+
unlock?: {
|
|
122
|
+
encryptionId: string;
|
|
123
|
+
encryptedContentId: string;
|
|
124
|
+
encryptionServiceId: string;
|
|
125
|
+
};
|
|
126
|
+
prepaid?: {
|
|
127
|
+
ratePerCall: string;
|
|
128
|
+
maxCalls?: string;
|
|
129
|
+
minDeposit: string;
|
|
130
|
+
withdrawalDelayMs: string; /** Provider Ed25519 pubkey (hex). Enables v0.2 signed receipt mode. @since v0.2 */
|
|
131
|
+
providerPubkey?: string; /** Dispute window in ms. Required when providerPubkey is set. @since v0.2 */
|
|
132
|
+
disputeWindowMs?: string;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { s402ServerScheme as a, s402RouteConfig as i, s402DirectScheme as n, s402SettlementVerification as o, s402FacilitatorScheme as r, s402ClientScheme as t };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { s402PaymentPayload, s402PaymentRequirements, s402SettleResponse, s402VerifyResponse } from "./types.mjs";
|
|
2
|
+
import { a as s402ServerScheme, r as s402FacilitatorScheme, t as s402ClientScheme } from "./scheme-D7qqwo3Q.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/test-utils.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create a mock client scheme for the `exact` payment type.
|
|
7
|
+
*
|
|
8
|
+
* Produces payloads with deterministic transaction/signature strings
|
|
9
|
+
* derived from the requirements (amount + payTo). Useful for testing
|
|
10
|
+
* the client→server→facilitator flow without real keys.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const client = new s402Client();
|
|
15
|
+
* client.register('sui:testnet', mockExactClientScheme());
|
|
16
|
+
*
|
|
17
|
+
* const payload = await client.createPayment(requirements);
|
|
18
|
+
* // payload.payload.transaction === 'mock-pay-1000000-to-0xabc...'
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function mockExactClientScheme(): s402ClientScheme;
|
|
22
|
+
/**
|
|
23
|
+
* Create a mock facilitator scheme for the `exact` payment type.
|
|
24
|
+
*
|
|
25
|
+
* Verifies that the payload's transaction string matches the expected
|
|
26
|
+
* format from `mockExactClientScheme()`. Settle always succeeds with
|
|
27
|
+
* a deterministic digest.
|
|
28
|
+
*
|
|
29
|
+
* Pair this with `mockExactClientScheme()` for end-to-end testing.
|
|
30
|
+
*
|
|
31
|
+
* @param options.txDigest - Custom transaction digest (default: 'mock-tx-digest')
|
|
32
|
+
* @param options.finalityMs - Custom finality time (default: 400)
|
|
33
|
+
* @param options.verifyFn - Override the verify function for custom behavior
|
|
34
|
+
* @param options.settleFn - Override the settle function for custom behavior
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const facilitator = new s402Facilitator();
|
|
39
|
+
* facilitator.register('sui:testnet', mockExactFacilitatorScheme());
|
|
40
|
+
*
|
|
41
|
+
* const result = await facilitator.process(payload, requirements);
|
|
42
|
+
* // result.success === true
|
|
43
|
+
* // result.txDigest === 'mock-tx-digest'
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
declare function mockExactFacilitatorScheme(options?: {
|
|
47
|
+
txDigest?: string;
|
|
48
|
+
finalityMs?: number;
|
|
49
|
+
verifyFn?: (payload: s402PaymentPayload, requirements: s402PaymentRequirements) => Promise<s402VerifyResponse>;
|
|
50
|
+
settleFn?: (payload: s402PaymentPayload, requirements: s402PaymentRequirements) => Promise<s402SettleResponse>;
|
|
51
|
+
}): s402FacilitatorScheme;
|
|
52
|
+
/**
|
|
53
|
+
* Create a mock server scheme for the `exact` payment type.
|
|
54
|
+
*
|
|
55
|
+
* Builds payment requirements from a route config. Useful for testing
|
|
56
|
+
* server middleware without chain-specific adapters.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const server = new s402ResourceServer();
|
|
61
|
+
* server.register('sui:testnet', mockExactServerScheme());
|
|
62
|
+
*
|
|
63
|
+
* const requirements = server.buildRequirements({
|
|
64
|
+
* schemes: ['exact'],
|
|
65
|
+
* price: '1000000',
|
|
66
|
+
* network: 'sui:testnet',
|
|
67
|
+
* payTo: '0xabc...',
|
|
68
|
+
* asset: 'SUI',
|
|
69
|
+
* });
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
declare function mockExactServerScheme(): s402ServerScheme;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { mockExactClientScheme, mockExactFacilitatorScheme, mockExactServerScheme };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { S402_VERSION } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/test-utils.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a mock client scheme for the `exact` payment type.
|
|
6
|
+
*
|
|
7
|
+
* Produces payloads with deterministic transaction/signature strings
|
|
8
|
+
* derived from the requirements (amount + payTo). Useful for testing
|
|
9
|
+
* the client→server→facilitator flow without real keys.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const client = new s402Client();
|
|
14
|
+
* client.register('sui:testnet', mockExactClientScheme());
|
|
15
|
+
*
|
|
16
|
+
* const payload = await client.createPayment(requirements);
|
|
17
|
+
* // payload.payload.transaction === 'mock-pay-1000000-to-0xabc...'
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function mockExactClientScheme() {
|
|
21
|
+
return {
|
|
22
|
+
scheme: "exact",
|
|
23
|
+
async createPayment(requirements) {
|
|
24
|
+
return {
|
|
25
|
+
s402Version: S402_VERSION,
|
|
26
|
+
scheme: "exact",
|
|
27
|
+
payload: {
|
|
28
|
+
transaction: `mock-pay-${requirements.amount}-to-${requirements.payTo}`,
|
|
29
|
+
signature: "mock-signature"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a mock facilitator scheme for the `exact` payment type.
|
|
37
|
+
*
|
|
38
|
+
* Verifies that the payload's transaction string matches the expected
|
|
39
|
+
* format from `mockExactClientScheme()`. Settle always succeeds with
|
|
40
|
+
* a deterministic digest.
|
|
41
|
+
*
|
|
42
|
+
* Pair this with `mockExactClientScheme()` for end-to-end testing.
|
|
43
|
+
*
|
|
44
|
+
* @param options.txDigest - Custom transaction digest (default: 'mock-tx-digest')
|
|
45
|
+
* @param options.finalityMs - Custom finality time (default: 400)
|
|
46
|
+
* @param options.verifyFn - Override the verify function for custom behavior
|
|
47
|
+
* @param options.settleFn - Override the settle function for custom behavior
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const facilitator = new s402Facilitator();
|
|
52
|
+
* facilitator.register('sui:testnet', mockExactFacilitatorScheme());
|
|
53
|
+
*
|
|
54
|
+
* const result = await facilitator.process(payload, requirements);
|
|
55
|
+
* // result.success === true
|
|
56
|
+
* // result.txDigest === 'mock-tx-digest'
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function mockExactFacilitatorScheme(options) {
|
|
60
|
+
const txDigest = options?.txDigest ?? "mock-tx-digest";
|
|
61
|
+
const finalityMs = options?.finalityMs ?? 400;
|
|
62
|
+
return {
|
|
63
|
+
scheme: "exact",
|
|
64
|
+
async verify(payload, requirements) {
|
|
65
|
+
if (options?.verifyFn) return options.verifyFn(payload, requirements);
|
|
66
|
+
if (payload.scheme !== "exact") return {
|
|
67
|
+
valid: false,
|
|
68
|
+
invalidReason: `Expected exact scheme, got ${payload.scheme}`
|
|
69
|
+
};
|
|
70
|
+
const exact = payload;
|
|
71
|
+
const expectedTx = `mock-pay-${requirements.amount}-to-${requirements.payTo}`;
|
|
72
|
+
if (exact.payload.transaction !== expectedTx) return {
|
|
73
|
+
valid: false,
|
|
74
|
+
invalidReason: "Transaction does not match expected mock format"
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
valid: true,
|
|
78
|
+
payerAddress: "0xmock-payer"
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
async settle(payload, requirements) {
|
|
82
|
+
if (options?.settleFn) return options.settleFn(payload, requirements);
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
txDigest,
|
|
86
|
+
finalityMs
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a mock server scheme for the `exact` payment type.
|
|
93
|
+
*
|
|
94
|
+
* Builds payment requirements from a route config. Useful for testing
|
|
95
|
+
* server middleware without chain-specific adapters.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const server = new s402ResourceServer();
|
|
100
|
+
* server.register('sui:testnet', mockExactServerScheme());
|
|
101
|
+
*
|
|
102
|
+
* const requirements = server.buildRequirements({
|
|
103
|
+
* schemes: ['exact'],
|
|
104
|
+
* price: '1000000',
|
|
105
|
+
* network: 'sui:testnet',
|
|
106
|
+
* payTo: '0xabc...',
|
|
107
|
+
* asset: 'SUI',
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
function mockExactServerScheme() {
|
|
112
|
+
return {
|
|
113
|
+
scheme: "exact",
|
|
114
|
+
buildRequirements(config) {
|
|
115
|
+
return {
|
|
116
|
+
s402Version: S402_VERSION,
|
|
117
|
+
accepts: [...new Set([...config.schemes, "exact"])],
|
|
118
|
+
network: config.network,
|
|
119
|
+
asset: config.asset,
|
|
120
|
+
amount: config.price,
|
|
121
|
+
payTo: config.payTo,
|
|
122
|
+
facilitatorUrl: config.facilitatorUrl,
|
|
123
|
+
protocolFeeBps: config.protocolFeeBps,
|
|
124
|
+
receiptRequired: config.receiptRequired,
|
|
125
|
+
settlementMode: config.settlementMode
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//#endregion
|
|
132
|
+
export { mockExactClientScheme, mockExactFacilitatorScheme, mockExactServerScheme };
|
package/dist/types.d.mts
CHANGED
|
@@ -24,7 +24,15 @@ interface s402PaymentRequirements {
|
|
|
24
24
|
amount: string;
|
|
25
25
|
/** Recipient address */
|
|
26
26
|
payTo: string;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Facilitator URL (optional for direct settlement).
|
|
29
|
+
*
|
|
30
|
+
* Validated at the wire layer for protocol (`https:` or `http:` only) and
|
|
31
|
+
* embedded credentials (rejected). Consumers that fetch this URL MUST apply
|
|
32
|
+
* their own hostname/IP restrictions to prevent SSRF (block RFC 1918 private
|
|
33
|
+
* addresses, link-local 169.254.x.x, loopback, cloud metadata endpoints).
|
|
34
|
+
* DNS-based SSRF cannot be caught at URL parse time.
|
|
35
|
+
*/
|
|
28
36
|
facilitatorUrl?: string;
|
|
29
37
|
/** AP2 mandate requirements (if agent spending authorization is needed) */
|
|
30
38
|
mandate?: s402MandateRequirements;
|
|
@@ -99,10 +107,10 @@ interface s402EscrowExtra {
|
|
|
99
107
|
interface s402UnlockExtra {
|
|
100
108
|
/** Encryption ID for key servers */
|
|
101
109
|
encryptionId: string;
|
|
102
|
-
/** Walrus blob ID
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
|
|
110
|
+
/** Content identifier for the encrypted blob (e.g., Walrus blob ID, IPFS CID) */
|
|
111
|
+
encryptedContentId: string;
|
|
112
|
+
/** Identifier for the encryption service or module (e.g., Sui package ID, EVM contract address) */
|
|
113
|
+
encryptionServiceId: string;
|
|
106
114
|
}
|
|
107
115
|
/**
|
|
108
116
|
* Prepaid-specific requirements.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s402",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "s402 — Chain-agnostic HTTP 402 wire format. Types, HTTP encoding, and scheme registry for five payment schemes. Wire-compatible with x402. Zero runtime dependencies.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -88,6 +88,13 @@
|
|
|
88
88
|
"default": "./dist/receipts.mjs"
|
|
89
89
|
},
|
|
90
90
|
"default": "./dist/receipts.mjs"
|
|
91
|
+
},
|
|
92
|
+
"./test-utils": {
|
|
93
|
+
"import": {
|
|
94
|
+
"types": "./dist/test-utils.d.mts",
|
|
95
|
+
"default": "./dist/test-utils.mjs"
|
|
96
|
+
},
|
|
97
|
+
"default": "./dist/test-utils.mjs"
|
|
91
98
|
}
|
|
92
99
|
},
|
|
93
100
|
"devDependencies": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
{
|
|
63
63
|
"description": "Decode unlock scheme",
|
|
64
64
|
"input": {
|
|
65
|
-
"header": "
|
|
65
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVubG9jayJdLCJuZXR3b3JrIjoic3VpOm1haW5uZXQiLCJhc3NldCI6IjB4Mjo6c3VpOjpTVUkiLCJhbW91bnQiOiIxMDAwMDAwIiwicGF5VG8iOiIweGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTAiLCJ1bmxvY2siOnsiZW5jcnlwdGlvbklkIjoiZW5jLWFiYy0xMjMiLCJlbmNyeXB0ZWRDb250ZW50SWQiOiJibG9iLXh5ei03ODkiLCJlbmNyeXB0aW9uU2VydmljZUlkIjoiMHhwa2cxMjM0NTY3ODkwIn19"
|
|
66
66
|
},
|
|
67
67
|
"expected": {
|
|
68
68
|
"s402Version": "1",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
|
|
76
76
|
"unlock": {
|
|
77
77
|
"encryptionId": "enc-abc-123",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
78
|
+
"encryptedContentId": "blob-xyz-789",
|
|
79
|
+
"encryptionServiceId": "0xpkg1234567890"
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"shouldReject": false
|
|
@@ -72,12 +72,12 @@
|
|
|
72
72
|
"payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
|
|
73
73
|
"unlock": {
|
|
74
74
|
"encryptionId": "enc-abc-123",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
75
|
+
"encryptedContentId": "blob-xyz-789",
|
|
76
|
+
"encryptionServiceId": "0xpkg1234567890"
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"expected": {
|
|
80
|
-
"header": "
|
|
80
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVubG9jayJdLCJuZXR3b3JrIjoic3VpOm1haW5uZXQiLCJhc3NldCI6IjB4Mjo6c3VpOjpTVUkiLCJhbW91bnQiOiIxMDAwMDAwIiwicGF5VG8iOiIweGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTAiLCJ1bmxvY2siOnsiZW5jcnlwdGlvbklkIjoiZW5jLWFiYy0xMjMiLCJlbmNyeXB0ZWRDb250ZW50SWQiOiJibG9iLXh5ei03ODkiLCJlbmNyeXB0aW9uU2VydmljZUlkIjoiMHhwa2cxMjM0NTY3ODkwIn19"
|
|
81
81
|
},
|
|
82
82
|
"shouldReject": false
|
|
83
83
|
},
|
|
@@ -175,14 +175,6 @@
|
|
|
175
175
|
"shouldReject": true,
|
|
176
176
|
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
177
177
|
},
|
|
178
|
-
{
|
|
179
|
-
"description": "Rejects amount exceeding u64 max",
|
|
180
|
-
"input": {
|
|
181
|
-
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbImV4YWN0Il0sIm5ldHdvcmsiOiJzdWk6bWFpbm5ldCIsImFzc2V0IjoiU1VJIiwiYW1vdW50IjoiMTg0NDY3NDQwNzM3MDk1NTE2MTYiLCJwYXlUbyI6IjB4YWJjIn0="
|
|
182
|
-
},
|
|
183
|
-
"shouldReject": true,
|
|
184
|
-
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
185
|
-
},
|
|
186
178
|
{
|
|
187
179
|
"description": "Rejects invalid base64 header",
|
|
188
180
|
"input": {
|