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.
Files changed (63) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/Receipt.d.ts +20 -2
  3. package/dist/Receipt.d.ts.map +1 -1
  4. package/dist/Receipt.js +19 -12
  5. package/dist/Receipt.js.map +1 -1
  6. package/dist/cli/cli.d.ts.map +1 -1
  7. package/dist/cli/cli.js +6 -4
  8. package/dist/cli/cli.js.map +1 -1
  9. package/dist/cli/plugins/tempo.d.ts.map +1 -1
  10. package/dist/cli/plugins/tempo.js +4 -2
  11. package/dist/cli/plugins/tempo.js.map +1 -1
  12. package/dist/client/internal/Fetch.d.ts.map +1 -1
  13. package/dist/client/internal/Fetch.js +6 -1
  14. package/dist/client/internal/Fetch.js.map +1 -1
  15. package/dist/evm/client/Charge.d.ts +2 -2
  16. package/dist/evm/client/Charge.d.ts.map +1 -1
  17. package/dist/evm/client/Charge.js +11 -17
  18. package/dist/evm/client/Charge.js.map +1 -1
  19. package/dist/evm/server/Charge.d.ts +5 -5
  20. package/dist/evm/server/Charge.d.ts.map +1 -1
  21. package/dist/evm/server/Charge.js +15 -7
  22. package/dist/evm/server/Charge.js.map +1 -1
  23. package/dist/tempo/client/Charge.d.ts.map +1 -1
  24. package/dist/tempo/client/Charge.js +6 -2
  25. package/dist/tempo/client/Charge.js.map +1 -1
  26. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  27. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  28. package/dist/tempo/server/internal/html.gen.js +1 -1
  29. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  30. package/dist/tempo/session/precompile/Chain.d.ts +26 -2
  31. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
  32. package/dist/tempo/session/precompile/Chain.js +38 -5
  33. package/dist/tempo/session/precompile/Chain.js.map +1 -1
  34. package/dist/x402/Assets.d.ts +40 -0
  35. package/dist/x402/Assets.d.ts.map +1 -1
  36. package/dist/x402/Assets.js +73 -0
  37. package/dist/x402/Assets.js.map +1 -1
  38. package/dist/x402/client/Exact.d.ts +2 -2
  39. package/dist/x402/client/Exact.d.ts.map +1 -1
  40. package/dist/x402/client/Exact.js +6 -13
  41. package/dist/x402/client/Exact.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/Receipt.test.ts +53 -0
  44. package/src/Receipt.ts +26 -13
  45. package/src/cli/cli.test.ts +41 -5
  46. package/src/cli/cli.ts +7 -4
  47. package/src/cli/plugins/tempo.ts +6 -2
  48. package/src/client/internal/Fetch.test.ts +44 -0
  49. package/src/client/internal/Fetch.ts +5 -1
  50. package/src/evm/PublicInterface.test-d.ts +15 -2
  51. package/src/evm/client/Charge.test.ts +248 -0
  52. package/src/evm/client/Charge.ts +12 -23
  53. package/src/evm/server/Charge.test.ts +140 -0
  54. package/src/evm/server/Charge.ts +19 -12
  55. package/src/tempo/client/Charge.test.ts +61 -0
  56. package/src/tempo/client/Charge.ts +6 -1
  57. package/src/tempo/server/internal/html.gen.ts +1 -1
  58. package/src/tempo/session/precompile/Chain.test.ts +121 -2
  59. package/src/tempo/session/precompile/Chain.ts +56 -6
  60. package/src/x402/Assets.test.ts +211 -0
  61. package/src/x402/Assets.ts +116 -0
  62. package/src/x402/client/Exact.test.ts +129 -0
  63. 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 = {
@@ -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 (`0x${string}` | Assets.KnownAsset)[] | undefined
102
+ currencies?: readonly Assets.Currency[] | undefined
103
103
  /** Legacy alias for `currencies`. */
104
- assets?: readonly (`0x${string}` | Assets.KnownAsset)[] | undefined
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) => !Assets.isAsset(currency)) && !parameters.networks?.length)
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
- currencyMatches(currency, acceptedCurrency, accepted.network),
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
- currencyMatches(currency, acceptedCurrency, accepted.network),
159
+ Assets.matches(currency, acceptedCurrency, accepted.network),
173
160
  )
174
- if (currency && Assets.isAsset(currency)) return currency.decimals
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