mppx 0.4.9 → 0.4.10
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 +17 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +155 -0
- package/dist/cli/cli.js.map +1 -1
- package/dist/discovery/Discovery.d.ts +146 -0
- package/dist/discovery/Discovery.d.ts.map +1 -0
- package/dist/discovery/Discovery.js +60 -0
- package/dist/discovery/Discovery.js.map +1 -0
- package/dist/discovery/OpenApi.d.ts +61 -0
- package/dist/discovery/OpenApi.d.ts.map +1 -0
- package/dist/discovery/OpenApi.js +139 -0
- package/dist/discovery/OpenApi.js.map +1 -0
- package/dist/discovery/Validate.d.ts +10 -0
- package/dist/discovery/Validate.d.ts.map +1 -0
- package/dist/discovery/Validate.js +63 -0
- package/dist/discovery/Validate.js.map +1 -0
- package/dist/discovery/index.d.ts +4 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +4 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/middlewares/elysia.d.ts +52 -1
- package/dist/middlewares/elysia.d.ts.map +1 -1
- package/dist/middlewares/elysia.js +17 -0
- package/dist/middlewares/elysia.js.map +1 -1
- package/dist/middlewares/express.d.ts +13 -1
- package/dist/middlewares/express.d.ts.map +1 -1
- package/dist/middlewares/express.js +18 -0
- package/dist/middlewares/express.js.map +1 -1
- package/dist/middlewares/hono.d.ts +19 -1
- package/dist/middlewares/hono.d.ts.map +1 -1
- package/dist/middlewares/hono.js +51 -0
- package/dist/middlewares/hono.js.map +1 -1
- package/dist/middlewares/internal/mppx.d.ts +4 -2
- package/dist/middlewares/internal/mppx.d.ts.map +1 -1
- package/dist/middlewares/internal/mppx.js +10 -3
- package/dist/middlewares/internal/mppx.js.map +1 -1
- package/dist/middlewares/nextjs.d.ts +11 -0
- package/dist/middlewares/nextjs.d.ts.map +1 -1
- package/dist/middlewares/nextjs.js +15 -0
- package/dist/middlewares/nextjs.js.map +1 -1
- package/dist/proxy/Proxy.d.ts +6 -0
- package/dist/proxy/Proxy.d.ts.map +1 -1
- package/dist/proxy/Proxy.js +56 -80
- package/dist/proxy/Proxy.js.map +1 -1
- package/dist/proxy/Service.d.ts +16 -23
- package/dist/proxy/Service.d.ts.map +1 -1
- package/dist/proxy/Service.js +19 -83
- package/dist/proxy/Service.js.map +1 -1
- package/dist/proxy/internal/Route.js +1 -1
- package/dist/proxy/internal/Route.js.map +1 -1
- package/dist/proxy/services/anthropic.d.ts.map +1 -1
- package/dist/proxy/services/anthropic.js +5 -0
- package/dist/proxy/services/anthropic.js.map +1 -1
- package/dist/proxy/services/openai.d.ts.map +1 -1
- package/dist/proxy/services/openai.js +6 -3
- package/dist/proxy/services/openai.js.map +1 -1
- package/dist/proxy/services/stripe.d.ts.map +1 -1
- package/dist/proxy/services/stripe.js +6 -3
- package/dist/proxy/services/stripe.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +18 -5
- package/dist/tempo/server/Session.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +8 -0
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/Chain.js +1 -1
- package/dist/tempo/session/Chain.js.map +1 -1
- package/package.json +6 -1
- package/src/BodyDigest.test.ts +1 -1
- package/src/Challenge.fuzz.test.ts +121 -0
- package/src/Challenge.test-d.ts +1 -1
- package/src/Challenge.test.ts +1 -1
- package/src/Credential.fuzz.test.ts +62 -0
- package/src/Credential.test.ts +1 -1
- package/src/Errors.test.ts +1 -1
- package/src/Expires.test.ts +1 -1
- package/src/Method.test.ts +1 -1
- package/src/PaymentRequest.test.ts +1 -1
- package/src/Receipt.test.ts +1 -1
- package/src/Store.test-d.ts +1 -1
- package/src/Store.test.ts +1 -1
- package/src/cli/cli.test.ts +212 -1
- package/src/cli/cli.ts +162 -0
- package/src/client/Mppx.test-d.ts +1 -1
- package/src/client/Mppx.test.ts +1 -1
- package/src/client/Transport.test.ts +1 -1
- package/src/client/internal/Fetch.browser.test.ts +1 -1
- package/src/client/internal/Fetch.test-d.ts +1 -1
- package/src/client/internal/Fetch.test.ts +2 -1
- package/src/discovery/Discovery.test.ts +152 -0
- package/src/discovery/Discovery.ts +72 -0
- package/src/discovery/OpenApi.test.ts +425 -0
- package/src/discovery/OpenApi.ts +224 -0
- package/src/discovery/Validate.test.ts +188 -0
- package/src/discovery/Validate.ts +76 -0
- package/src/discovery/index.ts +3 -0
- package/src/internal/constantTimeEqual.test.ts +1 -1
- package/src/mcp-sdk/client/McpClient.test-d.ts +1 -1
- package/src/mcp-sdk/client/McpClient.test.ts +1 -1
- package/src/mcp-sdk/server/Transport.test.ts +1 -1
- package/src/middlewares/elysia.test.ts +27 -2
- package/src/middlewares/elysia.ts +35 -1
- package/src/middlewares/express.test.ts +35 -7
- package/src/middlewares/express.ts +34 -0
- package/src/middlewares/hono.test.ts +28 -6
- package/src/middlewares/hono.ts +73 -1
- package/src/middlewares/internal/mppx.test.ts +1 -1
- package/src/middlewares/internal/mppx.ts +14 -6
- package/src/middlewares/nextjs.test.ts +31 -6
- package/src/middlewares/nextjs.ts +28 -0
- package/src/proxy/Proxy.test.ts +54 -270
- package/src/proxy/Proxy.ts +71 -93
- package/src/proxy/Service.test.ts +23 -1
- package/src/proxy/Service.ts +40 -86
- package/src/proxy/internal/Headers.test.ts +1 -1
- package/src/proxy/internal/Route.test.ts +9 -1
- package/src/proxy/internal/Route.ts +1 -1
- package/src/proxy/services/anthropic.test.ts +132 -0
- package/src/proxy/services/anthropic.ts +5 -0
- package/src/proxy/services/openai.test.ts +1 -1
- package/src/proxy/services/openai.ts +6 -4
- package/src/proxy/services/stripe.test.ts +132 -0
- package/src/proxy/services/stripe.ts +6 -4
- package/src/server/Mppx.test-d.ts +1 -1
- package/src/server/Mppx.test.ts +2 -1
- package/src/server/NodeListener.test.ts +1 -1
- package/src/server/Request.test.ts +1 -1
- package/src/server/Response.test.ts +1 -1
- package/src/server/Transport.test.ts +1 -1
- package/src/stripe/Charge.integration.test.ts +1 -1
- package/src/stripe/Methods.test.ts +1 -1
- package/src/stripe/client/Charge.test.ts +1 -1
- package/src/stripe/server/Charge.test.ts +1 -1
- package/src/tempo/Attribution.test.ts +1 -1
- package/src/tempo/Methods.test.ts +1 -1
- package/src/tempo/client/ChannelOps.test.ts +6 -3
- package/src/tempo/client/Session.test.ts +5 -2
- package/src/tempo/client/SessionManager.test.ts +1 -1
- package/src/tempo/internal/auto-swap.test.ts +1 -1
- package/src/tempo/internal/defaults.test.ts +1 -1
- package/src/tempo/internal/fee-payer.test.ts +1 -1
- package/src/tempo/server/Charge.test.ts +1 -1
- package/src/tempo/server/Session.test.ts +87 -37
- package/src/tempo/server/Session.ts +25 -8
- package/src/tempo/server/Sse.test.ts +1 -1
- package/src/tempo/server/internal/transport.test.ts +24 -1
- package/src/tempo/server/internal/transport.ts +11 -0
- package/src/tempo/session/Chain.test.ts +5 -2
- package/src/tempo/session/Chain.ts +1 -1
- package/src/tempo/session/Channel.test.ts +1 -1
- package/src/tempo/session/ChannelStore.test.ts +1 -1
- package/src/tempo/session/Receipt.test.ts +1 -1
- package/src/tempo/session/Sse.fuzz.test.ts +138 -0
- package/src/tempo/session/Sse.test.ts +1 -1
- package/src/tempo/session/Voucher.test.ts +1 -1
- package/src/viem/Account.test.ts +1 -1
- package/src/viem/Client.test.ts +1 -1
- package/src/zod.test.ts +147 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Receipt } from 'mppx'
|
|
2
|
+
import { Mppx as Mppx_client, tempo as tempo_client } from 'mppx/client'
|
|
3
|
+
import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
4
|
+
import { afterEach, describe, expect, test } from 'vp/test'
|
|
5
|
+
import * as Http from '~test/Http.js'
|
|
6
|
+
import { accounts, asset, client } from '~test/tempo/viem.js'
|
|
7
|
+
|
|
8
|
+
import * as ApiProxy from '../Proxy.js'
|
|
9
|
+
import { stripe } from './stripe.js'
|
|
10
|
+
|
|
11
|
+
const apiKey = 'sk_test_fake_stripe_key'
|
|
12
|
+
const secretKey = 'test-secret-key'
|
|
13
|
+
|
|
14
|
+
const mppx_server = Mppx_server.create({
|
|
15
|
+
methods: [
|
|
16
|
+
tempo_server({
|
|
17
|
+
account: accounts[0],
|
|
18
|
+
currency: asset,
|
|
19
|
+
getClient: () => client,
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
secretKey,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const mppx_client = Mppx_client.create({
|
|
26
|
+
polyfill: false,
|
|
27
|
+
methods: [
|
|
28
|
+
tempo_client({
|
|
29
|
+
account: accounts[1],
|
|
30
|
+
getClient: () => client,
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
let proxyServer: Awaited<ReturnType<typeof Http.createServer>> | undefined
|
|
36
|
+
let upstreamServer: Awaited<ReturnType<typeof Http.createServer>> | undefined
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
proxyServer?.close()
|
|
40
|
+
upstreamServer?.close()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('stripe', () => {
|
|
44
|
+
test('behavior: proxies POST /v1/charges with charge and injects Basic auth', async () => {
|
|
45
|
+
upstreamServer = await Http.createServer((req, res) => {
|
|
46
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
47
|
+
res.end(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
headers: {
|
|
50
|
+
authorization: req.headers.authorization,
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const proxy = ApiProxy.create({
|
|
57
|
+
services: [
|
|
58
|
+
stripe({
|
|
59
|
+
apiKey,
|
|
60
|
+
baseUrl: upstreamServer.url,
|
|
61
|
+
routes: {
|
|
62
|
+
'POST /v1/charges': mppx_server.charge({ amount: '1', decimals: 6 }),
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
],
|
|
66
|
+
})
|
|
67
|
+
proxyServer = await Http.createServer(proxy.listener)
|
|
68
|
+
|
|
69
|
+
const res = await mppx_client.fetch(`${proxyServer.url}/stripe/v1/charges`, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
72
|
+
body: 'amount=100¤cy=usd',
|
|
73
|
+
})
|
|
74
|
+
expect(res.status).toBe(200)
|
|
75
|
+
|
|
76
|
+
const body = (await res.json()) as { headers: { authorization: string } }
|
|
77
|
+
expect(body.headers.authorization).toBe(`Basic ${btoa(`${apiKey}:`)}`)
|
|
78
|
+
|
|
79
|
+
const receipt = Receipt.fromResponse(res)
|
|
80
|
+
expect(receipt.status).toBe('success')
|
|
81
|
+
expect(receipt.method).toBe('tempo')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('behavior: returns 402 without credential', async () => {
|
|
85
|
+
upstreamServer = await Http.createServer((_req, res) => {
|
|
86
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
87
|
+
res.end('{}')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const proxy = ApiProxy.create({
|
|
91
|
+
services: [
|
|
92
|
+
stripe({
|
|
93
|
+
apiKey,
|
|
94
|
+
baseUrl: upstreamServer.url,
|
|
95
|
+
routes: {
|
|
96
|
+
'POST /v1/charges': mppx_server.charge({ amount: '1', decimals: 6 }),
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
})
|
|
101
|
+
proxyServer = await Http.createServer(proxy.listener)
|
|
102
|
+
|
|
103
|
+
const res = await fetch(`${proxyServer.url}/stripe/v1/charges`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
})
|
|
106
|
+
expect(res.status).toBe(402)
|
|
107
|
+
expect(res.headers.get('WWW-Authenticate')).toContain('Payment')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('behavior: returns 404 for unmatched route', async () => {
|
|
111
|
+
upstreamServer = await Http.createServer((_req, res) => {
|
|
112
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
113
|
+
res.end('{}')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const proxy = ApiProxy.create({
|
|
117
|
+
services: [
|
|
118
|
+
stripe({
|
|
119
|
+
apiKey,
|
|
120
|
+
baseUrl: upstreamServer.url,
|
|
121
|
+
routes: {
|
|
122
|
+
'POST /v1/charges': mppx_server.charge({ amount: '1', decimals: 6 }),
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
],
|
|
126
|
+
})
|
|
127
|
+
proxyServer = await Http.createServer(proxy.listener)
|
|
128
|
+
|
|
129
|
+
const res = await fetch(`${proxyServer.url}/stripe/v1/unknown`)
|
|
130
|
+
expect(res.status).toBe(404)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
@@ -20,11 +20,13 @@ import * as Service from '../Service.js'
|
|
|
20
20
|
export function stripe(config: stripe.Config) {
|
|
21
21
|
return Service.from<stripe.Config>('stripe', {
|
|
22
22
|
baseUrl: config.baseUrl ?? 'https://api.stripe.com',
|
|
23
|
+
categories: ['payments'],
|
|
23
24
|
description: 'Payment processing, customers, subscriptions, and invoices.',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
docs: {
|
|
26
|
+
apiReference: 'https://docs.stripe.com/api',
|
|
27
|
+
homepage: 'https://docs.stripe.com',
|
|
28
|
+
llms: 'https://docs.stripe.com/llms.txt',
|
|
29
|
+
},
|
|
28
30
|
rewriteRequest(request, ctx) {
|
|
29
31
|
const apiKey = ctx.apiKey ?? config.apiKey
|
|
30
32
|
request.headers.set('Authorization', `Basic ${btoa(`${apiKey}:`)}`)
|
package/src/server/Mppx.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Challenge, Credential, Method, z } from 'mppx'
|
|
2
2
|
import { Mppx, Transport, tempo } from 'mppx/server'
|
|
3
|
-
import { describe, expect, test } from '
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
4
|
import * as Http from '~test/Http.js'
|
|
5
5
|
import { accounts, asset, client } from '~test/tempo/viem.js'
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ const secretKey = 'test-secret-key'
|
|
|
9
9
|
|
|
10
10
|
const method = tempo({
|
|
11
11
|
getClient: () => client,
|
|
12
|
+
account: accounts[0],
|
|
12
13
|
})
|
|
13
14
|
|
|
14
15
|
describe('create', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NodeListener, Request } from 'mppx/server'
|
|
2
|
-
import { afterEach, describe, expect, test } from '
|
|
2
|
+
import { afterEach, describe, expect, test } from 'vp/test'
|
|
3
3
|
import * as Http from '~test/Http.js'
|
|
4
4
|
|
|
5
5
|
let server: Awaited<ReturnType<typeof Http.createServer>> | undefined
|
|
@@ -2,7 +2,7 @@ import { EventEmitter } from 'node:events'
|
|
|
2
2
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
3
3
|
|
|
4
4
|
import { Request } from 'mppx/server'
|
|
5
|
-
import { describe, expect, test } from '
|
|
5
|
+
import { describe, expect, test } from 'vp/test'
|
|
6
6
|
|
|
7
7
|
function createMockRequest(options: {
|
|
8
8
|
method?: string
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Challenge, Credential, Mcp, Receipt } from 'mppx'
|
|
2
2
|
import { Transport } from 'mppx/server'
|
|
3
3
|
import { Methods } from 'mppx/tempo'
|
|
4
|
-
import { describe, expect, test } from '
|
|
4
|
+
import { describe, expect, test } from 'vp/test'
|
|
5
5
|
|
|
6
6
|
import { BadRequestError, ChannelClosedError } from '../Errors.js'
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Challenge, Credential, Receipt } from 'mppx'
|
|
2
2
|
import { Mppx as Mppx_client, stripe as stripe_client } from 'mppx/client'
|
|
3
3
|
import { Mppx as Mppx_server, stripe as stripe_server } from 'mppx/server'
|
|
4
|
-
import { afterEach, describe, expect, test } from '
|
|
4
|
+
import { afterEach, describe, expect, test } from 'vp/test'
|
|
5
5
|
import * as Http from '~test/Http.js'
|
|
6
6
|
|
|
7
7
|
const stripeSecretKey = process.env.VITE_STRIPE_SECRET_KEY
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Challenge, Credential } from 'mppx'
|
|
2
2
|
import { Mppx, stripe } from 'mppx/client'
|
|
3
3
|
import { Mppx as Mppx_server, stripe as stripe_server } from 'mppx/server'
|
|
4
|
-
import { describe, expect, test, vi } from '
|
|
4
|
+
import { describe, expect, test, vi } from 'vp/test'
|
|
5
5
|
|
|
6
6
|
import type { StripeJs } from '../internal/types.js'
|
|
7
7
|
import { charge as clientCharge_ } from './Charge.js'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Challenge, Credential } from 'mppx'
|
|
2
2
|
import { Mppx, stripe } from 'mppx/server'
|
|
3
|
-
import { afterEach, describe, expect, test, vi } from '
|
|
3
|
+
import { afterEach, describe, expect, test, vi } from 'vp/test'
|
|
4
4
|
import * as Http from '~test/Http.js'
|
|
5
5
|
|
|
6
6
|
import type { StripeClient } from '../internal/types.js'
|
|
@@ -2,10 +2,13 @@ import { Hex } from 'ox'
|
|
|
2
2
|
import { type Address, createClient } from 'viem'
|
|
3
3
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
4
4
|
import { Addresses } from 'viem/tempo'
|
|
5
|
-
import { beforeAll, describe, expect, test } from '
|
|
5
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
6
|
+
import { nodeEnv } from '~test/config.js'
|
|
6
7
|
import { deployEscrow, openChannel } from '~test/tempo/session.js'
|
|
7
8
|
import { accounts, asset, chain, client, fundAccount, http } from '~test/tempo/viem.js'
|
|
8
9
|
|
|
10
|
+
const isLocalnet = nodeEnv === 'localnet'
|
|
11
|
+
|
|
9
12
|
import type { Challenge } from '../../Challenge.js'
|
|
10
13
|
import * as Credential from '../../Credential.js'
|
|
11
14
|
import {
|
|
@@ -166,7 +169,7 @@ describe('createClosePayload', () => {
|
|
|
166
169
|
})
|
|
167
170
|
})
|
|
168
171
|
|
|
169
|
-
describe('createOpenPayload', () => {
|
|
172
|
+
describe.runIf(isLocalnet)('createOpenPayload', () => {
|
|
170
173
|
const payer = accounts[2]
|
|
171
174
|
const payee = accounts[1].address
|
|
172
175
|
const currency = asset
|
|
@@ -250,7 +253,7 @@ describe('createOpenPayload', () => {
|
|
|
250
253
|
})
|
|
251
254
|
})
|
|
252
255
|
|
|
253
|
-
describe('tryRecoverChannel', () => {
|
|
256
|
+
describe.runIf(isLocalnet)('tryRecoverChannel', () => {
|
|
254
257
|
const payer = accounts[3]
|
|
255
258
|
const payee = accounts[1].address
|
|
256
259
|
const currency = asset
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type Address, createClient, type Hex, http } from 'viem'
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
3
|
import { Addresses } from 'viem/tempo'
|
|
4
|
-
import { beforeAll, describe, expect, test } from '
|
|
4
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
5
|
+
import { nodeEnv } from '~test/config.js'
|
|
5
6
|
import { deployEscrow, openChannel } from '~test/tempo/session.js'
|
|
6
7
|
import { accounts, asset, chain, client, fundAccount } from '~test/tempo/viem.js'
|
|
7
8
|
|
|
9
|
+
const isLocalnet = nodeEnv === 'localnet'
|
|
10
|
+
|
|
8
11
|
import * as Challenge from '../../Challenge.js'
|
|
9
12
|
import * as Credential from '../../Credential.js'
|
|
10
13
|
import { chainId, escrowContract as escrowContractDefaults } from '../internal/defaults.js'
|
|
@@ -201,7 +204,7 @@ describe('session (pure)', () => {
|
|
|
201
204
|
})
|
|
202
205
|
})
|
|
203
206
|
|
|
204
|
-
describe('session (on-chain)', () => {
|
|
207
|
+
describe.runIf(isLocalnet)('session (on-chain)', () => {
|
|
205
208
|
const payer = accounts[2]
|
|
206
209
|
const payee = accounts[1].address
|
|
207
210
|
let escrowContract: Address
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { encodeFunctionData } from 'viem'
|
|
2
2
|
import { Abis, Addresses } from 'viem/tempo'
|
|
3
|
-
import { describe, expect, test } from '
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
4
|
|
|
5
5
|
import { callScopes, FeePayerValidationError, validateCalls } from './fee-payer.js'
|
|
6
6
|
import * as Selectors from './selectors.js'
|
|
@@ -7,7 +7,7 @@ import { Handler } from 'tempo.ts/server'
|
|
|
7
7
|
import { createClient, custom, encodeFunctionData, parseUnits } from 'viem'
|
|
8
8
|
import { getTransactionReceipt, prepareTransactionRequest, signTransaction } from 'viem/actions'
|
|
9
9
|
import { Abis, Account, Actions, Addresses, Secp256k1, Tick, Transaction } from 'viem/tempo'
|
|
10
|
-
import { beforeAll, describe, expect, test } from '
|
|
10
|
+
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
11
11
|
import * as Http from '~test/Http.js'
|
|
12
12
|
import { closeChannelOnChain, deployEscrow, openChannel } from '~test/tempo/session.js'
|
|
13
13
|
import { accounts, asset, chain, client, fundAccount } from '~test/tempo/viem.js'
|
|
@@ -4,7 +4,10 @@ import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
|
4
4
|
import { type Address, createClient, type Hex } from 'viem'
|
|
5
5
|
import { waitForTransactionReceipt } from 'viem/actions'
|
|
6
6
|
import { Addresses } from 'viem/tempo'
|
|
7
|
-
import { beforeAll, beforeEach, describe, expect, test } from '
|
|
7
|
+
import { beforeAll, beforeEach, describe, expect, expectTypeOf, test } from 'vp/test'
|
|
8
|
+
import { nodeEnv } from '~test/config.js'
|
|
9
|
+
|
|
10
|
+
const isLocalnet = nodeEnv === 'localnet'
|
|
8
11
|
import {
|
|
9
12
|
deployEscrow,
|
|
10
13
|
requestCloseChannel,
|
|
@@ -19,7 +22,6 @@ import {
|
|
|
19
22
|
ChannelNotFoundError,
|
|
20
23
|
InsufficientBalanceError,
|
|
21
24
|
InvalidSignatureError,
|
|
22
|
-
VerificationFailedError,
|
|
23
25
|
} from '../../Errors.js'
|
|
24
26
|
import * as Store from '../../Store.js'
|
|
25
27
|
import {
|
|
@@ -33,6 +35,7 @@ import { signVoucher } from '../session/Voucher.js'
|
|
|
33
35
|
import { charge, session, settle } from './Session.js'
|
|
34
36
|
|
|
35
37
|
const payer = accounts[2]
|
|
38
|
+
const recipientAccount = accounts[0]
|
|
36
39
|
const recipient = accounts[0].address
|
|
37
40
|
const currency = asset
|
|
38
41
|
|
|
@@ -40,12 +43,13 @@ let escrowContract: Address
|
|
|
40
43
|
let saltCounter = 0
|
|
41
44
|
|
|
42
45
|
beforeAll(async () => {
|
|
46
|
+
if (!isLocalnet) return
|
|
43
47
|
escrowContract = await deployEscrow()
|
|
44
48
|
await fundAccount({ address: payer.address, token: Addresses.pathUsd })
|
|
45
49
|
await fundAccount({ address: payer.address, token: currency })
|
|
46
50
|
})
|
|
47
51
|
|
|
48
|
-
describe('session', () => {
|
|
52
|
+
describe.runIf(isLocalnet)('session', () => {
|
|
49
53
|
let rawStore: Store.Store
|
|
50
54
|
let store: ChannelStore.ChannelStore
|
|
51
55
|
|
|
@@ -58,7 +62,7 @@ describe('session', () => {
|
|
|
58
62
|
return session({
|
|
59
63
|
store: rawStore,
|
|
60
64
|
getClient: () => client,
|
|
61
|
-
account:
|
|
65
|
+
account: recipientAccount,
|
|
62
66
|
currency,
|
|
63
67
|
escrowContract,
|
|
64
68
|
chainId: chain.id,
|
|
@@ -618,7 +622,47 @@ describe('session', () => {
|
|
|
618
622
|
).rejects.toThrow(InvalidSignatureError)
|
|
619
623
|
})
|
|
620
624
|
|
|
621
|
-
test('
|
|
625
|
+
test('accepts exact replay of already-verified voucher as idempotent', async () => {
|
|
626
|
+
const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
|
|
627
|
+
const server = createServer()
|
|
628
|
+
await openServerChannel(server, channelId, serializedTransaction)
|
|
629
|
+
|
|
630
|
+
const payload = {
|
|
631
|
+
action: 'voucher' as const,
|
|
632
|
+
channelId,
|
|
633
|
+
cumulativeAmount: '2000000',
|
|
634
|
+
signature: await signTestVoucher(channelId, 2000000n),
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
await server.verify({
|
|
638
|
+
credential: {
|
|
639
|
+
challenge: makeChallenge({ id: 'challenge-2', channelId }),
|
|
640
|
+
payload,
|
|
641
|
+
},
|
|
642
|
+
request: makeRequest(),
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
const channelAfterFirstAccept = await store.getChannel(channelId)
|
|
646
|
+
|
|
647
|
+
const replayReceipt = (await server.verify({
|
|
648
|
+
credential: {
|
|
649
|
+
challenge: makeChallenge({ id: 'challenge-3', channelId }),
|
|
650
|
+
payload,
|
|
651
|
+
},
|
|
652
|
+
request: makeRequest(),
|
|
653
|
+
})) as SessionReceipt
|
|
654
|
+
|
|
655
|
+
expect(replayReceipt.status).toBe('success')
|
|
656
|
+
expect(replayReceipt.acceptedCumulative).toBe('2000000')
|
|
657
|
+
expect(replayReceipt.spent).toBe(channelAfterFirstAccept!.spent.toString())
|
|
658
|
+
expect(replayReceipt.units).toBe(channelAfterFirstAccept!.units)
|
|
659
|
+
|
|
660
|
+
const channelAfterReplay = await store.getChannel(channelId)
|
|
661
|
+
expect(channelAfterReplay).toEqual(channelAfterFirstAccept)
|
|
662
|
+
expect(channelAfterReplay!.highestVoucherAmount).toBe(2000000n)
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
test('rejects exact replay with invalid signature', async () => {
|
|
622
666
|
const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
|
|
623
667
|
const server = createServer()
|
|
624
668
|
await openServerChannel(server, channelId, serializedTransaction)
|
|
@@ -642,11 +686,14 @@ describe('session', () => {
|
|
|
642
686
|
server.verify({
|
|
643
687
|
credential: {
|
|
644
688
|
challenge: makeChallenge({ id: 'challenge-3', channelId }),
|
|
645
|
-
payload
|
|
689
|
+
payload: {
|
|
690
|
+
...payload,
|
|
691
|
+
signature: `0x${'ab'.repeat(65)}` as Hex,
|
|
692
|
+
},
|
|
646
693
|
},
|
|
647
694
|
request: makeRequest(),
|
|
648
695
|
}),
|
|
649
|
-
).rejects.toThrow(
|
|
696
|
+
).rejects.toThrow(InvalidSignatureError)
|
|
650
697
|
})
|
|
651
698
|
|
|
652
699
|
test('rejects replayed voucher at settled amount after on-chain settlement', async () => {
|
|
@@ -1191,27 +1238,29 @@ describe('session', () => {
|
|
|
1191
1238
|
expect(ch!.finalized).toBe(true)
|
|
1192
1239
|
})
|
|
1193
1240
|
|
|
1194
|
-
test('
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1241
|
+
test('session() throws at initialization when no account provided', () => {
|
|
1242
|
+
expect(() =>
|
|
1243
|
+
session({
|
|
1244
|
+
store: rawStore,
|
|
1245
|
+
getClient: () => client,
|
|
1246
|
+
account: recipient as Address,
|
|
1247
|
+
currency,
|
|
1248
|
+
escrowContract,
|
|
1249
|
+
chainId: chain.id,
|
|
1250
|
+
} as session.Parameters),
|
|
1251
|
+
).toThrow('tempo.session() requires an `account`')
|
|
1252
|
+
})
|
|
1253
|
+
|
|
1254
|
+
test('session() throws at initialization with no account at all', () => {
|
|
1255
|
+
expect(() =>
|
|
1256
|
+
session({
|
|
1257
|
+
store: rawStore,
|
|
1258
|
+
getClient: () => client,
|
|
1259
|
+
currency,
|
|
1260
|
+
escrowContract,
|
|
1261
|
+
chainId: chain.id,
|
|
1262
|
+
} as session.Parameters),
|
|
1263
|
+
).toThrow('tempo.session() requires an `account`')
|
|
1215
1264
|
})
|
|
1216
1265
|
})
|
|
1217
1266
|
|
|
@@ -2208,6 +2257,7 @@ describe('monotonicity and TOCTOU (unit tests)', () => {
|
|
|
2208
2257
|
})
|
|
2209
2258
|
|
|
2210
2259
|
describe('session default currency resolution', () => {
|
|
2260
|
+
const mockAccount = accounts[0]
|
|
2211
2261
|
const mockClient = createClient({ transport: http('http://localhost:1') })
|
|
2212
2262
|
const mockMainnetClient = createClient({
|
|
2213
2263
|
chain: {
|
|
@@ -2232,7 +2282,7 @@ describe('session default currency resolution', () => {
|
|
|
2232
2282
|
const server = session({
|
|
2233
2283
|
store: Store.memory(),
|
|
2234
2284
|
getClient: () => mockClient,
|
|
2235
|
-
account:
|
|
2285
|
+
account: mockAccount,
|
|
2236
2286
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2237
2287
|
} as session.Parameters)
|
|
2238
2288
|
expect(server.defaults?.currency).toBe('0x20C000000000000000000000b9537d11c60E8b50')
|
|
@@ -2242,7 +2292,7 @@ describe('session default currency resolution', () => {
|
|
|
2242
2292
|
const server = session({
|
|
2243
2293
|
store: Store.memory(),
|
|
2244
2294
|
getClient: () => mockClient,
|
|
2245
|
-
account:
|
|
2295
|
+
account: mockAccount,
|
|
2246
2296
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2247
2297
|
testnet: true,
|
|
2248
2298
|
} as session.Parameters)
|
|
@@ -2253,7 +2303,7 @@ describe('session default currency resolution', () => {
|
|
|
2253
2303
|
const server = session({
|
|
2254
2304
|
store: Store.memory(),
|
|
2255
2305
|
getClient: () => mockClient,
|
|
2256
|
-
account:
|
|
2306
|
+
account: mockAccount,
|
|
2257
2307
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2258
2308
|
chainId: 69420,
|
|
2259
2309
|
} as session.Parameters)
|
|
@@ -2264,7 +2314,7 @@ describe('session default currency resolution', () => {
|
|
|
2264
2314
|
const server = session({
|
|
2265
2315
|
store: Store.memory(),
|
|
2266
2316
|
getClient: () => mockClient,
|
|
2267
|
-
account:
|
|
2317
|
+
account: mockAccount,
|
|
2268
2318
|
currency: '0xcustom',
|
|
2269
2319
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2270
2320
|
chainId: 4217,
|
|
@@ -2277,7 +2327,7 @@ describe('session default currency resolution', () => {
|
|
|
2277
2327
|
const server = session({
|
|
2278
2328
|
store: Store.memory(),
|
|
2279
2329
|
getClient: () => mockClient,
|
|
2280
|
-
account:
|
|
2330
|
+
account: mockAccount,
|
|
2281
2331
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2282
2332
|
chainId: 42431,
|
|
2283
2333
|
} as session.Parameters)
|
|
@@ -2290,7 +2340,7 @@ describe('session default currency resolution', () => {
|
|
|
2290
2340
|
tempo_server.session({
|
|
2291
2341
|
store: Store.memory(),
|
|
2292
2342
|
getClient: () => mockMainnetClient,
|
|
2293
|
-
account:
|
|
2343
|
+
account: mockAccount,
|
|
2294
2344
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2295
2345
|
chainId: 4217,
|
|
2296
2346
|
testnet: false,
|
|
@@ -2317,7 +2367,7 @@ describe('session default currency resolution', () => {
|
|
|
2317
2367
|
tempo_server.session({
|
|
2318
2368
|
store: Store.memory(),
|
|
2319
2369
|
getClient: () => mockTestnetClient,
|
|
2320
|
-
account:
|
|
2370
|
+
account: mockAccount,
|
|
2321
2371
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2322
2372
|
testnet: true,
|
|
2323
2373
|
}),
|
|
@@ -2344,7 +2394,7 @@ describe('session default currency resolution', () => {
|
|
|
2344
2394
|
tempo_server.session({
|
|
2345
2395
|
store: Store.memory(),
|
|
2346
2396
|
getClient: () => mockTestnetClient,
|
|
2347
|
-
account:
|
|
2397
|
+
account: mockAccount,
|
|
2348
2398
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2349
2399
|
chainId: 69420,
|
|
2350
2400
|
}),
|
|
@@ -2370,7 +2420,7 @@ describe('session default currency resolution', () => {
|
|
|
2370
2420
|
tempo_server.session({
|
|
2371
2421
|
store: Store.memory(),
|
|
2372
2422
|
getClient: () => mockClient,
|
|
2373
|
-
account:
|
|
2423
|
+
account: mockAccount,
|
|
2374
2424
|
currency: '0xcustom',
|
|
2375
2425
|
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
2376
2426
|
chainId: 4217,
|