mppx 0.8.4 → 0.8.6
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 +18 -0
- package/dist/Receipt.d.ts +20 -2
- package/dist/Receipt.d.ts.map +1 -1
- package/dist/Receipt.js +19 -12
- package/dist/Receipt.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +6 -4
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/plugins/tempo.d.ts.map +1 -1
- package/dist/cli/plugins/tempo.js +4 -2
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +6 -1
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/evm/client/Charge.d.ts +2 -2
- package/dist/evm/client/Charge.d.ts.map +1 -1
- package/dist/evm/client/Charge.js +11 -17
- package/dist/evm/client/Charge.js.map +1 -1
- package/dist/evm/server/Charge.d.ts +5 -5
- package/dist/evm/server/Charge.d.ts.map +1 -1
- package/dist/evm/server/Charge.js +15 -7
- package/dist/evm/server/Charge.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +6 -2
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +26 -2
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +38 -5
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/x402/Assets.d.ts +40 -0
- package/dist/x402/Assets.d.ts.map +1 -1
- package/dist/x402/Assets.js +73 -0
- package/dist/x402/Assets.js.map +1 -1
- package/dist/x402/client/Exact.d.ts +2 -2
- package/dist/x402/client/Exact.d.ts.map +1 -1
- package/dist/x402/client/Exact.js +6 -13
- package/dist/x402/client/Exact.js.map +1 -1
- package/package.json +1 -1
- package/src/Receipt.test.ts +53 -0
- package/src/Receipt.ts +26 -13
- package/src/cli/cli.test.ts +41 -5
- package/src/cli/cli.ts +7 -4
- package/src/cli/plugins/tempo.ts +6 -2
- package/src/client/internal/Fetch.test.ts +44 -0
- package/src/client/internal/Fetch.ts +5 -1
- package/src/evm/PublicInterface.test-d.ts +15 -2
- package/src/evm/client/Charge.test.ts +248 -0
- package/src/evm/client/Charge.ts +12 -23
- package/src/evm/server/Charge.test.ts +140 -0
- package/src/evm/server/Charge.ts +19 -12
- package/src/tempo/client/Charge.test.ts +61 -0
- package/src/tempo/client/Charge.ts +6 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/precompile/Chain.test.ts +121 -2
- package/src/tempo/session/precompile/Chain.ts +56 -6
- package/src/x402/Assets.test.ts +211 -0
- package/src/x402/Assets.ts +116 -0
- package/src/x402/client/Exact.test.ts +129 -0
- package/src/x402/client/Exact.ts +7 -19
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Challenge } from 'mppx'
|
|
2
2
|
import type { Account } from 'viem'
|
|
3
|
+
import { tokens } from 'viem/tokens'
|
|
3
4
|
import { describe, expect, test, vi } from 'vp/test'
|
|
4
5
|
|
|
5
6
|
import * as Chains from '../../evm/Chains.js'
|
|
@@ -78,6 +79,134 @@ describe('x402 exact credential helper', () => {
|
|
|
78
79
|
).rejects.toThrow('x402 exact raw currency allowlists require networks.')
|
|
79
80
|
})
|
|
80
81
|
|
|
82
|
+
test('uses known asset metadata for currency policy and maxAmount decimals', async () => {
|
|
83
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
84
|
+
|
|
85
|
+
const credential = await createCredential({
|
|
86
|
+
challenge: challenge(),
|
|
87
|
+
config: {
|
|
88
|
+
account: {
|
|
89
|
+
...account,
|
|
90
|
+
signTypedData,
|
|
91
|
+
} as unknown as Account,
|
|
92
|
+
currencies: [Assets.baseSepolia.USDC],
|
|
93
|
+
maxAmount: '0.01',
|
|
94
|
+
},
|
|
95
|
+
context: {},
|
|
96
|
+
})
|
|
97
|
+
const paymentPayload = Header.decodePaymentSignature(credential)
|
|
98
|
+
|
|
99
|
+
expect(signTypedData).toHaveBeenCalledOnce()
|
|
100
|
+
expect(paymentPayload.accepted.asset).toBe(Assets.baseSepolia.USDC.address)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('pins known asset currency policy to its network', async () => {
|
|
104
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
105
|
+
|
|
106
|
+
await expect(
|
|
107
|
+
createCredential({
|
|
108
|
+
challenge: challenge({ network: 'eip155:8453' }),
|
|
109
|
+
config: {
|
|
110
|
+
account: {
|
|
111
|
+
...account,
|
|
112
|
+
signTypedData,
|
|
113
|
+
} as unknown as Account,
|
|
114
|
+
currencies: [Assets.baseSepolia.USDC],
|
|
115
|
+
maxAmount: '0.01',
|
|
116
|
+
},
|
|
117
|
+
context: {},
|
|
118
|
+
}),
|
|
119
|
+
).rejects.toThrow(
|
|
120
|
+
'x402 exact currency is not allowed: 0x036CbD53842c5426634e7929541eC2318f3dCF7e.',
|
|
121
|
+
)
|
|
122
|
+
expect(signTypedData).not.toHaveBeenCalled()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('accepts viem token sets for currency policy and decimals', async () => {
|
|
126
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
127
|
+
|
|
128
|
+
const credential = await createCredential({
|
|
129
|
+
challenge: challenge({ amount: '1000000' }),
|
|
130
|
+
config: {
|
|
131
|
+
account: {
|
|
132
|
+
...account,
|
|
133
|
+
signTypedData,
|
|
134
|
+
} as unknown as Account,
|
|
135
|
+
currencies: tokens.popular,
|
|
136
|
+
maxAmount: '1',
|
|
137
|
+
},
|
|
138
|
+
context: {},
|
|
139
|
+
})
|
|
140
|
+
const paymentPayload = Header.decodePaymentSignature(credential)
|
|
141
|
+
|
|
142
|
+
expect(signTypedData).toHaveBeenCalledOnce()
|
|
143
|
+
expect(paymentPayload.accepted.asset).toBe(Assets.baseSepolia.USDC.address)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('uses viem token decimals for maxAmount policy', async () => {
|
|
147
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
148
|
+
|
|
149
|
+
await expect(
|
|
150
|
+
createCredential({
|
|
151
|
+
challenge: challenge({ amount: '1000001' }),
|
|
152
|
+
config: {
|
|
153
|
+
account: {
|
|
154
|
+
...account,
|
|
155
|
+
signTypedData,
|
|
156
|
+
} as unknown as Account,
|
|
157
|
+
currencies: tokens.popular,
|
|
158
|
+
maxAmount: '1',
|
|
159
|
+
},
|
|
160
|
+
context: {},
|
|
161
|
+
}),
|
|
162
|
+
).rejects.toThrow('x402 exact amount exceeds maxAmount.')
|
|
163
|
+
expect(signTypedData).not.toHaveBeenCalled()
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('accepts viem token sets through legacy assets policy', async () => {
|
|
167
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
168
|
+
|
|
169
|
+
const credential = await createCredential({
|
|
170
|
+
challenge: challenge({ amount: '1000000' }),
|
|
171
|
+
config: {
|
|
172
|
+
account: {
|
|
173
|
+
...account,
|
|
174
|
+
signTypedData,
|
|
175
|
+
} as unknown as Account,
|
|
176
|
+
assets: tokens.popular,
|
|
177
|
+
maxAmount: '1',
|
|
178
|
+
},
|
|
179
|
+
context: {},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(signTypedData).toHaveBeenCalledOnce()
|
|
183
|
+
expect(Header.decodePaymentSignature(credential).accepted.asset).toBe(
|
|
184
|
+
Assets.baseSepolia.USDC.address,
|
|
185
|
+
)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test('pins viem token currency policy to available networks', async () => {
|
|
189
|
+
const signTypedData = vi.fn(async () => '0x1234')
|
|
190
|
+
|
|
191
|
+
await expect(
|
|
192
|
+
createCredential({
|
|
193
|
+
challenge: challenge({ network: 'eip155:999999' }),
|
|
194
|
+
config: {
|
|
195
|
+
account: {
|
|
196
|
+
...account,
|
|
197
|
+
signTypedData,
|
|
198
|
+
} as unknown as Account,
|
|
199
|
+
currencies: tokens.popular,
|
|
200
|
+
maxAmount: '1',
|
|
201
|
+
},
|
|
202
|
+
context: {},
|
|
203
|
+
}),
|
|
204
|
+
).rejects.toThrow(
|
|
205
|
+
'x402 exact currency is not allowed: 0x036CbD53842c5426634e7929541eC2318f3dCF7e.',
|
|
206
|
+
)
|
|
207
|
+
expect(signTypedData).not.toHaveBeenCalled()
|
|
208
|
+
})
|
|
209
|
+
|
|
81
210
|
test('signs EIP-3009 exact payment payloads', async () => {
|
|
82
211
|
const signTypedData = vi.fn(async () => '0x1234')
|
|
83
212
|
const config = {
|
package/src/x402/client/Exact.ts
CHANGED
|
@@ -99,9 +99,9 @@ export type Config = {
|
|
|
99
99
|
/** Optional allowlist of supported EVM chain IDs. */
|
|
100
100
|
networks?: readonly number[] | undefined
|
|
101
101
|
/** Optional allowlist of supported currencies. */
|
|
102
|
-
currencies?: readonly
|
|
102
|
+
currencies?: readonly Assets.Currency[] | undefined
|
|
103
103
|
/** Legacy alias for `currencies`. */
|
|
104
|
-
assets?: readonly
|
|
104
|
+
assets?: readonly Assets.Currency[] | undefined
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
const authorizationTypes = {
|
|
@@ -125,12 +125,12 @@ function assertPolicy(parameters: Config, accepted: Types.PaymentRequirements) {
|
|
|
125
125
|
throw new Error(`x402 exact chain ID is not allowed: ${chainId}.`)
|
|
126
126
|
|
|
127
127
|
const currencies = parameters.currencies ?? parameters.assets
|
|
128
|
-
if (currencies?.some((currency) =>
|
|
128
|
+
if (currencies?.some((currency) => Assets.isRawAddress(currency)) && !parameters.networks?.length)
|
|
129
129
|
throw new Error('x402 exact raw currency allowlists require networks.')
|
|
130
130
|
if (currencies) {
|
|
131
131
|
const acceptedCurrency = getAddress(accepted.asset as `0x${string}`)
|
|
132
132
|
const allowed = currencies.some((currency) =>
|
|
133
|
-
|
|
133
|
+
Assets.matches(currency, acceptedCurrency, accepted.network),
|
|
134
134
|
)
|
|
135
135
|
if (!allowed) throw new Error(`x402 exact currency is not allowed: ${acceptedCurrency}.`)
|
|
136
136
|
}
|
|
@@ -149,19 +149,6 @@ function assertPolicy(parameters: Config, accepted: Types.PaymentRequirements) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function addressOf(currency: `0x${string}` | Assets.KnownAsset): `0x${string}` {
|
|
153
|
-
return Assets.isAsset(currency) ? currency.address : currency
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function currencyMatches(
|
|
157
|
-
currency: `0x${string}` | Assets.KnownAsset,
|
|
158
|
-
acceptedCurrency: `0x${string}`,
|
|
159
|
-
network: Types.EvmNetwork,
|
|
160
|
-
): boolean {
|
|
161
|
-
if (getAddress(addressOf(currency)) !== acceptedCurrency) return false
|
|
162
|
-
return !Assets.isAsset(currency) || currency.network === network
|
|
163
|
-
}
|
|
164
|
-
|
|
165
152
|
function decimalsOfAcceptedCurrency(
|
|
166
153
|
parameters: Config,
|
|
167
154
|
accepted: Types.PaymentRequirements,
|
|
@@ -169,9 +156,10 @@ function decimalsOfAcceptedCurrency(
|
|
|
169
156
|
const currencies = parameters.currencies ?? parameters.assets
|
|
170
157
|
const acceptedCurrency = getAddress(accepted.asset as `0x${string}`)
|
|
171
158
|
const currency = currencies?.find((currency) =>
|
|
172
|
-
|
|
159
|
+
Assets.matches(currency, acceptedCurrency, accepted.network),
|
|
173
160
|
)
|
|
174
|
-
|
|
161
|
+
const resolved = currency ? Assets.resolve(currency, accepted.network) : undefined
|
|
162
|
+
if (resolved?.decimals !== undefined) return resolved.decimals
|
|
175
163
|
return parameters.decimals
|
|
176
164
|
}
|
|
177
165
|
|