ox 1.0.0-beta.9 → 1.0.0

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 (244) hide show
  1. package/CHANGELOG.md +1143 -0
  2. package/dist/core/AbiParameters.d.ts.map +1 -1
  3. package/dist/core/AbiParameters.js +4 -1
  4. package/dist/core/AbiParameters.js.map +1 -1
  5. package/dist/core/Rlp.d.ts +27 -5
  6. package/dist/core/Rlp.d.ts.map +1 -1
  7. package/dist/core/Rlp.js +40 -4
  8. package/dist/core/Rlp.js.map +1 -1
  9. package/dist/core/RpcSchema.d.ts +1 -1
  10. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  11. package/dist/core/internal/abiParameters.js +21 -3
  12. package/dist/core/internal/abiParameters.js.map +1 -1
  13. package/dist/core/internal/rpcSchemas/eth.d.ts +36 -0
  14. package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  15. package/dist/erc4337/EntryPoint.d.ts +1072 -1
  16. package/dist/erc4337/EntryPoint.d.ts.map +1 -1
  17. package/dist/erc4337/EntryPoint.js +872 -0
  18. package/dist/erc4337/EntryPoint.js.map +1 -1
  19. package/dist/erc4337/RpcSchema.d.ts +2 -2
  20. package/dist/erc4337/RpcSchema.d.ts.map +1 -1
  21. package/dist/erc4337/UserOperation.d.ts +39 -10
  22. package/dist/erc4337/UserOperation.d.ts.map +1 -1
  23. package/dist/erc4337/UserOperation.js +62 -48
  24. package/dist/erc4337/UserOperation.js.map +1 -1
  25. package/dist/erc4337/UserOperationGas.d.ts +9 -5
  26. package/dist/erc4337/UserOperationGas.d.ts.map +1 -1
  27. package/dist/erc4337/UserOperationGas.js +17 -15
  28. package/dist/erc4337/UserOperationGas.js.map +1 -1
  29. package/dist/erc4337/UserOperationReceipt.d.ts +4 -4
  30. package/dist/erc4337/UserOperationReceipt.d.ts.map +1 -1
  31. package/dist/erc4337/UserOperationReceipt.js.map +1 -1
  32. package/dist/tempo/Channel.d.ts +8 -14
  33. package/dist/tempo/Channel.d.ts.map +1 -1
  34. package/dist/tempo/Channel.js +4 -8
  35. package/dist/tempo/Channel.js.map +1 -1
  36. package/dist/tempo/KeyAuthorization.d.ts +5 -5
  37. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  38. package/dist/tempo/KeyAuthorization.js +4 -4
  39. package/dist/tempo/KeyAuthorization.js.map +1 -1
  40. package/dist/tempo/MultisigConfig.d.ts +37 -85
  41. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  42. package/dist/tempo/MultisigConfig.js +39 -83
  43. package/dist/tempo/MultisigConfig.js.map +1 -1
  44. package/dist/tempo/PoolId.d.ts +8 -7
  45. package/dist/tempo/PoolId.d.ts.map +1 -1
  46. package/dist/tempo/PoolId.js +10 -6
  47. package/dist/tempo/PoolId.js.map +1 -1
  48. package/dist/tempo/SignatureEnvelope.d.ts +32 -30
  49. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  50. package/dist/tempo/SignatureEnvelope.js +56 -51
  51. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  52. package/dist/tempo/Transaction.d.ts +16 -5
  53. package/dist/tempo/Transaction.d.ts.map +1 -1
  54. package/dist/tempo/Transaction.js +34 -7
  55. package/dist/tempo/Transaction.js.map +1 -1
  56. package/dist/tempo/TransactionReceipt.d.ts +3 -3
  57. package/dist/tempo/TransactionReceipt.d.ts.map +1 -1
  58. package/dist/tempo/TransactionReceipt.js +8 -1
  59. package/dist/tempo/TransactionReceipt.js.map +1 -1
  60. package/dist/tempo/TransactionRequest.d.ts +15 -2
  61. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  62. package/dist/tempo/TransactionRequest.js +65 -29
  63. package/dist/tempo/TransactionRequest.js.map +1 -1
  64. package/dist/tempo/TxEnvelopeTempo.d.ts +2 -3
  65. package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  66. package/dist/tempo/TxEnvelopeTempo.js +5 -6
  67. package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
  68. package/dist/tempo/index.d.ts +6 -26
  69. package/dist/tempo/index.d.ts.map +1 -1
  70. package/dist/tempo/index.js +6 -26
  71. package/dist/tempo/index.js.map +1 -1
  72. package/dist/zod/Abi.d.ts +214 -0
  73. package/dist/zod/Abi.d.ts.map +1 -0
  74. package/dist/zod/Abi.js +5 -0
  75. package/dist/zod/Abi.js.map +1 -0
  76. package/dist/zod/AbiConstructor.d.ts +58 -0
  77. package/dist/zod/AbiConstructor.d.ts.map +1 -0
  78. package/dist/zod/AbiConstructor.js +18 -0
  79. package/dist/zod/AbiConstructor.js.map +1 -0
  80. package/dist/zod/AbiError.d.ts +20 -0
  81. package/dist/zod/AbiError.d.ts.map +1 -0
  82. package/dist/zod/AbiError.js +9 -0
  83. package/dist/zod/AbiError.js.map +1 -0
  84. package/dist/zod/AbiEvent.d.ts +23 -0
  85. package/dist/zod/AbiEvent.d.ts.map +1 -0
  86. package/dist/zod/AbiEvent.js +11 -0
  87. package/dist/zod/AbiEvent.js.map +1 -0
  88. package/dist/zod/AbiFallback.d.ts +16 -0
  89. package/dist/zod/AbiFallback.d.ts.map +1 -0
  90. package/dist/zod/AbiFallback.js +16 -0
  91. package/dist/zod/AbiFallback.js.map +1 -0
  92. package/dist/zod/AbiFunction.d.ts +109 -0
  93. package/dist/zod/AbiFunction.d.ts.map +1 -0
  94. package/dist/zod/AbiFunction.js +26 -0
  95. package/dist/zod/AbiFunction.js.map +1 -0
  96. package/dist/zod/AbiItem.d.ts +216 -0
  97. package/dist/zod/AbiItem.d.ts.map +1 -0
  98. package/dist/zod/AbiItem.js +26 -0
  99. package/dist/zod/AbiItem.js.map +1 -0
  100. package/dist/zod/AbiParameter.d.ts +32 -0
  101. package/dist/zod/AbiParameter.d.ts.map +1 -0
  102. package/dist/zod/AbiParameter.js +28 -0
  103. package/dist/zod/AbiParameter.js.map +1 -0
  104. package/dist/zod/AbiParameters.d.ts +30 -0
  105. package/dist/zod/AbiParameters.d.ts.map +1 -0
  106. package/dist/zod/AbiParameters.js +6 -0
  107. package/dist/zod/AbiParameters.js.map +1 -0
  108. package/dist/zod/AbiReceive.d.ts +6 -0
  109. package/dist/zod/AbiReceive.d.ts.map +1 -0
  110. package/dist/zod/AbiReceive.js +7 -0
  111. package/dist/zod/AbiReceive.js.map +1 -0
  112. package/dist/zod/RpcSchema.d.ts +1 -0
  113. package/dist/zod/RpcSchema.d.ts.map +1 -1
  114. package/dist/zod/Solidity.d.ts +15 -0
  115. package/dist/zod/Solidity.d.ts.map +1 -0
  116. package/dist/zod/Solidity.js +41 -0
  117. package/dist/zod/Solidity.js.map +1 -0
  118. package/dist/zod/TypedData.d.ts +226 -0
  119. package/dist/zod/TypedData.d.ts.map +1 -0
  120. package/dist/zod/TypedData.js +84 -0
  121. package/dist/zod/TypedData.js.map +1 -0
  122. package/dist/zod/internal/rpcSchemas/Eth.d.ts +1 -0
  123. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  124. package/dist/zod/internal/rpcSchemas/Eth.js +5 -0
  125. package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
  126. package/dist/zod/tempo/AuthorizationTempo.d.ts +0 -20
  127. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  128. package/dist/zod/tempo/KeyAuthorization.d.ts +503 -67
  129. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  130. package/dist/zod/tempo/KeyAuthorization.js +32 -15
  131. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  132. package/dist/zod/tempo/RpcSchemaTempo.d.ts +216 -34
  133. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  134. package/dist/zod/tempo/SignatureEnvelope.d.ts +0 -6
  135. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  136. package/dist/zod/tempo/SignatureEnvelope.js +0 -2
  137. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  138. package/dist/zod/tempo/Transaction.d.ts +1479 -333
  139. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  140. package/dist/zod/tempo/Transaction.js +65 -16
  141. package/dist/zod/tempo/Transaction.js.map +1 -1
  142. package/dist/zod/tempo/TransactionRequest.d.ts +410 -57
  143. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  144. package/dist/zod/tempo/TransactionRequest.js +81 -18
  145. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  146. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +225 -18
  147. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  148. package/dist/zod/tempo/TxEnvelopeTempo.js +1 -1
  149. package/dist/zod/tempo/TxEnvelopeTempo.js.map +1 -1
  150. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +0 -3
  151. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  152. package/dist/zod/tempo/z.d.ts +0 -1
  153. package/dist/zod/tempo/z.d.ts.map +1 -1
  154. package/dist/zod/tempo/z.js +0 -1
  155. package/dist/zod/tempo/z.js.map +1 -1
  156. package/dist/zod/z.d.ts +12 -0
  157. package/dist/zod/z.d.ts.map +1 -1
  158. package/dist/zod/z.js +12 -0
  159. package/dist/zod/z.js.map +1 -1
  160. package/package.json +62 -11
  161. package/src/core/AbiParameters.ts +3 -1
  162. package/src/core/Rlp.ts +66 -3
  163. package/src/core/RpcSchema.ts +1 -1
  164. package/src/core/_test/AbiParameters.decode.test.ts +88 -12
  165. package/src/core/_test/AbiParameters.encode.test.ts +68 -0
  166. package/src/core/_test/Rlp.test.ts +101 -0
  167. package/src/core/internal/abiParameters.ts +21 -3
  168. package/src/core/internal/rpcSchemas/eth.ts +36 -0
  169. package/src/erc4337/EntryPoint.ts +875 -1
  170. package/src/erc4337/RpcSchema.ts +4 -2
  171. package/src/erc4337/UserOperation.ts +130 -60
  172. package/src/erc4337/UserOperationGas.ts +36 -28
  173. package/src/erc4337/UserOperationReceipt.ts +15 -7
  174. package/src/erc4337/_test/EntryPoint.test.ts +48 -0
  175. package/src/erc4337/_test/RpcSchema.test-d.ts +56 -0
  176. package/src/erc4337/_test/UserOperation.snap-d.ts +31 -0
  177. package/src/erc4337/_test/UserOperation.test.ts +221 -5
  178. package/src/erc4337/_test/UserOperationGas.test.ts +30 -1
  179. package/src/tempo/Channel.test.ts +2 -23
  180. package/src/tempo/Channel.ts +10 -22
  181. package/src/tempo/KeyAuthorization.test.ts +22 -0
  182. package/src/tempo/KeyAuthorization.ts +9 -9
  183. package/src/tempo/MultisigConfig.test.ts +54 -37
  184. package/src/tempo/MultisigConfig.ts +59 -134
  185. package/src/tempo/PoolId.test.ts +7 -41
  186. package/src/tempo/PoolId.ts +15 -10
  187. package/src/tempo/SignatureEnvelope.test.ts +118 -17
  188. package/src/tempo/SignatureEnvelope.ts +77 -72
  189. package/src/tempo/Transaction.test.ts +0 -15
  190. package/src/tempo/Transaction.ts +63 -12
  191. package/src/tempo/TransactionReceipt.test.ts +76 -0
  192. package/src/tempo/TransactionReceipt.ts +11 -5
  193. package/src/tempo/TransactionRequest.test.ts +174 -1
  194. package/src/tempo/TransactionRequest.ts +84 -32
  195. package/src/tempo/TxEnvelopeTempo.test.ts +2 -2
  196. package/src/tempo/TxEnvelopeTempo.ts +7 -7
  197. package/src/tempo/e2e.test.ts +228 -50
  198. package/src/tempo/index.ts +6 -26
  199. package/src/version.ts +1 -1
  200. package/src/zod/Abi.ts +5 -0
  201. package/src/zod/AbiConstructor.ts +24 -0
  202. package/src/zod/AbiError.ts +9 -0
  203. package/src/zod/AbiEvent.ts +11 -0
  204. package/src/zod/AbiFallback.ts +22 -0
  205. package/src/zod/AbiFunction.ts +32 -0
  206. package/src/zod/AbiItem.ts +28 -0
  207. package/src/zod/AbiParameter.ts +36 -0
  208. package/src/zod/AbiParameters.ts +7 -0
  209. package/src/zod/AbiReceive.ts +7 -0
  210. package/src/zod/Solidity.ts +83 -0
  211. package/src/zod/TypedData.ts +111 -0
  212. package/src/zod/_test/Abi.test-d.ts +46 -0
  213. package/src/zod/_test/Abi.test.ts +163 -0
  214. package/src/zod/_test/TypedData.test-d.ts +20 -0
  215. package/src/zod/_test/TypedData.test.ts +87 -0
  216. package/src/zod/_test/z.test-d.ts +6 -0
  217. package/src/zod/_test/z.test.ts +17 -0
  218. package/src/zod/internal/rpcSchemas/Eth.ts +6 -0
  219. package/src/zod/tempo/KeyAuthorization.ts +34 -15
  220. package/src/zod/tempo/SignatureEnvelope.ts +0 -2
  221. package/src/zod/tempo/Transaction.ts +84 -22
  222. package/src/zod/tempo/TransactionRequest.ts +86 -20
  223. package/src/zod/tempo/TxEnvelopeTempo.ts +1 -1
  224. package/src/zod/tempo/_test/KeyAuthorization.test.ts +53 -0
  225. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +0 -2
  226. package/src/zod/tempo/_test/Transaction.test.ts +155 -0
  227. package/src/zod/tempo/_test/TransactionRequest.test.ts +44 -0
  228. package/src/zod/tempo/z.ts +0 -1
  229. package/src/zod/z.ts +12 -0
  230. package/dist/tempo/TokenId.d.ts +0 -87
  231. package/dist/tempo/TokenId.d.ts.map +0 -1
  232. package/dist/tempo/TokenId.js +0 -123
  233. package/dist/tempo/TokenId.js.map +0 -1
  234. package/dist/zod/tempo/TokenId.d.ts +0 -6
  235. package/dist/zod/tempo/TokenId.d.ts.map +0 -1
  236. package/dist/zod/tempo/TokenId.js +0 -14
  237. package/dist/zod/tempo/TokenId.js.map +0 -1
  238. package/src/CHANGELOG.md +0 -820
  239. package/src/tempo/TokenId.bench.ts +0 -36
  240. package/src/tempo/TokenId.test.ts +0 -79
  241. package/src/tempo/TokenId.ts +0 -141
  242. package/src/zod/tempo/TokenId.ts +0 -15
  243. package/src/zod/tempo/_test/TokenId.test-d.ts +0 -17
  244. package/src/zod/tempo/_test/TokenId.test.ts +0 -21
