s402 0.4.0 → 0.6.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.
@@ -60,6 +60,8 @@ interface s402ServerScheme {
60
60
  *
61
61
  * Critical: each scheme has its OWN verify logic.
62
62
  * - Exact: signature recovery + dry-run simulation + balance check
63
+ * - Upto: deposit PTB validation + maxAmount match + deadline check
64
+ * - Prepaid: deposit PTB validation + rate/cap match
63
65
  * - Stream: stream creation PTB validation + deposit check
64
66
  * - Escrow: escrow creation PTB validation + arbiter/deadline check
65
67
  * - Unlock: escrow validation (key release is separate PTB)
@@ -109,6 +111,20 @@ interface s402RouteConfig {
109
111
  protocolFeeBps?: number;
110
112
  /** Require on-chain receipt */
111
113
  receiptRequired?: boolean;
114
+ upto?: {
115
+ maxAmount: string;
116
+ settlementDeadlineMs: string;
117
+ usageReportUrl?: string;
118
+ estimatedAmount?: string;
119
+ };
120
+ prepaid?: {
121
+ ratePerCall: string;
122
+ maxCalls?: string;
123
+ minDeposit: string;
124
+ withdrawalDelayMs: string; /** Provider Ed25519 pubkey (hex). Enables v0.2 signed receipt mode. @since v0.2 */
125
+ providerPubkey?: string; /** Dispute window in ms. Required when providerPubkey is set. @since v0.2 */
126
+ disputeWindowMs?: string;
127
+ };
112
128
  stream?: {
113
129
  ratePerSecond: string;
114
130
  budgetCap: string;
@@ -124,14 +140,6 @@ interface s402RouteConfig {
124
140
  encryptedContentId: string;
125
141
  encryptionServiceId: string;
126
142
  };
127
- prepaid?: {
128
- ratePerCall: string;
129
- maxCalls?: string;
130
- minDeposit: string;
131
- withdrawalDelayMs: string; /** Provider Ed25519 pubkey (hex). Enables v0.2 signed receipt mode. @since v0.2 */
132
- providerPubkey?: string; /** Dispute window in ms. Required when providerPubkey is set. @since v0.2 */
133
- disputeWindowMs?: string;
134
- };
135
143
  }
136
144
  //#endregion
137
145
  export { s402ServerScheme as a, s402RouteConfig as i, s402DirectScheme as n, s402SettlementVerification as o, s402FacilitatorScheme as r, s402ClientScheme as t };
@@ -1,5 +1,5 @@
1
1
  import { s402PaymentPayload, s402PaymentRequirements, s402SettleResponse, s402VerifyResponse } from "./types.mjs";
2
- import { a as s402ServerScheme, r as s402FacilitatorScheme, t as s402ClientScheme } from "./scheme-tVj4sOr-.mjs";
2
+ import { a as s402ServerScheme, r as s402FacilitatorScheme, t as s402ClientScheme } from "./scheme-CKinOhyx.mjs";
3
3
 
4
4
  //#region src/test-utils.d.ts
5
5
  /**
package/dist/types.d.mts CHANGED
@@ -3,8 +3,14 @@ import { s402ErrorCodeType } from "./errors.mjs";
3
3
  //#region src/types.d.ts
4
4
  /** Current protocol version. Always lowercase s. */
5
5
  declare const S402_VERSION: "1";
6
- /** The five s402 payment schemes */
7
- type s402Scheme = 'exact' | 'stream' | 'escrow' | 'unlock' | 'prepaid';
6
+ /**
7
+ * The six s402 payment schemes, ordered by complexity:
8
+ *
9
+ * TIER 1 — Single Payment: exact (fixed amount), upto (variable amount)
10
+ * TIER 2 — Persistent Balance: prepaid (multi-claim), stream (time-based)
11
+ * TIER 3 — Conditional Release: escrow (arbiter), unlock (encryption)
12
+ */
13
+ type s402Scheme = 'exact' | 'upto' | 'prepaid' | 'stream' | 'escrow' | 'unlock';
8
14
  /** Settlement mode: facilitator-mediated or direct on-chain */
9
15
  type s402SettlementMode = 'facilitator' | 'direct';
10
16
  /**
@@ -64,14 +70,18 @@ interface s402PaymentRequirements {
64
70
  settlementMode?: s402SettlementMode;
65
71
  /** When these requirements expire (Unix timestamp ms). Facilitator MUST reject after this. */
66
72
  expiresAt?: number;
73
+ /** Extra fields for upto scheme (usage-based, variable settlement) */
74
+ upto?: s402UptoExtra;
75
+ /** Extra fields for prepaid scheme */
76
+ prepaid?: s402PrepaidExtra;
67
77
  /** Extra fields for stream scheme */
68
78
  stream?: s402StreamExtra;
69
79
  /** Extra fields for escrow scheme */
70
80
  escrow?: s402EscrowExtra;
71
81
  /** Extra fields for unlock scheme (pay-to-decrypt encrypted content) */
72
82
  unlock?: s402UnlockExtra;
73
- /** Extra fields for prepaid scheme */
74
- prepaid?: s402PrepaidExtra;
83
+ /** Settlement overrides (used by upto scheme — server provides actual amount at settle-time) */
84
+ settlementOverrides?: s402SettlementOverrides;
75
85
  /**
76
86
  * Arbitrary extension data (forward-compatible extensibility).
77
87
  *
@@ -83,34 +93,57 @@ interface s402PaymentRequirements {
83
93
  */
84
94
  extensions?: Record<string, unknown>;
85
95
  }
86
- /** Stream-specific requirements */
87
- interface s402StreamExtra {
88
- /** Rate in base units per second */
89
- ratePerSecond: string;
90
- /** Maximum budget cap in base units */
91
- budgetCap: string;
92
- /** Minimum initial deposit in base units */
93
- minDeposit: string;
94
- /** URL for stream status checks (phase 2) */
95
- streamSetupUrl?: string;
96
- }
97
- /** Escrow-specific requirements */
98
- interface s402EscrowExtra {
99
- /** Seller/payee address */
100
- seller: string;
101
- /** Arbiter address for dispute resolution */
102
- arbiter?: string;
103
- /** Escrow deadline in milliseconds since epoch */
104
- deadlineMs: string;
96
+ /**
97
+ * Upto-specific requirements (usage-based, variable settlement).
98
+ *
99
+ * The client authorizes up to `maxAmount`; the facilitator settles the actual
100
+ * amount (provided by the server via `settlementOverrides`) at settlement time.
101
+ * Remainder is returned to the payer on-chain.
102
+ *
103
+ * TRUST MODEL: The client can bound its exposure via `settlementCeiling` in the
104
+ * payment payload an on-chain-enforced cap tighter than `maxAmount`. The server
105
+ * advertises `estimatedAmount` so the client can set a tight ceiling (e.g., 1.2x
106
+ * the estimate). Without `settlementCeiling`, the facilitator can settle up to
107
+ * `maxAmount`. See ADR-003 §Decision 3 and §Decision 8.
108
+ *
109
+ * SEMANTIC CLARITY: `amount` on the parent `s402PaymentRequirements` is the
110
+ * EXACT price for the `exact` scheme. For `upto`, `maxAmount` here is the
111
+ * ceiling the two are intentionally separate fields to avoid the semantic
112
+ * overloading that x402 suffers from.
113
+ */
114
+ interface s402UptoExtra {
115
+ /** Maximum authorized amount in base units. Client deposits this; actual may be less. */
116
+ maxAmount: string;
117
+ /**
118
+ * Deadline for settlement in milliseconds since epoch.
119
+ * After this time, the payer can reclaim the full deposit via `expire()`.
120
+ * Must be in the future at verify-time. Facilitator MUST reject expired deposits.
121
+ */
122
+ settlementDeadlineMs: string;
123
+ /** Optional URL where the client can query usage/metering data (informational) */
124
+ usageReportUrl?: string;
125
+ /**
126
+ * Server's estimated cost in base units (advisory, optional).
127
+ * Helps the client set a tight `settlementCeiling` in the payload.
128
+ * Must be ≤ maxAmount when present. Not enforced on-chain — purely informational.
129
+ */
130
+ estimatedAmount?: string;
105
131
  }
106
- /** Unlock-specific requirements (pay-to-decrypt encrypted content) */
107
- interface s402UnlockExtra {
108
- /** Encryption ID for key servers */
109
- encryptionId: string;
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;
132
+ /**
133
+ * Settlement overrides — server provides the actual amount to the facilitator.
134
+ *
135
+ * Used by the `upto` scheme: the resource server tells the facilitator how much
136
+ * of the authorized maximum to actually charge, based on observed usage.
137
+ * Threaded via `requirements.settlementOverrides` so the facilitator's `process()`
138
+ * signature (payload, requirements) doesn't need to change.
139
+ *
140
+ * TRUST MODEL: The server is trusted to report honest usage. The facilitator
141
+ * enforces `actualAmount <= maxAmount` but cannot verify usage independently.
142
+ * On-chain events provide an audit trail for dispute resolution.
143
+ */
144
+ interface s402SettlementOverrides {
145
+ /** Actual amount to settle in base units. Must be ≤ maxAmount from UptoExtra. */
146
+ actualAmount: string;
114
147
  }
115
148
  /**
116
149
  * Prepaid-specific requirements.
@@ -152,6 +185,35 @@ interface s402PrepaidExtra {
152
185
  */
153
186
  disputeWindowMs?: string;
154
187
  }
188
+ /** Stream-specific requirements */
189
+ interface s402StreamExtra {
190
+ /** Rate in base units per second */
191
+ ratePerSecond: string;
192
+ /** Maximum budget cap in base units */
193
+ budgetCap: string;
194
+ /** Minimum initial deposit in base units */
195
+ minDeposit: string;
196
+ /** URL for stream status checks (phase 2) */
197
+ streamSetupUrl?: string;
198
+ }
199
+ /** Escrow-specific requirements */
200
+ interface s402EscrowExtra {
201
+ /** Seller/payee address */
202
+ seller: string;
203
+ /** Arbiter address for dispute resolution */
204
+ arbiter?: string;
205
+ /** Escrow deadline in milliseconds since epoch */
206
+ deadlineMs: string;
207
+ }
208
+ /** Unlock-specific requirements (pay-to-decrypt encrypted content) */
209
+ interface s402UnlockExtra {
210
+ /** Encryption ID for key servers */
211
+ encryptionId: string;
212
+ /** Content identifier for the encrypted blob (e.g., Walrus blob ID, IPFS CID) */
213
+ encryptedContentId: string;
214
+ /** Identifier for the encryption service or module (e.g., Sui package ID, EVM contract address) */
215
+ encryptionServiceId: string;
216
+ }
155
217
  /** Mandate requirements in a 402 response — tells client what mandate is needed */
156
218
  interface s402MandateRequirements {
157
219
  /** Whether a mandate is required (true) or optional (false = speeds up if present) */
@@ -193,6 +255,46 @@ interface s402ExactPayload extends s402PaymentPayloadBase {
193
255
  signature: string;
194
256
  };
195
257
  }
258
+ /**
259
+ * Upto payment: signed deposit transaction for variable-amount settlement.
260
+ *
261
+ * The client deposits `maxAmount` into an on-chain UptoDeposit proxy.
262
+ * The facilitator later calls `settle(actual_amount)` where
263
+ * `actual ≤ min(maxAmount, settlementCeiling)`, returning the remainder
264
+ * to the payer. If settlement doesn't happen before the deadline, the
265
+ * payer can reclaim via `expire()`.
266
+ */
267
+ interface s402UptoPayload extends s402PaymentPayloadBase {
268
+ scheme: 'upto';
269
+ payload: {
270
+ /** Base64-encoded signed deposit transaction (creates UptoDeposit on-chain) */transaction: string; /** Base64-encoded signature */
271
+ signature: string; /** Maximum authorized amount (must match requirements.upto.maxAmount) */
272
+ maxAmount: string;
273
+ /**
274
+ * Client-chosen settlement ceiling (optional, on-chain enforced).
275
+ * The Move contract rejects settlements where `actualAmount > settlementCeiling`.
276
+ * Must satisfy: `1 <= settlementCeiling <= maxAmount`.
277
+ * Omit to allow settlement up to `maxAmount` (backwards compatible).
278
+ *
279
+ * Servers SHOULD check this before serving expensive resources — if
280
+ * `settlementCeiling < estimatedCost`, respond with an updated 402.
281
+ */
282
+ settlementCeiling?: string;
283
+ };
284
+ }
285
+ /**
286
+ * Prepaid payment: agent deposits into a PrepaidBalance shared object.
287
+ * This is the deposit phase only — claims are provider-initiated (not via HTTP 402).
288
+ */
289
+ interface s402PrepaidPayload extends s402PaymentPayloadBase {
290
+ scheme: 'prepaid';
291
+ payload: {
292
+ /** Base64-encoded deposit PTB */transaction: string; /** Agent's signature */
293
+ signature: string; /** Committed rate per call (must match requirements) */
294
+ ratePerCall: string; /** Committed max calls cap (must match requirements) */
295
+ maxCalls?: string;
296
+ };
297
+ }
196
298
  /** Stream payment: signed stream creation transaction */
197
299
  interface s402StreamPayload extends s402PaymentPayloadBase {
198
300
  scheme: 'stream';
@@ -226,21 +328,8 @@ interface s402UnlockPayload extends s402PaymentPayloadBase {
226
328
  encryptionId: string;
227
329
  };
228
330
  }
229
- /**
230
- * Prepaid payment: agent deposits into a PrepaidBalance shared object.
231
- * This is the deposit phase only — claims are provider-initiated (not via HTTP 402).
232
- */
233
- interface s402PrepaidPayload extends s402PaymentPayloadBase {
234
- scheme: 'prepaid';
235
- payload: {
236
- /** Base64-encoded deposit PTB */transaction: string; /** Agent's signature */
237
- signature: string; /** Committed rate per call (must match requirements) */
238
- ratePerCall: string; /** Committed max calls cap (must match requirements) */
239
- maxCalls?: string;
240
- };
241
- }
242
331
  /** Discriminated union of all payment payloads */
243
- type s402PaymentPayload = s402ExactPayload | s402StreamPayload | s402EscrowPayload | s402UnlockPayload | s402PrepaidPayload;
332
+ type s402PaymentPayload = s402ExactPayload | s402UptoPayload | s402PrepaidPayload | s402StreamPayload | s402EscrowPayload | s402UnlockPayload;
244
333
  interface s402SettleResponse {
245
334
  /** Whether settlement was successful */
246
335
  success: boolean;
@@ -250,12 +339,16 @@ interface s402SettleResponse {
250
339
  receiptId?: string;
251
340
  /** Time to finality in milliseconds */
252
341
  finalityMs?: number;
342
+ /** Actual amount settled in base units (for upto scheme — fixes x402's opacity) */
343
+ actualAmount?: string;
344
+ /** UptoDeposit object ID (for upto scheme) */
345
+ depositId?: string;
346
+ /** PrepaidBalance object ID (for prepaid scheme) */
347
+ balanceId?: string;
253
348
  /** Stream object ID (for stream scheme) */
254
349
  streamId?: string;
255
350
  /** Escrow object ID (for escrow scheme) */
256
351
  escrowId?: string;
257
- /** PrepaidBalance object ID (for prepaid scheme) */
258
- balanceId?: string;
259
352
  /** Error message if settlement failed */
260
353
  error?: string;
261
354
  /** Typed error code for programmatic failure handling */
@@ -353,7 +446,8 @@ declare const S402_HEADERS: {
353
446
  /** Server → client: payment requirements (base64 JSON in 402 response) */readonly PAYMENT_REQUIRED: "payment-required"; /** Client → server: payment payload (base64 JSON) */
354
447
  readonly PAYMENT: "x-payment"; /** Server → client: settlement result (base64 JSON) */
355
448
  readonly PAYMENT_RESPONSE: "payment-response"; /** Client → server: active stream ID (phase 2 of stream protocol) */
356
- readonly STREAM_ID: "x-stream-id";
449
+ readonly STREAM_ID: "x-stream-id"; /** Client → server: scheme preference negotiation (RFC 7231-style q-values) */
450
+ readonly ACCEPT_PAYMENT: "accept-payment";
357
451
  };
358
452
  //#endregion
359
- export { 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 };
453
+ export { S402_HEADERS, S402_VERSION, s402Discovery, s402EscrowExtra, s402EscrowPayload, s402ExactPayload, s402Mandate, s402MandateRequirements, s402PaymentPayload, s402PaymentPayloadBase, s402PaymentRequirements, s402PaymentSession, s402PrepaidExtra, s402PrepaidPayload, s402RegistryQuery, s402Scheme, s402ServiceEntry, s402SettleResponse, s402SettlementMode, s402SettlementOverrides, s402StreamExtra, s402StreamPayload, s402UnlockExtra, s402UnlockPayload, s402UptoExtra, s402UptoPayload, s402VerifyResponse };
package/dist/types.mjs CHANGED
@@ -10,7 +10,8 @@ const S402_HEADERS = {
10
10
  PAYMENT_REQUIRED: "payment-required",
11
11
  PAYMENT: "x-payment",
12
12
  PAYMENT_RESPONSE: "payment-response",
13
- STREAM_ID: "x-stream-id"
13
+ STREAM_ID: "x-stream-id",
14
+ ACCEPT_PAYMENT: "accept-payment"
14
15
  };
15
16
 
16
17
  //#endregion
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "s402",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
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.",
5
+ "description": "s402 — Chain-agnostic HTTP 402 wire format. Types, HTTP encoding, and scheme registry for six payment schemes. Wire-compatible with x402. Zero runtime dependencies.",
6
6
  "license": "Apache-2.0",
7
7
  "author": "SweeInc <daniel@sweeinc.com> (https://s402-protocol.org)",
8
8
  "repository": {
@@ -75,6 +75,13 @@
75
75
  },
76
76
  "default": "./dist/compat.mjs"
77
77
  },
78
+ "./compat-mpp": {
79
+ "import": {
80
+ "types": "./dist/compat-mpp.d.mts",
81
+ "default": "./dist/compat-mpp.mjs"
82
+ },
83
+ "default": "./dist/compat-mpp.mjs"
84
+ },
78
85
  "./errors": {
79
86
  "import": {
80
87
  "types": "./dist/errors.d.mts",
@@ -89,6 +96,13 @@
89
96
  },
90
97
  "default": "./dist/receipts.mjs"
91
98
  },
99
+ "./extensions": {
100
+ "import": {
101
+ "types": "./dist/extensions.d.mts",
102
+ "default": "./dist/extensions.mjs"
103
+ },
104
+ "default": "./dist/extensions.mjs"
105
+ },
92
106
  "./test-utils": {
93
107
  "import": {
94
108
  "types": "./dist/test-utils.d.mts",
@@ -73,6 +73,48 @@
73
73
  },
74
74
  "shouldReject": false
75
75
  },
76
+ {
77
+ "description": "Upto requirements body with estimatedAmount",
78
+ "input": {
79
+ "type": "requirements",
80
+ "value": {
81
+ "s402Version": "1",
82
+ "accepts": [
83
+ "upto"
84
+ ],
85
+ "network": "sui:mainnet",
86
+ "asset": "0x2::sui::SUI",
87
+ "amount": "1000000",
88
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
89
+ "upto": {
90
+ "maxAmount": "10000000",
91
+ "settlementDeadlineMs": "1700000000000",
92
+ "usageReportUrl": "https://api.example.com/usage",
93
+ "estimatedAmount": "7500000"
94
+ }
95
+ }
96
+ },
97
+ "expected": {
98
+ "body": "{\"s402Version\":\"1\",\"accepts\":[\"upto\"],\"network\":\"sui:mainnet\",\"asset\":\"0x2::sui::SUI\",\"amount\":\"1000000\",\"payTo\":\"0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890\",\"upto\":{\"maxAmount\":\"10000000\",\"settlementDeadlineMs\":\"1700000000000\",\"usageReportUrl\":\"https://api.example.com/usage\",\"estimatedAmount\":\"7500000\"}}",
99
+ "decoded": {
100
+ "s402Version": "1",
101
+ "accepts": [
102
+ "upto"
103
+ ],
104
+ "network": "sui:mainnet",
105
+ "asset": "0x2::sui::SUI",
106
+ "amount": "1000000",
107
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
108
+ "upto": {
109
+ "maxAmount": "10000000",
110
+ "settlementDeadlineMs": "1700000000000",
111
+ "usageReportUrl": "https://api.example.com/usage",
112
+ "estimatedAmount": "7500000"
113
+ }
114
+ }
115
+ },
116
+ "shouldReject": false
117
+ },
76
118
  {
77
119
  "description": "Payload body encode/decode",
78
120
  "input": {
@@ -18,7 +18,8 @@
18
18
  "network": "sui:mainnet",
19
19
  "asset": "0x2::sui::SUI",
20
20
  "amount": "1000000",
21
- "payTo": "0xrecipient123"
21
+ "payTo": "0xrecipient123",
22
+ "expiresAt": 1700000060000
22
23
  },
23
24
  "shouldReject": false
24
25
  },
@@ -47,6 +47,39 @@
47
47
  },
48
48
  "shouldReject": false
49
49
  },
50
+ {
51
+ "description": "Decode upto payload with settlementCeiling",
52
+ "input": {
53
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjEwMDAwMDAwIiwic2V0dGxlbWVudENlaWxpbmciOiI4MDAwMDAwIn19"
54
+ },
55
+ "expected": {
56
+ "s402Version": "1",
57
+ "scheme": "upto",
58
+ "payload": {
59
+ "transaction": "dXB0b190eA==",
60
+ "signature": "dXB0b19zaWc=",
61
+ "maxAmount": "10000000",
62
+ "settlementCeiling": "8000000"
63
+ }
64
+ },
65
+ "shouldReject": false
66
+ },
67
+ {
68
+ "description": "Decode upto payload without settlementCeiling",
69
+ "input": {
70
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjUwMDAwMDAifX0="
71
+ },
72
+ "expected": {
73
+ "s402Version": "1",
74
+ "scheme": "upto",
75
+ "payload": {
76
+ "transaction": "dXB0b190eA==",
77
+ "signature": "dXB0b19zaWc=",
78
+ "maxAmount": "5000000"
79
+ }
80
+ },
81
+ "shouldReject": false
82
+ },
50
83
  {
51
84
  "description": "Decode stream payload",
52
85
  "input": {
@@ -31,6 +31,39 @@
31
31
  },
32
32
  "shouldReject": false
33
33
  },
34
+ {
35
+ "description": "Upto payload with maxAmount and settlementCeiling",
36
+ "input": {
37
+ "s402Version": "1",
38
+ "scheme": "upto",
39
+ "payload": {
40
+ "transaction": "dXB0b190eA==",
41
+ "signature": "dXB0b19zaWc=",
42
+ "maxAmount": "10000000",
43
+ "settlementCeiling": "8000000"
44
+ }
45
+ },
46
+ "expected": {
47
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjEwMDAwMDAwIiwic2V0dGxlbWVudENlaWxpbmciOiI4MDAwMDAwIn19"
48
+ },
49
+ "shouldReject": false
50
+ },
51
+ {
52
+ "description": "Upto payload without settlementCeiling (backwards compatible)",
53
+ "input": {
54
+ "s402Version": "1",
55
+ "scheme": "upto",
56
+ "payload": {
57
+ "transaction": "dXB0b190eA==",
58
+ "signature": "dXB0b19zaWc=",
59
+ "maxAmount": "5000000"
60
+ }
61
+ },
62
+ "expected": {
63
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjUwMDAwMDAifX0="
64
+ },
65
+ "shouldReject": false
66
+ },
34
67
  {
35
68
  "description": "Stream payload",
36
69
  "input": {
@@ -38,6 +38,50 @@
38
38
  },
39
39
  "shouldReject": false
40
40
  },
41
+ {
42
+ "description": "Decode upto scheme with estimatedAmount",
43
+ "input": {
44
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiIxMDAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsInVzYWdlUmVwb3J0VXJsIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vdXNhZ2UiLCJlc3RpbWF0ZWRBbW91bnQiOiI3NTAwMDAwIn19"
45
+ },
46
+ "expected": {
47
+ "s402Version": "1",
48
+ "accepts": [
49
+ "upto"
50
+ ],
51
+ "network": "sui:mainnet",
52
+ "asset": "0x2::sui::SUI",
53
+ "amount": "1000000",
54
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
55
+ "upto": {
56
+ "maxAmount": "10000000",
57
+ "settlementDeadlineMs": "1700000000000",
58
+ "usageReportUrl": "https://api.example.com/usage",
59
+ "estimatedAmount": "7500000"
60
+ }
61
+ },
62
+ "shouldReject": false
63
+ },
64
+ {
65
+ "description": "Decode upto scheme minimal",
66
+ "input": {
67
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiI1MDAwMDAwIiwic2V0dGxlbWVudERlYWRsaW5lTXMiOiIxNzAwMDAwMDAwMDAwIn19"
68
+ },
69
+ "expected": {
70
+ "s402Version": "1",
71
+ "accepts": [
72
+ "upto"
73
+ ],
74
+ "network": "sui:mainnet",
75
+ "asset": "0x2::sui::SUI",
76
+ "amount": "1000000",
77
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
78
+ "upto": {
79
+ "maxAmount": "5000000",
80
+ "settlementDeadlineMs": "1700000000000"
81
+ }
82
+ },
83
+ "shouldReject": false
84
+ },
41
85
  {
42
86
  "description": "Decode escrow scheme",
43
87
  "input": {
@@ -38,6 +38,50 @@
38
38
  },
39
39
  "shouldReject": false
40
40
  },
41
+ {
42
+ "description": "Upto scheme with estimatedAmount",
43
+ "input": {
44
+ "s402Version": "1",
45
+ "accepts": [
46
+ "upto"
47
+ ],
48
+ "network": "sui:mainnet",
49
+ "asset": "0x2::sui::SUI",
50
+ "amount": "1000000",
51
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
52
+ "upto": {
53
+ "maxAmount": "10000000",
54
+ "settlementDeadlineMs": "1700000000000",
55
+ "usageReportUrl": "https://api.example.com/usage",
56
+ "estimatedAmount": "7500000"
57
+ }
58
+ },
59
+ "expected": {
60
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiIxMDAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsInVzYWdlUmVwb3J0VXJsIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vdXNhZ2UiLCJlc3RpbWF0ZWRBbW91bnQiOiI3NTAwMDAwIn19"
61
+ },
62
+ "shouldReject": false
63
+ },
64
+ {
65
+ "description": "Upto scheme minimal (no estimatedAmount)",
66
+ "input": {
67
+ "s402Version": "1",
68
+ "accepts": [
69
+ "upto"
70
+ ],
71
+ "network": "sui:mainnet",
72
+ "asset": "0x2::sui::SUI",
73
+ "amount": "1000000",
74
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
75
+ "upto": {
76
+ "maxAmount": "5000000",
77
+ "settlementDeadlineMs": "1700000000000"
78
+ }
79
+ },
80
+ "expected": {
81
+ "header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiI1MDAwMDAwIiwic2V0dGxlbWVudERlYWRsaW5lTXMiOiIxNzAwMDAwMDAwMDAwIn19"
82
+ },
83
+ "shouldReject": false
84
+ },
41
85
  {
42
86
  "description": "Escrow scheme with extras",
43
87
  "input": {
@@ -123,6 +123,58 @@
123
123
  },
124
124
  "shouldReject": false
125
125
  },
126
+ {
127
+ "description": "Upto requirements with estimatedAmount roundtrip",
128
+ "input": {
129
+ "type": "requirements",
130
+ "transport": "header",
131
+ "value": {
132
+ "s402Version": "1",
133
+ "accepts": [
134
+ "upto"
135
+ ],
136
+ "network": "sui:mainnet",
137
+ "asset": "0x2::sui::SUI",
138
+ "amount": "1000000",
139
+ "payTo": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
140
+ "upto": {
141
+ "maxAmount": "10000000",
142
+ "settlementDeadlineMs": "1700000000000",
143
+ "usageReportUrl": "https://api.example.com/usage",
144
+ "estimatedAmount": "7500000"
145
+ }
146
+ }
147
+ },
148
+ "expected": {
149
+ "firstEncode": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiIxMDAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsInVzYWdlUmVwb3J0VXJsIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vdXNhZ2UiLCJlc3RpbWF0ZWRBbW91bnQiOiI3NTAwMDAwIn19",
150
+ "reEncode": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiIweDI6OnN1aTo6U1VJIiwiYW1vdW50IjoiMTAwMDAwMCIsInBheVRvIjoiMHhhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwIiwidXB0byI6eyJtYXhBbW91bnQiOiIxMDAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsInVzYWdlUmVwb3J0VXJsIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vdXNhZ2UiLCJlc3RpbWF0ZWRBbW91bnQiOiI3NTAwMDAwIn19",
151
+ "identical": true
152
+ },
153
+ "shouldReject": false
154
+ },
155
+ {
156
+ "description": "Upto payload with settlementCeiling roundtrip",
157
+ "input": {
158
+ "type": "payload",
159
+ "transport": "header",
160
+ "value": {
161
+ "s402Version": "1",
162
+ "scheme": "upto",
163
+ "payload": {
164
+ "transaction": "dXB0b190eA==",
165
+ "signature": "dXB0b19zaWc=",
166
+ "maxAmount": "10000000",
167
+ "settlementCeiling": "8000000"
168
+ }
169
+ }
170
+ },
171
+ "expected": {
172
+ "firstEncode": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjEwMDAwMDAwIiwic2V0dGxlbWVudENlaWxpbmciOiI4MDAwMDAwIn19",
173
+ "reEncode": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRYQjBiMTkwZUE9PSIsInNpZ25hdHVyZSI6ImRYQjBiMTl6YVdjPSIsIm1heEFtb3VudCI6IjEwMDAwMDAwIiwic2V0dGxlbWVudENlaWxpbmciOiI4MDAwMDAwIn19",
174
+ "identical": true
175
+ },
176
+ "shouldReject": false
177
+ },
126
178
  {
127
179
  "description": "Complex requirements with extensions roundtrip",
128
180
  "input": {