mppx 0.8.5 → 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 +25 -0
- package/dist/Receipt.d.ts +20 -2
- package/dist/Receipt.d.ts.map +1 -1
- package/dist/Receipt.js +19 -12
- package/dist/Receipt.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +6 -4
- package/dist/cli/cli.js.map +1 -1
- 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/plugins/tempo.d.ts.map +1 -1
- package/dist/cli/plugins/tempo.js +4 -2
- package/dist/cli/plugins/tempo.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/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +6 -1
- package/dist/client/internal/Fetch.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/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +6 -2
- package/dist/tempo/client/Charge.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/precompile/Chain.d.ts +26 -2
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +38 -5
- package/dist/tempo/session/precompile/Chain.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/Receipt.test.ts +53 -0
- package/src/Receipt.ts +26 -13
- package/src/cli/cli.test.ts +41 -5
- package/src/cli/cli.ts +7 -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/plugins/tempo.ts +6 -2
- 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 +186 -2
- package/src/client/internal/Fetch.ts +5 -1
- 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/client/Charge.test.ts +61 -0
- package/src/tempo/client/Charge.ts +6 -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/precompile/Chain.test.ts +121 -2
- package/src/tempo/session/precompile/Chain.ts +56 -6
- 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
package/src/Receipt.ts
CHANGED
|
@@ -3,17 +3,7 @@ import { Base64 } from 'ox'
|
|
|
3
3
|
import * as Constants from './Constants.js'
|
|
4
4
|
import * as z from './zod.js'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* Schema for a payment receipt.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { Receipt } from 'mppx'
|
|
12
|
-
*
|
|
13
|
-
* const receipt = Receipt.Schema.parse(data)
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export const Schema = z.object({
|
|
6
|
+
const shape = {
|
|
17
7
|
/** Payment method used (e.g., "tempo", "stripe"). */
|
|
18
8
|
method: z.string(),
|
|
19
9
|
/** Method-specific reference (e.g., transaction hash). */
|
|
@@ -26,11 +16,34 @@ export const Schema = z.object({
|
|
|
26
16
|
status: z.literal('success'),
|
|
27
17
|
/** RFC 3339 settlement timestamp. */
|
|
28
18
|
timestamp: z.datetime(),
|
|
29
|
-
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Base-field schema used only to derive the {@link Receipt} type without an index signature. */
|
|
22
|
+
const BaseSchema = z.object(shape)
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Schema for a payment receipt.
|
|
26
|
+
*
|
|
27
|
+
* Method specifications may define additional receipt fields beyond the
|
|
28
|
+
* base set (per the core spec's Payment-Receipt section); unknown fields
|
|
29
|
+
* are preserved through parse/serialize round-trips rather than stripped.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { Receipt } from 'mppx'
|
|
34
|
+
*
|
|
35
|
+
* const receipt = Receipt.Schema.parse(data)
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const Schema = z.looseObject(shape)
|
|
30
39
|
|
|
31
40
|
/**
|
|
32
41
|
* Payment receipt returned after verification.
|
|
33
42
|
*
|
|
43
|
+
* Method-specific extension fields are preserved at runtime but not part of
|
|
44
|
+
* this base type; method packages can type them via intersection
|
|
45
|
+
* (e.g. `Receipt.Receipt & { originTxHash: string }`).
|
|
46
|
+
*
|
|
34
47
|
* @example
|
|
35
48
|
* ```ts
|
|
36
49
|
* import { Receipt } from 'mppx'
|
|
@@ -43,7 +56,7 @@ export const Schema = z.object({
|
|
|
43
56
|
* }
|
|
44
57
|
* ```
|
|
45
58
|
*/
|
|
46
|
-
export type Receipt = z.infer<typeof
|
|
59
|
+
export type Receipt = z.infer<typeof BaseSchema>
|
|
47
60
|
|
|
48
61
|
/**
|
|
49
62
|
* Deserializes a Payment-Receipt header value to a receipt.
|
package/src/cli/cli.test.ts
CHANGED
|
@@ -43,7 +43,7 @@ afterAll(() => {
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
async function serve(argv: string[], options?: { env?: Record<string, string | undefined> }) {
|
|
46
|
-
|
|
46
|
+
const stdoutChunks: Buffer[] = []
|
|
47
47
|
let stderr = ''
|
|
48
48
|
let exitCode: number | undefined
|
|
49
49
|
const saved: Record<string, string | undefined> = {}
|
|
@@ -58,7 +58,9 @@ async function serve(argv: string[], options?: { env?: Record<string, string | u
|
|
|
58
58
|
const origLog = console.log
|
|
59
59
|
const origError = console.error
|
|
60
60
|
process.stdout.write = ((chunk: unknown) => {
|
|
61
|
-
|
|
61
|
+
if (typeof chunk === 'string') stdoutChunks.push(Buffer.from(chunk))
|
|
62
|
+
else if (chunk instanceof Uint8Array) stdoutChunks.push(Buffer.from(chunk))
|
|
63
|
+
else stdoutChunks.push(Buffer.from(String(chunk)))
|
|
62
64
|
return true
|
|
63
65
|
}) as typeof process.stdout.write
|
|
64
66
|
process.stderr.write = ((chunk: unknown) => {
|
|
@@ -66,7 +68,7 @@ async function serve(argv: string[], options?: { env?: Record<string, string | u
|
|
|
66
68
|
return true
|
|
67
69
|
}) as typeof process.stderr.write
|
|
68
70
|
console.log = (...args: unknown[]) => {
|
|
69
|
-
|
|
71
|
+
stdoutChunks.push(Buffer.from(`${args.map(String).join(' ')}\n`))
|
|
70
72
|
}
|
|
71
73
|
console.error = (...args: unknown[]) => {
|
|
72
74
|
stderr += `${args.map(String).join(' ')}\n`
|
|
@@ -74,7 +76,7 @@ async function serve(argv: string[], options?: { env?: Record<string, string | u
|
|
|
74
76
|
try {
|
|
75
77
|
await cli.serve(argv, {
|
|
76
78
|
stdout(s: string) {
|
|
77
|
-
|
|
79
|
+
stdoutChunks.push(Buffer.from(s))
|
|
78
80
|
},
|
|
79
81
|
exit(code: number) {
|
|
80
82
|
exitCode = code
|
|
@@ -90,7 +92,8 @@ async function serve(argv: string[], options?: { env?: Record<string, string | u
|
|
|
90
92
|
else process.env[key] = value
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
|
-
|
|
95
|
+
const stdoutBytes = Buffer.concat(stdoutChunks)
|
|
96
|
+
return { output: stdoutBytes.toString(), stderr, exitCode, stdoutBytes }
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
function createMockChargeMethod(name: string) {
|
|
@@ -430,6 +433,24 @@ describe('services', () => {
|
|
|
430
433
|
})
|
|
431
434
|
})
|
|
432
435
|
|
|
436
|
+
describe('request output', () => {
|
|
437
|
+
test('writes non-402 response bodies to stdout without text decoding', async () => {
|
|
438
|
+
const body = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x0a, 0x0a])
|
|
439
|
+
const httpServer = await Http.createServer((_req, res) => {
|
|
440
|
+
res.writeHead(200, { 'Content-Type': 'application/pdf' })
|
|
441
|
+
res.end(body)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
const { exitCode, stdoutBytes } = await serve([httpServer.url, '-s'])
|
|
446
|
+
expect(exitCode).toBeUndefined()
|
|
447
|
+
expect(stdoutBytes).toEqual(body)
|
|
448
|
+
} finally {
|
|
449
|
+
httpServer.close()
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
})
|
|
453
|
+
|
|
433
454
|
describe('basic charge (examples/basic)', () => {
|
|
434
455
|
test('happy path: makes payment and receives response', { timeout: 120_000 }, async () => {
|
|
435
456
|
const { Actions } = await import('viem/tempo')
|
|
@@ -1819,6 +1840,21 @@ test('mppx --help', async () => {
|
|
|
1819
1840
|
expect(output).toContain('sign')
|
|
1820
1841
|
})
|
|
1821
1842
|
|
|
1843
|
+
describe('account fund help', () => {
|
|
1844
|
+
test('only advertises testnet network funding', async () => {
|
|
1845
|
+
const { output } = await serve(['account', 'fund', '--help'])
|
|
1846
|
+
expect(output).toContain('--network <testnet>')
|
|
1847
|
+
expect(output).not.toContain('mainnet|testnet')
|
|
1848
|
+
})
|
|
1849
|
+
|
|
1850
|
+
test('rejects mainnet network funding', async () => {
|
|
1851
|
+
const { exitCode, output } = await serve(['account', 'fund', '--network', 'mainnet'])
|
|
1852
|
+
expect(exitCode).toBe(1)
|
|
1853
|
+
expect(output).toContain('Invalid input')
|
|
1854
|
+
expect(output).toContain('testnet')
|
|
1855
|
+
})
|
|
1856
|
+
})
|
|
1857
|
+
|
|
1822
1858
|
// ---------------------------------------------------------------------------
|
|
1823
1859
|
// sign
|
|
1824
1860
|
// ---------------------------------------------------------------------------
|
package/src/cli/cli.ts
CHANGED
|
@@ -99,6 +99,10 @@ function outputResult<Data>(
|
|
|
99
99
|
return undefined as unknown as Data
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
async function writeResponseBody(response: Response) {
|
|
103
|
+
process.stdout.write(Buffer.from(await response.arrayBuffer()))
|
|
104
|
+
}
|
|
105
|
+
|
|
102
106
|
function canReadCommandStdin() {
|
|
103
107
|
if (process.stdin.isTTY !== false) return false
|
|
104
108
|
return process.stdin.listenerCount('data') === 0 && process.stdin.listenerCount('readable') === 0
|
|
@@ -371,7 +375,7 @@ const cli = Cli.create('mppx', {
|
|
|
371
375
|
exitCode: 22,
|
|
372
376
|
})
|
|
373
377
|
printResponseHeaders(challengeResponse, headerOpts)
|
|
374
|
-
|
|
378
|
+
await writeResponseBody(challengeResponse)
|
|
375
379
|
return
|
|
376
380
|
}
|
|
377
381
|
|
|
@@ -625,8 +629,7 @@ const cli = Cli.create('mppx', {
|
|
|
625
629
|
} catch {}
|
|
626
630
|
}
|
|
627
631
|
|
|
628
|
-
|
|
629
|
-
console.log(body)
|
|
632
|
+
await writeResponseBody(credentialResponse)
|
|
630
633
|
}
|
|
631
634
|
} catch (err) {
|
|
632
635
|
// Re-throw IncurError so incur's error handler formats it properly
|
|
@@ -864,7 +867,7 @@ const account = Cli.create('account', {
|
|
|
864
867
|
description: 'Fund account with testnet tokens',
|
|
865
868
|
options: z.object({
|
|
866
869
|
account: z.string().optional().describe('Account name (env: MPPX_ACCOUNT)'),
|
|
867
|
-
network: z.enum(['
|
|
870
|
+
network: z.enum(['testnet']).optional().describe('Tempo network'),
|
|
868
871
|
rpcUrl: z.string().optional().describe('RPC endpoint (env: MPPX_RPC_URL)'),
|
|
869
872
|
}),
|
|
870
873
|
output: z.object({ account: z.string(), chain: z.string(), transactions: z.array(z.string()) }),
|
package/src/cli/internal.ts
CHANGED
|
@@ -5,10 +5,10 @@ import type * as Challenge from '../Challenge.js'
|
|
|
5
5
|
import * as AcceptPayment from '../internal/AcceptPayment.js'
|
|
6
6
|
import type * as Method from '../Method.js'
|
|
7
7
|
import type { Config } from './config.js'
|
|
8
|
-
import { stripe as stripePlugin, tempo as tempoPlugin } from './plugins/index.js'
|
|
8
|
+
import { evm as evmPlugin, stripe as stripePlugin, tempo as tempoPlugin } from './plugins/index.js'
|
|
9
9
|
import type { Plugin } from './plugins/plugin.js'
|
|
10
10
|
|
|
11
|
-
const builtinPlugins: Plugin[] = [tempoPlugin(), stripePlugin()]
|
|
11
|
+
const builtinPlugins: Plugin[] = [tempoPlugin(), stripePlugin(), evmPlugin()]
|
|
12
12
|
|
|
13
13
|
export function resolvePlugin(
|
|
14
14
|
challenge: Challenge.Challenge,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Address, type Chain, createClient, erc20Abi, http } from 'viem'
|
|
2
|
+
import { readContract } from 'viem/actions'
|
|
3
|
+
import * as viemChains from 'viem/chains'
|
|
4
|
+
|
|
5
|
+
import { evm as evmMethods, assets as evmAssets } from '../../evm/client/index.js'
|
|
6
|
+
import { resolveAccount } from '../account.js'
|
|
7
|
+
import { createPlugin } from './plugin.js'
|
|
8
|
+
|
|
9
|
+
export function evm() {
|
|
10
|
+
return createPlugin({
|
|
11
|
+
method: 'evm',
|
|
12
|
+
|
|
13
|
+
async setup({ challenge }) {
|
|
14
|
+
const request = challenge.request as Record<string, unknown>
|
|
15
|
+
const methodDetails = request.methodDetails as Record<string, unknown> | undefined
|
|
16
|
+
const chainId = methodDetails?.chainId as number | undefined
|
|
17
|
+
const chain = chainId ? resolveChain(chainId) : undefined
|
|
18
|
+
const currency = request.currency as string | undefined
|
|
19
|
+
|
|
20
|
+
const account = await resolveAccount()
|
|
21
|
+
// Known assets provide EIP-712 domain metadata (token name/version) needed for EIP-3009 signing
|
|
22
|
+
const knownCurrencies = [
|
|
23
|
+
...Object.values(evmAssets.base),
|
|
24
|
+
...Object.values(evmAssets.baseSepolia),
|
|
25
|
+
...Object.values(evmAssets.celo),
|
|
26
|
+
...Object.values(evmAssets.celoSepolia),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
let tokenSymbol = currency ?? ''
|
|
30
|
+
if (chain && currency) {
|
|
31
|
+
try {
|
|
32
|
+
const client = createClient({ chain, transport: http() })
|
|
33
|
+
const symbol = await readContract(client, {
|
|
34
|
+
address: currency as Address,
|
|
35
|
+
abi: erc20Abi,
|
|
36
|
+
functionName: 'symbol',
|
|
37
|
+
})
|
|
38
|
+
if (symbol) tokenSymbol = symbol
|
|
39
|
+
} catch {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
tokenSymbol,
|
|
44
|
+
tokenDecimals: (methodDetails?.decimals as number | undefined) ?? 6,
|
|
45
|
+
explorerUrl: chain?.blockExplorers?.default?.url,
|
|
46
|
+
methods: [...evmMethods({ account, currencies: knownCurrencies })],
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveChain(chainId: number): Chain | undefined {
|
|
53
|
+
const all = Object.values(viemChains) as Chain[]
|
|
54
|
+
return all.find((c) => c.id === chainId)
|
|
55
|
+
}
|
package/src/cli/plugins/index.ts
CHANGED
package/src/cli/plugins/tempo.ts
CHANGED
|
@@ -42,6 +42,11 @@ const booleanOption = z.union([
|
|
|
42
42
|
z.literal('true').transform(() => true),
|
|
43
43
|
z.literal('false').transform(() => false),
|
|
44
44
|
])
|
|
45
|
+
|
|
46
|
+
async function writeResponseBody(response: Response) {
|
|
47
|
+
process.stdout.write(Buffer.from(await response.arrayBuffer()))
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
const tempoOptionSchema = z.object({
|
|
46
51
|
autoSwap: z.optional(booleanOption),
|
|
47
52
|
channel: z.optional(z.coerce.string()),
|
|
@@ -425,8 +430,7 @@ export function tempo() {
|
|
|
425
430
|
})
|
|
426
431
|
} else {
|
|
427
432
|
// Non-SSE: print body, then close channel
|
|
428
|
-
|
|
429
|
-
console.log(body)
|
|
433
|
+
await writeResponseBody(credentialResponse)
|
|
430
434
|
|
|
431
435
|
if (channelId && escrowContract && chainId) {
|
|
432
436
|
if (confirmEnabled) info('\n')
|