@@ -215,6 +215,52 @@ describe('getSignPayload', () => {
215
215
  })
216
216
 
217
217
  describe('hash', () => {
218
+ describe('v0.9', () => {
219
+ const userOperation = {
220
+ callData: '0x',
221
+ callGasLimit: 6942069n,
222
+ maxFeePerGas: 69420n,
223
+ maxPriorityFeePerGas: 69n,
224
+ nonce: 0n,
225
+ paymaster: '0x1234567890123456789012345678901234567890',
226
+ paymasterData: '0xdeadbeef',
227
+ paymasterPostOpGasLimit: 6942069n,
228
+ paymasterVerificationGasLimit: 6942069n,
229
+ preVerificationGas: 6942069n,
230
+ sender: '0x1234567890123456789012345678901234567890',
231
+ signature: '0x',
232
+ verificationGasLimit: 6942069n,
233
+ } as const satisfies UserOperation.UserOperation<'0.9', true>
234
+
235
+ test('args: paymasterSignature', () => {
236
+ const options = {
237
+ chainId: 1,
238
+ entryPointAddress: '0x433709009B8330FDa32311DF1C2AFA402eD8D009',
239
+ entryPointVersion: '0.9',
240
+ } as const
241
+
242
+ expect(
243
+ UserOperation.hash(
244
+ { ...userOperation, paymasterSignature: '0xcafebabe' },
245
+ options,
246
+ ),
247
+ ).toMatchInlineSnapshot(
248
+ `"0x802c25af5d98cd349b2118227faa93172fb791c4130a482272095cec45c4fc6e"`,
249
+ )
250
+ expect(
251
+ UserOperation.hash(
252
+ { ...userOperation, paymasterSignature: '0xdeadbeef' },
253
+ options,
254
+ ),
255
+ ).toMatchInlineSnapshot(
256
+ `"0x802c25af5d98cd349b2118227faa93172fb791c4130a482272095cec45c4fc6e"`,
257
+ )
258
+ expect(UserOperation.hash(userOperation, options)).toMatchInlineSnapshot(
259
+ `"0xde381ae0368b77df677808c3a2da72abd68a07eab11ef31c35923f56a75bbe6d"`,
260
+ )
261
+ })
262
+ })
263
+
218
264
  describe('v0.8', () => {
219
265
  test('default', () => {
220
266
  expect(
@@ -818,6 +864,54 @@ describe('toPacked', () => {
818
864
  }
819
865
  `)
820
866
  })
867
+
868
+ describe('v0.9 paymasterSignature', () => {
869
+ const userOperation = {
870
+ callData: '0x',
871
+ callGasLimit: 6942069n,
872
+ maxFeePerGas: 69420n,
873
+ maxPriorityFeePerGas: 69n,
874
+ nonce: 0n,
875
+ paymaster: '0x1234567890123456789012345678901234567890',
876
+ paymasterData: '0xdeadbeef',
877
+ paymasterPostOpGasLimit: 6942069n,
878
+ paymasterSignature: '0xcafebabe',
879
+ paymasterVerificationGasLimit: 6942069n,
880
+ preVerificationGas: 6942069n,
881
+ sender: '0x1234567890123456789012345678901234567890',
882
+ signature: '0x',
883
+ verificationGasLimit: 6942069n,
884
+ } as const satisfies UserOperation.UserOperation<'0.9', true>
885
+
886
+ test('default', () => {
887
+ expect(
888
+ UserOperation.toPacked(userOperation).paymasterAndData,
889
+ ).toMatchInlineSnapshot(
890
+ `"0x12345678901234567890123456789012345678900000000000000000000000000069ed750000000000000000000000000069ed75deadbeefcafebabe000422e325a297439656"`,
891
+ )
892
+ })
893
+
894
+ test('options: forHash', () => {
895
+ expect(
896
+ UserOperation.toPacked(userOperation, { forHash: true })
897
+ .paymasterAndData,
898
+ ).toMatchInlineSnapshot(
899
+ `"0x12345678901234567890123456789012345678900000000000000000000000000069ed750000000000000000000000000069ed75deadbeef22e325a297439656"`,
900
+ )
901
+ })
902
+
903
+ test('encodes a uint16 signature length', () => {
904
+ const paymasterSignature = Hex.fromBytes(new Uint8Array(65).fill(0x11))
905
+ const paymasterAndData = UserOperation.toPacked({
906
+ ...userOperation,
907
+ paymasterSignature,
908
+ }).paymasterAndData
909
+
910
+ expect(
911
+ Hex.slice(paymasterAndData, Hex.size(paymasterAndData) - 10),
912
+ ).toMatchInlineSnapshot(`"0x004122e325a297439656"`)
913
+ })
914
+ })
821
915
  })
822
916
 
823
917
  describe('toInitCode', () => {
@@ -1170,6 +1264,96 @@ describe('toRpc', () => {
1170
1264
  }
1171
1265
  `)
1172
1266
  })
1267
+
1268
+ test('v0.6 preserves paymasterAndData', () => {
1269
+ expect(
1270
+ UserOperation.toRpc({
1271
+ callData: '0x',
1272
+ callGasLimit: 0n,
1273
+ maxFeePerGas: 0n,
1274
+ maxPriorityFeePerGas: 0n,
1275
+ nonce: 0n,
1276
+ paymasterAndData: '0x1234567890123456789012345678901234567890deadbeef',
1277
+ preVerificationGas: 0n,
1278
+ sender: '0x1234567890123456789012345678901234567890',
1279
+ signature: '0x',
1280
+ verificationGasLimit: 0n,
1281
+ } satisfies UserOperation.UserOperation<'0.6', true>),
1282
+ ).toMatchInlineSnapshot(`
1283
+ {
1284
+ "callData": "0x",
1285
+ "callGasLimit": "0x0",
1286
+ "maxFeePerGas": "0x0",
1287
+ "maxPriorityFeePerGas": "0x0",
1288
+ "nonce": "0x0",
1289
+ "paymasterAndData": "0x1234567890123456789012345678901234567890deadbeef",
1290
+ "preVerificationGas": "0x0",
1291
+ "sender": "0x1234567890123456789012345678901234567890",
1292
+ "signature": "0x",
1293
+ "verificationGasLimit": "0x0",
1294
+ }
1295
+ `)
1296
+ })
1297
+
1298
+ test('v0.6 preserves empty paymasterAndData', () => {
1299
+ expect(
1300
+ UserOperation.toRpc({
1301
+ callData: '0x',
1302
+ callGasLimit: 0n,
1303
+ maxFeePerGas: 0n,
1304
+ maxPriorityFeePerGas: 0n,
1305
+ nonce: 0n,
1306
+ paymasterAndData: '0x',
1307
+ preVerificationGas: 0n,
1308
+ sender: '0x1234567890123456789012345678901234567890',
1309
+ signature: '0x',
1310
+ verificationGasLimit: 0n,
1311
+ } satisfies UserOperation.UserOperation<'0.6', true>),
1312
+ ).toMatchInlineSnapshot(`
1313
+ {
1314
+ "callData": "0x",
1315
+ "callGasLimit": "0x0",
1316
+ "maxFeePerGas": "0x0",
1317
+ "maxPriorityFeePerGas": "0x0",
1318
+ "nonce": "0x0",
1319
+ "paymasterAndData": "0x",
1320
+ "preVerificationGas": "0x0",
1321
+ "sender": "0x1234567890123456789012345678901234567890",
1322
+ "signature": "0x",
1323
+ "verificationGasLimit": "0x0",
1324
+ }
1325
+ `)
1326
+ })
1327
+
1328
+ test('v0.9 preserves paymasterSignature', () => {
1329
+ expect(
1330
+ UserOperation.toRpc({
1331
+ callData: '0x',
1332
+ callGasLimit: 0n,
1333
+ maxFeePerGas: 0n,
1334
+ maxPriorityFeePerGas: 0n,
1335
+ nonce: 0n,
1336
+ paymasterSignature: '0xcafebabe',
1337
+ preVerificationGas: 0n,
1338
+ sender: '0x1234567890123456789012345678901234567890',
1339
+ signature: '0x',
1340
+ verificationGasLimit: 0n,
1341
+ } satisfies UserOperation.UserOperation<'0.9', true>),
1342
+ ).toMatchInlineSnapshot(`
1343
+ {
1344
+ "callData": "0x",
1345
+ "callGasLimit": "0x0",
1346
+ "maxFeePerGas": "0x0",
1347
+ "maxPriorityFeePerGas": "0x0",
1348
+ "nonce": "0x0",
1349
+ "paymasterSignature": "0xcafebabe",
1350
+ "preVerificationGas": "0x0",
1351
+ "sender": "0x1234567890123456789012345678901234567890",
1352
+ "signature": "0x",
1353
+ "verificationGasLimit": "0x0",
1354
+ }
1355
+ `)
1356
+ })
1173
1357
  })
1174
1358
 
1175
1359
  describe('toTypedData', () => {
@@ -1465,9 +1649,9 @@ describe('v0.8 authorization round-trip', () => {
1465
1649
  verificationGasLimit: 100_000n,
1466
1650
  } as const satisfies UserOperation.UserOperation<'0.8', true>
1467
1651
 
1468
- test('toRpc serializes authorization', () => {
1652
+ test('toRpc serializes eip7702Auth', () => {
1469
1653
  const rpc = UserOperation.toRpc(userOperation) as UserOperation.Rpc<'0.8'>
1470
- expect(rpc.authorization).toMatchInlineSnapshot(`
1654
+ expect(rpc.eip7702Auth).toMatchInlineSnapshot(`
1471
1655
  {
1472
1656
  "address": "0x1234567890123456789012345678901234567890",
1473
1657
  "chainId": "0x1",
@@ -1479,7 +1663,7 @@ describe('v0.8 authorization round-trip', () => {
1479
1663
  `)
1480
1664
  })
1481
1665
 
1482
- test('fromRpc parses authorization', () => {
1666
+ test('fromRpc parses eip7702Auth', () => {
1483
1667
  const rpc = UserOperation.toRpc(userOperation) as UserOperation.Rpc<'0.8'>
1484
1668
  const parsed = UserOperation.fromRpc(rpc) as UserOperation.UserOperation<
1485
1669
  '0.8',
@@ -1494,9 +1678,41 @@ describe('v0.8 authorization round-trip', () => {
1494
1678
  expect(parsed).toEqual(userOperation)
1495
1679
  })
1496
1680
 
1497
- test('toRpc omits authorization when absent', () => {
1681
+ test('toRpc omits eip7702Auth when absent', () => {
1498
1682
  const { authorization: _omit, ...rest } = userOperation
1499
1683
  const rpc = UserOperation.toRpc(rest)
1500
- expect('authorization' in rpc).toBe(false)
1684
+ expect('eip7702Auth' in rpc).toBe(false)
1685
+ })
1686
+ })
1687
+
1688
+ describe('v0.9 RPC round-trip', () => {
1689
+ test('preserves paymasterSignature and parses eip7702Auth', () => {
1690
+ const userOperation = {
1691
+ authorization: {
1692
+ address: '0x1234567890123456789012345678901234567890',
1693
+ chainId: 1,
1694
+ nonce: 0n,
1695
+ r: '0x0000000000000000000000000000000000000000000000000000000000000001',
1696
+ s: '0x0000000000000000000000000000000000000000000000000000000000000002',
1697
+ yParity: 0,
1698
+ },
1699
+ callData: '0x',
1700
+ callGasLimit: 0n,
1701
+ maxFeePerGas: 0n,
1702
+ maxPriorityFeePerGas: 0n,
1703
+ nonce: 0n,
1704
+ paymasterSignature: '0xcafebabe',
1705
+ preVerificationGas: 0n,
1706
+ sender: '0x1234567890123456789012345678901234567890',
1707
+ signature: '0x',
1708
+ verificationGasLimit: 0n,
1709
+ } as const satisfies UserOperation.UserOperation<'0.9', true>
1710
+
1711
+ const rpc = UserOperation.toRpc(userOperation)
1712
+ const parsed = UserOperation.fromRpc(rpc)
1713
+
1714
+ expect(rpc).toHaveProperty('eip7702Auth')
1715
+ expect(rpc).not.toHaveProperty('authorization')
1716
+ expect(parsed).toEqual(userOperation)
1501
1717
  })
1502
1718
  })
@@ -1,5 +1,5 @@
1
1
  import { UserOperationGas } from 'ox/erc4337'
2
- import { describe, expect, test } from 'vp/test'
2
+ import { describe, expect, expectTypeOf, test } from 'vp/test'
3
3
 
4
4
  describe('fromRpc', () => {
5
5
  test('default', () => {
@@ -91,3 +91,32 @@ describe('v0.8', () => {
91
91
  expect(parsed).toEqual(v08)
92
92
  })
93
93
  })
94
+
95
+ describe('v0.9', () => {
96
+ test('round-trips matching v0.8 shape', () => {
97
+ const v09 = {
98
+ callGasLimit: 300_000n,
99
+ paymasterPostOpGasLimit: 100_000n,
100
+ paymasterVerificationGasLimit: 100_000n,
101
+ preVerificationGas: 100_000n,
102
+ verificationGasLimit: 100_000n,
103
+ } as const satisfies UserOperationGas.UserOperationGas<'0.9'>
104
+
105
+ const rpc = UserOperationGas.toRpc<'0.9'>(v09)
106
+ const parsed = UserOperationGas.fromRpc<'0.9'>(rpc)
107
+
108
+ expectTypeOf(rpc).toEqualTypeOf<UserOperationGas.Rpc<'0.9'>>()
109
+ expectTypeOf(parsed).toEqualTypeOf<
110
+ UserOperationGas.UserOperationGas<'0.9'>
111
+ >()
112
+ expect(parsed).toMatchInlineSnapshot(`
113
+ {
114
+ "callGasLimit": 300000n,
115
+ "paymasterPostOpGasLimit": 100000n,
116
+ "paymasterVerificationGasLimit": 100000n,
117
+ "preVerificationGas": 100000n,
118
+ "verificationGasLimit": 100000n,
119
+ }
120
+ `)
121
+ })
122
+ })
@@ -10,7 +10,7 @@ const channel = {
10
10
  payee: '0x2222222222222222222222222222222222222222',
11
11
  payer: '0x1111111111111111111111111111111111111111',
12
12
  salt: '0x0000000000000000000000000000000000000000000000000000000000000001',
13
- token: 1n,
13
+ token: '0x20c0000000000000000000000000000000000001',
14
14
  } as const
15
15
 
16
16
  const chainId = 4217
@@ -84,26 +84,6 @@ describe('from', () => {
84
84
  `)
85
85
  })
86
86
 
87
- test('token address input', () => {
88
- expect(
89
- Channel.from({
90
- ...channel,
91
- operator: '0x4444444444444444444444444444444444444444',
92
- token: '0x20c0000000000000000000000000000000000001',
93
- }),
94
- ).toMatchInlineSnapshot(`
95
- {
96
- "authorizedSigner": "0x3333333333333333333333333333333333333333",
97
- "expiringNonceHash": "0x0000000000000000000000000000000000000000000000000000000000000002",
98
- "operator": "0x4444444444444444444444444444444444444444",
99
- "payee": "0x2222222222222222222222222222222222222222",
100
- "payer": "0x1111111111111111111111111111111111111111",
101
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000001",
102
- "token": "0x20c0000000000000000000000000000000000001",
103
- }
104
- `)
105
- })
106
-
107
87
  test('return type', () => {
108
88
  const resolved = Channel.from({
109
89
  expiringNonceHash:
@@ -111,10 +91,9 @@ describe('from', () => {
111
91
  payee: '0x2222222222222222222222222222222222222222',
112
92
  payer: '0x1111111111111111111111111111111111111111',
113
93
  salt: '0x0000000000000000000000000000000000000000000000000000000000000001',
114
- token: 1n,
94
+ token: '0x20c0000000000000000000000000000000000001',
115
95
  })
116
96
 
117
- resolved satisfies Channel.Resolved
118
97
  resolved satisfies Channel.Channel
119
98
  resolved.payer satisfies Address.Address
120
99
  resolved.token satisfies Address.Address
@@ -3,7 +3,6 @@ import * as Address from '../core/Address.js'
3
3
  import type * as Errors from '../core/Errors.js'
4
4
  import * as Hash from '../core/Hash.js'
5
5
  import * as Hex from '../core/Hex.js'
6
- import * as TokenId from './TokenId.js'
7
6
 
8
7
  const channelIdParameters = AbiParameters.from(
9
8
  'address, address, address, address, bytes32, address, bytes32, address, uint256',
@@ -57,18 +56,14 @@ export type Channel = {
57
56
  payer: Address.Address
58
57
  /** User-supplied salt to distinguish otherwise identical channels. */
59
58
  salt: Hex.Hex
60
- /** TIP-20 token address or ID held by the channel. */
61
- token: TokenId.TokenIdOrAddress
59
+ /** TIP-20 token address held by the channel. */
60
+ token: Address.Address
62
61
  }
63
62
 
64
- /** Hex-address-normalized {@link ox#Channel.Channel}. */
65
- export type Resolved = Omit<Channel, 'token'> & { token: Address.Address }
66
-
67
63
  /**
68
64
  * Instantiates a TIP-20 channel reserve descriptor.
69
65
  *
70
- * Accepts a TIP-20 token ID or address, and defaults `operator` and
71
- * `authorizedSigner` to the zero address.
66
+ * Defaults `operator` and `authorizedSigner` to the zero address.
72
67
  *
73
68
  * @example
74
69
  * ```ts twoslash
@@ -80,7 +75,7 @@ export type Resolved = Omit<Channel, 'token'> & { token: Address.Address }
80
75
  * payee: '0x2222222222222222222222222222222222222222',
81
76
  * payer: '0x1111111111111111111111111111111111111111',
82
77
  * salt: '0x0000000000000000000000000000000000000000000000000000000000000001',
83
- * token: 1n
78
+ * token: '0x20c0000000000000000000000000000000000001'
84
79
  * })
85
80
  * ```
86
81
  *
@@ -105,10 +100,7 @@ export function from(value: from.Value): from.ReturnType {
105
100
  payee: resolveAddress(payee),
106
101
  payer: resolveAddress(payer),
107
102
  salt,
108
- token:
109
- typeof token === 'string'
110
- ? resolveAddress(token)
111
- : TokenId.toAddress(token),
103
+ token: resolveAddress(token),
112
104
  }
113
105
  }
114
106
 
@@ -126,17 +118,13 @@ export declare namespace from {
126
118
  payer: Address.Address
127
119
  /** User-supplied salt to distinguish otherwise identical channels. */
128
120
  salt: Hex.Hex
129
- /** TIP-20 token address or ID held by the channel. */
130
- token: TokenId.TokenIdOrAddress
121
+ /** TIP-20 token address held by the channel. */
122
+ token: Address.Address
131
123
  }
132
124
 
133
- type ReturnType = Resolved
125
+ type ReturnType = Channel
134
126
 
135
- type ErrorType =
136
- | Address.from.ErrorType
137
- | Hex.concat.ErrorType
138
- | Hex.fromNumber.ErrorType
139
- | Errors.GlobalErrorType
127
+ type ErrorType = Address.from.ErrorType | Errors.GlobalErrorType
140
128
  }
141
129
 
142
130
  /**
@@ -159,7 +147,7 @@ export declare namespace from {
159
147
  * payee: '0x2222222222222222222222222222222222222222',
160
148
  * payer: '0x1111111111111111111111111111111111111111',
161
149
  * salt: '0x0000000000000000000000000000000000000000000000000000000000000001',
162
- * token: 1n
150
+ * token: '0x20c0000000000000000000000000000000000001'
163
151
  * },
164
152
  * {
165
153
  * chainId: 4217
@@ -2407,6 +2407,28 @@ describe('admin keys (TIP-1049)', () => {
2407
2407
  expect(restored.account).toBe(account)
2408
2408
  })
2409
2409
 
2410
+ test('fromRpc: drops orphan isAdmin without account', () => {
2411
+ const authorization = KeyAuthorization.from(
2412
+ { address, chainId: 1n, type: 'secp256k1' },
2413
+ { signature: SignatureEnvelope.from(signature_secp256k1) },
2414
+ )
2415
+ const rpc = KeyAuthorization.toRpc(authorization)
2416
+ const restored = KeyAuthorization.fromRpc({ ...rpc, isAdmin: true })
2417
+ expect(restored.isAdmin).toBeUndefined()
2418
+ expect(restored.account).toBeUndefined()
2419
+ })
2420
+
2421
+ test('fromRpc: drops orphan account without isAdmin', () => {
2422
+ const authorization = KeyAuthorization.from(
2423
+ { address, chainId: 1n, type: 'secp256k1' },
2424
+ { signature: SignatureEnvelope.from(signature_secp256k1) },
2425
+ )
2426
+ const rpc = KeyAuthorization.toRpc(authorization)
2427
+ const restored = KeyAuthorization.fromRpc({ ...rpc, account })
2428
+ expect(restored.isAdmin).toBeUndefined()
2429
+ expect(restored.account).toBeUndefined()
2430
+ })
2431
+
2410
2432
  test('hash: changes when admin pair is added', () => {
2411
2433
  const plain = KeyAuthorization.from({
2412
2434
  address,
@@ -91,7 +91,7 @@ export type Rpc = {
91
91
  /** Optional account address binding (TIP-1049). */
92
92
  account?: Address.Address | null | undefined
93
93
  /** Allowed call scopes (node field: `allowedCalls`). */
94
- allowedCalls?: readonly RpcCallScope[] | undefined
94
+ allowedCalls?: readonly RpcCallScope[] | null | undefined
95
95
  /** Chain ID (hex quantity). */
96
96
  chainId: Hex.Hex
97
97
  /** Expiry timestamp (hex quantity or null). */
@@ -103,7 +103,7 @@ export type Rpc = {
103
103
  /** Key type. */
104
104
  keyType: SignatureEnvelope.Type
105
105
  /** Token spending limits. */
106
- limits?: readonly RpcTokenLimit[] | undefined
106
+ limits?: readonly RpcTokenLimit[] | null | undefined
107
107
  /** Signature envelope. */
108
108
  signature: SignatureEnvelope.SignatureEnvelopeRpc
109
109
  /** Optional 32-byte witness (hex). */
@@ -114,19 +114,19 @@ export type Rpc = {
114
114
  export type RpcTokenLimit = {
115
115
  token: Address.Address
116
116
  limit: Hex.Hex
117
- period?: Hex.Hex | undefined
117
+ period?: Hex.Hex | null | undefined
118
118
  }
119
119
 
120
120
  /** RPC representation of a call scope (matches node's `CallScope` serde). */
121
121
  export type RpcCallScope = {
122
122
  target: Address.Address
123
- selectorRules?: readonly RpcSelectorRule[]
123
+ selectorRules?: readonly RpcSelectorRule[] | null | undefined
124
124
  }
125
125
 
126
126
  /** RPC representation of a selector rule (matches node's `SelectorRule` serde). */
127
127
  export type RpcSelectorRule = {
128
128
  selector: Hex.Hex
129
- recipients?: readonly Address.Address[]
129
+ recipients?: readonly Address.Address[] | null | undefined
130
130
  }
131
131
 
132
132
  /** Signed representation of a Key Authorization. */
@@ -532,12 +532,13 @@ export function fromRpc(authorization: Rpc): Signed {
532
532
  })
533
533
  : undefined
534
534
 
535
- // TIP-1049 admin fields are paired: only emit both when both are present on
536
- // the wire. Orphan fields are dropped since the public API requires both.
535
+ // TIP-1049 admin fields are paired: emit both or neither; orphan wire
536
+ // fields are dropped. Separate conditional spreads break `Signed`
537
+ // assignability without `exactOptionalPropertyTypes` (#290).
537
538
  const adminPair =
538
539
  account !== undefined && isAdmin ? { account, isAdmin: true as const } : {}
539
540
 
540
- const args: KeyAuthorization = {
541
+ return {
541
542
  address: keyId,
542
543
  chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
543
544
  ...(expiry != null ? { expiry: Number(expiry) } : {}),
@@ -554,7 +555,6 @@ export function fromRpc(authorization: Rpc): Signed {
554
555
  ...(witness !== undefined ? { witness } : {}),
555
556
  ...adminPair,
556
557
  }
557
- return args as Signed
558
558
  }
559
559
 
560
560
  export declare namespace fromRpc {
@@ -30,31 +30,51 @@ describe('from', () => {
30
30
  })
31
31
  })
32
32
 
33
- describe('genesisConfigId', () => {
33
+ describe('getAddress', () => {
34
34
  test('matches independent ground truth', () => {
35
- expect(MultisigConfig.toId(singleOwnerConfig)).toMatchInlineSnapshot(
36
- `"0xd1f20e1a5bfdd89488f57f68db5bd1aae9a51b510f4a042b2604b57a0b7b471d"`,
35
+ expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
36
+ `"0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da"`,
37
37
  )
38
38
  })
39
39
 
40
+ test('matches independent ground truth (salt + weights)', () => {
41
+ expect(
42
+ MultisigConfig.getAddress({
43
+ salt: `0x${'42'.repeat(32)}`,
44
+ threshold: 2,
45
+ owners: [
46
+ { owner: owner1, weight: 1 },
47
+ { owner: owner2, weight: 2 },
48
+ ],
49
+ }),
50
+ ).toMatchInlineSnapshot(`"0x0773e28146400643e42cb28f6659b74e7c0b451d"`)
51
+ })
52
+
40
53
  test('is stable across calls', () => {
41
- expect(MultisigConfig.toId(singleOwnerConfig)).toBe(
42
- MultisigConfig.toId(singleOwnerConfig),
54
+ expect(MultisigConfig.getAddress(singleOwnerConfig)).toBe(
55
+ MultisigConfig.getAddress(singleOwnerConfig),
43
56
  )
44
57
  })
45
58
 
46
59
  test('differs for a different salt', () => {
47
- expect(MultisigConfig.toId(singleOwnerConfig)).not.toBe(
48
- MultisigConfig.toId({
60
+ expect(MultisigConfig.getAddress(singleOwnerConfig)).not.toBe(
61
+ MultisigConfig.getAddress({
49
62
  ...singleOwnerConfig,
50
63
  salt: `0x${'42'.repeat(32)}`,
51
64
  }),
52
65
  )
53
66
  })
54
67
 
68
+ test('address is chain-independent', () => {
69
+ // Derivation does not include chain ID; identical config → identical address.
70
+ const a = MultisigConfig.getAddress(singleOwnerConfig)
71
+ const b = MultisigConfig.getAddress(MultisigConfig.from(singleOwnerConfig))
72
+ expect(a).toBe(b)
73
+ })
74
+
55
75
  test('throws on invalid config', () => {
56
76
  expect(() =>
57
- MultisigConfig.toId({
77
+ MultisigConfig.getAddress({
58
78
  threshold: 5,
59
79
  owners: singleOwnerConfig.owners,
60
80
  }),
@@ -62,50 +82,27 @@ describe('genesisConfigId', () => {
62
82
  })
63
83
  })
64
84
 
65
- describe('getAddress', () => {
66
- test('matches independent ground truth', () => {
67
- expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
68
- `"0x6ca655065b1de473d903eebd50e5cb4996e10468"`,
69
- )
70
- })
71
-
72
- test('derives from positional config or `{ genesisConfigId }` identically', () => {
73
- const genesisConfigId = MultisigConfig.toId(singleOwnerConfig)
74
- expect(MultisigConfig.getAddress({ genesisConfigId })).toBe(
75
- MultisigConfig.getAddress(singleOwnerConfig),
76
- )
77
- })
78
-
79
- test('config ID and address are chain-independent', () => {
80
- // Derivation does not include chain ID; identical config → identical id/address.
81
- const a = MultisigConfig.toId(singleOwnerConfig)
82
- const b = MultisigConfig.toId(MultisigConfig.from(singleOwnerConfig))
83
- expect(a).toBe(b)
84
- })
85
- })
86
-
87
85
  describe('getSignPayload', () => {
88
86
  test('matches independent ground truth', () => {
89
87
  expect(
90
88
  MultisigConfig.getSignPayload({
91
- payload: `0x${'42'.repeat(32)}`,
89
+ payload: `0x${'de'.repeat(32)}`,
92
90
  genesisConfig: singleOwnerConfig,
93
91
  }),
94
92
  ).toMatchInlineSnapshot(
95
- `"0xe3d66f6118b89a67c71c8137c46abf0c829056a46ee6a038a1b42c84529fc17e"`,
93
+ `"0x7df8cb9fef4fc2aeb271b617d1a4c6178b720ae1d7564f48a363069b7d77a079"`,
96
94
  )
97
95
  })
98
96
 
99
- test('behavior: `genesisConfig` and `{account, genesisConfigId}` produce identical digests', () => {
100
- const genesisConfigId = MultisigConfig.toId(singleOwnerConfig)
101
- const account = MultisigConfig.getAddress({ genesisConfigId })
97
+ test('behavior: `genesisConfig` and `{ account }` produce identical digests', () => {
98
+ const account = MultisigConfig.getAddress(singleOwnerConfig)
102
99
  const payload = `0x${'42'.repeat(32)}` as const
103
100
  expect(
104
101
  MultisigConfig.getSignPayload({
105
102
  payload,
106
103
  genesisConfig: singleOwnerConfig,
107
104
  }),
108
- ).toBe(MultisigConfig.getSignPayload({ payload, account, genesisConfigId }))
105
+ ).toBe(MultisigConfig.getSignPayload({ payload, account }))
109
106
  })
110
107
  })
111
108
 
@@ -152,8 +149,16 @@ describe('assert / validate', () => {
152
149
  expect(MultisigConfig.validate({ threshold: 1, owners: [] })).toBe(false)
153
150
  })
154
151
 
152
+ test('accepts 255 owners', () => {
153
+ const owners = Array.from({ length: 255 }, (_, i) => ({
154
+ owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
155
+ weight: 1,
156
+ }))
157
+ expect(MultisigConfig.validate({ threshold: 255, owners })).toBe(true)
158
+ })
159
+
155
160
  test('too many owners', () => {
156
- const owners = Array.from({ length: 11 }, (_, i) => ({
161
+ const owners = Array.from({ length: 256 }, (_, i) => ({
157
162
  owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
158
163
  weight: 1,
159
164
  }))
@@ -178,6 +183,18 @@ describe('assert / validate', () => {
178
183
  ).toBe(false)
179
184
  })
180
185
 
186
+ test('total weight exceeds u8 max', () => {
187
+ expect(
188
+ MultisigConfig.validate({
189
+ threshold: 255,
190
+ owners: [
191
+ { owner: owner1, weight: 128 },
192
+ { owner: owner2, weight: 128 },
193
+ ],
194
+ }),
195
+ ).toBe(false)
196
+ })
197
+
181
198
  test('zero owner weight', () => {
182
199
  expect(
183
200
  MultisigConfig.validate({