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
@@ -69,6 +69,40 @@ describe('fromRpc', () => {
69
69
  expect(request.nonceKey).toBe(255n)
70
70
  })
71
71
 
72
+ test('behavior: gas-model hints and capabilities pass through', () => {
73
+ const request = TransactionRequest.fromRpc({
74
+ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
75
+ capabilities: { balanceDiffs: true },
76
+ feePayer: true,
77
+ keyData: '0x0578',
78
+ keyId: '0xcccccccccccccccccccccccccccccccccccccccc',
79
+ keyType: 'webAuthn',
80
+ multisigInit: {
81
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
82
+ threshold: 2,
83
+ owners: [
84
+ {
85
+ owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
86
+ weight: 1,
87
+ },
88
+ {
89
+ owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
90
+ weight: 1,
91
+ },
92
+ ],
93
+ },
94
+ multisigSignatureCount: 2,
95
+ type: '0x76',
96
+ })
97
+ expect(request.capabilities).toEqual({ balanceDiffs: true })
98
+ expect(request.feePayer).toBe(true)
99
+ expect(request.keyData).toBe('0x0578')
100
+ expect(request.keyId).toBe('0xcccccccccccccccccccccccccccccccccccccccc')
101
+ expect(request.keyType).toBe('webAuthn')
102
+ expect(request.multisigInit).toMatchObject({ threshold: 2 })
103
+ expect(request.multisigSignatureCount).toBe(2)
104
+ })
105
+
72
106
  test('behavior: empty', () => {
73
107
  const request = TransactionRequest.fromRpc({})
74
108
  expect(request).toMatchInlineSnapshot('{}')
@@ -123,6 +157,101 @@ describe('toRpc', () => {
123
157
  `)
124
158
  })
125
159
 
160
+ test('behavior: gas-model hints and capabilities are carried', () => {
161
+ const request = TransactionRequest.toRpc({
162
+ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
163
+ capabilities: { balanceDiffs: true },
164
+ feePayer: false,
165
+ keyData: '0x0578',
166
+ keyId: '0xcccccccccccccccccccccccccccccccccccccccc',
167
+ keyType: 'webAuthn',
168
+ multisigInit: {
169
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
170
+ threshold: 2,
171
+ owners: [
172
+ {
173
+ owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
174
+ weight: 1,
175
+ },
176
+ {
177
+ owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
178
+ weight: 1,
179
+ },
180
+ ],
181
+ },
182
+ multisigSignatureCount: 2,
183
+ })
184
+ expect(request.capabilities).toEqual({ balanceDiffs: true })
185
+ expect(request.feePayer).toBe(false)
186
+ expect(request.keyData).toBe('0x0578')
187
+ expect(request.keyId).toBe('0xcccccccccccccccccccccccccccccccccccccccc')
188
+ expect(request.keyType).toBe('webAuthn')
189
+ expect(request.multisigInit).toMatchObject({ threshold: 2 })
190
+ expect(request.multisigSignatureCount).toBe(2)
191
+ expect(request.type).toBe('0x76')
192
+ })
193
+
194
+ test('behavior: key data longer than 4 bytes shims into a length hint', () => {
195
+ const request = TransactionRequest.toRpc({
196
+ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
197
+ keyData: `0x${'aa'.repeat(1400)}`,
198
+ keyType: 'webAuthn',
199
+ })
200
+ // 1400 bytes -> 0x0578 big-endian length hint.
201
+ expect(request.keyData).toBe('0x0578')
202
+ })
203
+
204
+ test('behavior: feeToken is withheld until the fee payer signs (TIP-76)', () => {
205
+ const pending = TransactionRequest.toRpc({
206
+ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
207
+ feePayer: true,
208
+ feeToken: '0x20c0000000000000000000000000000000000000',
209
+ })
210
+ expect(pending.feeToken).toBeUndefined()
211
+ expect(pending.feePayer).toBe(true)
212
+
213
+ const signed = TransactionRequest.toRpc({
214
+ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
215
+ feePayer: true,
216
+ feePayerSignature: {
217
+ r: '0x0000000000000000000000000000000000000000000000000000000000000001',
218
+ s: '0x0000000000000000000000000000000000000000000000000000000000000002',
219
+ yParity: 1,
220
+ },
221
+ feeToken: '0x20c0000000000000000000000000000000000000',
222
+ })
223
+ expect(signed.feeToken).toBe('0x20c0000000000000000000000000000000000000')
224
+ })
225
+
226
+ test('behavior: data-less call defaults to the zero address', () => {
227
+ const flat = TransactionRequest.toRpc({
228
+ feeToken: '0x20c0000000000000000000000000000000000000',
229
+ value: 100n,
230
+ })
231
+ expect(flat.calls).toMatchInlineSnapshot(`
232
+ [
233
+ {
234
+ "data": "0x",
235
+ "to": "0x0000000000000000000000000000000000000000",
236
+ "value": "0x64",
237
+ },
238
+ ]
239
+ `)
240
+
241
+ const bare = TransactionRequest.toRpc({
242
+ feeToken: '0x20c0000000000000000000000000000000000000',
243
+ })
244
+ expect(bare.calls).toMatchInlineSnapshot(`
245
+ [
246
+ {
247
+ "data": "0x",
248
+ "to": "0x0000000000000000000000000000000000000000",
249
+ "value": "0x",
250
+ },
251
+ ]
252
+ `)
253
+ })
254
+
126
255
  test("behavior: nonceKey 'random' generates a 192-bit value", () => {
127
256
  const request = TransactionRequest.toRpc({
128
257
  calls: [
@@ -311,9 +440,53 @@ describe('toEnvelope', () => {
311
440
  expect(envelope.validAfter).toBe(100)
312
441
  })
313
442
 
314
- test('error: empty calls throws via envelope assert', () => {
443
+ test('behavior: data-less request defaults to a zero-address call', () => {
444
+ const envelope = TransactionRequest.toEnvelope({
445
+ chainId: 1,
446
+ maxFeePerGas: 1n,
447
+ })
448
+ expect(envelope.calls).toEqual([
449
+ { to: '0x0000000000000000000000000000000000000000' },
450
+ ])
451
+ })
452
+
453
+ test('behavior: feePayer marks the envelope as pending a fee payer signature (TIP-76)', () => {
454
+ const envelope = TransactionRequest.toEnvelope({
455
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
456
+ chainId: 1,
457
+ feePayer: true,
458
+ feeToken: '0x20c0000000000000000000000000000000000000',
459
+ maxFeePerGas: 1n,
460
+ })
461
+ expect(envelope.feePayerSignature).toBeNull()
462
+
463
+ // An explicit fee payer signature wins over the marker.
464
+ const signed = TransactionRequest.toEnvelope({
465
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
466
+ chainId: 1,
467
+ feePayer: true,
468
+ feePayerSignature: {
469
+ r: '0x0000000000000000000000000000000000000000000000000000000000000001',
470
+ s: '0x0000000000000000000000000000000000000000000000000000000000000002',
471
+ yParity: 1,
472
+ },
473
+ maxFeePerGas: 1n,
474
+ })
475
+ expect(signed.feePayerSignature).not.toBeNull()
476
+
477
+ // No fee payer, no marker.
478
+ const plain = TransactionRequest.toEnvelope({
479
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
480
+ chainId: 1,
481
+ maxFeePerGas: 1n,
482
+ })
483
+ expect(plain.feePayerSignature).toBeUndefined()
484
+ })
485
+
486
+ test('error: explicit empty calls throws via envelope assert', () => {
315
487
  expect(() =>
316
488
  TransactionRequest.toEnvelope({
489
+ calls: [],
317
490
  chainId: 1,
318
491
  maxFeePerGas: 1n,
319
492
  }),
@@ -1,3 +1,4 @@
1
+ import type * as Address from '../core/Address.js'
1
2
  import type * as Errors from '../core/Errors.js'
2
3
  import * as Hex from '../core/Hex.js'
3
4
  import * as Quantity from '../core/internal/quantity.js'
@@ -6,14 +7,21 @@ import * as Signature from '../core/Signature.js'
6
7
  import * as ox_TransactionRequest from '../core/TransactionRequest.js'
7
8
  import * as AuthorizationTempo from './AuthorizationTempo.js'
8
9
  import * as KeyAuthorization from './KeyAuthorization.js'
10
+ import type * as MultisigConfig from './MultisigConfig.js'
9
11
  import * as SignatureEnvelope from './SignatureEnvelope.js'
10
- import * as TokenId from './TokenId.js'
11
12
  import * as Transaction from './Transaction.js'
12
13
  import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
13
14
  import type { Call } from './TxEnvelopeTempo.js'
14
15
 
15
16
  type KeyType = 'secp256k1' | 'p256' | 'webAuthn'
16
17
 
18
+ /**
19
+ * Bootstrap multisig config hint for node-side gas modeling (TIP-1061).
20
+ * The node prices multisig gas from it during simulation, and ignores it
21
+ * for registered senders.
22
+ */
23
+ export type MultisigInit = Compute<MultisigConfig.Config & { salt: Hex.Hex }>
24
+
17
25
  /**
18
26
  * A Transaction Request that is generic to all transaction types.
19
27
  *
@@ -35,12 +43,16 @@ export type TransactionRequest<
35
43
  | AuthorizationTempo.ListSigned<bigintType, numberType>
36
44
  | undefined
37
45
  calls?: readonly Call<bigintType>[] | undefined
46
+ capabilities?: Record<string, unknown> | undefined
38
47
  feePayerSignature?: Signature.Signature<true, numberType> | null | undefined
39
48
  keyAuthorization?: KeyAuthorization.KeyAuthorization<true> | undefined
40
49
  keyData?: Hex.Hex | undefined
50
+ keyId?: Address.Address | undefined
41
51
  keyType?: KeyType | undefined
42
52
  feePayer?: boolean | undefined
43
- feeToken?: TokenId.TokenIdOrAddress | undefined
53
+ feeToken?: Address.Address | undefined
54
+ multisigInit?: MultisigInit | undefined
55
+ multisigSignatureCount?: number | undefined
44
56
  nonceKey?: 'random' | bigintType | undefined
45
57
  signature?: SignatureEnvelope.SignatureEnvelope<numberType> | undefined
46
58
  validBefore?: numberType | undefined
@@ -202,6 +214,22 @@ export function toRpc(request: toRpc.Input): Rpc {
202
214
  authorizationList: undefined,
203
215
  }) as Rpc
204
216
 
217
+ const tempo =
218
+ typeof request.calls !== 'undefined' ||
219
+ typeof request.capabilities !== 'undefined' ||
220
+ typeof request.feePayer !== 'undefined' ||
221
+ typeof request.feeToken !== 'undefined' ||
222
+ typeof request.keyAuthorization !== 'undefined' ||
223
+ typeof request.keyData !== 'undefined' ||
224
+ typeof request.keyId !== 'undefined' ||
225
+ typeof request.keyType !== 'undefined' ||
226
+ typeof request.multisigInit !== 'undefined' ||
227
+ typeof request.multisigSignatureCount !== 'undefined' ||
228
+ typeof request.nonceKey !== 'undefined' ||
229
+ typeof request.validBefore !== 'undefined' ||
230
+ typeof request.validAfter !== 'undefined' ||
231
+ request.type === 'tempo'
232
+
205
233
  if (request.authorizationList)
206
234
  request_rpc.authorizationList = AuthorizationTempo.toRpcList(
207
235
  request.authorizationList,
@@ -216,20 +244,40 @@ export function toRpc(request: toRpc.Input): Rpc {
216
244
  value: call.value ? Quantity.fromNumberish(call.value) : '0x',
217
245
  data: call.data ?? '0x',
218
246
  }))
219
- else if (request.to || request.data || request.value)
247
+ else if (request.to || request.data || request.value || tempo)
220
248
  request_rpc.calls = [
221
249
  {
222
- to: request.to ?? undefined,
250
+ to:
251
+ request.to ||
252
+ (tempo && (!request.data || request.data === '0x')
253
+ ? '0x0000000000000000000000000000000000000000'
254
+ : undefined),
223
255
  value: request.value ? Quantity.fromNumberish(request.value) : '0x',
224
256
  data: request.data ?? '0x',
225
257
  },
226
258
  ]
227
- if (typeof request.feeToken !== 'undefined')
228
- request_rpc.feeToken = TokenId.toAddress(request.feeToken)
259
+ if (
260
+ typeof request.feeToken !== 'undefined' &&
261
+ !(request.feePayer === true && !request.feePayerSignature)
262
+ )
263
+ request_rpc.feeToken = request.feeToken
229
264
  if (request.keyAuthorization)
230
265
  request_rpc.keyAuthorization = KeyAuthorization.toRpc(
231
266
  request.keyAuthorization,
232
267
  )
268
+ if (typeof request.capabilities !== 'undefined')
269
+ request_rpc.capabilities = request.capabilities
270
+ if (typeof request.feePayer !== 'undefined')
271
+ request_rpc.feePayer = request.feePayer
272
+ if (typeof request.keyData !== 'undefined')
273
+ request_rpc.keyData = shimKeyData(request.keyData)
274
+ if (typeof request.keyId !== 'undefined') request_rpc.keyId = request.keyId
275
+ if (typeof request.keyType !== 'undefined')
276
+ request_rpc.keyType = request.keyType
277
+ if (typeof request.multisigInit !== 'undefined')
278
+ request_rpc.multisigInit = request.multisigInit
279
+ if (typeof request.multisigSignatureCount !== 'undefined')
280
+ request_rpc.multisigSignatureCount = request.multisigSignatureCount
233
281
  if (typeof request.validBefore !== 'undefined')
234
282
  request_rpc.validBefore = Quantity.fromNumberish(request.validBefore)
235
283
  if (typeof request.validAfter !== 'undefined')
@@ -243,16 +291,7 @@ export function toRpc(request: toRpc.Input): Rpc {
243
291
  })()
244
292
  if (nonceKey) request_rpc.nonceKey = nonceKey
245
293
 
246
- if (
247
- typeof request.calls !== 'undefined' ||
248
- typeof request.feePayer !== 'undefined' ||
249
- typeof request.feeToken !== 'undefined' ||
250
- typeof request.keyAuthorization !== 'undefined' ||
251
- typeof request.nonceKey !== 'undefined' ||
252
- typeof request.validBefore !== 'undefined' ||
253
- typeof request.validAfter !== 'undefined' ||
254
- request.type === 'tempo'
255
- ) {
294
+ if (tempo) {
256
295
  request_rpc.type = Transaction.toRpcType.tempo
257
296
  delete request_rpc.data
258
297
  delete request_rpc.input
@@ -306,21 +345,20 @@ export function toEnvelope(
306
345
  ): TxEnvelopeTempo.TxEnvelopeTempo {
307
346
  const calls = (() => {
308
347
  if (request.calls) return request.calls
309
- if (request.to || request.data || request.value)
310
- return [
311
- {
312
- ...(typeof request.to !== 'undefined' && request.to !== null
313
- ? { to: request.to }
314
- : {}),
315
- ...(typeof request.data !== 'undefined'
316
- ? { data: request.data }
317
- : {}),
318
- ...(typeof request.value !== 'undefined'
319
- ? { value: request.value }
320
- : {}),
321
- },
322
- ] satisfies readonly Call[]
323
- return [] as readonly Call[]
348
+ const to =
349
+ request.to ||
350
+ (!request.data || request.data === '0x'
351
+ ? '0x0000000000000000000000000000000000000000'
352
+ : undefined)
353
+ return [
354
+ {
355
+ ...(typeof to !== 'undefined' ? { to } : {}),
356
+ ...(typeof request.data !== 'undefined' ? { data: request.data } : {}),
357
+ ...(typeof request.value !== 'undefined'
358
+ ? { value: request.value }
359
+ : {}),
360
+ },
361
+ ] satisfies readonly Call[]
324
362
  })()
325
363
 
326
364
  const nonceKey = (() => {
@@ -342,7 +380,9 @@ export function toEnvelope(
342
380
  : {}),
343
381
  ...(typeof request.feePayerSignature !== 'undefined'
344
382
  ? { feePayerSignature: request.feePayerSignature }
345
- : {}),
383
+ : request.feePayer
384
+ ? { feePayerSignature: null }
385
+ : {}),
346
386
  ...(typeof request.feeToken !== 'undefined'
347
387
  ? { feeToken: request.feeToken }
348
388
  : {}),
@@ -387,3 +427,15 @@ export declare namespace toEnvelope {
387
427
  | Hex.toBigInt.ErrorType
388
428
  | Errors.GlobalErrorType
389
429
  }
430
+
431
+ /**
432
+ * Shims key data longer than 4 bytes into a 2-byte big-endian length hint.
433
+ * The node's gas estimator only accepts 1, 2, or 4-byte key data as a
434
+ * signature-size hint; anything else silently falls back to the default.
435
+ * @internal
436
+ */
437
+ function shimKeyData(data: Hex.Hex): Hex.Hex {
438
+ const byteLength = Hex.size(data)
439
+ if (byteLength <= 4) return data
440
+ return Hex.fromNumber(byteLength, { size: 2 })
441
+ }
@@ -1464,7 +1464,7 @@ describe('encodeForSigning', () => {
1464
1464
  },
1465
1465
  ],
1466
1466
  feePayerSignature: null,
1467
- feeToken: 1n,
1467
+ feeToken: '0x20c0000000000000000000000000000000000001',
1468
1468
  nonce: 0n,
1469
1469
  })
1470
1470
  const signature = Secp256k1.sign({
@@ -1485,7 +1485,7 @@ describe('encodeForSigning', () => {
1485
1485
  const sponsored = TxEnvelopeTempo.from({
1486
1486
  ...signed,
1487
1487
  feePayerSignature,
1488
- feeToken: 2n,
1488
+ feeToken: '0x20c0000000000000000000000000000000000002',
1489
1489
  })
1490
1490
 
1491
1491
  expect(TxEnvelopeTempo.encodeForSigning(sponsored)).toBe(
@@ -17,7 +17,6 @@ import * as TransactionEnvelope from '../core/TxEnvelope.js'
17
17
  import * as AuthorizationTempo from './AuthorizationTempo.js'
18
18
  import * as KeyAuthorization from './KeyAuthorization.js'
19
19
  import * as SignatureEnvelope from './SignatureEnvelope.js'
20
- import * as TokenId from './TokenId.js'
21
20
  import type * as TransactionRequest from './TransactionRequest.js'
22
21
 
23
22
  /**
@@ -87,8 +86,8 @@ export type TxEnvelopeTempo<
87
86
  gas?: bigintType | undefined
88
87
  /** Fee payer signature. */
89
88
  feePayerSignature?: Signature.Signature<true, numberType> | null | undefined
90
- /** Fee token preference. Address or ID of the TIP-20 token. */
91
- feeToken?: TokenId.TokenIdOrAddress | undefined
89
+ /** Fee token preference. Address of the TIP-20 token. */
90
+ feeToken?: Address.Address | undefined
92
91
  /**
93
92
  * Key authorization for provisioning a new access key.
94
93
  *
@@ -173,6 +172,7 @@ export function assert(envelope: PartialBy<TxEnvelopeTempo, 'type'>) {
173
172
  const {
174
173
  calls,
175
174
  chainId,
175
+ feeToken,
176
176
  maxFeePerGas,
177
177
  maxPriorityFeePerGas,
178
178
  validBefore,
@@ -199,6 +199,9 @@ export function assert(envelope: PartialBy<TxEnvelopeTempo, 'type'>) {
199
199
  for (const call of calls)
200
200
  if (call.to) Address.assert(call.to, { strict: false })
201
201
 
202
+ // Validate fee token
203
+ if (feeToken) Address.assert(feeToken, { strict: false })
204
+
202
205
  // Validate chain ID
203
206
  if (chainId <= 0)
204
207
  throw new TransactionEnvelope.InvalidChainIdError({ chainId })
@@ -719,10 +722,7 @@ export function serialize(
719
722
  nonce ? Hex.fromNumber(nonce) : '0x',
720
723
  typeof validBefore === 'number' ? Hex.fromNumber(validBefore) : '0x',
721
724
  typeof validAfter === 'number' ? Hex.fromNumber(validAfter) : '0x',
722
- !skipFeeToken &&
723
- (typeof feeToken === 'bigint' || typeof feeToken === 'string')
724
- ? TokenId.toAddress(feeToken)
725
- : '0x',
725
+ !skipFeeToken && feeToken ? feeToken : '0x',
726
726
  feePayerSignatureOrSender,
727
727
  authorizationTupleList,
728
728
  ...(keyAuthorization ? [KeyAuthorization.toTuple(keyAuthorization)] : []),