mppx 0.3.2 → 0.3.4
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/dist/Errors.d.ts +7 -7
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +7 -7
- package/dist/Errors.js.map +1 -1
- package/dist/cli.js +98 -64
- package/dist/cli.js.map +1 -1
- package/dist/internal/env.d.ts +19 -0
- package/dist/internal/env.d.ts.map +1 -0
- package/dist/internal/env.js +55 -0
- package/dist/internal/env.js.map +1 -0
- package/dist/server/Mppx.d.ts +2 -2
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +2 -1
- package/dist/server/Mppx.js.map +1 -1
- package/dist/tempo/client/ChannelOps.d.ts +5 -5
- package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/client/ChannelOps.js +3 -3
- package/dist/tempo/client/ChannelOps.js.map +1 -1
- package/dist/tempo/client/Session.d.ts +2 -2
- package/dist/tempo/client/Session.d.ts.map +1 -1
- package/dist/tempo/client/Session.js +3 -3
- package/dist/tempo/client/Session.js.map +1 -1
- package/dist/tempo/client/SessionManager.d.ts +4 -4
- package/dist/tempo/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/client/SessionManager.js +4 -4
- package/dist/tempo/client/SessionManager.js.map +1 -1
- package/dist/tempo/index.d.ts +1 -1
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +1 -1
- package/dist/tempo/index.js.map +1 -1
- package/dist/tempo/server/Charge.js +1 -1
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +1 -1
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Session.d.ts +8 -8
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +24 -24
- package/dist/tempo/server/Session.js.map +1 -1
- package/dist/tempo/server/index.d.ts +2 -2
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js +2 -2
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts +4 -4
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +3 -3
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/Chain.d.ts.map +1 -0
- package/dist/tempo/session/Chain.js.map +1 -0
- package/dist/tempo/session/Channel.d.ts.map +1 -0
- package/dist/tempo/session/Channel.js.map +1 -0
- package/dist/tempo/session/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/ChannelStore.js.map +1 -0
- package/dist/tempo/session/Receipt.d.ts +22 -0
- package/dist/tempo/session/Receipt.d.ts.map +1 -0
- package/dist/tempo/{stream → session}/Receipt.js +6 -6
- package/dist/tempo/session/Receipt.js.map +1 -0
- package/dist/tempo/{stream → session}/Sse.d.ts +7 -7
- package/dist/tempo/session/Sse.d.ts.map +1 -0
- package/dist/tempo/{stream → session}/Sse.js +4 -4
- package/dist/tempo/session/Sse.js.map +1 -0
- package/dist/tempo/{stream → session}/Types.d.ts +4 -4
- package/dist/tempo/session/Types.d.ts.map +1 -0
- package/dist/tempo/{stream → session}/Types.js.map +1 -1
- package/dist/tempo/session/Voucher.d.ts.map +1 -0
- package/dist/tempo/session/Voucher.js.map +1 -0
- package/dist/tempo/{stream → session}/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/session/escrow.abi.js.map +1 -0
- package/dist/tempo/session/index.d.ts.map +1 -0
- package/dist/tempo/session/index.js.map +1 -0
- package/package.json +1 -1
- package/src/Errors.test.ts +10 -10
- package/src/Errors.ts +7 -7
- package/src/Expires.test.ts +111 -0
- package/src/cli.test.ts +3 -3
- package/src/cli.ts +125 -70
- package/src/internal/env.ts +54 -0
- package/src/mcp-sdk/server/Transport.test.ts +171 -0
- package/src/middlewares/express.test.ts +1 -1
- package/src/middlewares/hono.test.ts +1 -1
- package/src/middlewares/nextjs.test.ts +1 -1
- package/src/server/Mppx.ts +5 -4
- package/src/server/Transport.test.ts +1 -1
- package/src/tempo/client/ChannelOps.test.ts +290 -0
- package/src/tempo/client/ChannelOps.ts +8 -8
- package/src/tempo/client/Session.test.ts +467 -0
- package/src/tempo/client/Session.ts +9 -9
- package/src/tempo/client/SessionManager.test.ts +3 -3
- package/src/tempo/client/SessionManager.ts +9 -9
- package/src/tempo/index.ts +1 -1
- package/src/tempo/server/Charge.ts +1 -1
- package/src/tempo/server/Session.test.ts +9 -9
- package/src/tempo/server/Session.ts +47 -47
- package/src/tempo/server/Sse.test.ts +3 -3
- package/src/tempo/server/index.ts +2 -2
- package/src/tempo/server/internal/transport.ts +6 -6
- package/src/tempo/session/Chain.test.ts +511 -0
- package/src/tempo/session/Channel.test.ts +108 -0
- package/src/tempo/{stream → session}/Receipt.test.ts +16 -12
- package/src/tempo/{stream → session}/Receipt.ts +9 -9
- package/src/tempo/{stream → session}/Sse.test.ts +5 -5
- package/src/tempo/{stream → session}/Sse.ts +11 -11
- package/src/tempo/{stream → session}/Types.ts +4 -4
- package/dist/tempo/stream/Chain.d.ts.map +0 -1
- package/dist/tempo/stream/Chain.js.map +0 -1
- package/dist/tempo/stream/Channel.d.ts.map +0 -1
- package/dist/tempo/stream/Channel.js.map +0 -1
- package/dist/tempo/stream/ChannelStore.d.ts.map +0 -1
- package/dist/tempo/stream/ChannelStore.js.map +0 -1
- package/dist/tempo/stream/Receipt.d.ts +0 -22
- package/dist/tempo/stream/Receipt.d.ts.map +0 -1
- package/dist/tempo/stream/Receipt.js.map +0 -1
- package/dist/tempo/stream/Sse.d.ts.map +0 -1
- package/dist/tempo/stream/Sse.js.map +0 -1
- package/dist/tempo/stream/Types.d.ts.map +0 -1
- package/dist/tempo/stream/Voucher.d.ts.map +0 -1
- package/dist/tempo/stream/Voucher.js.map +0 -1
- package/dist/tempo/stream/escrow.abi.js.map +0 -1
- package/dist/tempo/stream/index.d.ts.map +0 -1
- package/dist/tempo/stream/index.js.map +0 -1
- /package/dist/tempo/{stream → session}/Chain.d.ts +0 -0
- /package/dist/tempo/{stream → session}/Chain.js +0 -0
- /package/dist/tempo/{stream → session}/Channel.d.ts +0 -0
- /package/dist/tempo/{stream → session}/Channel.js +0 -0
- /package/dist/tempo/{stream → session}/ChannelStore.d.ts +0 -0
- /package/dist/tempo/{stream → session}/ChannelStore.js +0 -0
- /package/dist/tempo/{stream → session}/Types.js +0 -0
- /package/dist/tempo/{stream → session}/Voucher.d.ts +0 -0
- /package/dist/tempo/{stream → session}/Voucher.js +0 -0
- /package/dist/tempo/{stream → session}/escrow.abi.d.ts +0 -0
- /package/dist/tempo/{stream → session}/escrow.abi.js +0 -0
- /package/dist/tempo/{stream → session}/index.d.ts +0 -0
- /package/dist/tempo/{stream → session}/index.js +0 -0
- /package/src/tempo/{stream → session}/Chain.ts +0 -0
- /package/src/tempo/{stream → session}/Channel.ts +0 -0
- /package/src/tempo/{stream → session}/ChannelStore.test.ts +0 -0
- /package/src/tempo/{stream → session}/ChannelStore.ts +0 -0
- /package/src/tempo/{stream → session}/Voucher.test.ts +0 -0
- /package/src/tempo/{stream → session}/Voucher.ts +0 -0
- /package/src/tempo/{stream → session}/escrow.abi.ts +0 -0
- /package/src/tempo/{stream → session}/index.ts +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
|
|
2
|
+
import * as Expires from './Expires.js'
|
|
3
|
+
|
|
4
|
+
const FIXED_NOW = new Date('2025-06-15T12:00:00.000Z').getTime()
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.useFakeTimers()
|
|
8
|
+
vi.setSystemTime(FIXED_NOW)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.useRealTimers()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('seconds', () => {
|
|
16
|
+
test('returns ISO string n seconds from now', () => {
|
|
17
|
+
expect(Expires.seconds(30)).toBe('2025-06-15T12:00:30.000Z')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('0 returns current time', () => {
|
|
21
|
+
expect(Expires.seconds(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('negative n returns past time', () => {
|
|
25
|
+
expect(Expires.seconds(-60)).toBe('2025-06-15T11:59:00.000Z')
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('minutes', () => {
|
|
30
|
+
test('returns ISO string n minutes from now', () => {
|
|
31
|
+
expect(Expires.minutes(5)).toBe('2025-06-15T12:05:00.000Z')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('0 returns current time', () => {
|
|
35
|
+
expect(Expires.minutes(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('negative n returns past time', () => {
|
|
39
|
+
expect(Expires.minutes(-10)).toBe('2025-06-15T11:50:00.000Z')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('hours', () => {
|
|
44
|
+
test('returns ISO string n hours from now', () => {
|
|
45
|
+
expect(Expires.hours(2)).toBe('2025-06-15T14:00:00.000Z')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('0 returns current time', () => {
|
|
49
|
+
expect(Expires.hours(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('negative n returns past time', () => {
|
|
53
|
+
expect(Expires.hours(-3)).toBe('2025-06-15T09:00:00.000Z')
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe('days', () => {
|
|
58
|
+
test('returns ISO string n days from now', () => {
|
|
59
|
+
expect(Expires.days(1)).toBe('2025-06-16T12:00:00.000Z')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('0 returns current time', () => {
|
|
63
|
+
expect(Expires.days(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('negative n returns past time', () => {
|
|
67
|
+
expect(Expires.days(-2)).toBe('2025-06-13T12:00:00.000Z')
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('weeks', () => {
|
|
72
|
+
test('returns ISO string n weeks from now', () => {
|
|
73
|
+
expect(Expires.weeks(1)).toBe('2025-06-22T12:00:00.000Z')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('0 returns current time', () => {
|
|
77
|
+
expect(Expires.weeks(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('negative n returns past time', () => {
|
|
81
|
+
expect(Expires.weeks(-1)).toBe('2025-06-08T12:00:00.000Z')
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('months', () => {
|
|
86
|
+
test('returns ISO string n months (30 days) from now', () => {
|
|
87
|
+
expect(Expires.months(1)).toBe('2025-07-15T12:00:00.000Z')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('0 returns current time', () => {
|
|
91
|
+
expect(Expires.months(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('negative n returns past time', () => {
|
|
95
|
+
expect(Expires.months(-1)).toBe('2025-05-16T12:00:00.000Z')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('years', () => {
|
|
100
|
+
test('returns ISO string n years (365 days) from now', () => {
|
|
101
|
+
expect(Expires.years(1)).toBe('2026-06-15T12:00:00.000Z')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('0 returns current time', () => {
|
|
105
|
+
expect(Expires.years(0)).toBe('2025-06-15T12:00:00.000Z')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('negative n returns past time', () => {
|
|
109
|
+
expect(Expires.years(-1)).toBe('2024-06-15T12:00:00.000Z')
|
|
110
|
+
})
|
|
111
|
+
})
|
package/src/cli.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Addresses } from 'viem/tempo'
|
|
|
6
6
|
import { afterAll, describe, expect, test } from 'vitest'
|
|
7
7
|
import * as Http from '~test/Http.js'
|
|
8
8
|
import { rpcUrl } from '~test/tempo/prool.js'
|
|
9
|
-
import { deployEscrow } from '~test/tempo/
|
|
9
|
+
import { deployEscrow } from '~test/tempo/session.js'
|
|
10
10
|
import { accounts, asset, client, fundAccount } from '~test/tempo/viem.js'
|
|
11
11
|
import * as Store from './Store.js'
|
|
12
12
|
import * as Mppx_server from './server/Mppx.js'
|
|
@@ -517,9 +517,9 @@ test('mppx --help', () => {
|
|
|
517
517
|
-H, --header <header> Add header (repeatable)
|
|
518
518
|
-L, --location Follow redirects
|
|
519
519
|
-X, --method <method> HTTP method
|
|
520
|
-
--channel <id> Reuse existing
|
|
520
|
+
--channel <id> Reuse existing session channel ID
|
|
521
521
|
--confirm Show confirmation prompts
|
|
522
|
-
--deposit <amount> Deposit amount for
|
|
522
|
+
--deposit <amount> Deposit amount for session payments (human-readable units)
|
|
523
523
|
--json <json> Send JSON body (sets Content-Type and Accept, implies POST)
|
|
524
524
|
-V, --version Display version number
|
|
525
525
|
-h, --help Display this message
|
package/src/cli.ts
CHANGED
|
@@ -16,8 +16,8 @@ import * as Challenge from './Challenge.js'
|
|
|
16
16
|
import * as Credential from './Credential.js'
|
|
17
17
|
import * as Mppx from './client/Mppx.js'
|
|
18
18
|
import { tempo } from './tempo/client/index.js'
|
|
19
|
-
import type {
|
|
20
|
-
import { signVoucher } from './tempo/
|
|
19
|
+
import type { SessionCredentialPayload } from './tempo/session/Types.js'
|
|
20
|
+
import { signVoucher } from './tempo/session/Voucher.js'
|
|
21
21
|
|
|
22
22
|
const require = createRequire(import.meta.url)
|
|
23
23
|
const { name, version } = require('../package.json') as { name: string; version: string }
|
|
@@ -41,9 +41,9 @@ cli
|
|
|
41
41
|
.option('-H, --header <header>', 'Add header (repeatable)')
|
|
42
42
|
.option('-L, --location', 'Follow redirects')
|
|
43
43
|
.option('-X, --method <method>', 'HTTP method')
|
|
44
|
-
.option('--channel <id>', 'Reuse existing
|
|
44
|
+
.option('--channel <id>', 'Reuse existing session channel ID')
|
|
45
45
|
.option('--confirm', 'Show confirmation prompts')
|
|
46
|
-
.option('--deposit <amount>', 'Deposit amount for
|
|
46
|
+
.option('--deposit <amount>', 'Deposit amount for session payments (human-readable units)')
|
|
47
47
|
.option('--json <json>', 'Send JSON body (sets Content-Type and Accept, implies POST)')
|
|
48
48
|
.example(`${name} example.com/content`)
|
|
49
49
|
.example(`${name} example.com/api --json '{"key":"value"}'`)
|
|
@@ -79,7 +79,7 @@ cli
|
|
|
79
79
|
if (silent) options.confirm = false
|
|
80
80
|
|
|
81
81
|
const accountName = resolveAccountName(options.account)
|
|
82
|
-
const privateKey = process.env.MPPX_PRIVATE_KEY
|
|
82
|
+
const privateKey = process.env.MPPX_PRIVATE_KEY || (await createKeychain(accountName).get())
|
|
83
83
|
if (!privateKey) {
|
|
84
84
|
if (options.account) console.log(`Account "${accountName}" not found.`)
|
|
85
85
|
else console.log(`No account found.`)
|
|
@@ -158,7 +158,7 @@ cli
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
const account = privateKeyToAccount(privateKey as `0x${string}`)
|
|
161
|
-
const rpcUrl = options.rpcUrl ?? process.env.MPPX_RPC_URL
|
|
161
|
+
const rpcUrl = options.rpcUrl ?? (process.env.MPPX_RPC_URL || undefined)
|
|
162
162
|
const client = createClient({
|
|
163
163
|
chain: await resolveChain({ ...options, rpcUrl }),
|
|
164
164
|
transport: http(rpcUrl),
|
|
@@ -289,7 +289,7 @@ cli
|
|
|
289
289
|
suggestedDeposit ?? cliDeposit ?? (isTestnet(client.chain!) ? '10' : undefined)
|
|
290
290
|
if (!resolved) {
|
|
291
291
|
console.error(
|
|
292
|
-
'
|
|
292
|
+
'Session payment requires a deposit. Use --deposit <amount> or connect to testnet.',
|
|
293
293
|
)
|
|
294
294
|
process.exit(1)
|
|
295
295
|
}
|
|
@@ -313,21 +313,21 @@ cli
|
|
|
313
313
|
})(),
|
|
314
314
|
)
|
|
315
315
|
|
|
316
|
-
const
|
|
316
|
+
const sessionMd = challenge.request.methodDetails as
|
|
317
317
|
| { escrowContract?: string; chainId?: number }
|
|
318
318
|
| undefined
|
|
319
|
-
let
|
|
320
|
-
let
|
|
321
|
-
let
|
|
322
|
-
let
|
|
319
|
+
let sessionChannelId: `0x${string}` | undefined
|
|
320
|
+
let sessionEscrowContract: Address | undefined
|
|
321
|
+
let sessionChainId = 0
|
|
322
|
+
let sessionCumulativeAmount = 0n
|
|
323
323
|
|
|
324
324
|
if (challenge.intent === 'session') {
|
|
325
|
-
const parsed = Credential.deserialize<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
325
|
+
const parsed = Credential.deserialize<SessionCredentialPayload>(credential)
|
|
326
|
+
sessionChannelId = parsed.payload.channelId
|
|
327
|
+
sessionChainId = sessionMd?.chainId ?? client.chain?.id ?? 0
|
|
328
|
+
sessionEscrowContract = sessionMd?.escrowContract as Address | undefined
|
|
329
329
|
if ('cumulativeAmount' in parsed.payload && parsed.payload.cumulativeAmount)
|
|
330
|
-
|
|
330
|
+
sessionCumulativeAmount = BigInt(parsed.payload.cumulativeAmount)
|
|
331
331
|
|
|
332
332
|
if (parsed.payload.action === 'open') {
|
|
333
333
|
const depositRaw =
|
|
@@ -379,8 +379,9 @@ cli
|
|
|
379
379
|
typeof receiptJson.acceptedCumulative === 'string' &&
|
|
380
380
|
receiptJson.acceptedCumulative
|
|
381
381
|
) {
|
|
382
|
-
|
|
383
|
-
if (
|
|
382
|
+
sessionCumulativeAmount = BigInt(receiptJson.acceptedCumulative)
|
|
383
|
+
if (sessionChannelId)
|
|
384
|
+
writeChannelCumulative(sessionChannelId, sessionCumulativeAmount)
|
|
384
385
|
}
|
|
385
386
|
info(`\n${pc.bold(pc.green('Payment Receipt'))}\n`)
|
|
386
387
|
const rows: [string, string][] = []
|
|
@@ -421,21 +422,21 @@ cli
|
|
|
421
422
|
let buffer = ''
|
|
422
423
|
let currentEvent = ''
|
|
423
424
|
|
|
424
|
-
const
|
|
425
|
+
const sessionCred =
|
|
425
426
|
challenge.intent === 'session'
|
|
426
|
-
? Credential.deserialize<
|
|
427
|
+
? Credential.deserialize<SessionCredentialPayload>(credential)
|
|
427
428
|
: undefined
|
|
428
|
-
const channelId =
|
|
429
|
+
const channelId = sessionCred?.payload.channelId
|
|
429
430
|
const md = challenge.request.methodDetails as
|
|
430
431
|
| { escrowContract?: string; chainId?: number }
|
|
431
432
|
| undefined
|
|
432
|
-
const
|
|
433
|
+
const sessionChainId = md?.chainId ?? client.chain?.id ?? 0
|
|
433
434
|
const escrowContract = md?.escrowContract as Address | undefined
|
|
434
435
|
let cumulativeAmount =
|
|
435
|
-
|
|
436
|
-
'cumulativeAmount' in
|
|
437
|
-
|
|
438
|
-
? BigInt(
|
|
436
|
+
sessionCred?.payload &&
|
|
437
|
+
'cumulativeAmount' in sessionCred.payload &&
|
|
438
|
+
sessionCred.payload.cumulativeAmount
|
|
439
|
+
? BigInt(sessionCred.payload.cumulativeAmount)
|
|
439
440
|
: 0n
|
|
440
441
|
let _voucherSeq = 0
|
|
441
442
|
|
|
@@ -481,7 +482,7 @@ cli
|
|
|
481
482
|
currentEvent === 'payment-need-voucher' &&
|
|
482
483
|
channelId &&
|
|
483
484
|
escrowContract &&
|
|
484
|
-
|
|
485
|
+
sessionChainId
|
|
485
486
|
) {
|
|
486
487
|
try {
|
|
487
488
|
const event = JSON.parse(data) as {
|
|
@@ -496,7 +497,7 @@ cli
|
|
|
496
497
|
account,
|
|
497
498
|
{ channelId, cumulativeAmount },
|
|
498
499
|
escrowContract,
|
|
499
|
-
|
|
500
|
+
sessionChainId,
|
|
500
501
|
)
|
|
501
502
|
const voucherCred = Credential.serialize({
|
|
502
503
|
challenge,
|
|
@@ -506,7 +507,7 @@ cli
|
|
|
506
507
|
cumulativeAmount: cumulativeAmount.toString(),
|
|
507
508
|
signature,
|
|
508
509
|
},
|
|
509
|
-
source: `did:pkh:eip155:${
|
|
510
|
+
source: `did:pkh:eip155:${sessionChainId}:${account.address}`,
|
|
510
511
|
})
|
|
511
512
|
await globalThis.fetch(url, {
|
|
512
513
|
method: 'POST',
|
|
@@ -583,15 +584,15 @@ cli
|
|
|
583
584
|
}
|
|
584
585
|
if (buffer.trim()) await processLines([buffer])
|
|
585
586
|
|
|
586
|
-
if (channelId && escrowContract &&
|
|
587
|
+
if (channelId && escrowContract && sessionChainId) {
|
|
587
588
|
const signature = await signVoucher(
|
|
588
589
|
client,
|
|
589
590
|
account,
|
|
590
591
|
{ channelId, cumulativeAmount },
|
|
591
592
|
escrowContract,
|
|
592
|
-
|
|
593
|
+
sessionChainId,
|
|
593
594
|
)
|
|
594
|
-
const closePayload:
|
|
595
|
+
const closePayload: SessionCredentialPayload = {
|
|
595
596
|
action: 'close',
|
|
596
597
|
channelId,
|
|
597
598
|
cumulativeAmount: cumulativeAmount.toString(),
|
|
@@ -600,7 +601,7 @@ cli
|
|
|
600
601
|
const closeCred = Credential.serialize({
|
|
601
602
|
challenge,
|
|
602
603
|
payload: closePayload,
|
|
603
|
-
source: `did:pkh:eip155:${
|
|
604
|
+
source: `did:pkh:eip155:${sessionChainId}:${account.address}`,
|
|
604
605
|
})
|
|
605
606
|
const closeRes = await globalThis.fetch(url, {
|
|
606
607
|
method: 'POST',
|
|
@@ -638,9 +639,9 @@ cli
|
|
|
638
639
|
const shouldClose =
|
|
639
640
|
challenge.intent === 'session' &&
|
|
640
641
|
credentialResponse.ok &&
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
642
|
+
sessionChannelId &&
|
|
643
|
+
sessionEscrowContract &&
|
|
644
|
+
sessionChainId
|
|
644
645
|
if (shouldClose && options.confirm) {
|
|
645
646
|
info('\n')
|
|
646
647
|
}
|
|
@@ -650,20 +651,20 @@ cli
|
|
|
650
651
|
const signature = await signVoucher(
|
|
651
652
|
client,
|
|
652
653
|
account,
|
|
653
|
-
{ channelId:
|
|
654
|
-
|
|
655
|
-
|
|
654
|
+
{ channelId: sessionChannelId!, cumulativeAmount: sessionCumulativeAmount },
|
|
655
|
+
sessionEscrowContract!,
|
|
656
|
+
sessionChainId,
|
|
656
657
|
)
|
|
657
|
-
const closePayload:
|
|
658
|
+
const closePayload: SessionCredentialPayload = {
|
|
658
659
|
action: 'close',
|
|
659
|
-
channelId:
|
|
660
|
-
cumulativeAmount:
|
|
660
|
+
channelId: sessionChannelId!,
|
|
661
|
+
cumulativeAmount: sessionCumulativeAmount.toString(),
|
|
661
662
|
signature,
|
|
662
663
|
}
|
|
663
664
|
const closeCred = Credential.serialize({
|
|
664
665
|
challenge,
|
|
665
666
|
payload: closePayload,
|
|
666
|
-
source: `did:pkh:eip155:${
|
|
667
|
+
source: `did:pkh:eip155:${sessionChainId}:${account.address}`,
|
|
667
668
|
})
|
|
668
669
|
const closeRes = await globalThis.fetch(url, {
|
|
669
670
|
...fetchInit,
|
|
@@ -673,7 +674,7 @@ cli
|
|
|
673
674
|
},
|
|
674
675
|
})
|
|
675
676
|
if (closeRes.ok) {
|
|
676
|
-
deleteChannelState(
|
|
677
|
+
deleteChannelState(sessionChannelId!)
|
|
677
678
|
const closeReceiptHeader = closeRes.headers.get('Payment-Receipt')
|
|
678
679
|
let closeTxHash: string | undefined
|
|
679
680
|
if (closeReceiptHeader) {
|
|
@@ -690,7 +691,7 @@ cli
|
|
|
690
691
|
? ` ${pc.dim(pc.link(`${explorerUrl}/tx/${closeTxHash}`, closeTxHash))}`
|
|
691
692
|
: ''
|
|
692
693
|
info(
|
|
693
|
-
`\n${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(
|
|
694
|
+
`\n${pc.dim('Channel closed.')} ${pc.dim(`Spent ${fmtBalance(sessionCumulativeAmount, tokenSymbol, tokenDecimals)}.`)}${txInfo}\n`,
|
|
694
695
|
)
|
|
695
696
|
} else {
|
|
696
697
|
const closeBody = await closeRes.text().catch(() => '')
|
|
@@ -698,10 +699,10 @@ cli
|
|
|
698
699
|
`${pc.dim(pc.yellow('Channel close failed'))} ${pc.dim(`(${closeRes.status})`)}\n`,
|
|
699
700
|
)
|
|
700
701
|
info(
|
|
701
|
-
`${pc.dim(` channelId: ${
|
|
702
|
-
`${pc.dim(` cumulativeAmount: ${
|
|
703
|
-
`${pc.dim(` escrowContract: ${
|
|
704
|
-
`${pc.dim(` chainId: ${
|
|
702
|
+
`${pc.dim(` channelId: ${sessionChannelId}`)}\n` +
|
|
703
|
+
`${pc.dim(` cumulativeAmount: ${sessionCumulativeAmount}`)}\n` +
|
|
704
|
+
`${pc.dim(` escrowContract: ${sessionEscrowContract}`)}\n` +
|
|
705
|
+
`${pc.dim(` chainId: ${sessionChainId}`)}\n` +
|
|
705
706
|
`${pc.dim(` account: ${account.address}`)}\n` +
|
|
706
707
|
`${pc.dim(` response: ${closeBody || '(empty)'}`)}\n`,
|
|
707
708
|
)
|
|
@@ -784,7 +785,11 @@ cli
|
|
|
784
785
|
const accounts = await createKeychain().list()
|
|
785
786
|
if (accounts.length === 1) createDefaultStore().set(resolvedName)
|
|
786
787
|
console.log(`Account "${resolvedName}" saved to keychain.`)
|
|
787
|
-
|
|
788
|
+
const explorerUrl = tempoMainnet.blockExplorers?.default?.url
|
|
789
|
+
const addrDisplay = explorerUrl
|
|
790
|
+
? pc.link(`${explorerUrl}/address/${account.address}`, account.address)
|
|
791
|
+
: account.address
|
|
792
|
+
console.log(pc.dim(`Address ${addrDisplay}`))
|
|
788
793
|
resolveChain(options)
|
|
789
794
|
.then((chain) => createClient({ chain, transport: http(options.rpcUrl) }))
|
|
790
795
|
.then((client) =>
|
|
@@ -823,8 +828,12 @@ cli
|
|
|
823
828
|
const account = privateKeyToAccount(key as `0x${string}`)
|
|
824
829
|
const balanceLines = await fetchBalanceLines(account.address, { includeTestnet: false })
|
|
825
830
|
if (!options.yes) {
|
|
831
|
+
const explorerUrl = tempoMainnet.blockExplorers?.default?.url
|
|
832
|
+
const addrDisplay = explorerUrl
|
|
833
|
+
? pc.link(`${explorerUrl}/address/${account.address}`, account.address)
|
|
834
|
+
: account.address
|
|
826
835
|
process.stderr.write(pc.dim(`Delete account "${options.account}"\n`))
|
|
827
|
-
process.stderr.write(pc.dim(` Address ${
|
|
836
|
+
process.stderr.write(pc.dim(` Address ${addrDisplay}\n`))
|
|
828
837
|
for (let i = 0; i < balanceLines.length; i++)
|
|
829
838
|
process.stderr.write(
|
|
830
839
|
pc.dim(` ${i === 0 ? 'Balance' : ' '} ${balanceLines[i]}\n`),
|
|
@@ -897,6 +906,7 @@ cli
|
|
|
897
906
|
}),
|
|
898
907
|
)
|
|
899
908
|
const resolved = entries.filter((e) => e !== undefined)
|
|
909
|
+
const explorerUrl = tempoMainnet.blockExplorers?.default?.url
|
|
900
910
|
const maxWidth = Math.max(
|
|
901
911
|
...resolved.map((e) => e.name.length + (e.name === currentDefault ? 1 : 0)),
|
|
902
912
|
)
|
|
@@ -904,7 +914,10 @@ cli
|
|
|
904
914
|
const isDefault = entry.name === currentDefault
|
|
905
915
|
const label = isDefault ? `${entry.name}${pc.dim('*')}` : entry.name
|
|
906
916
|
const width = entry.name.length + (isDefault ? 1 : 0)
|
|
907
|
-
|
|
917
|
+
const addrDisplay = explorerUrl
|
|
918
|
+
? pc.link(`${explorerUrl}/address/${entry.address}`, entry.address)
|
|
919
|
+
: entry.address
|
|
920
|
+
console.log(`${label}${' '.repeat(maxWidth - width + 2)}${pc.dim(addrDisplay)}`)
|
|
908
921
|
}
|
|
909
922
|
return
|
|
910
923
|
}
|
|
@@ -918,10 +931,14 @@ cli
|
|
|
918
931
|
process.exit(1)
|
|
919
932
|
}
|
|
920
933
|
const account = privateKeyToAccount(key as `0x${string}`)
|
|
921
|
-
|
|
934
|
+
const rpcUrl = options.rpcUrl ?? (process.env.MPPX_RPC_URL || undefined)
|
|
935
|
+
const chain = rpcUrl ? await resolveChain({ rpcUrl }) : tempoMainnet
|
|
936
|
+
const explorerUrl = chain.blockExplorers?.default?.url
|
|
937
|
+
const addrDisplay = explorerUrl
|
|
938
|
+
? pc.link(`${explorerUrl}/address/${account.address}`, account.address)
|
|
939
|
+
: account.address
|
|
940
|
+
console.log(`${pc.dim('Address')} ${addrDisplay}`)
|
|
922
941
|
|
|
923
|
-
const rpcUrl = options.rpcUrl ?? process.env.MPPX_RPC_URL
|
|
924
|
-
const chain = rpcUrl ? await resolveChain({ rpcUrl }) : undefined
|
|
925
942
|
const balanceLines = await fetchBalanceLines(
|
|
926
943
|
account.address,
|
|
927
944
|
chain && rpcUrl ? { chain, rpcUrl } : undefined,
|
|
@@ -1054,7 +1071,7 @@ function createDefaultStore() {
|
|
|
1054
1071
|
|
|
1055
1072
|
function resolveAccountName(explicit?: string): string {
|
|
1056
1073
|
if (explicit) return explicit
|
|
1057
|
-
if (process.env.MPPX_ACCOUNT) return process.env.MPPX_ACCOUNT
|
|
1074
|
+
if (process.env.MPPX_ACCOUNT?.trim()) return process.env.MPPX_ACCOUNT
|
|
1058
1075
|
return createDefaultStore().get()
|
|
1059
1076
|
}
|
|
1060
1077
|
|
|
@@ -1238,9 +1255,9 @@ const pc = (() => {
|
|
|
1238
1255
|
bgMagentaBright: f('\x1b[105m', '\x1b[49m'),
|
|
1239
1256
|
bgCyanBright: f('\x1b[106m', '\x1b[49m'),
|
|
1240
1257
|
bgWhiteBright: f('\x1b[107m', '\x1b[49m'),
|
|
1241
|
-
link(url: string, text: string) {
|
|
1258
|
+
link(url: string, text: string, noUnderline?: boolean) {
|
|
1242
1259
|
if (!isColorSupported) return text
|
|
1243
|
-
return `\x1b]8;;${url}\x07${pc.underline(text)}\x1b]8;;\x07`
|
|
1260
|
+
return `\x1b]8;;${url}\x07${noUnderline ? text : pc.underline(text)}\x1b]8;;\x07`
|
|
1244
1261
|
},
|
|
1245
1262
|
}
|
|
1246
1263
|
})()
|
|
@@ -1268,6 +1285,8 @@ function chainName(chain: { id: number; name: string }) {
|
|
|
1268
1285
|
}
|
|
1269
1286
|
|
|
1270
1287
|
const pathUsd = '0x20c0000000000000000000000000000000000000' as Address
|
|
1288
|
+
const usdcE = '0x20C000000000000000000000b9537d11c60E8b50' as Address
|
|
1289
|
+
const mainnetTokens = [pathUsd, usdcE] as const
|
|
1271
1290
|
const testnetTokens = [
|
|
1272
1291
|
'0x20c0000000000000000000000000000000000000',
|
|
1273
1292
|
'0x20c0000000000000000000000000000000000001',
|
|
@@ -1275,11 +1294,20 @@ const testnetTokens = [
|
|
|
1275
1294
|
'0x20c0000000000000000000000000000000000003',
|
|
1276
1295
|
] as const
|
|
1277
1296
|
|
|
1278
|
-
function fmtBalance(
|
|
1297
|
+
function fmtBalance(
|
|
1298
|
+
b: bigint,
|
|
1299
|
+
symbol: string,
|
|
1300
|
+
decimals = 6,
|
|
1301
|
+
opts?: { explorerUrl?: string | undefined; token?: string | undefined },
|
|
1302
|
+
) {
|
|
1279
1303
|
const value = Number(b) / 10 ** decimals
|
|
1280
1304
|
const [int, dec] = value.toString().split('.')
|
|
1281
1305
|
const formatted = int!.replace(/\B(?=(\d{3})+(?!\d))/g, '_')
|
|
1282
|
-
|
|
1306
|
+
const sym =
|
|
1307
|
+
opts?.explorerUrl && opts.token
|
|
1308
|
+
? pc.dim(pc.link(`${opts.explorerUrl}/token/${opts.token}`, symbol, true))
|
|
1309
|
+
: pc.dim(symbol)
|
|
1310
|
+
return `${dec ? `${formatted}.${dec}` : formatted} ${sym}`
|
|
1283
1311
|
}
|
|
1284
1312
|
|
|
1285
1313
|
function isTestnet(chain: Chain) {
|
|
@@ -1296,9 +1324,13 @@ async function fetchTokenInfo(
|
|
|
1296
1324
|
Actions.token.getBalance(client, { account, token }).catch(() => 0n),
|
|
1297
1325
|
Actions.token.getMetadata(client, { token }).catch(() => ({ symbol: token as string })),
|
|
1298
1326
|
])
|
|
1299
|
-
const
|
|
1327
|
+
const knownSymbols: Record<string, string> = {
|
|
1328
|
+
[pathUsd]: 'PathUSD',
|
|
1329
|
+
[usdcE]: 'USDC.e',
|
|
1330
|
+
}
|
|
1331
|
+
const symbol = knownSymbols[token] ?? metadata.symbol
|
|
1300
1332
|
const decimals = 'decimals' in metadata ? metadata.decimals : 6
|
|
1301
|
-
return { balance, symbol, decimals }
|
|
1333
|
+
return { balance, symbol, decimals, token }
|
|
1302
1334
|
}
|
|
1303
1335
|
|
|
1304
1336
|
function detectTerminalBg(
|
|
@@ -1340,6 +1372,7 @@ async function fetchBalanceLines(
|
|
|
1340
1372
|
): Promise<string[]> {
|
|
1341
1373
|
if (opts?.chain) {
|
|
1342
1374
|
const client = createClient({ chain: opts.chain, transport: http(opts.rpcUrl) })
|
|
1375
|
+
const explorerUrl = opts.chain.blockExplorers?.default?.url
|
|
1343
1376
|
const label = pc.dim(`(${chainName(opts.chain)})`)
|
|
1344
1377
|
if (isTestnet(opts.chain)) {
|
|
1345
1378
|
const results = await Promise.all(
|
|
@@ -1347,24 +1380,46 @@ async function fetchBalanceLines(
|
|
|
1347
1380
|
)
|
|
1348
1381
|
return results
|
|
1349
1382
|
.filter((t) => t.balance > 0n)
|
|
1350
|
-
.map(
|
|
1383
|
+
.map(
|
|
1384
|
+
(t) =>
|
|
1385
|
+
`${fmtBalance(t.balance, t.symbol, t.decimals, { explorerUrl, token: t.token })} ${label}`,
|
|
1386
|
+
)
|
|
1351
1387
|
}
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1388
|
+
const results = await Promise.all(
|
|
1389
|
+
mainnetTokens.map((token) => fetchTokenInfo(client, token, address)),
|
|
1390
|
+
)
|
|
1391
|
+
return results.map(
|
|
1392
|
+
(t) =>
|
|
1393
|
+
`${fmtBalance(t.balance, t.symbol, t.decimals, { explorerUrl, token: t.token })} ${label}`,
|
|
1394
|
+
)
|
|
1354
1395
|
}
|
|
1355
1396
|
|
|
1356
|
-
const mainnetClient = createClient({
|
|
1357
|
-
|
|
1358
|
-
|
|
1397
|
+
const mainnetClient = createClient({
|
|
1398
|
+
chain: tempoMainnet,
|
|
1399
|
+
transport: http(process.env.MPPX_RPC_URL || undefined),
|
|
1400
|
+
})
|
|
1401
|
+
const mainnetExplorerUrl = tempoMainnet.blockExplorers?.default?.url
|
|
1402
|
+
const mainnetResults = await Promise.all(
|
|
1403
|
+
mainnetTokens.map((token) => fetchTokenInfo(mainnetClient, token, address)),
|
|
1404
|
+
)
|
|
1405
|
+
const lines = mainnetResults.map((t) =>
|
|
1406
|
+
fmtBalance(t.balance, t.symbol, t.decimals, {
|
|
1407
|
+
explorerUrl: mainnetExplorerUrl,
|
|
1408
|
+
token: t.token,
|
|
1409
|
+
}),
|
|
1410
|
+
)
|
|
1359
1411
|
|
|
1360
1412
|
if (opts?.includeTestnet !== false) {
|
|
1361
1413
|
const testnetClient = createClient({ chain: tempoModerato, transport: http() })
|
|
1414
|
+
const testnetExplorerUrl = tempoModerato.blockExplorers?.default?.url
|
|
1362
1415
|
const testnetResults = await Promise.all(
|
|
1363
1416
|
testnetTokens.map((token) => fetchTokenInfo(testnetClient, token, address)),
|
|
1364
1417
|
)
|
|
1365
1418
|
for (const t of testnetResults) {
|
|
1366
1419
|
if (t.balance > 0n)
|
|
1367
|
-
lines.push(
|
|
1420
|
+
lines.push(
|
|
1421
|
+
`${fmtBalance(t.balance, t.symbol, t.decimals, { explorerUrl: testnetExplorerUrl, token: t.token })} ${pc.dim('(testnet)')}`,
|
|
1422
|
+
)
|
|
1368
1423
|
}
|
|
1369
1424
|
}
|
|
1370
1425
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Map of configuration keys to environment variable names, checked in order. */
|
|
2
|
+
const variables = {
|
|
3
|
+
realm: [
|
|
4
|
+
'FLY_APP_NAME',
|
|
5
|
+
'HEROKU_APP_NAME',
|
|
6
|
+
'HOST',
|
|
7
|
+
'HOSTNAME',
|
|
8
|
+
'MPP_REALM',
|
|
9
|
+
'RAILWAY_PUBLIC_DOMAIN',
|
|
10
|
+
'RENDER_EXTERNAL_HOSTNAME',
|
|
11
|
+
'VERCEL_URL',
|
|
12
|
+
'WEBSITE_HOSTNAME',
|
|
13
|
+
],
|
|
14
|
+
secretKey: ['MPP_SECRET_KEY'],
|
|
15
|
+
} as const satisfies Record<string, readonly string[]>
|
|
16
|
+
|
|
17
|
+
/** Fallback values when no environment variable is set. */
|
|
18
|
+
const defaults = {
|
|
19
|
+
realm: 'MPP Payment',
|
|
20
|
+
secretKey: 'tmp',
|
|
21
|
+
} as const satisfies Record<keyof typeof variables, string>
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a configuration value from environment variables.
|
|
25
|
+
*
|
|
26
|
+
* Checks platform-specific env vars in order, falling back to a default.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* Env.get('realm') // e.g. "my-app.vercel.app"
|
|
31
|
+
* Env.get('secretKey') // e.g. value of MPP_SECRET_KEY
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function get(key: keyof typeof variables): string {
|
|
35
|
+
for (const name of variables[key]) {
|
|
36
|
+
const value = read(name)
|
|
37
|
+
if (value) return value
|
|
38
|
+
}
|
|
39
|
+
return defaults[key]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Reads a single environment variable, probing available runtime APIs. */
|
|
43
|
+
function read(name: string): string | undefined {
|
|
44
|
+
try {
|
|
45
|
+
if (typeof process !== 'undefined' && process?.env) return process.env[name] || undefined
|
|
46
|
+
} catch {}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const deno = (globalThis as any).Deno
|
|
50
|
+
if (deno?.env?.get) return deno.env.get(name) || undefined
|
|
51
|
+
} catch {}
|
|
52
|
+
|
|
53
|
+
return undefined
|
|
54
|
+
}
|