s402 0.3.0 → 0.5.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 +46 -0
- package/dist/compat.d.mts +4 -4
- package/dist/compat.mjs +9 -7
- package/dist/errors.d.mts +1 -0
- package/dist/errors.mjs +6 -1
- package/dist/http.d.mts +7 -3
- package/dist/http.mjs +88 -23
- package/dist/index.d.mts +161 -4
- package/dist/index.mjs +218 -23
- package/dist/{scheme-D7qqwo3Q.d.mts → scheme-m-uk4zyH.d.mts} +26 -17
- package/dist/test-utils.d.mts +1 -1
- package/dist/test-utils.mjs +8 -0
- package/dist/types.d.mts +141 -48
- package/package.json +9 -2
- package/test/conformance/vectors/body-transport.json +42 -0
- package/test/conformance/vectors/compat-normalize.json +2 -1
- package/test/conformance/vectors/payload-decode.json +33 -0
- package/test/conformance/vectors/payload-encode.json +33 -0
- package/test/conformance/vectors/requirements-decode.json +44 -0
- package/test/conformance/vectors/requirements-encode.json +44 -0
- package/test/conformance/vectors/roundtrip.json +52 -0
- package/test/conformance/vectors/settle-decode.json +14 -0
- package/test/conformance/vectors/settle-encode.json +28 -0
- package/test/conformance/vectors/settlement-verification.json +180 -0
- package/test/conformance/vectors/validation-reject.json +69 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s402",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "s402 — Chain-agnostic HTTP 402 wire format. Types, HTTP encoding, and scheme registry for
|
|
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": {
|
|
@@ -89,6 +89,13 @@
|
|
|
89
89
|
},
|
|
90
90
|
"default": "./dist/receipts.mjs"
|
|
91
91
|
},
|
|
92
|
+
"./extensions": {
|
|
93
|
+
"import": {
|
|
94
|
+
"types": "./dist/extensions.d.mts",
|
|
95
|
+
"default": "./dist/extensions.mjs"
|
|
96
|
+
},
|
|
97
|
+
"default": "./dist/extensions.mjs"
|
|
98
|
+
},
|
|
92
99
|
"./test-utils": {
|
|
93
100
|
"import": {
|
|
94
101
|
"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": {
|
|
@@ -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": {
|
|
@@ -61,6 +61,20 @@
|
|
|
61
61
|
},
|
|
62
62
|
"shouldReject": false
|
|
63
63
|
},
|
|
64
|
+
{
|
|
65
|
+
"description": "Decode success with actualAmount and depositId (upto)",
|
|
66
|
+
"input": {
|
|
67
|
+
"header": "eyJzdWNjZXNzIjp0cnVlLCJ0eERpZ2VzdCI6InVwdG9fZGVjb2RlX2RpZ2VzdCIsImFjdHVhbEFtb3VudCI6Ijc1MDAwMDAiLCJkZXBvc2l0SWQiOiIweGRlcG9zaXRfZGVjb2RlXzEyMyIsImZpbmFsaXR5TXMiOjI1MH0="
|
|
68
|
+
},
|
|
69
|
+
"expected": {
|
|
70
|
+
"success": true,
|
|
71
|
+
"txDigest": "upto_decode_digest",
|
|
72
|
+
"finalityMs": 250,
|
|
73
|
+
"actualAmount": "7500000",
|
|
74
|
+
"depositId": "0xdeposit_decode_123"
|
|
75
|
+
},
|
|
76
|
+
"shouldReject": false
|
|
77
|
+
},
|
|
64
78
|
{
|
|
65
79
|
"description": "Decode strips unknown top-level keys from settle response",
|
|
66
80
|
"input": {
|
|
@@ -61,5 +61,33 @@
|
|
|
61
61
|
"header": "eyJzdWNjZXNzIjp0cnVlLCJ0eERpZ2VzdCI6InByZXAxMjMiLCJiYWxhbmNlSWQiOiIweGJhbGFuY2U3ODkiLCJmaW5hbGl0eU1zIjozMDB9"
|
|
62
62
|
},
|
|
63
63
|
"shouldReject": false
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"description": "Success with actualAmount and depositId (upto)",
|
|
67
|
+
"input": {
|
|
68
|
+
"success": true,
|
|
69
|
+
"txDigest": "upto_digest_abc",
|
|
70
|
+
"actualAmount": "7500000",
|
|
71
|
+
"depositId": "0xdeposit_upto_123",
|
|
72
|
+
"finalityMs": 250
|
|
73
|
+
},
|
|
74
|
+
"expected": {
|
|
75
|
+
"header": "eyJzdWNjZXNzIjp0cnVlLCJ0eERpZ2VzdCI6InVwdG9fZGlnZXN0X2FiYyIsImFjdHVhbEFtb3VudCI6Ijc1MDAwMDAiLCJkZXBvc2l0SWQiOiIweGRlcG9zaXRfdXB0b18xMjMiLCJmaW5hbGl0eU1zIjoyNTB9"
|
|
76
|
+
},
|
|
77
|
+
"shouldReject": false
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"description": "Success with actualAmount at zero (upto full refund)",
|
|
81
|
+
"input": {
|
|
82
|
+
"success": true,
|
|
83
|
+
"txDigest": "upto_refund_xyz",
|
|
84
|
+
"actualAmount": "0",
|
|
85
|
+
"depositId": "0xdeposit_refund_456",
|
|
86
|
+
"finalityMs": 180
|
|
87
|
+
},
|
|
88
|
+
"expected": {
|
|
89
|
+
"header": "eyJzdWNjZXNzIjp0cnVlLCJ0eERpZ2VzdCI6InVwdG9fcmVmdW5kX3h5eiIsImFjdHVhbEFtb3VudCI6IjAiLCJkZXBvc2l0SWQiOiIweGRlcG9zaXRfcmVmdW5kXzQ1NiIsImZpbmFsaXR5TXMiOjE4MH0="
|
|
90
|
+
},
|
|
91
|
+
"shouldReject": false
|
|
64
92
|
}
|
|
65
93
|
]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"description": "Matching digest — verified is true, digests equal",
|
|
4
|
+
"payload": {
|
|
5
|
+
"s402Version": "1",
|
|
6
|
+
"scheme": "exact",
|
|
7
|
+
"payload": {
|
|
8
|
+
"transaction": "dHgtYnl0ZXMtaGVyZQ==",
|
|
9
|
+
"signature": "c2lnLWJ5dGVzLWhlcmU="
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"settleResponse": {
|
|
13
|
+
"success": true,
|
|
14
|
+
"txDigest": "KNOWN_DIGEST_ABC123"
|
|
15
|
+
},
|
|
16
|
+
"expectedShape": {
|
|
17
|
+
"verified": "boolean",
|
|
18
|
+
"expectedDigest": "string",
|
|
19
|
+
"actualDigest": "string|null"
|
|
20
|
+
},
|
|
21
|
+
"invariants": [
|
|
22
|
+
"IF verified === true THEN expectedDigest === actualDigest",
|
|
23
|
+
"actualDigest MUST equal settleResponse.txDigest"
|
|
24
|
+
],
|
|
25
|
+
"notes": "The implementation derives expectedDigest from payload.payload.transaction using a chain-specific algorithm. If the facilitator returned the correct digest, verified MUST be true."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Mismatched digest — verified is false with DIGEST_MISMATCH reason",
|
|
29
|
+
"payload": {
|
|
30
|
+
"s402Version": "1",
|
|
31
|
+
"scheme": "exact",
|
|
32
|
+
"payload": {
|
|
33
|
+
"transaction": "dHgtYnl0ZXMtaGVyZQ==",
|
|
34
|
+
"signature": "c2lnLWJ5dGVzLWhlcmU="
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"settleResponse": {
|
|
38
|
+
"success": true,
|
|
39
|
+
"txDigest": "WRONG_DIGEST_XYZ789"
|
|
40
|
+
},
|
|
41
|
+
"expectedShape": {
|
|
42
|
+
"verified": "boolean",
|
|
43
|
+
"expectedDigest": "string",
|
|
44
|
+
"actualDigest": "string|null"
|
|
45
|
+
},
|
|
46
|
+
"invariants": [
|
|
47
|
+
"verified MUST be false",
|
|
48
|
+
"expectedDigest MUST NOT equal actualDigest",
|
|
49
|
+
"actualDigest MUST equal settleResponse.txDigest",
|
|
50
|
+
"reason SHOULD contain 'DIGEST_MISMATCH' or indicate the mismatch"
|
|
51
|
+
],
|
|
52
|
+
"notes": "A malicious facilitator returns a real but unrelated digest. The client detects the mismatch locally without any RPC call."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"description": "Settle failed — no txDigest available",
|
|
56
|
+
"payload": {
|
|
57
|
+
"s402Version": "1",
|
|
58
|
+
"scheme": "exact",
|
|
59
|
+
"payload": {
|
|
60
|
+
"transaction": "dHgtYnl0ZXMtaGVyZQ==",
|
|
61
|
+
"signature": "c2lnLWJ5dGVzLWhlcmU="
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"settleResponse": {
|
|
65
|
+
"success": false,
|
|
66
|
+
"error": "Insufficient gas"
|
|
67
|
+
},
|
|
68
|
+
"expectedShape": {
|
|
69
|
+
"verified": "boolean",
|
|
70
|
+
"expectedDigest": "string",
|
|
71
|
+
"actualDigest": "string|null"
|
|
72
|
+
},
|
|
73
|
+
"invariants": [
|
|
74
|
+
"verified MUST be false",
|
|
75
|
+
"actualDigest MUST be null (no digest was returned)",
|
|
76
|
+
"reason SHOULD indicate no digest was available"
|
|
77
|
+
],
|
|
78
|
+
"notes": "When settlement fails, there is no digest to verify. The implementation should handle this gracefully rather than throwing."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"description": "Settle succeeded but txDigest is undefined",
|
|
82
|
+
"payload": {
|
|
83
|
+
"s402Version": "1",
|
|
84
|
+
"scheme": "exact",
|
|
85
|
+
"payload": {
|
|
86
|
+
"transaction": "dHgtYnl0ZXMtaGVyZQ==",
|
|
87
|
+
"signature": "c2lnLWJ5dGVzLWhlcmU="
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"settleResponse": {
|
|
91
|
+
"success": true
|
|
92
|
+
},
|
|
93
|
+
"expectedShape": {
|
|
94
|
+
"verified": "boolean",
|
|
95
|
+
"expectedDigest": "string",
|
|
96
|
+
"actualDigest": "string|null"
|
|
97
|
+
},
|
|
98
|
+
"invariants": [
|
|
99
|
+
"verified MUST be false",
|
|
100
|
+
"actualDigest MUST be null",
|
|
101
|
+
"reason SHOULD indicate missing digest"
|
|
102
|
+
],
|
|
103
|
+
"notes": "Edge case: facilitator reports success but omits the digest. This should be treated as unverifiable — the client cannot confirm settlement."
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"description": "Invalid base64 in payload transaction bytes",
|
|
107
|
+
"payload": {
|
|
108
|
+
"s402Version": "1",
|
|
109
|
+
"scheme": "exact",
|
|
110
|
+
"payload": {
|
|
111
|
+
"transaction": "!!!NOT-BASE64!!!",
|
|
112
|
+
"signature": "c2lnLWJ5dGVzLWhlcmU="
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"settleResponse": {
|
|
116
|
+
"success": true,
|
|
117
|
+
"txDigest": "SOME_DIGEST"
|
|
118
|
+
},
|
|
119
|
+
"expectedShape": {
|
|
120
|
+
"verified": "boolean",
|
|
121
|
+
"expectedDigest": "string",
|
|
122
|
+
"actualDigest": "string|null"
|
|
123
|
+
},
|
|
124
|
+
"invariants": [
|
|
125
|
+
"verified MUST be false",
|
|
126
|
+
"MUST NOT throw — return a verification result with reason"
|
|
127
|
+
],
|
|
128
|
+
"notes": "Malformed input must fail gracefully. Implementations MUST catch decode errors and return { verified: false } rather than propagating exceptions."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"description": "Stream scheme — same verification contract applies",
|
|
132
|
+
"payload": {
|
|
133
|
+
"s402Version": "1",
|
|
134
|
+
"scheme": "stream",
|
|
135
|
+
"payload": {
|
|
136
|
+
"transaction": "c3RyZWFtLXR4LWJ5dGVz",
|
|
137
|
+
"signature": "c3RyZWFtLXNpZw=="
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"settleResponse": {
|
|
141
|
+
"success": true,
|
|
142
|
+
"txDigest": "STREAM_DIGEST_123"
|
|
143
|
+
},
|
|
144
|
+
"expectedShape": {
|
|
145
|
+
"verified": "boolean",
|
|
146
|
+
"expectedDigest": "string",
|
|
147
|
+
"actualDigest": "string|null"
|
|
148
|
+
},
|
|
149
|
+
"invariants": [
|
|
150
|
+
"IF verified === true THEN expectedDigest === actualDigest",
|
|
151
|
+
"actualDigest MUST equal settleResponse.txDigest"
|
|
152
|
+
],
|
|
153
|
+
"notes": "The S8 invariant applies to ALL client-signed schemes, not just exact. Stream, escrow, prepaid, and unlock-TX1 all sign full transactions before sending to the facilitator."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"description": "Scheme that cannot verify locally (e.g. unlock-TX2)",
|
|
157
|
+
"payload": {
|
|
158
|
+
"s402Version": "1",
|
|
159
|
+
"scheme": "unlock",
|
|
160
|
+
"payload": {
|
|
161
|
+
"transaction": "",
|
|
162
|
+
"signature": ""
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"settleResponse": {
|
|
166
|
+
"success": true,
|
|
167
|
+
"txDigest": "FACILITATOR_BUILT_TX"
|
|
168
|
+
},
|
|
169
|
+
"expectedShape": {
|
|
170
|
+
"verified": "boolean",
|
|
171
|
+
"expectedDigest": "string",
|
|
172
|
+
"actualDigest": "string|null"
|
|
173
|
+
},
|
|
174
|
+
"invariants": [
|
|
175
|
+
"verified MUST be false",
|
|
176
|
+
"reason MUST be present and explain why verification is not possible"
|
|
177
|
+
],
|
|
178
|
+
"notes": "For schemes where the facilitator constructs the transaction (e.g. unlock phase 2), the client has no signed bytes to derive a digest from. The implementation MUST return { verified: false } with a reason, not throw."
|
|
179
|
+
}
|
|
180
|
+
]
|
|
@@ -175,6 +175,75 @@
|
|
|
175
175
|
"shouldReject": true,
|
|
176
176
|
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
177
177
|
},
|
|
178
|
+
{
|
|
179
|
+
"description": "Rejects upto.estimatedAmount exceeding maxAmount",
|
|
180
|
+
"input": {
|
|
181
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiJTVUkiLCJhbW91bnQiOiIxMDAwIiwicGF5VG8iOiIweGFiYyIsInVwdG8iOnsibWF4QW1vdW50IjoiNTAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsImVzdGltYXRlZEFtb3VudCI6IjUwMDAwMDEifX0="
|
|
182
|
+
},
|
|
183
|
+
"shouldReject": true,
|
|
184
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"description": "Rejects upto payload with settlementCeiling of zero",
|
|
188
|
+
"input": {
|
|
189
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRIZz0iLCJzaWduYXR1cmUiOiJjMmxuIiwibWF4QW1vdW50IjoiMTAwMDAwMCIsInNldHRsZW1lbnRDZWlsaW5nIjoiMCJ9fQ==",
|
|
190
|
+
"decodeAs": "payload"
|
|
191
|
+
},
|
|
192
|
+
"shouldReject": true,
|
|
193
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"description": "Rejects upto payload with settlementCeiling exceeding maxAmount",
|
|
197
|
+
"input": {
|
|
198
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRIZz0iLCJzaWduYXR1cmUiOiJjMmxuIiwibWF4QW1vdW50IjoiMTAwMDAwMCIsInNldHRsZW1lbnRDZWlsaW5nIjoiMTAwMDAwMSJ9fQ==",
|
|
199
|
+
"decodeAs": "payload"
|
|
200
|
+
},
|
|
201
|
+
"shouldReject": true,
|
|
202
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"description": "Rejects upto.estimatedAmount as number (must be string)",
|
|
206
|
+
"input": {
|
|
207
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbInVwdG8iXSwibmV0d29yayI6InN1aTptYWlubmV0IiwiYXNzZXQiOiJTVUkiLCJhbW91bnQiOiIxMDAwIiwicGF5VG8iOiIweGFiYyIsInVwdG8iOnsibWF4QW1vdW50IjoiNTAwMDAwMCIsInNldHRsZW1lbnREZWFkbGluZU1zIjoiMTcwMDAwMDAwMDAwMCIsImVzdGltYXRlZEFtb3VudCI6MTIzfX0="
|
|
208
|
+
},
|
|
209
|
+
"shouldReject": true,
|
|
210
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"description": "Rejects upto payload with numeric settlementCeiling (must be string)",
|
|
214
|
+
"input": {
|
|
215
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJ1cHRvIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRIZz0iLCJzaWduYXR1cmUiOiJjMmxuIiwibWF4QW1vdW50IjoiMTAwMDAwMCIsInNldHRsZW1lbnRDZWlsaW5nIjo1MDAwMDB9fQ==",
|
|
216
|
+
"decodeAs": "payload"
|
|
217
|
+
},
|
|
218
|
+
"shouldReject": true,
|
|
219
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"description": "Rejects prepaid payload with negative ratePerCall",
|
|
223
|
+
"input": {
|
|
224
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJwcmVwYWlkIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRIZz0iLCJzaWduYXR1cmUiOiJjMmxuIiwicmF0ZVBlckNhbGwiOiItNSJ9fQ==",
|
|
225
|
+
"decodeAs": "payload"
|
|
226
|
+
},
|
|
227
|
+
"shouldReject": true,
|
|
228
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"description": "Rejects prepaid payload with non-numeric maxCalls",
|
|
232
|
+
"input": {
|
|
233
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJzY2hlbWUiOiJwcmVwYWlkIiwicGF5bG9hZCI6eyJ0cmFuc2FjdGlvbiI6ImRIZz0iLCJzaWduYXR1cmUiOiJjMmxuIiwicmF0ZVBlckNhbGwiOiIxMDAiLCJtYXhDYWxscyI6ImFiYyJ9fQ==",
|
|
234
|
+
"decodeAs": "payload"
|
|
235
|
+
},
|
|
236
|
+
"shouldReject": true,
|
|
237
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"description": "Rejects mandate.minPerTx with leading zeros",
|
|
241
|
+
"input": {
|
|
242
|
+
"header": "eyJzNDAyVmVyc2lvbiI6IjEiLCJhY2NlcHRzIjpbImV4YWN0Il0sIm5ldHdvcmsiOiJzdWk6bWFpbm5ldCIsImFzc2V0IjoiU1VJIiwiYW1vdW50IjoiMTAwMCIsInBheVRvIjoiMHhhYmMiLCJtYW5kYXRlIjp7InJlcXVpcmVkIjp0cnVlLCJtaW5QZXJUeCI6IjAwNyJ9fQ=="
|
|
243
|
+
},
|
|
244
|
+
"shouldReject": true,
|
|
245
|
+
"expectedErrorCode": "INVALID_PAYLOAD"
|
|
246
|
+
},
|
|
178
247
|
{
|
|
179
248
|
"description": "Rejects invalid base64 header",
|
|
180
249
|
"input": {
|