mppx 0.6.28 → 0.6.30
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 +23 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +16 -10
- package/dist/Challenge.js.map +1 -1
- package/dist/Method.d.ts +1 -1
- package/dist/Method.d.ts.map +1 -1
- package/dist/client/Methods.d.ts +1 -0
- package/dist/client/Methods.d.ts.map +1 -1
- package/dist/client/Methods.js +1 -0
- package/dist/client/Methods.js.map +1 -1
- package/dist/client/Mppx.d.ts +3 -3
- package/dist/client/Mppx.d.ts.map +1 -1
- package/dist/client/Mppx.js +1 -0
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +10 -3
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +60 -7
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts +3 -0
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +12 -20
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/evm/Assets.d.ts +2 -0
- package/dist/evm/Assets.d.ts.map +1 -0
- package/dist/evm/Assets.js +2 -0
- package/dist/evm/Assets.js.map +1 -0
- package/dist/evm/Chains.d.ts +5 -0
- package/dist/evm/Chains.d.ts.map +1 -0
- package/dist/evm/Chains.js +5 -0
- package/dist/evm/Chains.js.map +1 -0
- package/dist/evm/Methods.d.ts +68 -0
- package/dist/evm/Methods.d.ts.map +1 -0
- package/dist/evm/Methods.js +28 -0
- package/dist/evm/Methods.js.map +1 -0
- package/dist/evm/Types.d.ts +143 -0
- package/dist/evm/Types.d.ts.map +1 -0
- package/dist/evm/Types.js +102 -0
- package/dist/evm/Types.js.map +1 -0
- package/dist/evm/client/Charge.d.ts +102 -0
- package/dist/evm/client/Charge.d.ts.map +1 -0
- package/dist/evm/client/Charge.js +141 -0
- package/dist/evm/client/Charge.js.map +1 -0
- package/dist/evm/client/Methods.d.ts +81 -0
- package/dist/evm/client/Methods.d.ts.map +1 -0
- package/dist/evm/client/Methods.js +16 -0
- package/dist/evm/client/Methods.js.map +1 -0
- package/dist/evm/client/index.d.ts +6 -0
- package/dist/evm/client/index.d.ts.map +1 -0
- package/dist/evm/client/index.js +6 -0
- package/dist/evm/client/index.js.map +1 -0
- package/dist/evm/index.d.ts +10 -0
- package/dist/evm/index.d.ts.map +1 -0
- package/dist/evm/index.js +9 -0
- package/dist/evm/index.js.map +1 -0
- package/dist/evm/server/Charge.d.ts +62 -0
- package/dist/evm/server/Charge.d.ts.map +1 -0
- package/dist/evm/server/Charge.js +172 -0
- package/dist/evm/server/Charge.js.map +1 -0
- package/dist/evm/server/Methods.d.ts +80 -0
- package/dist/evm/server/Methods.d.ts.map +1 -0
- package/dist/evm/server/Methods.js +16 -0
- package/dist/evm/server/Methods.js.map +1 -0
- package/dist/evm/server/index.d.ts +6 -0
- package/dist/evm/server/index.d.ts.map +1 -0
- package/dist/evm/server/index.js +6 -0
- package/dist/evm/server/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/HeaderCodec.d.ts +18 -0
- package/dist/internal/HeaderCodec.d.ts.map +1 -0
- package/dist/internal/HeaderCodec.js +31 -0
- package/dist/internal/HeaderCodec.js.map +1 -0
- package/dist/middlewares/elysia.d.ts.map +1 -1
- package/dist/middlewares/elysia.js +2 -3
- package/dist/middlewares/elysia.js.map +1 -1
- package/dist/middlewares/express.js +2 -1
- package/dist/middlewares/express.js.map +1 -1
- package/dist/proxy/internal/Headers.d.ts.map +1 -1
- package/dist/proxy/internal/Headers.js +11 -1
- package/dist/proxy/internal/Headers.js.map +1 -1
- package/dist/proxy/services/openai.d.ts.map +1 -1
- package/dist/proxy/services/openai.js +2 -0
- package/dist/proxy/services/openai.js.map +1 -1
- package/dist/server/Methods.d.ts +1 -0
- package/dist/server/Methods.d.ts.map +1 -1
- package/dist/server/Methods.js +1 -0
- package/dist/server/Methods.js.map +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +90 -12
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Transport.d.ts +10 -0
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +9 -0
- package/dist/server/Transport.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.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/ChannelOps.d.ts +3 -3
- package/dist/tempo/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/client/ChannelOps.js +13 -6
- package/dist/tempo/client/ChannelOps.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +8 -5
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +0 -1
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Session.d.ts +2 -4
- package/dist/tempo/client/Session.d.ts.map +1 -1
- package/dist/tempo/client/Session.js +10 -12
- package/dist/tempo/client/Session.js.map +1 -1
- package/dist/tempo/client/SessionManager.d.ts +3 -3
- package/dist/tempo/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/client/SessionManager.js +1 -1
- package/dist/tempo/client/SessionManager.js.map +1 -1
- package/dist/tempo/internal/account.d.ts +5 -0
- package/dist/tempo/internal/account.d.ts.map +1 -1
- package/dist/tempo/internal/account.js +8 -0
- package/dist/tempo/internal/account.js.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +5 -2
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +23 -1
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +13 -12
- package/dist/tempo/server/Session.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/session/Chain.d.ts +2 -0
- package/dist/tempo/session/Chain.d.ts.map +1 -1
- package/dist/tempo/session/Chain.js +8 -8
- package/dist/tempo/session/Chain.js.map +1 -1
- package/dist/tempo/session/Voucher.d.ts +4 -3
- package/dist/tempo/session/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/Voucher.js +71 -44
- package/dist/tempo/session/Voucher.js.map +1 -1
- package/dist/tempo/session/Ws.d.ts.map +1 -1
- package/dist/tempo/session/Ws.js +15 -0
- package/dist/tempo/session/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +2 -2
- package/dist/x402/Assets.d.ts +29 -0
- package/dist/x402/Assets.d.ts.map +1 -0
- package/dist/x402/Assets.js +46 -0
- package/dist/x402/Assets.js.map +1 -0
- package/dist/x402/Header.d.ts +14 -0
- package/dist/x402/Header.d.ts.map +1 -0
- package/dist/x402/Header.js +18 -0
- package/dist/x402/Header.js.map +1 -0
- package/dist/x402/Types.d.ts +289 -0
- package/dist/x402/Types.d.ts.map +1 -0
- package/dist/x402/Types.js +139 -0
- package/dist/x402/Types.js.map +1 -0
- package/dist/x402/client/Exact.d.ts +38 -0
- package/dist/x402/client/Exact.d.ts.map +1 -0
- package/dist/x402/client/Exact.js +141 -0
- package/dist/x402/client/Exact.js.map +1 -0
- package/dist/x402/index.d.ts +6 -0
- package/dist/x402/index.d.ts.map +1 -0
- package/dist/x402/index.js +6 -0
- package/dist/x402/index.js.map +1 -0
- package/dist/x402/internal/ChallengeBrand.d.ts +5 -0
- package/dist/x402/internal/ChallengeBrand.d.ts.map +1 -0
- package/dist/x402/internal/ChallengeBrand.js +13 -0
- package/dist/x402/internal/ChallengeBrand.js.map +1 -0
- package/dist/x402/internal/RouteBinding.d.ts +8 -0
- package/dist/x402/internal/RouteBinding.d.ts.map +1 -0
- package/dist/x402/internal/RouteBinding.js +12 -0
- package/dist/x402/internal/RouteBinding.js.map +1 -0
- package/dist/x402/server/EvmCharge.d.ts +50 -0
- package/dist/x402/server/EvmCharge.d.ts.map +1 -0
- package/dist/x402/server/EvmCharge.js +301 -0
- package/dist/x402/server/EvmCharge.js.map +1 -0
- package/dist/x402/server/Facilitator.d.ts +12 -0
- package/dist/x402/server/Facilitator.d.ts.map +1 -0
- package/dist/x402/server/Facilitator.js +42 -0
- package/dist/x402/server/Facilitator.js.map +1 -0
- package/package.json +41 -21
- package/src/Challenge.test.ts +54 -0
- package/src/Challenge.ts +17 -10
- package/src/Method.ts +1 -1
- package/src/client/Methods.ts +1 -0
- package/src/client/Mppx.ts +4 -3
- package/src/client/Transport.test.ts +165 -30
- package/src/client/Transport.ts +76 -8
- package/src/client/index.ts +1 -1
- package/src/client/internal/Fetch.test.ts +31 -2
- package/src/client/internal/Fetch.ts +26 -19
- package/src/evm/Assets.ts +1 -0
- package/src/evm/Chains.ts +5 -0
- package/src/evm/Methods.ts +44 -0
- package/src/evm/PublicInterface.test-d.ts +114 -0
- package/src/evm/Types.ts +140 -0
- package/src/evm/client/Charge.test.ts +99 -0
- package/src/evm/client/Charge.ts +198 -0
- package/src/evm/client/Methods.ts +19 -0
- package/src/evm/client/index.ts +5 -0
- package/src/evm/index.ts +14 -0
- package/src/evm/server/Charge.test.ts +199 -0
- package/src/evm/server/Charge.ts +283 -0
- package/src/evm/server/Methods.ts +22 -0
- package/src/evm/server/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/internal/HeaderCodec.ts +36 -0
- package/src/middlewares/elysia.test.ts +25 -0
- package/src/middlewares/elysia.ts +1 -2
- package/src/middlewares/express.test.ts +28 -0
- package/src/middlewares/express.ts +1 -1
- package/src/middlewares/hono.test.ts +138 -2
- package/src/middlewares/nextjs.test.ts +22 -0
- package/src/proxy/internal/Headers.test.ts +20 -0
- package/src/proxy/internal/Headers.ts +12 -1
- package/src/proxy/services/openai.test.ts +57 -1
- package/src/proxy/services/openai.ts +2 -0
- package/src/server/Methods.ts +1 -0
- package/src/server/Mppx.test.ts +244 -1
- package/src/server/Mppx.ts +124 -11
- package/src/server/NodeListener.test.ts +28 -1
- package/src/server/Transport.test.ts +19 -0
- package/src/server/Transport.ts +20 -0
- package/src/server/index.ts +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/AccessKeyAuthorization.test.ts +231 -0
- package/src/tempo/client/ChannelOps.test.ts +61 -7
- package/src/tempo/client/ChannelOps.ts +18 -7
- package/src/tempo/client/Charge.test.ts +126 -0
- package/src/tempo/client/Charge.ts +10 -6
- package/src/tempo/client/Session.test.ts +130 -1
- package/src/tempo/client/Session.ts +12 -19
- package/src/tempo/client/SessionManager.test.ts +69 -2
- package/src/tempo/client/SessionManager.ts +4 -4
- package/src/tempo/internal/account.ts +13 -0
- package/src/tempo/internal/fee-payer.test.ts +32 -2
- package/src/tempo/internal/fee-payer.ts +6 -2
- package/src/tempo/server/Charge.test.ts +69 -0
- package/src/tempo/server/Charge.ts +32 -0
- package/src/tempo/server/Session.test.ts +30 -0
- package/src/tempo/server/Session.ts +15 -16
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/Chain.test.ts +4 -4
- package/src/tempo/session/Chain.ts +10 -6
- package/src/tempo/session/Voucher.test.ts +230 -1
- package/src/tempo/session/Voucher.ts +96 -48
- package/src/tempo/session/Ws.test.ts +71 -0
- package/src/tempo/session/Ws.ts +13 -0
- package/src/x402/Assets.ts +65 -0
- package/src/x402/Exact.e2e.test.ts +448 -0
- package/src/x402/Header.test.ts +73 -0
- package/src/x402/Header.ts +34 -0
- package/src/x402/PublicInterface.test-d.ts +39 -0
- package/src/x402/Types.ts +248 -0
- package/src/x402/client/Exact.test.ts +180 -0
- package/src/x402/client/Exact.ts +198 -0
- package/src/x402/index.ts +5 -0
- package/src/x402/internal/ChallengeBrand.ts +14 -0
- package/src/x402/internal/RouteBinding.ts +18 -0
- package/src/x402/server/EvmCharge.ts +394 -0
- package/src/x402/server/Facilitator.test.ts +111 -0
- package/src/x402/server/Facilitator.ts +56 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Challenge, Credential } from 'mppx'
|
|
2
|
+
import { createClient, http } from 'viem'
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
4
|
+
import { tempoLocalnet } from 'viem/chains'
|
|
5
|
+
import { describe, expect, test, vi } from 'vp/test'
|
|
6
|
+
|
|
7
|
+
import * as Methods from '../Methods.js'
|
|
8
|
+
import { charge } from './Charge.js'
|
|
9
|
+
|
|
10
|
+
const account = privateKeyToAccount(
|
|
11
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001',
|
|
12
|
+
)
|
|
13
|
+
const currency = '0x3333333333333333333333333333333333333333'
|
|
14
|
+
const recipient = '0x2222222222222222222222222222222222222222'
|
|
15
|
+
|
|
16
|
+
type ChargeRequest = ReturnType<typeof Methods.charge.schema.request.parse>
|
|
17
|
+
|
|
18
|
+
function createChallenge(
|
|
19
|
+
overrides: Partial<Parameters<typeof Methods.charge.schema.request.parse>[0]> = {},
|
|
20
|
+
): Challenge.Challenge<ChargeRequest, 'charge', 'tempo'> {
|
|
21
|
+
const request = Methods.charge.schema.request.parse({
|
|
22
|
+
amount: '0',
|
|
23
|
+
currency,
|
|
24
|
+
decimals: 6,
|
|
25
|
+
recipient,
|
|
26
|
+
...overrides,
|
|
27
|
+
})
|
|
28
|
+
return Challenge.from({
|
|
29
|
+
id: 'test-challenge-id',
|
|
30
|
+
intent: 'charge',
|
|
31
|
+
method: 'tempo',
|
|
32
|
+
realm: 'api.example.com',
|
|
33
|
+
request,
|
|
34
|
+
}) as Challenge.Challenge<ChargeRequest, 'charge', 'tempo'>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('tempo.charge client', () => {
|
|
38
|
+
test('uses client chain ID when the challenge omits chainId', async () => {
|
|
39
|
+
const client = createClient({
|
|
40
|
+
account,
|
|
41
|
+
chain: tempoLocalnet,
|
|
42
|
+
transport: http('http://127.0.0.1'),
|
|
43
|
+
})
|
|
44
|
+
const method = charge({
|
|
45
|
+
account,
|
|
46
|
+
getClient: () => client,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const credential = Credential.deserialize(
|
|
50
|
+
await method.createCredential({
|
|
51
|
+
challenge: createChallenge(),
|
|
52
|
+
context: {},
|
|
53
|
+
}),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expect(credential.source).toBe(`did:pkh:eip155:${tempoLocalnet.id}:${account.address}`)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('uses challenge chainId for client resolution and proof source', async () => {
|
|
60
|
+
let requestedChainId: number | undefined
|
|
61
|
+
const chainId = 42431
|
|
62
|
+
const client = createClient({
|
|
63
|
+
account,
|
|
64
|
+
chain: tempoLocalnet,
|
|
65
|
+
transport: http('http://127.0.0.1'),
|
|
66
|
+
})
|
|
67
|
+
const method = charge({
|
|
68
|
+
account,
|
|
69
|
+
getClient: (parameters) => {
|
|
70
|
+
requestedChainId = parameters.chainId
|
|
71
|
+
return client
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const credential = Credential.deserialize(
|
|
76
|
+
await method.createCredential({
|
|
77
|
+
challenge: createChallenge({ chainId }),
|
|
78
|
+
context: {},
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
expect(requestedChainId).toBe(chainId)
|
|
83
|
+
expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('uses challenge chainId for non-zero transaction source', async () => {
|
|
87
|
+
vi.resetModules()
|
|
88
|
+
const prepareTransactionRequest = vi.fn(async () => ({}))
|
|
89
|
+
const signTransaction = vi.fn(async () => '0xdeadbeef')
|
|
90
|
+
vi.doMock('viem/actions', () => ({
|
|
91
|
+
prepareTransactionRequest,
|
|
92
|
+
sendCallsSync: vi.fn(),
|
|
93
|
+
signTransaction,
|
|
94
|
+
signTypedData: vi.fn(),
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const { charge: chargeWithMockedActions } = await import('./Charge.js')
|
|
99
|
+
const chainId = 42431
|
|
100
|
+
const client = createClient({
|
|
101
|
+
account,
|
|
102
|
+
chain: tempoLocalnet,
|
|
103
|
+
transport: http('http://127.0.0.1'),
|
|
104
|
+
})
|
|
105
|
+
const method = chargeWithMockedActions({
|
|
106
|
+
account,
|
|
107
|
+
getClient: () => client,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const credential = Credential.deserialize(
|
|
111
|
+
await method.createCredential({
|
|
112
|
+
challenge: createChallenge({ amount: '1', chainId, supportedModes: ['pull'] }),
|
|
113
|
+
context: {},
|
|
114
|
+
}),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
expect(prepareTransactionRequest).toHaveBeenCalledOnce()
|
|
118
|
+
expect(signTransaction).toHaveBeenCalledOnce()
|
|
119
|
+
expect(credential.payload).toEqual({ signature: '0xdeadbeef', type: 'transaction' })
|
|
120
|
+
expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
|
|
121
|
+
} finally {
|
|
122
|
+
vi.doUnmock('viem/actions')
|
|
123
|
+
vi.resetModules()
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
})
|
|
@@ -51,8 +51,12 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
51
51
|
}),
|
|
52
52
|
|
|
53
53
|
async createCredential({ challenge, context }) {
|
|
54
|
-
const
|
|
55
|
-
const client = await getClient({ chainId })
|
|
54
|
+
const challengeChainId = challenge.request.methodDetails?.chainId
|
|
55
|
+
const client = await getClient({ chainId: challengeChainId })
|
|
56
|
+
const chainId = challengeChainId ?? client.chain?.id
|
|
57
|
+
if (chainId === undefined)
|
|
58
|
+
throw new Error('No `chainId` provided. Pass a chain ID in the challenge or client.')
|
|
59
|
+
|
|
56
60
|
const account = getAccount(client, context)
|
|
57
61
|
|
|
58
62
|
const { request } = challenge
|
|
@@ -62,7 +66,7 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
62
66
|
if (BigInt(amount) === 0n) {
|
|
63
67
|
const signature = await signTypedData(client, {
|
|
64
68
|
account,
|
|
65
|
-
domain: Proof.domain(chainId
|
|
69
|
+
domain: Proof.domain(chainId),
|
|
66
70
|
types: Proof.types,
|
|
67
71
|
primaryType: 'Proof',
|
|
68
72
|
message: Proof.message(challenge.id, challenge.realm),
|
|
@@ -70,7 +74,7 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
70
74
|
return Credential.serialize({
|
|
71
75
|
challenge,
|
|
72
76
|
payload: { signature, type: 'proof' },
|
|
73
|
-
source: Proof.proofSource({ address: account.address, chainId
|
|
77
|
+
source: Proof.proofSource({ address: account.address, chainId }),
|
|
74
78
|
})
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -156,7 +160,7 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
156
160
|
return Credential.serialize({
|
|
157
161
|
challenge,
|
|
158
162
|
payload: { hash, type: 'hash' },
|
|
159
|
-
source:
|
|
163
|
+
source: Proof.proofSource({ address: account.address, chainId }),
|
|
160
164
|
})
|
|
161
165
|
}
|
|
162
166
|
|
|
@@ -175,7 +179,7 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
175
179
|
return Credential.serialize({
|
|
176
180
|
challenge,
|
|
177
181
|
payload: { signature, type: 'transaction' },
|
|
178
|
-
source:
|
|
182
|
+
source: Proof.proofSource({ address: account.address, chainId }),
|
|
179
183
|
})
|
|
180
184
|
},
|
|
181
185
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { SignatureEnvelope } from 'ox/tempo'
|
|
1
2
|
import { type Address, createClient, decodeFunctionData, erc20Abi, type Hex, http } from 'viem'
|
|
2
3
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
|
-
import { Addresses, Transaction } from 'viem/tempo'
|
|
4
|
+
import { Account as TempoAccount, Addresses, Transaction, WebCryptoP256 } from 'viem/tempo'
|
|
4
5
|
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
5
6
|
import { nodeEnv } from '~test/config.js'
|
|
6
7
|
import { deployEscrow, openChannel } from '~test/tempo/session.js'
|
|
@@ -13,6 +14,7 @@ import * as Credential from '../../Credential.js'
|
|
|
13
14
|
import { chainId, escrowContract as escrowContractDefaults } from '../internal/defaults.js'
|
|
14
15
|
import { escrowAbi } from '../session/Chain.js'
|
|
15
16
|
import type { SessionCredentialPayload } from '../session/Types.js'
|
|
17
|
+
import { verifyVoucher } from '../session/Voucher.js'
|
|
16
18
|
import { session } from './Session.js'
|
|
17
19
|
|
|
18
20
|
function deserializePayload(result: string) {
|
|
@@ -184,6 +186,133 @@ describe('session (pure)', () => {
|
|
|
184
186
|
expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
|
|
185
187
|
})
|
|
186
188
|
|
|
189
|
+
test('manual open rejects P256 voucher signer while TIP-1020 verification is disabled', async () => {
|
|
190
|
+
const keyPair = await WebCryptoP256.createKeyPair()
|
|
191
|
+
const voucherSigner = TempoAccount.fromWebCryptoP256(keyPair)
|
|
192
|
+
const method = session({
|
|
193
|
+
getClient: () => pureClient,
|
|
194
|
+
account: pureAccount,
|
|
195
|
+
voucherSigner,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
await expect(
|
|
199
|
+
method.createCredential({
|
|
200
|
+
challenge: makeChallenge(),
|
|
201
|
+
context: {
|
|
202
|
+
action: 'open',
|
|
203
|
+
channelId,
|
|
204
|
+
cumulativeAmount: '5',
|
|
205
|
+
transaction: '0xdeadbeef',
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
).rejects.toThrow('Session vouchers only support secp256k1 signatures')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('manual open signs access-key vouchers with raw signatures', async () => {
|
|
212
|
+
const accessKey = TempoAccount.fromSecp256k1(
|
|
213
|
+
'0x59c6995e998f97a5a0044966f09453863d462d2b3f1446a99f0a3d7b5d0f5a0d',
|
|
214
|
+
{ access: pureAccount },
|
|
215
|
+
)
|
|
216
|
+
const accessKeyClient = createClient({
|
|
217
|
+
account: accessKey,
|
|
218
|
+
transport: http('http://127.0.0.1'),
|
|
219
|
+
})
|
|
220
|
+
const method = session({
|
|
221
|
+
getClient: () => accessKeyClient,
|
|
222
|
+
account: accessKey,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const result = await method.createCredential({
|
|
226
|
+
challenge: makeChallenge(),
|
|
227
|
+
context: {
|
|
228
|
+
action: 'open',
|
|
229
|
+
channelId,
|
|
230
|
+
cumulativeAmount: '5',
|
|
231
|
+
transaction: '0xdeadbeef',
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const cred = deserializePayload(result)
|
|
236
|
+
expect(cred.payload.action).toBe('open')
|
|
237
|
+
if (cred.payload.action !== 'open') throw new Error('unexpected action')
|
|
238
|
+
expect(cred.payload.authorizedSigner).toBeDefined()
|
|
239
|
+
if (!cred.payload.authorizedSigner) throw new Error('missing authorizedSigner')
|
|
240
|
+
expect(cred.payload.authorizedSigner.toLowerCase()).toBe(
|
|
241
|
+
accessKey.accessKeyAddress.toLowerCase(),
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
const envelope = SignatureEnvelope.from(
|
|
245
|
+
cred.payload.signature as SignatureEnvelope.Serialized,
|
|
246
|
+
)
|
|
247
|
+
expect(envelope.type).toBe('secp256k1')
|
|
248
|
+
|
|
249
|
+
const isValid = await verifyVoucher(
|
|
250
|
+
escrowAddress,
|
|
251
|
+
42431,
|
|
252
|
+
{
|
|
253
|
+
channelId,
|
|
254
|
+
cumulativeAmount: 5_000_000n,
|
|
255
|
+
signature: cred.payload.signature,
|
|
256
|
+
},
|
|
257
|
+
accessKey.accessKeyAddress,
|
|
258
|
+
)
|
|
259
|
+
expect(isValid).toBe(true)
|
|
260
|
+
expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('manual open signs access-key vouchers with direct voucher signer', async () => {
|
|
264
|
+
const privateKey = '0x59c6995e998f97a5a0044966f09453863d462d2b3f1446a99f0a3d7b5d0f5a0d'
|
|
265
|
+
const accessKey = TempoAccount.fromSecp256k1(privateKey, { access: pureAccount })
|
|
266
|
+
const voucherSigner = TempoAccount.fromSecp256k1(privateKey)
|
|
267
|
+
const accessKeyClient = createClient({
|
|
268
|
+
account: accessKey,
|
|
269
|
+
transport: http('http://127.0.0.1'),
|
|
270
|
+
})
|
|
271
|
+
const method = session({
|
|
272
|
+
getClient: () => accessKeyClient,
|
|
273
|
+
account: accessKey,
|
|
274
|
+
voucherSigner,
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
const result = await method.createCredential({
|
|
278
|
+
challenge: makeChallenge(),
|
|
279
|
+
context: {
|
|
280
|
+
action: 'open',
|
|
281
|
+
channelId,
|
|
282
|
+
cumulativeAmount: '5',
|
|
283
|
+
transaction: '0xdeadbeef',
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const cred = deserializePayload(result)
|
|
288
|
+
expect(cred.payload.action).toBe('open')
|
|
289
|
+
if (cred.payload.action !== 'open') throw new Error('unexpected action')
|
|
290
|
+
expect(cred.payload.authorizedSigner).toBeDefined()
|
|
291
|
+
if (!cred.payload.authorizedSigner) throw new Error('missing authorizedSigner')
|
|
292
|
+
expect(cred.payload.authorizedSigner.toLowerCase()).toBe(
|
|
293
|
+
accessKey.accessKeyAddress.toLowerCase(),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
const envelope = SignatureEnvelope.from(
|
|
297
|
+
cred.payload.signature as SignatureEnvelope.Serialized,
|
|
298
|
+
)
|
|
299
|
+
expect(envelope.type).toBe('secp256k1')
|
|
300
|
+
expect(cred.payload.signature.length).toBe(132)
|
|
301
|
+
|
|
302
|
+
const isValid = await verifyVoucher(
|
|
303
|
+
escrowAddress,
|
|
304
|
+
42431,
|
|
305
|
+
{
|
|
306
|
+
channelId,
|
|
307
|
+
cumulativeAmount: 5_000_000n,
|
|
308
|
+
signature: cred.payload.signature,
|
|
309
|
+
},
|
|
310
|
+
accessKey.accessKeyAddress,
|
|
311
|
+
)
|
|
312
|
+
expect(isValid).toBe(true)
|
|
313
|
+
expect(cred.source).toBe(`did:pkh:eip155:42431:${pureAccount.address}`)
|
|
314
|
+
})
|
|
315
|
+
|
|
187
316
|
test('manual close produces valid credential', async () => {
|
|
188
317
|
const method = session({ getClient: () => pureClient, account: pureAccount })
|
|
189
318
|
|
|
@@ -7,6 +7,7 @@ import * as Method from '../../Method.js'
|
|
|
7
7
|
import * as Account from '../../viem/Account.js'
|
|
8
8
|
import * as Client from '../../viem/Client.js'
|
|
9
9
|
import * as z from '../../zod.js'
|
|
10
|
+
import { getAccountSignerAddress } from '../internal/account.js'
|
|
10
11
|
import * as defaults from '../internal/defaults.js'
|
|
11
12
|
import * as Methods from '../Methods.js'
|
|
12
13
|
import type { SessionCredentialPayload } from '../session/Types.js'
|
|
@@ -27,7 +28,6 @@ export const sessionContextSchema = z.object({
|
|
|
27
28
|
cumulativeAmount: z.optional(z.amount()),
|
|
28
29
|
cumulativeAmountRaw: z.optional(z.string()),
|
|
29
30
|
transaction: z.optional(z.string()),
|
|
30
|
-
authorizedSigner: z.optional(z.string()),
|
|
31
31
|
additionalDeposit: z.optional(z.amount()),
|
|
32
32
|
additionalDepositRaw: z.optional(z.string()),
|
|
33
33
|
depositRaw: z.optional(z.string()),
|
|
@@ -79,9 +79,6 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
79
79
|
rpcUrl: defaults.rpcUrl,
|
|
80
80
|
})
|
|
81
81
|
const getAccount = Account.getResolver({ account: parameters.account })
|
|
82
|
-
const getAuthorizedSigner = (account: viem_Account) =>
|
|
83
|
-
parameters.authorizedSigner ??
|
|
84
|
-
(account as unknown as { accessKeyAddress?: Address }).accessKeyAddress
|
|
85
82
|
|
|
86
83
|
const maxDeposit =
|
|
87
84
|
parameters.maxDeposit !== undefined ? parseUnits(parameters.maxDeposit, decimals) : undefined
|
|
@@ -141,7 +138,7 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
141
138
|
)
|
|
142
139
|
})()
|
|
143
140
|
|
|
144
|
-
const
|
|
141
|
+
const voucherSigner = parameters.voucherSigner ?? account
|
|
145
142
|
|
|
146
143
|
const key = channelKey(payee, currency, escrowContract)
|
|
147
144
|
let entry = channels.get(key)
|
|
@@ -186,12 +183,12 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
186
183
|
entry.cumulativeAmount,
|
|
187
184
|
escrowContract,
|
|
188
185
|
chainId,
|
|
189
|
-
|
|
186
|
+
voucherSigner,
|
|
190
187
|
)
|
|
191
188
|
notifyUpdate(entry)
|
|
192
189
|
} else {
|
|
193
190
|
const result = await createOpenPayload(client, account, {
|
|
194
|
-
|
|
191
|
+
voucherSigner,
|
|
195
192
|
escrowContract,
|
|
196
193
|
payee,
|
|
197
194
|
currency,
|
|
@@ -222,12 +219,8 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
222
219
|
const client = await getClient({ chainId })
|
|
223
220
|
|
|
224
221
|
const action = context.action!
|
|
225
|
-
const {
|
|
226
|
-
|
|
227
|
-
transaction,
|
|
228
|
-
authorizedSigner: contextAuthorizedSigner,
|
|
229
|
-
} = context
|
|
230
|
-
const authorizedSigner = (contextAuthorizedSigner as Address) ?? getAuthorizedSigner(account)
|
|
222
|
+
const { channelId: channelIdRaw, transaction } = context
|
|
223
|
+
const voucherSigner = parameters.voucherSigner ?? account
|
|
231
224
|
const channelId = channelIdRaw as Hex.Hex
|
|
232
225
|
const cumulativeAmount = context.cumulativeAmountRaw
|
|
233
226
|
? BigInt(context.cumulativeAmountRaw)
|
|
@@ -256,14 +249,14 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
256
249
|
{ channelId, cumulativeAmount },
|
|
257
250
|
escrowContract,
|
|
258
251
|
chainId,
|
|
259
|
-
|
|
252
|
+
voucherSigner,
|
|
260
253
|
)
|
|
261
254
|
payload = {
|
|
262
255
|
action: 'open',
|
|
263
256
|
type: 'transaction',
|
|
264
257
|
channelId,
|
|
265
258
|
transaction: transaction as Hex.Hex,
|
|
266
|
-
authorizedSigner:
|
|
259
|
+
authorizedSigner: getAccountSignerAddress(voucherSigner),
|
|
267
260
|
cumulativeAmount: cumulativeAmount.toString(),
|
|
268
261
|
signature,
|
|
269
262
|
}
|
|
@@ -293,7 +286,7 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
293
286
|
cumulativeAmount,
|
|
294
287
|
escrowContract,
|
|
295
288
|
chainId,
|
|
296
|
-
|
|
289
|
+
voucherSigner,
|
|
297
290
|
)
|
|
298
291
|
const key = channelIdToKey.get(channelId)
|
|
299
292
|
if (key) {
|
|
@@ -316,7 +309,7 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
316
309
|
{ channelId, cumulativeAmount },
|
|
317
310
|
escrowContract,
|
|
318
311
|
chainId,
|
|
319
|
-
|
|
312
|
+
voucherSigner,
|
|
320
313
|
)
|
|
321
314
|
payload = {
|
|
322
315
|
action: 'close',
|
|
@@ -364,8 +357,8 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
364
357
|
export declare namespace session {
|
|
365
358
|
type Parameters = Account.getResolver.Parameters &
|
|
366
359
|
Client.getResolver.Parameters & {
|
|
367
|
-
/**
|
|
368
|
-
|
|
360
|
+
/** Account that signs voucher digests. Defaults to `account`; access-key accounts sign raw vouchers as their access-key address. */
|
|
361
|
+
voucherSigner?: viem_Account | undefined
|
|
369
362
|
/** Token decimals for parsing human-readable amounts (default: 6). */
|
|
370
363
|
decimals?: number | undefined
|
|
371
364
|
/** Initial deposit amount in human-readable units (e.g. "10" for 10 tokens). When set, the method handles the full channel lifecycle (open, voucher, cumulative tracking) automatically. */
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { createClient, type Hex, http } from 'viem'
|
|
2
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
|
+
import { Account as TempoAccount } from 'viem/tempo'
|
|
2
4
|
import { describe, expect, test, vi } from 'vp/test'
|
|
3
5
|
|
|
4
6
|
import * as Challenge from '../../Challenge.js'
|
|
7
|
+
import * as PaymentCredential from '../../Credential.js'
|
|
5
8
|
import { formatNeedVoucherEvent, parseEvent } from '../session/Sse.js'
|
|
6
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
NeedVoucherEvent,
|
|
11
|
+
SessionCredentialPayload,
|
|
12
|
+
SessionReceipt,
|
|
13
|
+
} from '../session/Types.js'
|
|
7
14
|
import { sessionManager } from './SessionManager.js'
|
|
8
15
|
|
|
9
16
|
const channelId = '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex
|
|
@@ -82,6 +89,66 @@ describe('Session', () => {
|
|
|
82
89
|
expect(s.cumulative).toBe(0n)
|
|
83
90
|
expect(s.opened).toBe(false)
|
|
84
91
|
})
|
|
92
|
+
|
|
93
|
+
test('uses voucherSigner for managed open credentials', async () => {
|
|
94
|
+
vi.resetModules()
|
|
95
|
+
vi.doMock('viem/actions', () => ({
|
|
96
|
+
prepareTransactionRequest: vi.fn(async () => ({})),
|
|
97
|
+
sendCallsSync: vi.fn(),
|
|
98
|
+
signTransaction: vi.fn(async () => '0xdeadbeef'),
|
|
99
|
+
signTypedData: vi.fn(),
|
|
100
|
+
}))
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const { sessionManager: sessionManagerWithMocks } = await import('./SessionManager.js')
|
|
104
|
+
const account = privateKeyToAccount(
|
|
105
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001',
|
|
106
|
+
)
|
|
107
|
+
const voucherSigner = TempoAccount.fromSecp256k1(
|
|
108
|
+
'0x0000000000000000000000000000000000000000000000000000000000000002',
|
|
109
|
+
{ access: account },
|
|
110
|
+
)
|
|
111
|
+
const client = createClient({
|
|
112
|
+
account,
|
|
113
|
+
transport: http('http://127.0.0.1'),
|
|
114
|
+
})
|
|
115
|
+
const challenge = makeChallenge({
|
|
116
|
+
recipient: '0x742d35cc6634c0532925a3b844bc9e7595f8fe00',
|
|
117
|
+
methodDetails: {
|
|
118
|
+
escrowContract: '0x9d136eea063ede5418a6bc7beaff009bbb6cfa70',
|
|
119
|
+
chainId: 4217,
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
const mockFetch = vi
|
|
123
|
+
.fn()
|
|
124
|
+
.mockResolvedValueOnce(make402Response(challenge))
|
|
125
|
+
.mockResolvedValueOnce(makeOkResponse())
|
|
126
|
+
|
|
127
|
+
const manager = sessionManagerWithMocks({
|
|
128
|
+
account,
|
|
129
|
+
client,
|
|
130
|
+
fetch: mockFetch as typeof globalThis.fetch,
|
|
131
|
+
maxDeposit: '10',
|
|
132
|
+
voucherSigner,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const response = await manager.fetch('https://api.example.com/data')
|
|
136
|
+
const authorization = new Headers((mockFetch.mock.calls[1]![1] as RequestInit).headers).get(
|
|
137
|
+
'Authorization',
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
expect(response.status).toBe(200)
|
|
141
|
+
expect(authorization).toBeDefined()
|
|
142
|
+
if (!authorization) throw new Error('missing authorization header')
|
|
143
|
+
const credential = PaymentCredential.deserialize<SessionCredentialPayload>(authorization)
|
|
144
|
+
expect(credential.payload.action).toBe('open')
|
|
145
|
+
if (credential.payload.action !== 'open') throw new Error('unexpected action')
|
|
146
|
+
expect(credential.payload.authorizedSigner).toBe(voucherSigner.accessKeyAddress)
|
|
147
|
+
} finally {
|
|
148
|
+
vi.doUnmock('viem/actions')
|
|
149
|
+
vi.resetModules()
|
|
150
|
+
}
|
|
151
|
+
})
|
|
85
152
|
})
|
|
86
153
|
|
|
87
154
|
describe('.fetch()', () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Hex } from 'ox'
|
|
2
|
-
import { parseUnits, type Address } from 'viem'
|
|
2
|
+
import { parseUnits, type Account as viem_Account, type Address } from 'viem'
|
|
3
3
|
|
|
4
4
|
import * as Challenge from '../../Challenge.js'
|
|
5
5
|
import * as Fetch from '../../client/internal/Fetch.js'
|
|
@@ -124,7 +124,7 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
124
124
|
|
|
125
125
|
const method = sessionPlugin({
|
|
126
126
|
account: parameters.account,
|
|
127
|
-
|
|
127
|
+
voucherSigner: parameters.voucherSigner,
|
|
128
128
|
getClient: parameters.client ? () => parameters.client! : parameters.getClient,
|
|
129
129
|
escrowContract: parameters.escrowContract,
|
|
130
130
|
decimals: parameters.decimals,
|
|
@@ -853,8 +853,8 @@ export function sessionManager(parameters: sessionManager.Parameters): SessionMa
|
|
|
853
853
|
export declare namespace sessionManager {
|
|
854
854
|
type Parameters = Account.getResolver.Parameters &
|
|
855
855
|
Client.getResolver.Parameters & {
|
|
856
|
-
/**
|
|
857
|
-
|
|
856
|
+
/** Account that signs voucher digests. Defaults to `account`; access-key accounts sign raw vouchers as their access-key address. */
|
|
857
|
+
voucherSigner?: viem_Account | undefined
|
|
858
858
|
/** Viem client instance. Shorthand for `getClient: () => client`. */
|
|
859
859
|
client?: import('viem').Client | undefined
|
|
860
860
|
/** Token decimals used to convert `maxDeposit` to raw units. Defaults to `6`. */
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { Account, Address } from 'viem'
|
|
2
|
+
import type { Account as TempoAccount } from 'viem/tempo'
|
|
3
|
+
|
|
4
|
+
/** Returns whether an account is a Tempo access-key account. */
|
|
5
|
+
export function isAccessKeyAccount(
|
|
6
|
+
account: Account,
|
|
7
|
+
): account is Account & TempoAccount.AccessKeyAccount {
|
|
8
|
+
return 'accessKeyAddress' in account
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Returns the address that should authorize direct account signatures. */
|
|
12
|
+
export function getAccountSignerAddress(account: Account): Address {
|
|
13
|
+
return isAccessKeyAccount(account) ? account.accessKeyAddress : account.address
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
/**
|
|
4
17
|
* Resolves a recipient address and optional fee payer from flexible input parameters.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeFunctionData } from 'viem'
|
|
1
|
+
import { encodeFunctionData, maxUint256 } from 'viem'
|
|
2
2
|
import { Abis, Addresses } from 'viem/tempo'
|
|
3
3
|
import { describe, expect, test } from 'vp/test'
|
|
4
4
|
|
|
@@ -443,7 +443,7 @@ describe('prepareSponsoredTransaction', () => {
|
|
|
443
443
|
maxFeePerGas: 1_000_000_000n,
|
|
444
444
|
maxPriorityFeePerGas: 1_000_000_000n,
|
|
445
445
|
nonce: 1n,
|
|
446
|
-
nonceKey:
|
|
446
|
+
nonceKey: 'expiring',
|
|
447
447
|
signature: { r: 1n, s: 1n, yParity: 0 } as any,
|
|
448
448
|
validBefore: Math.floor(Date.now() / 1_000) + 300,
|
|
449
449
|
} as const
|
|
@@ -460,6 +460,36 @@ describe('prepareSponsoredTransaction', () => {
|
|
|
460
460
|
).not.toThrow()
|
|
461
461
|
})
|
|
462
462
|
|
|
463
|
+
test('accepts serialized expiring nonce key', () => {
|
|
464
|
+
expect(() =>
|
|
465
|
+
prepareSponsoredTransaction({
|
|
466
|
+
account: sponsor,
|
|
467
|
+
chainId: 42431,
|
|
468
|
+
details,
|
|
469
|
+
expectedFeeToken: bogus,
|
|
470
|
+
transaction: {
|
|
471
|
+
...baseTransaction,
|
|
472
|
+
nonceKey: maxUint256,
|
|
473
|
+
} as any,
|
|
474
|
+
}),
|
|
475
|
+
).not.toThrow()
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
test('error: rejects non-expiring nonce keys', () => {
|
|
479
|
+
expect(() =>
|
|
480
|
+
prepareSponsoredTransaction({
|
|
481
|
+
account: sponsor,
|
|
482
|
+
chainId: 42431,
|
|
483
|
+
details,
|
|
484
|
+
expectedFeeToken: bogus,
|
|
485
|
+
transaction: {
|
|
486
|
+
...baseTransaction,
|
|
487
|
+
nonceKey: 1n,
|
|
488
|
+
} as any,
|
|
489
|
+
}),
|
|
490
|
+
).toThrow('must use an expiring nonce')
|
|
491
|
+
})
|
|
492
|
+
|
|
463
493
|
test('accepts higher Moderato priority fees by default', () => {
|
|
464
494
|
expect(() =>
|
|
465
495
|
prepareSponsoredTransaction({
|
|
@@ -2,7 +2,7 @@ import type { TempoAddress } from 'ox/tempo'
|
|
|
2
2
|
import { TxEnvelopeTempo } from 'ox/tempo'
|
|
3
3
|
import type { Hex } from 'viem'
|
|
4
4
|
import type { Account } from 'viem'
|
|
5
|
-
import { decodeFunctionData } from 'viem'
|
|
5
|
+
import { decodeFunctionData, maxUint256 } from 'viem'
|
|
6
6
|
import { Abis, Addresses, Transaction } from 'viem/tempo'
|
|
7
7
|
|
|
8
8
|
import * as TempoAddress_internal from './address.js'
|
|
@@ -126,6 +126,10 @@ function getPolicy(chainId: number, overrides: Partial<Policy> | undefined): Pol
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
function isExpiringNonceKey(nonceKey: SponsoredTransaction['nonceKey']): boolean {
|
|
130
|
+
return nonceKey === 'expiring' || nonceKey === maxUint256
|
|
131
|
+
}
|
|
132
|
+
|
|
129
133
|
/** Validates that a set of transaction calls matches an allowed fee-payer pattern. */
|
|
130
134
|
export function validateCalls(
|
|
131
135
|
calls: readonly { data?: `0x${string}` | undefined; to?: TempoAddress.Address | undefined }[],
|
|
@@ -358,7 +362,7 @@ export function prepareSponsoredTransaction(parameters: {
|
|
|
358
362
|
maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
|
|
359
363
|
})
|
|
360
364
|
|
|
361
|
-
if (nonceKey
|
|
365
|
+
if (!isExpiringNonceKey(nonceKey)) fail('fee-sponsored transaction must use an expiring nonce')
|
|
362
366
|
if (validBefore === undefined)
|
|
363
367
|
fail('fee-sponsored transaction must declare validBefore for the expiring nonce')
|
|
364
368
|
const validBeforeValue = validBefore
|