mppx 0.8.6 → 0.8.7
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 +13 -0
- package/dist/cli/internal.js +2 -2
- package/dist/cli/internal.js.map +1 -1
- package/dist/cli/plugins/evm.d.ts +2 -0
- package/dist/cli/plugins/evm.d.ts.map +1 -0
- package/dist/cli/plugins/evm.js +51 -0
- package/dist/cli/plugins/evm.js.map +1 -0
- package/dist/cli/plugins/index.d.ts +1 -0
- package/dist/cli/plugins/index.d.ts.map +1 -1
- package/dist/cli/plugins/index.js +1 -0
- package/dist/cli/plugins/index.js.map +1 -1
- package/dist/cli/validate/challenge.d.ts +2 -0
- package/dist/cli/validate/challenge.d.ts.map +1 -1
- package/dist/cli/validate/challenge.js +184 -78
- package/dist/cli/validate/challenge.js.map +1 -1
- package/dist/cli/validate/discovery.d.ts +1 -0
- package/dist/cli/validate/discovery.d.ts.map +1 -1
- package/dist/cli/validate/discovery.js +99 -35
- package/dist/cli/validate/discovery.js.map +1 -1
- package/dist/cli/validate/helpers.d.ts +14 -1
- package/dist/cli/validate/helpers.d.ts.map +1 -1
- package/dist/cli/validate/helpers.js +0 -13
- package/dist/cli/validate/helpers.js.map +1 -1
- package/dist/cli/validate/index.d.ts +1 -0
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/index.js +98 -151
- package/dist/cli/validate/index.js.map +1 -1
- package/dist/cli/validate/payment.d.ts +1 -0
- package/dist/cli/validate/payment.d.ts.map +1 -1
- package/dist/cli/validate/payment.js +215 -144
- package/dist/cli/validate/payment.js.map +1 -1
- package/dist/evm/Chains.d.ts +4 -0
- package/dist/evm/Chains.d.ts.map +1 -1
- package/dist/evm/Chains.js +4 -0
- package/dist/evm/Chains.js.map +1 -1
- package/dist/internal/AcceptPayment.d.ts.map +1 -1
- package/dist/internal/AcceptPayment.js +7 -1
- package/dist/internal/AcceptPayment.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
- package/dist/tempo/server/internal/request-body.js +2 -0
- package/dist/tempo/server/internal/request-body.js.map +1 -1
- package/dist/tempo/session/server/ChannelStore.js +4 -4
- package/dist/tempo/session/server/ChannelStore.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +8 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +21 -1
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +1 -0
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/validation/core.d.ts +79 -0
- package/dist/validation/core.d.ts.map +1 -0
- package/dist/validation/core.js +256 -0
- package/dist/validation/core.js.map +1 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/x402/Assets.d.ts +9 -0
- package/dist/x402/Assets.d.ts.map +1 -1
- package/dist/x402/Assets.js +38 -0
- package/dist/x402/Assets.js.map +1 -1
- package/package.json +9 -4
- package/src/cli/internal.ts +2 -2
- package/src/cli/plugins/evm.ts +55 -0
- package/src/cli/plugins/index.ts +1 -0
- package/src/cli/validate/challenge.ts +334 -121
- package/src/cli/validate/discovery.test.ts +307 -0
- package/src/cli/validate/discovery.ts +104 -38
- package/src/cli/validate/helpers.ts +15 -13
- package/src/cli/validate/index.ts +117 -240
- package/src/cli/validate/payment.ts +261 -178
- package/src/cli/validate.test.ts +143 -3
- package/src/client/internal/Fetch.test.ts +142 -2
- package/src/evm/Chains.ts +6 -0
- package/src/evm/PublicInterface.test-d.ts +7 -0
- package/src/internal/AcceptPayment.test.ts +45 -0
- package/src/internal/AcceptPayment.ts +11 -1
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/html/package.json +1 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/request-body.test.ts +9 -0
- package/src/tempo/server/internal/request-body.ts +2 -0
- package/src/tempo/session/server/ChannelStore.test.ts +17 -0
- package/src/tempo/session/server/ChannelStore.ts +4 -4
- package/src/tempo/session/server/CredentialVerification.test.ts +118 -1
- package/src/tempo/session/server/CredentialVerification.ts +30 -0
- package/src/tempo/session/server/Session.integration.test.ts +5 -0
- package/src/tempo/session/server/Session.test.ts +119 -53
- package/src/tempo/session/server/Session.ts +1 -0
- package/src/validation/core.ts +348 -0
- package/src/validation/index.ts +11 -0
- package/src/x402/Assets.test.ts +42 -0
- package/src/x402/Assets.ts +40 -0
- package/src/x402/Exact.localnet.test.ts +475 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { validateChallenge, validateErrorHandling } from '../cli/validate/challenge.js'
|
|
2
|
+
import {
|
|
3
|
+
buildUrl,
|
|
4
|
+
extractEndpointsFromDiscovery,
|
|
5
|
+
extractRequestBodyFromDiscovery,
|
|
6
|
+
fetchDiscoveryDoc,
|
|
7
|
+
} from '../cli/validate/discovery.js'
|
|
8
|
+
import type { CheckResult, EndpointSpec, PathParameter } from '../cli/validate/helpers.js'
|
|
9
|
+
import {
|
|
10
|
+
isValidIntegerAmount,
|
|
11
|
+
parseEndpointArg,
|
|
12
|
+
resolveBodyForEndpoint,
|
|
13
|
+
} from '../cli/validate/helpers.js'
|
|
14
|
+
import { validatePaymentFlow } from '../cli/validate/payment.js'
|
|
15
|
+
import { validate as validateDiscoveryDoc } from '../discovery/Validate.js'
|
|
16
|
+
|
|
17
|
+
export { buildUrl }
|
|
18
|
+
export type { CheckResult, EndpointSpec, PathParameter }
|
|
19
|
+
|
|
20
|
+
export type ValidateOptions = {
|
|
21
|
+
url: string
|
|
22
|
+
endpoint?: string | undefined
|
|
23
|
+
body?: string | undefined
|
|
24
|
+
query?: string[] | undefined
|
|
25
|
+
verbose?: boolean | undefined
|
|
26
|
+
yes?: boolean | undefined
|
|
27
|
+
skipPayment?: boolean | undefined
|
|
28
|
+
discoveryPath?: string | undefined
|
|
29
|
+
onPaymentResults?: (results: CheckResult[]) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type DiscoveryResult = {
|
|
33
|
+
found: boolean
|
|
34
|
+
valid: boolean
|
|
35
|
+
endpoints: EndpointSpec[]
|
|
36
|
+
checks: CheckResult[]
|
|
37
|
+
doc: Record<string, unknown> | null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type EndpointValidationResult = {
|
|
41
|
+
method: string
|
|
42
|
+
path: string
|
|
43
|
+
challenge: CheckResult[]
|
|
44
|
+
errorHandling: CheckResult[]
|
|
45
|
+
payment: CheckResult[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type ValidateResult = {
|
|
49
|
+
url: string
|
|
50
|
+
discovery: DiscoveryResult
|
|
51
|
+
endpoints: EndpointValidationResult[]
|
|
52
|
+
summary: { passed: number; failed: number; warnings: number; skipped: number }
|
|
53
|
+
flags: {
|
|
54
|
+
sawMppEndpoint: boolean
|
|
55
|
+
sawNonMppPaymentEndpoint: boolean
|
|
56
|
+
sawMalformedChallenge: boolean
|
|
57
|
+
sawTestnet: boolean
|
|
58
|
+
sawMainnet: boolean
|
|
59
|
+
paymentSucceeded: boolean
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ValidateEvent =
|
|
64
|
+
| { phase: 'discovery'; discovery: DiscoveryResult; results: CheckResult[] }
|
|
65
|
+
| { phase: 'endpoint'; endpoint: EndpointSpec }
|
|
66
|
+
| {
|
|
67
|
+
phase: 'challenge'
|
|
68
|
+
endpoint: EndpointSpec
|
|
69
|
+
results: CheckResult[]
|
|
70
|
+
isMpp: boolean
|
|
71
|
+
isTestnet: boolean
|
|
72
|
+
isNonMppPayment: boolean
|
|
73
|
+
isMalformedChallenge: boolean
|
|
74
|
+
}
|
|
75
|
+
| { phase: 'errorHandling'; endpoint: EndpointSpec; results: CheckResult[] }
|
|
76
|
+
| {
|
|
77
|
+
phase: 'payment'
|
|
78
|
+
endpoint: EndpointSpec
|
|
79
|
+
results: CheckResult[]
|
|
80
|
+
succeeded: boolean
|
|
81
|
+
body?: string | undefined
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Streams validation results as each phase completes. */
|
|
85
|
+
export async function* validateStream(options: ValidateOptions): AsyncGenerator<ValidateEvent> {
|
|
86
|
+
const baseUrl = options.url.replace(/\/$/, '').replace(/\/openapi\.json$/i, '')
|
|
87
|
+
const verbose = options.verbose ?? false
|
|
88
|
+
|
|
89
|
+
const discovery = await runDiscovery(baseUrl, options)
|
|
90
|
+
yield { phase: 'discovery', discovery, results: discovery.checks }
|
|
91
|
+
|
|
92
|
+
for (const endpoint of discovery.endpoints) {
|
|
93
|
+
yield { phase: 'endpoint', endpoint }
|
|
94
|
+
|
|
95
|
+
let body: string | undefined
|
|
96
|
+
if (options.endpoint) {
|
|
97
|
+
body = options.body
|
|
98
|
+
} else {
|
|
99
|
+
body = resolveBodyForEndpoint(options.body, endpoint.path)
|
|
100
|
+
if (!body && discovery.doc) {
|
|
101
|
+
body = extractRequestBodyFromDiscovery(discovery.doc, endpoint)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { results: challengeResults, resolvedBody } = await validateChallenge(
|
|
106
|
+
baseUrl,
|
|
107
|
+
endpoint,
|
|
108
|
+
verbose,
|
|
109
|
+
{
|
|
110
|
+
body,
|
|
111
|
+
query: options.query,
|
|
112
|
+
discoveryDoc: discovery.doc ?? undefined,
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
const effectiveBody = resolvedBody ?? body
|
|
116
|
+
const isMpp = challengeResults.some(
|
|
117
|
+
(r) => r.severity === 'pass' && r.label === 'Challenge parseable',
|
|
118
|
+
)
|
|
119
|
+
const isTestnet = challengeResults.some(
|
|
120
|
+
(r) =>
|
|
121
|
+
r.severity === 'pass' &&
|
|
122
|
+
r.label.endsWith('Valid currency address') &&
|
|
123
|
+
r.detail === 'testnet',
|
|
124
|
+
)
|
|
125
|
+
const isNonMppPayment =
|
|
126
|
+
!isMpp && challengeResults.some((r) => r.label === 'Not an MPP endpoint')
|
|
127
|
+
const hasPaymentScheme = challengeResults.some(
|
|
128
|
+
(r) => r.severity === 'pass' && r.label === 'WWW-Authenticate header present',
|
|
129
|
+
)
|
|
130
|
+
const challengeFailed = challengeResults.some(
|
|
131
|
+
(r) => r.severity === 'fail' && r.label === 'Challenge parseable',
|
|
132
|
+
)
|
|
133
|
+
const isMalformedChallenge = !isMpp && hasPaymentScheme && challengeFailed
|
|
134
|
+
|
|
135
|
+
yield {
|
|
136
|
+
phase: 'challenge',
|
|
137
|
+
endpoint,
|
|
138
|
+
results: challengeResults,
|
|
139
|
+
isMpp,
|
|
140
|
+
isTestnet,
|
|
141
|
+
isNonMppPayment,
|
|
142
|
+
isMalformedChallenge,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (isMpp) {
|
|
146
|
+
const errorResults = await validateErrorHandling(baseUrl, endpoint, {
|
|
147
|
+
body: effectiveBody,
|
|
148
|
+
query: options.query,
|
|
149
|
+
})
|
|
150
|
+
yield { phase: 'errorHandling', endpoint, results: errorResults }
|
|
151
|
+
|
|
152
|
+
if (!options.skipPayment) {
|
|
153
|
+
const onResults = options.onPaymentResults
|
|
154
|
+
const paymentResults = await validatePaymentFlow(baseUrl, endpoint, verbose, {
|
|
155
|
+
body: effectiveBody,
|
|
156
|
+
query: options.query,
|
|
157
|
+
yes: options.yes,
|
|
158
|
+
...(onResults && { onResults }),
|
|
159
|
+
})
|
|
160
|
+
const succeeded = paymentResults.some(
|
|
161
|
+
(r) => r.severity === 'pass' && r.label === 'Payment: successful',
|
|
162
|
+
)
|
|
163
|
+
yield { phase: 'payment', endpoint, results: paymentResults, succeeded }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Runs the full validation suite and returns all results as a batch. */
|
|
170
|
+
export async function validate(options: ValidateOptions): Promise<ValidateResult> {
|
|
171
|
+
const baseUrl = options.url.replace(/\/$/, '').replace(/\/openapi\.json$/i, '')
|
|
172
|
+
let discovery: DiscoveryResult | undefined
|
|
173
|
+
const endpointResults: EndpointValidationResult[] = []
|
|
174
|
+
let current: EndpointValidationResult | undefined
|
|
175
|
+
|
|
176
|
+
let sawTestnet = false
|
|
177
|
+
let sawMainnet = false
|
|
178
|
+
let paymentSucceeded = false
|
|
179
|
+
let sawMppEndpoint = false
|
|
180
|
+
let sawNonMppPaymentEndpoint = false
|
|
181
|
+
let sawMalformedChallenge = false
|
|
182
|
+
|
|
183
|
+
for await (const event of validateStream(options)) {
|
|
184
|
+
switch (event.phase) {
|
|
185
|
+
case 'discovery':
|
|
186
|
+
discovery = event.discovery
|
|
187
|
+
break
|
|
188
|
+
case 'endpoint':
|
|
189
|
+
current = {
|
|
190
|
+
method: event.endpoint.method,
|
|
191
|
+
path: event.endpoint.path,
|
|
192
|
+
challenge: [],
|
|
193
|
+
errorHandling: [],
|
|
194
|
+
payment: [],
|
|
195
|
+
}
|
|
196
|
+
endpointResults.push(current)
|
|
197
|
+
break
|
|
198
|
+
case 'challenge':
|
|
199
|
+
if (current) current.challenge = event.results
|
|
200
|
+
if (event.isMpp) {
|
|
201
|
+
sawMppEndpoint = true
|
|
202
|
+
if (event.isTestnet) sawTestnet = true
|
|
203
|
+
else sawMainnet = true
|
|
204
|
+
}
|
|
205
|
+
if (event.isNonMppPayment) sawNonMppPaymentEndpoint = true
|
|
206
|
+
if (event.isMalformedChallenge) sawMalformedChallenge = true
|
|
207
|
+
break
|
|
208
|
+
case 'errorHandling':
|
|
209
|
+
if (current) current.errorHandling = event.results
|
|
210
|
+
break
|
|
211
|
+
case 'payment':
|
|
212
|
+
if (current) current.payment = event.results
|
|
213
|
+
if (event.succeeded) paymentSucceeded = true
|
|
214
|
+
break
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const summary = { passed: 0, failed: 0, warnings: 0, skipped: 0 }
|
|
219
|
+
if (discovery) {
|
|
220
|
+
for (const r of discovery.checks) {
|
|
221
|
+
if (r.severity === 'pass') summary.passed++
|
|
222
|
+
else if (r.severity === 'fail') summary.failed++
|
|
223
|
+
else if (r.severity === 'warn') summary.warnings++
|
|
224
|
+
else if (r.severity === 'skip') summary.skipped++
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const ep of endpointResults) {
|
|
228
|
+
for (const r of [...ep.challenge, ...ep.errorHandling, ...ep.payment]) {
|
|
229
|
+
if (r.severity === 'pass') summary.passed++
|
|
230
|
+
else if (r.severity === 'fail') summary.failed++
|
|
231
|
+
else if (r.severity === 'warn') summary.warnings++
|
|
232
|
+
else if (r.severity === 'skip') summary.skipped++
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!discovery) throw new Error('Discovery phase did not complete')
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
url: baseUrl,
|
|
240
|
+
discovery,
|
|
241
|
+
endpoints: endpointResults,
|
|
242
|
+
summary,
|
|
243
|
+
flags: {
|
|
244
|
+
sawMppEndpoint,
|
|
245
|
+
sawNonMppPaymentEndpoint,
|
|
246
|
+
sawMalformedChallenge,
|
|
247
|
+
sawTestnet,
|
|
248
|
+
sawMainnet,
|
|
249
|
+
paymentSucceeded,
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function runDiscovery(baseUrl: string, options: ValidateOptions): Promise<DiscoveryResult> {
|
|
255
|
+
const checks: CheckResult[] = []
|
|
256
|
+
let endpoints: EndpointSpec[] = []
|
|
257
|
+
let discoveryDoc: Record<string, unknown> | null = null
|
|
258
|
+
let found = false
|
|
259
|
+
let valid = false
|
|
260
|
+
|
|
261
|
+
// Try the user's path first, then root, then /api.
|
|
262
|
+
const origin = new URL(baseUrl).origin
|
|
263
|
+
let candidates: string[]
|
|
264
|
+
if (options.discoveryPath) {
|
|
265
|
+
candidates = [
|
|
266
|
+
new URL(options.discoveryPath, baseUrl + '/').href.replace(/\/openapi\.json$/i, ''),
|
|
267
|
+
]
|
|
268
|
+
} else {
|
|
269
|
+
candidates = [baseUrl, origin, `${origin}/api`]
|
|
270
|
+
candidates = [...new Set(candidates)]
|
|
271
|
+
}
|
|
272
|
+
let discoveryResult: Awaited<ReturnType<typeof fetchDiscoveryDoc>> | undefined
|
|
273
|
+
const attemptedErrors: string[] = []
|
|
274
|
+
for (const candidate of candidates) {
|
|
275
|
+
discoveryResult = await fetchDiscoveryDoc(candidate)
|
|
276
|
+
if ('error' in discoveryResult) {
|
|
277
|
+
attemptedErrors.push(`${candidate}/openapi.json: ${discoveryResult.error}`)
|
|
278
|
+
} else {
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!discoveryResult || 'error' in discoveryResult) {
|
|
284
|
+
checks.push({
|
|
285
|
+
label: 'Document found',
|
|
286
|
+
detail: attemptedErrors.join('; '),
|
|
287
|
+
hint: 'MPP servers must serve an OpenAPI document at /openapi.json with x-payment-info extensions.',
|
|
288
|
+
severity: 'fail',
|
|
289
|
+
})
|
|
290
|
+
} else {
|
|
291
|
+
found = true
|
|
292
|
+
checks.push({ label: 'Document found and parseable', severity: 'pass' })
|
|
293
|
+
|
|
294
|
+
const issues = validateDiscoveryDoc(discoveryResult.doc)
|
|
295
|
+
const errors = issues.filter((i) => i.severity === 'error')
|
|
296
|
+
const warnings = issues.filter((i) => i.severity === 'warning')
|
|
297
|
+
|
|
298
|
+
if (errors.length > 0) {
|
|
299
|
+
checks.push({
|
|
300
|
+
label: 'Valid OpenAPI structure',
|
|
301
|
+
detail: `${errors.length} error(s)`,
|
|
302
|
+
severity: 'fail',
|
|
303
|
+
})
|
|
304
|
+
for (const e of errors) {
|
|
305
|
+
checks.push({ label: e.message, detail: e.path, severity: 'fail' })
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
valid = true
|
|
309
|
+
checks.push({ label: 'Valid OpenAPI structure', severity: 'pass' })
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (const w of warnings) {
|
|
313
|
+
checks.push({ label: w.message, detail: w.path, severity: 'warn' })
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
discoveryDoc = discoveryResult.doc as Record<string, unknown>
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (options.endpoint) {
|
|
320
|
+
const parsed = parseEndpointArg(options.endpoint)
|
|
321
|
+
if (parsed) endpoints.push(parsed)
|
|
322
|
+
} else if (discoveryDoc) {
|
|
323
|
+
endpoints = extractEndpointsFromDiscovery(discoveryDoc)
|
|
324
|
+
|
|
325
|
+
const NO_AMOUNT = BigInt('999999999999999999')
|
|
326
|
+
endpoints.sort((a, b) => {
|
|
327
|
+
const aAmt = isValidIntegerAmount(a.amount) ? BigInt(a.amount!) : NO_AMOUNT
|
|
328
|
+
const bAmt = isValidIntegerAmount(b.amount) ? BigInt(b.amount!) : NO_AMOUNT
|
|
329
|
+
return aAmt < bAmt ? -1 : aAmt > bAmt ? 1 : 0
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
if (endpoints.length > 0) {
|
|
333
|
+
checks.push({
|
|
334
|
+
label: 'Paid endpoints found',
|
|
335
|
+
detail: `${endpoints.length} endpoint(s)`,
|
|
336
|
+
severity: 'pass',
|
|
337
|
+
})
|
|
338
|
+
} else {
|
|
339
|
+
checks.push({
|
|
340
|
+
label: 'Paid endpoints found',
|
|
341
|
+
detail: 'No endpoints with x-payment-info',
|
|
342
|
+
severity: 'warn',
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return { found, valid, endpoints, checks, doc: discoveryDoc }
|
|
348
|
+
}
|
package/src/x402/Assets.test.ts
CHANGED
|
@@ -208,4 +208,46 @@ describe('x402 assets', () => {
|
|
|
208
208
|
},
|
|
209
209
|
})
|
|
210
210
|
})
|
|
211
|
+
|
|
212
|
+
test('exports Celo USDC metadata', () => {
|
|
213
|
+
expect(Assets.isAsset(Assets.celo.USDC)).toBe(true)
|
|
214
|
+
expect(Assets.celo.USDC).toMatchObject({
|
|
215
|
+
address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
216
|
+
decimals: 6,
|
|
217
|
+
network: 'eip155:42220',
|
|
218
|
+
transfer: {
|
|
219
|
+
name: 'USDC',
|
|
220
|
+
type: 'eip3009',
|
|
221
|
+
version: '2',
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('exports Celo USDT metadata', () => {
|
|
227
|
+
expect(Assets.isAsset(Assets.celo.USDT)).toBe(true)
|
|
228
|
+
expect(Assets.celo.USDT).toMatchObject({
|
|
229
|
+
address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
|
|
230
|
+
decimals: 6,
|
|
231
|
+
network: 'eip155:42220',
|
|
232
|
+
transfer: {
|
|
233
|
+
name: 'Tether USD',
|
|
234
|
+
type: 'eip3009',
|
|
235
|
+
version: '1',
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('exports Celo Sepolia USDC metadata', () => {
|
|
241
|
+
expect(Assets.isAsset(Assets.celoSepolia.USDC)).toBe(true)
|
|
242
|
+
expect(Assets.celoSepolia.USDC).toMatchObject({
|
|
243
|
+
address: '0x01C5C0122039549AD1493B8220cABEdD739BC44E',
|
|
244
|
+
decimals: 6,
|
|
245
|
+
network: 'eip155:11142220',
|
|
246
|
+
transfer: {
|
|
247
|
+
name: 'USDC',
|
|
248
|
+
type: 'eip3009',
|
|
249
|
+
version: '2',
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
})
|
|
211
253
|
})
|
package/src/x402/Assets.ts
CHANGED
|
@@ -91,6 +91,46 @@ export const baseSepolia = {
|
|
|
91
91
|
}),
|
|
92
92
|
} as const
|
|
93
93
|
|
|
94
|
+
/** Celo network known assets. */
|
|
95
|
+
export const celo = {
|
|
96
|
+
USDC: define({
|
|
97
|
+
address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
98
|
+
decimals: 6,
|
|
99
|
+
network: 'eip155:42220',
|
|
100
|
+
transfer: {
|
|
101
|
+
// Celo USDC signs with the shorter EIP-712 domain name.
|
|
102
|
+
name: 'USDC',
|
|
103
|
+
type: 'eip3009',
|
|
104
|
+
version: '2',
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
107
|
+
USDT: define({
|
|
108
|
+
address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e',
|
|
109
|
+
decimals: 6,
|
|
110
|
+
network: 'eip155:42220',
|
|
111
|
+
transfer: {
|
|
112
|
+
// Celo USDT signs with domain version "1", unlike Circle's FiatToken "2".
|
|
113
|
+
name: 'Tether USD',
|
|
114
|
+
type: 'eip3009',
|
|
115
|
+
version: '1',
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
} as const
|
|
119
|
+
|
|
120
|
+
/** Celo Sepolia known assets. */
|
|
121
|
+
export const celoSepolia = {
|
|
122
|
+
USDC: define({
|
|
123
|
+
address: '0x01C5C0122039549AD1493B8220cABEdD739BC44E',
|
|
124
|
+
decimals: 6,
|
|
125
|
+
network: 'eip155:11142220',
|
|
126
|
+
transfer: {
|
|
127
|
+
name: 'USDC',
|
|
128
|
+
type: 'eip3009',
|
|
129
|
+
version: '2',
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
} as const
|
|
133
|
+
|
|
94
134
|
/** Returns true when a value is known x402 asset metadata. */
|
|
95
135
|
export function isAsset(value: unknown): value is KnownAsset {
|
|
96
136
|
if (typeof value !== 'object' || value === null) return false
|