ox 0.9.16 → 0.10.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 (216) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +4 -4
  3. package/TxEnvelope/package.json +6 -0
  4. package/TxEnvelopeEip1559/package.json +6 -0
  5. package/TxEnvelopeEip2930/package.json +6 -0
  6. package/TxEnvelopeEip4844/package.json +6 -0
  7. package/TxEnvelopeEip7702/package.json +6 -0
  8. package/TxEnvelopeLegacy/package.json +6 -0
  9. package/_cjs/core/{TransactionEnvelope.js → TxEnvelope.js} +1 -1
  10. package/_cjs/core/TxEnvelope.js.map +1 -0
  11. package/_cjs/core/{TransactionEnvelopeEip1559.js → TxEnvelopeEip1559.js} +2 -2
  12. package/_cjs/core/TxEnvelopeEip1559.js.map +1 -0
  13. package/_cjs/core/{TransactionEnvelopeEip2930.js → TxEnvelopeEip2930.js} +2 -2
  14. package/_cjs/core/TxEnvelopeEip2930.js.map +1 -0
  15. package/_cjs/core/{TransactionEnvelopeEip4844.js → TxEnvelopeEip4844.js} +4 -4
  16. package/_cjs/core/TxEnvelopeEip4844.js.map +1 -0
  17. package/_cjs/core/{TransactionEnvelopeEip7702.js → TxEnvelopeEip7702.js} +4 -4
  18. package/_cjs/core/TxEnvelopeEip7702.js.map +1 -0
  19. package/_cjs/core/{TransactionEnvelopeLegacy.js → TxEnvelopeLegacy.js} +2 -2
  20. package/_cjs/core/TxEnvelopeLegacy.js.map +1 -0
  21. package/_cjs/core/WebAuthnP256.js +1 -1
  22. package/_cjs/core/WebAuthnP256.js.map +1 -1
  23. package/_cjs/erc8021/Attribution.js +36 -6
  24. package/_cjs/erc8021/Attribution.js.map +1 -1
  25. package/_cjs/index.docs.js +1 -0
  26. package/_cjs/index.docs.js.map +1 -1
  27. package/_cjs/index.js +7 -7
  28. package/_cjs/index.js.map +1 -1
  29. package/_cjs/tempo/AuthorizationTempo.js +101 -0
  30. package/_cjs/tempo/AuthorizationTempo.js.map +1 -0
  31. package/_cjs/tempo/KeyAuthorization.js +123 -0
  32. package/_cjs/tempo/KeyAuthorization.js.map +1 -0
  33. package/_cjs/tempo/PoolId.js +10 -0
  34. package/_cjs/tempo/PoolId.js.map +1 -0
  35. package/_cjs/tempo/SignatureEnvelope.js +394 -0
  36. package/_cjs/tempo/SignatureEnvelope.js.map +1 -0
  37. package/_cjs/tempo/Tick.js +77 -0
  38. package/_cjs/tempo/Tick.js.map +1 -0
  39. package/_cjs/tempo/TokenId.js +28 -0
  40. package/_cjs/tempo/TokenId.js.map +1 -0
  41. package/_cjs/tempo/TokenRole.js +26 -0
  42. package/_cjs/tempo/TokenRole.js.map +1 -0
  43. package/_cjs/tempo/Transaction.js +80 -0
  44. package/_cjs/tempo/Transaction.js.map +1 -0
  45. package/_cjs/tempo/TransactionReceipt.js +26 -0
  46. package/_cjs/tempo/TransactionReceipt.js.map +1 -0
  47. package/_cjs/tempo/TransactionRequest.js +53 -0
  48. package/_cjs/tempo/TransactionRequest.js.map +1 -0
  49. package/_cjs/tempo/TxEnvelopeTempo.js +267 -0
  50. package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -0
  51. package/_cjs/tempo/index.js +15 -0
  52. package/_cjs/tempo/index.js.map +1 -0
  53. package/_cjs/version.js +1 -1
  54. package/_esm/core/Blobs.js +8 -8
  55. package/_esm/core/{TransactionEnvelope.js → TxEnvelope.js} +11 -11
  56. package/_esm/core/TxEnvelope.js.map +1 -0
  57. package/_esm/core/{TransactionEnvelopeEip1559.js → TxEnvelopeEip1559.js} +42 -42
  58. package/_esm/core/TxEnvelopeEip1559.js.map +1 -0
  59. package/_esm/core/{TransactionEnvelopeEip2930.js → TxEnvelopeEip2930.js} +43 -43
  60. package/_esm/core/TxEnvelopeEip2930.js.map +1 -0
  61. package/_esm/core/{TransactionEnvelopeEip4844.js → TxEnvelopeEip4844.js} +42 -42
  62. package/_esm/core/TxEnvelopeEip4844.js.map +1 -0
  63. package/_esm/core/{TransactionEnvelopeEip7702.js → TxEnvelopeEip7702.js} +40 -40
  64. package/_esm/core/TxEnvelopeEip7702.js.map +1 -0
  65. package/_esm/core/{TransactionEnvelopeLegacy.js → TxEnvelopeLegacy.js} +42 -42
  66. package/_esm/core/TxEnvelopeLegacy.js.map +1 -0
  67. package/_esm/core/WebAuthnP256.js +1 -1
  68. package/_esm/core/WebAuthnP256.js.map +1 -1
  69. package/_esm/erc8021/Attribution.js +58 -13
  70. package/_esm/erc8021/Attribution.js.map +1 -1
  71. package/_esm/index.docs.js +1 -0
  72. package/_esm/index.docs.js.map +1 -1
  73. package/_esm/index.js +192 -192
  74. package/_esm/index.js.map +1 -1
  75. package/_esm/tempo/AuthorizationTempo.js +664 -0
  76. package/_esm/tempo/AuthorizationTempo.js.map +1 -0
  77. package/_esm/tempo/KeyAuthorization.js +426 -0
  78. package/_esm/tempo/KeyAuthorization.js.map +1 -0
  79. package/_esm/tempo/PoolId.js +28 -0
  80. package/_esm/tempo/PoolId.js.map +1 -0
  81. package/_esm/tempo/SignatureEnvelope.js +660 -0
  82. package/_esm/tempo/SignatureEnvelope.js.map +1 -0
  83. package/_esm/tempo/Tick.js +147 -0
  84. package/_esm/tempo/Tick.js.map +1 -0
  85. package/_esm/tempo/TokenId.js +71 -0
  86. package/_esm/tempo/TokenId.js.map +1 -0
  87. package/_esm/tempo/TokenRole.js +40 -0
  88. package/_esm/tempo/TokenRole.js.map +1 -0
  89. package/_esm/tempo/Transaction.js +167 -0
  90. package/_esm/tempo/Transaction.js.map +1 -0
  91. package/_esm/tempo/TransactionReceipt.js +138 -0
  92. package/_esm/tempo/TransactionReceipt.js.map +1 -0
  93. package/_esm/tempo/TransactionRequest.js +99 -0
  94. package/_esm/tempo/TransactionRequest.js.map +1 -0
  95. package/_esm/tempo/TxEnvelopeTempo.js +607 -0
  96. package/_esm/tempo/TxEnvelopeTempo.js.map +1 -0
  97. package/_esm/tempo/index.js +298 -0
  98. package/_esm/tempo/index.js.map +1 -0
  99. package/_esm/version.js +1 -1
  100. package/_types/core/Blobs.d.ts +8 -8
  101. package/_types/core/{TransactionEnvelope.d.ts → TxEnvelope.d.ts} +11 -11
  102. package/_types/core/TxEnvelope.d.ts.map +1 -0
  103. package/_types/core/{TransactionEnvelopeEip1559.d.ts → TxEnvelopeEip1559.d.ts} +54 -54
  104. package/_types/core/TxEnvelopeEip1559.d.ts.map +1 -0
  105. package/_types/core/{TransactionEnvelopeEip2930.d.ts → TxEnvelopeEip2930.d.ts} +55 -55
  106. package/_types/core/TxEnvelopeEip2930.d.ts.map +1 -0
  107. package/_types/core/{TransactionEnvelopeEip4844.d.ts → TxEnvelopeEip4844.d.ts} +54 -54
  108. package/_types/core/TxEnvelopeEip4844.d.ts.map +1 -0
  109. package/_types/core/{TransactionEnvelopeEip7702.d.ts → TxEnvelopeEip7702.d.ts} +49 -49
  110. package/_types/core/TxEnvelopeEip7702.d.ts.map +1 -0
  111. package/_types/core/{TransactionEnvelopeLegacy.d.ts → TxEnvelopeLegacy.d.ts} +54 -54
  112. package/_types/core/TxEnvelopeLegacy.d.ts.map +1 -0
  113. package/_types/core/WebAuthnP256.d.ts +1 -1
  114. package/_types/core/WebAuthnP256.d.ts.map +1 -1
  115. package/_types/erc8021/Attribution.d.ts +20 -6
  116. package/_types/erc8021/Attribution.d.ts.map +1 -1
  117. package/_types/index.d.ts +192 -192
  118. package/_types/index.d.ts.map +1 -1
  119. package/_types/index.docs.d.ts +1 -0
  120. package/_types/index.docs.d.ts.map +1 -1
  121. package/_types/tempo/AuthorizationTempo.d.ts +688 -0
  122. package/_types/tempo/AuthorizationTempo.d.ts.map +1 -0
  123. package/_types/tempo/KeyAuthorization.d.ts +437 -0
  124. package/_types/tempo/KeyAuthorization.d.ts.map +1 -0
  125. package/_types/tempo/PoolId.d.ts +33 -0
  126. package/_types/tempo/PoolId.d.ts.map +1 -0
  127. package/_types/tempo/SignatureEnvelope.d.ts +438 -0
  128. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -0
  129. package/_types/tempo/Tick.d.ts +120 -0
  130. package/_types/tempo/Tick.d.ts.map +1 -0
  131. package/_types/tempo/TokenId.d.ts +55 -0
  132. package/_types/tempo/TokenId.d.ts.map +1 -0
  133. package/_types/tempo/TokenRole.d.ts +29 -0
  134. package/_types/tempo/TokenRole.d.ts.map +1 -0
  135. package/_types/tempo/Transaction.d.ts +208 -0
  136. package/_types/tempo/Transaction.d.ts.map +1 -0
  137. package/_types/tempo/TransactionReceipt.d.ts +165 -0
  138. package/_types/tempo/TransactionReceipt.d.ts.map +1 -0
  139. package/_types/tempo/TransactionRequest.d.ts +89 -0
  140. package/_types/tempo/TransactionRequest.d.ts.map +1 -0
  141. package/_types/tempo/TxEnvelopeTempo.d.ts +551 -0
  142. package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -0
  143. package/_types/tempo/index.d.ts +300 -0
  144. package/_types/tempo/index.d.ts.map +1 -0
  145. package/_types/version.d.ts +1 -1
  146. package/core/Blobs.ts +8 -8
  147. package/core/{TransactionEnvelope.ts → TxEnvelope.ts} +10 -10
  148. package/core/{TransactionEnvelopeEip1559.ts → TxEnvelopeEip1559.ts} +60 -69
  149. package/core/{TransactionEnvelopeEip2930.ts → TxEnvelopeEip2930.ts} +61 -72
  150. package/core/{TransactionEnvelopeEip4844.ts → TxEnvelopeEip4844.ts} +62 -71
  151. package/core/{TransactionEnvelopeEip7702.ts → TxEnvelopeEip7702.ts} +58 -67
  152. package/core/{TransactionEnvelopeLegacy.ts → TxEnvelopeLegacy.ts} +59 -68
  153. package/core/WebAuthnP256.ts +3 -1
  154. package/erc8021/Attribution.ts +77 -15
  155. package/index.docs.ts +1 -0
  156. package/index.ts +192 -195
  157. package/package.json +91 -31
  158. package/tempo/AuthorizationTempo/package.json +6 -0
  159. package/tempo/AuthorizationTempo.test.ts +1293 -0
  160. package/tempo/AuthorizationTempo.ts +884 -0
  161. package/tempo/KeyAuthorization/package.json +6 -0
  162. package/tempo/KeyAuthorization.test.ts +1373 -0
  163. package/tempo/KeyAuthorization.ts +622 -0
  164. package/tempo/PoolId/package.json +6 -0
  165. package/tempo/PoolId.test.ts +33 -0
  166. package/tempo/PoolId.ts +42 -0
  167. package/tempo/SignatureEnvelope/package.json +6 -0
  168. package/tempo/SignatureEnvelope.test.ts +1877 -0
  169. package/tempo/SignatureEnvelope.ts +973 -0
  170. package/tempo/Tick/package.json +6 -0
  171. package/tempo/Tick.test.ts +281 -0
  172. package/tempo/Tick.ts +186 -0
  173. package/tempo/TokenId/package.json +6 -0
  174. package/tempo/TokenId.test.ts +40 -0
  175. package/tempo/TokenId.ts +80 -0
  176. package/tempo/TokenRole/package.json +6 -0
  177. package/tempo/TokenRole.test.ts +16 -0
  178. package/tempo/TokenRole.ts +45 -0
  179. package/tempo/Transaction/package.json +6 -0
  180. package/tempo/Transaction.test.ts +523 -0
  181. package/tempo/Transaction.ts +339 -0
  182. package/tempo/TransactionReceipt/package.json +6 -0
  183. package/tempo/TransactionReceipt.ts +200 -0
  184. package/tempo/TransactionRequest/package.json +6 -0
  185. package/tempo/TransactionRequest.ts +160 -0
  186. package/tempo/TxEnvelopeTempo/package.json +6 -0
  187. package/tempo/TxEnvelopeTempo.test.ts +1371 -0
  188. package/tempo/TxEnvelopeTempo.ts +972 -0
  189. package/tempo/e2e.test.ts +1387 -0
  190. package/tempo/index.ts +308 -0
  191. package/tempo/package.json +6 -0
  192. package/version.ts +1 -1
  193. package/TransactionEnvelope/package.json +0 -6
  194. package/TransactionEnvelopeEip1559/package.json +0 -6
  195. package/TransactionEnvelopeEip2930/package.json +0 -6
  196. package/TransactionEnvelopeEip4844/package.json +0 -6
  197. package/TransactionEnvelopeEip7702/package.json +0 -6
  198. package/TransactionEnvelopeLegacy/package.json +0 -6
  199. package/_cjs/core/TransactionEnvelope.js.map +0 -1
  200. package/_cjs/core/TransactionEnvelopeEip1559.js.map +0 -1
  201. package/_cjs/core/TransactionEnvelopeEip2930.js.map +0 -1
  202. package/_cjs/core/TransactionEnvelopeEip4844.js.map +0 -1
  203. package/_cjs/core/TransactionEnvelopeEip7702.js.map +0 -1
  204. package/_cjs/core/TransactionEnvelopeLegacy.js.map +0 -1
  205. package/_esm/core/TransactionEnvelope.js.map +0 -1
  206. package/_esm/core/TransactionEnvelopeEip1559.js.map +0 -1
  207. package/_esm/core/TransactionEnvelopeEip2930.js.map +0 -1
  208. package/_esm/core/TransactionEnvelopeEip4844.js.map +0 -1
  209. package/_esm/core/TransactionEnvelopeEip7702.js.map +0 -1
  210. package/_esm/core/TransactionEnvelopeLegacy.js.map +0 -1
  211. package/_types/core/TransactionEnvelope.d.ts.map +0 -1
  212. package/_types/core/TransactionEnvelopeEip1559.d.ts.map +0 -1
  213. package/_types/core/TransactionEnvelopeEip2930.d.ts.map +0 -1
  214. package/_types/core/TransactionEnvelopeEip4844.d.ts.map +0 -1
  215. package/_types/core/TransactionEnvelopeEip7702.d.ts.map +0 -1
  216. package/_types/core/TransactionEnvelopeLegacy.d.ts.map +0 -1
@@ -0,0 +1,1371 @@
1
+ import { Hex, P256, Rlp, Secp256k1, Value, WebAuthnP256 } from 'ox'
2
+ import { describe, expect, test } from 'vitest'
3
+ import * as AuthorizationTempo from './AuthorizationTempo.js'
4
+ import { SignatureEnvelope } from './index.js'
5
+ import * as KeyAuthorization from './KeyAuthorization.js'
6
+ import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
7
+
8
+ const privateKey =
9
+ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
10
+
11
+ describe('assert', () => {
12
+ test('empty calls list', () => {
13
+ expect(() =>
14
+ TxEnvelopeTempo.assert({
15
+ calls: [],
16
+ chainId: 1,
17
+ }),
18
+ ).toThrowErrorMatchingInlineSnapshot(
19
+ `[TxEnvelopeTempo.CallsEmptyError: Calls list cannot be empty.]`,
20
+ )
21
+ })
22
+
23
+ test('missing calls', () => {
24
+ expect(() =>
25
+ TxEnvelopeTempo.assert({
26
+ chainId: 1,
27
+ } as any),
28
+ ).toThrowErrorMatchingInlineSnapshot(
29
+ `[TxEnvelopeTempo.CallsEmptyError: Calls list cannot be empty.]`,
30
+ )
31
+ })
32
+
33
+ test('invalid validity window', () => {
34
+ expect(() =>
35
+ TxEnvelopeTempo.assert({
36
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
37
+ chainId: 1,
38
+ validBefore: 100,
39
+ validAfter: 200,
40
+ }),
41
+ ).toThrowErrorMatchingInlineSnapshot(
42
+ `[TxEnvelopeTempo.InvalidValidityWindowError: validBefore (100) must be greater than validAfter (200).]`,
43
+ )
44
+ })
45
+
46
+ test('invalid validity window (equal)', () => {
47
+ expect(() =>
48
+ TxEnvelopeTempo.assert({
49
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
50
+ chainId: 1,
51
+ validBefore: 100,
52
+ validAfter: 100,
53
+ }),
54
+ ).toThrowErrorMatchingInlineSnapshot(
55
+ `[TxEnvelopeTempo.InvalidValidityWindowError: validBefore (100) must be greater than validAfter (100).]`,
56
+ )
57
+ })
58
+
59
+ test('invalid call address', () => {
60
+ expect(() =>
61
+ TxEnvelopeTempo.assert({
62
+ calls: [{ to: '0x000000000000000000000000000000000000000z' }],
63
+ chainId: 1,
64
+ }),
65
+ ).toThrowErrorMatchingInlineSnapshot(
66
+ `
67
+ [Address.InvalidAddressError: Address "0x000000000000000000000000000000000000000z" is invalid.
68
+
69
+ Details: Address is not a 20 byte (40 hexadecimal character) value.]
70
+ `,
71
+ )
72
+ })
73
+
74
+ test('fee cap too high', () => {
75
+ expect(() =>
76
+ TxEnvelopeTempo.assert({
77
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
78
+ maxFeePerGas: 2n ** 256n - 1n + 1n,
79
+ chainId: 1,
80
+ }),
81
+ ).toThrowErrorMatchingInlineSnapshot(
82
+ `[TransactionEnvelope.FeeCapTooHighError: The fee cap (\`maxFeePerGas\`/\`maxPriorityFeePerGas\` = 115792089237316195423570985008687907853269984665640564039457584007913.129639936 gwei) cannot be higher than the maximum allowed value (2^256-1).]`,
83
+ )
84
+ })
85
+
86
+ test('tip above fee cap', () => {
87
+ expect(() =>
88
+ TxEnvelopeTempo.assert({
89
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
90
+ chainId: 1,
91
+ maxFeePerGas: 10n,
92
+ maxPriorityFeePerGas: 20n,
93
+ }),
94
+ ).toThrowErrorMatchingInlineSnapshot(
95
+ `[TransactionEnvelope.TipAboveFeeCapError: The provided tip (\`maxPriorityFeePerGas\` = 0.00000002 gwei) cannot be higher than the fee cap (\`maxFeePerGas\` = 0.00000001 gwei).]`,
96
+ )
97
+ })
98
+
99
+ test('invalid chain id', () => {
100
+ expect(() =>
101
+ TxEnvelopeTempo.assert({
102
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
103
+ chainId: 0,
104
+ }),
105
+ ).toThrowErrorMatchingInlineSnapshot(
106
+ `[TransactionEnvelope.InvalidChainIdError: Chain ID "0" is invalid.]`,
107
+ )
108
+ })
109
+ })
110
+
111
+ describe('deserialize', () => {
112
+ const transaction = TxEnvelopeTempo.from({
113
+ chainId: 1,
114
+ calls: [
115
+ {
116
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
117
+ },
118
+ ],
119
+ nonce: 785n,
120
+ nonceKey: 0n,
121
+ maxFeePerGas: Value.fromGwei('2'),
122
+ maxPriorityFeePerGas: Value.fromGwei('2'),
123
+ })
124
+
125
+ test('default', () => {
126
+ const serialized = TxEnvelopeTempo.serialize(transaction)
127
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
128
+ expect(deserialized).toEqual(transaction)
129
+ })
130
+
131
+ test('minimal', () => {
132
+ const transaction = TxEnvelopeTempo.from({
133
+ chainId: 1,
134
+ calls: [{}],
135
+ nonce: 0n,
136
+ nonceKey: 0n,
137
+ })
138
+ const serialized = TxEnvelopeTempo.serialize(transaction)
139
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(transaction)
140
+ })
141
+
142
+ test('multiple calls', () => {
143
+ const transaction_multiCall = TxEnvelopeTempo.from({
144
+ ...transaction,
145
+ calls: [
146
+ {
147
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
148
+ },
149
+ {
150
+ to: '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc',
151
+ value: Value.from('0.002', 6),
152
+ data: '0x1234',
153
+ },
154
+ ],
155
+ })
156
+ const serialized = TxEnvelopeTempo.serialize(transaction_multiCall)
157
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
158
+ transaction_multiCall,
159
+ )
160
+ })
161
+
162
+ test('gas', () => {
163
+ const transaction_gas = TxEnvelopeTempo.from({
164
+ ...transaction,
165
+ gas: 21001n,
166
+ })
167
+ const serialized = TxEnvelopeTempo.serialize(transaction_gas)
168
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(transaction_gas)
169
+ })
170
+
171
+ test('accessList', () => {
172
+ const transaction_accessList = TxEnvelopeTempo.from({
173
+ ...transaction,
174
+ accessList: [
175
+ {
176
+ address: '0x0000000000000000000000000000000000000000',
177
+ storageKeys: [
178
+ '0x0000000000000000000000000000000000000000000000000000000000000001',
179
+ '0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe',
180
+ ],
181
+ },
182
+ ],
183
+ })
184
+ const serialized = TxEnvelopeTempo.serialize(transaction_accessList)
185
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
186
+ transaction_accessList,
187
+ )
188
+ })
189
+
190
+ test('nonce', () => {
191
+ const transaction_nonce = TxEnvelopeTempo.from({
192
+ ...transaction,
193
+ nonce: 0n,
194
+ })
195
+ const serialized = TxEnvelopeTempo.serialize(transaction_nonce)
196
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(transaction_nonce)
197
+ })
198
+
199
+ test('nonceKey', () => {
200
+ const transaction_nonceKey = TxEnvelopeTempo.from({
201
+ ...transaction,
202
+ nonceKey: 0n,
203
+ })
204
+ const serialized = TxEnvelopeTempo.serialize(transaction_nonceKey)
205
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
206
+ transaction_nonceKey,
207
+ )
208
+ })
209
+
210
+ test('validBefore', () => {
211
+ const transaction_validBefore = TxEnvelopeTempo.from({
212
+ ...transaction,
213
+ validBefore: 1000000,
214
+ })
215
+ const serialized = TxEnvelopeTempo.serialize(transaction_validBefore)
216
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
217
+ transaction_validBefore,
218
+ )
219
+ })
220
+
221
+ test('validAfter', () => {
222
+ const transaction_validAfter = TxEnvelopeTempo.from({
223
+ ...transaction,
224
+ validAfter: 500000,
225
+ })
226
+ const serialized = TxEnvelopeTempo.serialize(transaction_validAfter)
227
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
228
+ transaction_validAfter,
229
+ )
230
+ })
231
+
232
+ test('validBefore and validAfter', () => {
233
+ const transaction_validity = TxEnvelopeTempo.from({
234
+ ...transaction,
235
+ validBefore: 1000000,
236
+ validAfter: 500000,
237
+ })
238
+ const serialized = TxEnvelopeTempo.serialize(transaction_validity)
239
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
240
+ transaction_validity,
241
+ )
242
+ })
243
+
244
+ test('feeToken', () => {
245
+ const transaction_feeToken = TxEnvelopeTempo.from({
246
+ ...transaction,
247
+ feeToken: '0x20c0000000000000000000000000000000000000',
248
+ })
249
+ const serialized = TxEnvelopeTempo.serialize(transaction_feeToken)
250
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
251
+ transaction_feeToken,
252
+ )
253
+ })
254
+
255
+ test('keyAuthorization', () => {
256
+ const keyAuthorization = KeyAuthorization.from({
257
+ expiry: 1234567890,
258
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
259
+ type: 'secp256k1',
260
+ limits: [
261
+ {
262
+ token: '0x20c0000000000000000000000000000000000001',
263
+ limit: Value.from('10', 6),
264
+ },
265
+ ],
266
+ signature: SignatureEnvelope.from({
267
+ r: 44944627813007772897391531230081695102703289123332187696115181104739239197517n,
268
+ s: 36528503505192438307355164441104001310566505351980369085208178712678799181120n,
269
+ yParity: 0,
270
+ }),
271
+ })
272
+
273
+ const transaction_keyAuthorization = TxEnvelopeTempo.from({
274
+ ...transaction,
275
+ keyAuthorization,
276
+ })
277
+
278
+ const serialized = TxEnvelopeTempo.serialize(transaction_keyAuthorization)
279
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
280
+ transaction_keyAuthorization,
281
+ )
282
+ })
283
+
284
+ test('authorizationList', () => {
285
+ const authorizationList = [
286
+ AuthorizationTempo.from({
287
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
288
+ chainId: 1,
289
+ nonce: 40n,
290
+ signature: SignatureEnvelope.from({
291
+ r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
292
+ s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
293
+ yParity: 0,
294
+ }),
295
+ }),
296
+ AuthorizationTempo.from({
297
+ address: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
298
+ chainId: 1,
299
+ nonce: 55n,
300
+ signature: SignatureEnvelope.from({
301
+ r: 12345678901234567890n,
302
+ s: 98765432109876543210n,
303
+ yParity: 1,
304
+ }),
305
+ }),
306
+ ] as const
307
+
308
+ const transaction_authorizationList = TxEnvelopeTempo.from({
309
+ ...transaction,
310
+ authorizationList,
311
+ })
312
+
313
+ const serialized = TxEnvelopeTempo.serialize(transaction_authorizationList)
314
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
315
+ transaction_authorizationList,
316
+ )
317
+ })
318
+
319
+ test('authorizationList (empty)', () => {
320
+ const transaction_authorizationList = TxEnvelopeTempo.from({
321
+ ...transaction,
322
+ authorizationList: [],
323
+ })
324
+
325
+ const serialized = TxEnvelopeTempo.serialize(transaction_authorizationList)
326
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
327
+
328
+ // Empty authorizationList should be undefined after deserialization
329
+ expect(deserialized.authorizationList).toBeUndefined()
330
+ })
331
+
332
+ describe('signature', () => {
333
+ test('secp256k1', () => {
334
+ const signature = Secp256k1.sign({
335
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
336
+ privateKey,
337
+ })
338
+ const serialized = TxEnvelopeTempo.serialize(transaction, {
339
+ signature,
340
+ })
341
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual({
342
+ ...transaction,
343
+ signature: { signature, type: 'secp256k1' },
344
+ })
345
+ })
346
+
347
+ test('secp256k1', () => {
348
+ const signature = Secp256k1.sign({
349
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
350
+ privateKey,
351
+ })
352
+ const serialized = TxEnvelopeTempo.serialize(transaction, {
353
+ signature: SignatureEnvelope.from(signature),
354
+ })
355
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual({
356
+ ...transaction,
357
+ signature: { signature, type: 'secp256k1' },
358
+ })
359
+ })
360
+
361
+ test('p256', () => {
362
+ const privateKey = P256.randomPrivateKey()
363
+ const publicKey = P256.getPublicKey({ privateKey })
364
+ const signature = P256.sign({
365
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
366
+ privateKey,
367
+ })
368
+ const serialized = TxEnvelopeTempo.serialize(transaction, {
369
+ signature: SignatureEnvelope.from({
370
+ signature,
371
+ publicKey,
372
+ prehash: true,
373
+ }),
374
+ })
375
+ // biome-ignore lint/suspicious/noTsIgnore: _
376
+ // @ts-ignore
377
+ delete signature.yParity
378
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual({
379
+ ...transaction,
380
+ signature: { prehash: true, publicKey, signature, type: 'p256' },
381
+ })
382
+ })
383
+ })
384
+
385
+ test('feePayerSignature null', () => {
386
+ const transaction_feePayer = TxEnvelopeTempo.from({
387
+ ...transaction,
388
+ feePayerSignature: null,
389
+ })
390
+ const serialized = TxEnvelopeTempo.serialize(transaction_feePayer)
391
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual(
392
+ transaction_feePayer,
393
+ )
394
+ })
395
+
396
+ test('feePayerSignature with address', () => {
397
+ const serialized = `0x76${Rlp.fromHex([
398
+ Hex.fromNumber(1), // chainId
399
+ Hex.fromNumber(1), // maxPriorityFeePerGas
400
+ Hex.fromNumber(1), // maxFeePerGas
401
+ Hex.fromNumber(1), // gas
402
+ [
403
+ [
404
+ '0x0000000000000000000000000000000000000000', // to
405
+ Hex.fromNumber(0), // value
406
+ '0x', // data
407
+ ],
408
+ ], // calls
409
+ '0x', // accessList
410
+ Hex.fromNumber(0), // nonceKey
411
+ Hex.fromNumber(0), // nonce
412
+ '0x', // validBefore
413
+ '0x', // validAfter
414
+ '0x', // feeToken
415
+ '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // feePayerSignatureOrSender (address)
416
+ [], // authorizationList
417
+ ]).slice(2)}` as const
418
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
419
+ expect(deserialized.feePayerSignature).toBe(null)
420
+ })
421
+
422
+ test('feePayerSignature with signature tuple', () => {
423
+ const serialized = `0x76${Rlp.fromHex([
424
+ Hex.fromNumber(1), // chainId
425
+ Hex.fromNumber(1), // maxPriorityFeePerGas
426
+ Hex.fromNumber(1), // maxFeePerGas
427
+ Hex.fromNumber(1), // gas
428
+ [
429
+ [
430
+ '0x0000000000000000000000000000000000000000', // to
431
+ Hex.fromNumber(0), // value
432
+ '0x', // data
433
+ ],
434
+ ], // calls
435
+ '0x', // accessList
436
+ Hex.fromNumber(0), // nonceKey
437
+ Hex.fromNumber(0), // nonce
438
+ '0x', // validBefore
439
+ '0x', // validAfter
440
+ '0x', // feeToken
441
+ [Hex.fromNumber(0), Hex.fromNumber(1), Hex.fromNumber(2)], // feePayerSignatureOrSender (signature tuple)
442
+ [], // authorizationList
443
+ ]).slice(2)}` as const
444
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
445
+ expect(deserialized.feePayerSignature).toEqual({
446
+ yParity: 0,
447
+ r: 1n,
448
+ s: 2n,
449
+ })
450
+ })
451
+
452
+ describe('raw', () => {
453
+ test('default', () => {
454
+ const serialized = `0x76${Rlp.fromHex([
455
+ Hex.fromNumber(1), // chainId
456
+ Hex.fromNumber(1), // maxPriorityFeePerGas
457
+ Hex.fromNumber(1), // maxFeePerGas
458
+ Hex.fromNumber(1), // gas
459
+ [
460
+ [
461
+ '0x0000000000000000000000000000000000000000', // to
462
+ Hex.fromNumber(0), // value
463
+ '0x', // data
464
+ ],
465
+ ], // calls
466
+ '0x', // accessList
467
+ Hex.fromNumber(0), // nonceKey
468
+ Hex.fromNumber(0), // nonce
469
+ '0x', // validBefore
470
+ '0x', // validAfter
471
+ '0x', // feeToken
472
+ '0x', // feePayerSignature
473
+ [], // authorizationList
474
+ ]).slice(2)}` as const
475
+ expect(TxEnvelopeTempo.deserialize(serialized)).toMatchInlineSnapshot(`
476
+ {
477
+ "calls": [
478
+ {
479
+ "to": "0x0000000000000000000000000000000000000000",
480
+ "value": 0n,
481
+ },
482
+ ],
483
+ "chainId": 1,
484
+ "gas": 1n,
485
+ "maxFeePerGas": 1n,
486
+ "maxPriorityFeePerGas": 1n,
487
+ "nonce": 0n,
488
+ "nonceKey": 0n,
489
+ "type": "tempo",
490
+ }
491
+ `)
492
+ })
493
+
494
+ test('empty sig', () => {
495
+ const serialized = `0x76${Rlp.fromHex([
496
+ Hex.fromNumber(1), // chainId
497
+ Hex.fromNumber(1), // maxPriorityFeePerGas
498
+ Hex.fromNumber(1), // maxFeePerGas
499
+ Hex.fromNumber(1), // gas
500
+ [
501
+ [
502
+ '0x0000000000000000000000000000000000000000', // to
503
+ Hex.fromNumber(0), // value
504
+ '0x', // data
505
+ ],
506
+ ], // calls
507
+ '0x', // accessList
508
+ Hex.fromNumber(0), // nonceKey
509
+ Hex.fromNumber(0), // nonce
510
+ '0x', // validBefore
511
+ '0x', // validAfter
512
+ '0x', // feeToken
513
+ '0x', // feePayerSignature
514
+ [], // authorizationList
515
+ ]).slice(2)}` as const
516
+ expect(TxEnvelopeTempo.deserialize(serialized)).toMatchInlineSnapshot(`
517
+ {
518
+ "calls": [
519
+ {
520
+ "to": "0x0000000000000000000000000000000000000000",
521
+ "value": 0n,
522
+ },
523
+ ],
524
+ "chainId": 1,
525
+ "gas": 1n,
526
+ "maxFeePerGas": 1n,
527
+ "maxPriorityFeePerGas": 1n,
528
+ "nonce": 0n,
529
+ "nonceKey": 0n,
530
+ "type": "tempo",
531
+ }
532
+ `)
533
+ })
534
+ })
535
+
536
+ describe('errors', () => {
537
+ test('invalid transaction (all missing)', () => {
538
+ expect(() =>
539
+ TxEnvelopeTempo.deserialize(`0x76${Rlp.fromHex([]).slice(2)}`),
540
+ ).toThrowErrorMatchingInlineSnapshot(`
541
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "tempo" was provided.
542
+
543
+ Serialized Transaction: "0x76c0"
544
+ Missing Attributes: authorizationList, chainId, maxPriorityFeePerGas, maxFeePerGas, gas, calls, accessList, keyAuthorization, nonceKey, nonce, validBefore, validAfter, feeToken, feePayerSignatureOrSender]
545
+ `)
546
+ })
547
+
548
+ test('invalid transaction (some missing)', () => {
549
+ expect(() =>
550
+ TxEnvelopeTempo.deserialize(
551
+ `0x76${Rlp.fromHex(['0x00', '0x01']).slice(2)}`,
552
+ ),
553
+ ).toThrowErrorMatchingInlineSnapshot(`
554
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "tempo" was provided.
555
+
556
+ Serialized Transaction: "0x76c20001"
557
+ Missing Attributes: authorizationList, maxFeePerGas, gas, calls, accessList, keyAuthorization, nonceKey, nonce, validBefore, validAfter, feeToken, feePayerSignatureOrSender]
558
+ `)
559
+ })
560
+
561
+ test('invalid transaction (empty calls)', () => {
562
+ expect(() =>
563
+ TxEnvelopeTempo.deserialize(
564
+ `0x76${Rlp.fromHex([
565
+ Hex.fromNumber(1), // chainId
566
+ Hex.fromNumber(1), // maxPriorityFeePerGas
567
+ Hex.fromNumber(1), // maxFeePerGas
568
+ Hex.fromNumber(1), // gas
569
+ [], // calls (empty)
570
+ '0x', // accessList
571
+ Hex.fromNumber(0), // nonceKey
572
+ Hex.fromNumber(0), // nonce
573
+ '0x', // validBefore
574
+ '0x', // validAfter
575
+ '0x', // feeToken
576
+ '0x', // feePayerSignature
577
+ [], // authorizationList
578
+ ]).slice(2)}`,
579
+ ),
580
+ ).toThrowErrorMatchingInlineSnapshot(
581
+ `[TxEnvelopeTempo.CallsEmptyError: Calls list cannot be empty.]`,
582
+ )
583
+ })
584
+
585
+ test('invalid transaction (too many fields with signature)', () => {
586
+ expect(() =>
587
+ TxEnvelopeTempo.deserialize(
588
+ `0x76${Rlp.fromHex([
589
+ Hex.fromNumber(1), // chainId
590
+ Hex.fromNumber(1), // maxPriorityFeePerGas
591
+ Hex.fromNumber(1), // maxFeePerGas
592
+ Hex.fromNumber(1), // gas
593
+ [
594
+ [
595
+ '0x0000000000000000000000000000000000000000',
596
+ Hex.fromNumber(0),
597
+ '0x',
598
+ ],
599
+ ], // calls
600
+ '0x', // accessList
601
+ Hex.fromNumber(0), // nonceKey
602
+ Hex.fromNumber(0), // nonce
603
+ '0x', // validBefore
604
+ '0x', // validAfter
605
+ '0x', // feeToken
606
+ '0x', // feePayerSignature
607
+ [], // authorizationList
608
+ [], // keyAuthorization
609
+ '0x1234', // signature
610
+ '0x5678', // extra field
611
+ ]).slice(2)}`,
612
+ ),
613
+ ).toThrowErrorMatchingInlineSnapshot(`
614
+ [TransactionEnvelope.InvalidSerializedError: Invalid serialized transaction of type "tempo" was provided.
615
+
616
+ Serialized Transaction: "0x76ec01010101d8d7940000000000000000000000000000000000000000008080000080808080c0c0821234825678"]
617
+ `)
618
+ })
619
+ })
620
+ })
621
+
622
+ describe('from', () => {
623
+ test('default', () => {
624
+ {
625
+ const envelope = TxEnvelopeTempo.from({
626
+ chainId: 1,
627
+ calls: [{}],
628
+ nonce: 0n,
629
+ nonceKey: 0n,
630
+ })
631
+ expect(envelope).toMatchInlineSnapshot(`
632
+ {
633
+ "calls": [
634
+ {},
635
+ ],
636
+ "chainId": 1,
637
+ "nonce": 0n,
638
+ "nonceKey": 0n,
639
+ "type": "tempo",
640
+ }
641
+ `)
642
+ const serialized = TxEnvelopeTempo.serialize(envelope)
643
+ const envelope2 = TxEnvelopeTempo.from(serialized)
644
+ expect(envelope2).toEqual(envelope)
645
+ }
646
+
647
+ {
648
+ const envelope = TxEnvelopeTempo.from({
649
+ chainId: 1,
650
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
651
+ nonce: 0n,
652
+ nonceKey: 0n,
653
+ signature: SignatureEnvelope.from({
654
+ r: 0n,
655
+ s: 1n,
656
+ yParity: 0,
657
+ }),
658
+ })
659
+ expect(envelope).toMatchInlineSnapshot(`
660
+ {
661
+ "calls": [
662
+ {
663
+ "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
664
+ },
665
+ ],
666
+ "chainId": 1,
667
+ "nonce": 0n,
668
+ "nonceKey": 0n,
669
+ "signature": {
670
+ "signature": {
671
+ "r": 0n,
672
+ "s": 1n,
673
+ "yParity": 0,
674
+ },
675
+ "type": "secp256k1",
676
+ },
677
+ "type": "tempo",
678
+ }
679
+ `)
680
+ const serialized = TxEnvelopeTempo.serialize(envelope)
681
+ const envelope2 = TxEnvelopeTempo.from(serialized)
682
+ expect(envelope2).toEqual({
683
+ ...envelope,
684
+ signature: { ...envelope.signature, type: 'secp256k1' },
685
+ })
686
+ }
687
+ })
688
+
689
+ test('options: signature', () => {
690
+ const envelope = TxEnvelopeTempo.from(
691
+ {
692
+ chainId: 1,
693
+ calls: [{}],
694
+ nonce: 0n,
695
+ nonceKey: 0n,
696
+ },
697
+ {
698
+ signature: SignatureEnvelope.from({
699
+ r: 0n,
700
+ s: 1n,
701
+ yParity: 0,
702
+ }),
703
+ },
704
+ )
705
+ expect(envelope).toMatchInlineSnapshot(`
706
+ {
707
+ "calls": [
708
+ {},
709
+ ],
710
+ "chainId": 1,
711
+ "nonce": 0n,
712
+ "nonceKey": 0n,
713
+ "signature": {
714
+ "signature": {
715
+ "r": 0n,
716
+ "s": 1n,
717
+ "yParity": 0,
718
+ },
719
+ "type": "secp256k1",
720
+ },
721
+ "type": "tempo",
722
+ }
723
+ `)
724
+ const serialized = TxEnvelopeTempo.serialize(envelope)
725
+ const envelope2 = TxEnvelopeTempo.from(serialized)
726
+ expect(envelope2).toEqual(envelope)
727
+ })
728
+
729
+ test('options: signature', () => {
730
+ const envelope = TxEnvelopeTempo.from(
731
+ {
732
+ chainId: 1,
733
+ calls: [{}],
734
+ nonce: 0n,
735
+ nonceKey: 0n,
736
+ },
737
+ {
738
+ signature: { r: 0n, s: 1n, yParity: 0 },
739
+ },
740
+ )
741
+ expect(envelope).toMatchInlineSnapshot(`
742
+ {
743
+ "calls": [
744
+ {},
745
+ ],
746
+ "chainId": 1,
747
+ "nonce": 0n,
748
+ "nonceKey": 0n,
749
+ "signature": {
750
+ "signature": {
751
+ "r": 0n,
752
+ "s": 1n,
753
+ "yParity": 0,
754
+ },
755
+ "type": "secp256k1",
756
+ },
757
+ "type": "tempo",
758
+ }
759
+ `)
760
+ const serialized = TxEnvelopeTempo.serialize(envelope)
761
+ const envelope2 = TxEnvelopeTempo.from(serialized)
762
+ expect(envelope2).toEqual(envelope)
763
+ })
764
+
765
+ test('options: feePayerSignature', () => {
766
+ const envelope = TxEnvelopeTempo.from(
767
+ {
768
+ chainId: 1,
769
+ calls: [{}],
770
+ nonce: 0n,
771
+ r: 1n,
772
+ s: 2n,
773
+ yParity: 0,
774
+ },
775
+ {
776
+ feePayerSignature: {
777
+ r: 0n,
778
+ s: 1n,
779
+ yParity: 0,
780
+ },
781
+ },
782
+ )
783
+ expect(envelope).toMatchInlineSnapshot(`
784
+ {
785
+ "calls": [
786
+ {},
787
+ ],
788
+ "chainId": 1,
789
+ "feePayerSignature": {
790
+ "r": 0n,
791
+ "s": 1n,
792
+ "yParity": 0,
793
+ },
794
+ "nonce": 0n,
795
+ "r": 1n,
796
+ "s": 2n,
797
+ "type": "tempo",
798
+ "yParity": 0,
799
+ }
800
+ `)
801
+ })
802
+
803
+ test('options: feePayerSignature (null)', () => {
804
+ const envelope = TxEnvelopeTempo.from(
805
+ {
806
+ chainId: 1,
807
+ calls: [{}],
808
+ nonce: 0n,
809
+ },
810
+ {
811
+ feePayerSignature: null,
812
+ },
813
+ )
814
+ expect(envelope).toMatchInlineSnapshot(`
815
+ {
816
+ "calls": [
817
+ {},
818
+ ],
819
+ "chainId": 1,
820
+ "nonce": 0n,
821
+ "type": "tempo",
822
+ }
823
+ `)
824
+ })
825
+ })
826
+
827
+ describe('serialize', () => {
828
+ test('default', () => {
829
+ const transaction = TxEnvelopeTempo.from({
830
+ chainId: 1,
831
+ calls: [
832
+ {
833
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
834
+ },
835
+ ],
836
+ nonce: 785n,
837
+ maxFeePerGas: Value.fromGwei('2'),
838
+ maxPriorityFeePerGas: Value.fromGwei('2'),
839
+ })
840
+ expect(TxEnvelopeTempo.serialize(transaction)).toMatchInlineSnapshot(
841
+ `"0x76ef018477359400847735940080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c08082031180808080c0"`,
842
+ )
843
+ })
844
+
845
+ test('minimal', () => {
846
+ const transaction = TxEnvelopeTempo.from({
847
+ chainId: 1,
848
+ calls: [{}],
849
+ nonce: 0n,
850
+ })
851
+ expect(TxEnvelopeTempo.serialize(transaction)).toMatchInlineSnapshot(
852
+ `"0x76d101808080c4c3808080c0808080808080c0"`,
853
+ )
854
+ })
855
+
856
+ test('undefined nonceKey', () => {
857
+ const transaction = TxEnvelopeTempo.from({
858
+ chainId: 1,
859
+ calls: [{}],
860
+ nonce: 0n,
861
+ nonceKey: undefined,
862
+ })
863
+ const serialized = TxEnvelopeTempo.serialize(transaction)
864
+ expect(serialized).toMatchInlineSnapshot(
865
+ `"0x76d101808080c4c3808080c0808080808080c0"`,
866
+ )
867
+ })
868
+
869
+ test('multiple calls', () => {
870
+ const transaction = TxEnvelopeTempo.from({
871
+ chainId: 1,
872
+ calls: [
873
+ {
874
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
875
+ },
876
+ {
877
+ to: '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc',
878
+ value: Value.from('0.002', 6),
879
+ data: '0x1234',
880
+ },
881
+ ],
882
+ nonce: 0n,
883
+ })
884
+ expect(TxEnvelopeTempo.serialize(transaction)).toMatchInlineSnapshot(
885
+ `"0x76f84101808080f4d79470997970c51812dc3a010c7d01b50e0d17dc79c88080db943c44cdddb6a900fa2b585dd299e03d12fa4293bc8207d0821234c0808080808080c0"`,
886
+ )
887
+ })
888
+
889
+ test('keyAuthorization (secp256k1)', () => {
890
+ const keyAuthorization = KeyAuthorization.from({
891
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
892
+ expiry: 1234567890,
893
+ type: 'secp256k1',
894
+ limits: [
895
+ {
896
+ token: '0x20c0000000000000000000000000000000000001',
897
+ limit: Value.from('10', 6),
898
+ },
899
+ ],
900
+ signature: SignatureEnvelope.from({
901
+ r: 44944627813007772897391531230081695102703289123332187696115181104739239197517n,
902
+ s: 36528503505192438307355164441104001310566505351980369085208178712678799181120n,
903
+ yParity: 0,
904
+ }),
905
+ })
906
+
907
+ const transaction = TxEnvelopeTempo.from({
908
+ chainId: 1,
909
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
910
+ nonce: 0n,
911
+ keyAuthorization,
912
+ })
913
+
914
+ const serialized = TxEnvelopeTempo.serialize(transaction)
915
+ expect(serialized).toMatchInlineSnapshot(
916
+ `"0x76f8a201808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f87bf7808094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b841635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f05401b"`,
917
+ )
918
+
919
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
920
+ expect(deserialized.keyAuthorization).toEqual(keyAuthorization)
921
+ })
922
+
923
+ test('keyAuthorization (p256)', () => {
924
+ const keyAuthorization = KeyAuthorization.from({
925
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
926
+ expiry: 1234567890,
927
+ type: 'p256',
928
+ limits: [
929
+ {
930
+ token: '0x20c0000000000000000000000000000000000001',
931
+ limit: Value.from('10', 6),
932
+ },
933
+ ],
934
+ signature: SignatureEnvelope.from({
935
+ signature: {
936
+ r: 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
937
+ s: 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
938
+ },
939
+ publicKey: {
940
+ prefix: 4,
941
+ x: 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
942
+ y: 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
943
+ },
944
+ prehash: true,
945
+ }),
946
+ })
947
+
948
+ const transaction = TxEnvelopeTempo.from({
949
+ chainId: 1,
950
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
951
+ nonce: 0n,
952
+ keyAuthorization,
953
+ })
954
+
955
+ const serialized = TxEnvelopeTempo.serialize(transaction)
956
+ expect(serialized).toMatchInlineSnapshot(
957
+ `"0x76f8e301808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f8bcf7800194be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b88201ccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba001"`,
958
+ )
959
+
960
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
961
+ expect(deserialized.keyAuthorization).toEqual(keyAuthorization)
962
+ })
963
+
964
+ test('keyAuthorization (webAuthn)', () => {
965
+ const metadata = {
966
+ authenticatorData: WebAuthnP256.getAuthenticatorData({
967
+ rpId: 'localhost',
968
+ }),
969
+ clientDataJSON: WebAuthnP256.getClientDataJSON({
970
+ challenge: '0xdeadbeef',
971
+ origin: 'http://localhost',
972
+ }),
973
+ }
974
+
975
+ const keyAuthorization = KeyAuthorization.from({
976
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
977
+ expiry: 1234567890,
978
+ type: 'webAuthn',
979
+ limits: [
980
+ {
981
+ token: '0x20c0000000000000000000000000000000000001',
982
+ limit: Value.from('10', 6),
983
+ },
984
+ ],
985
+ signature: SignatureEnvelope.from({
986
+ signature: {
987
+ r: 92602584010956101470289867944347135737570451066466093224269890121909314569518n,
988
+ s: 54171125190222965779385658110416711469231271457324878825831748147306957269813n,
989
+ },
990
+ publicKey: {
991
+ prefix: 4,
992
+ x: 78495282704852028275327922540131762143565388050940484317945369745559774511861n,
993
+ y: 8109764566587999957624872393871720746996669263962991155166704261108473113504n,
994
+ },
995
+ metadata,
996
+ }),
997
+ })
998
+
999
+ const transaction = TxEnvelopeTempo.from({
1000
+ chainId: 1,
1001
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1002
+ nonce: 0n,
1003
+ keyAuthorization,
1004
+ })
1005
+
1006
+ const serialized = TxEnvelopeTempo.serialize(transaction)
1007
+ expect(serialized).toMatchInlineSnapshot(
1008
+ `"0x76f9016501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f9013df7800294be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b901020249960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a223371322d3777222c226f726967696e223a22687474703a2f2f6c6f63616c686f7374222c2263726f73734f726967696e223a66616c73657dccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba0"`,
1009
+ )
1010
+
1011
+ // Verify roundtrip
1012
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
1013
+ expect(deserialized.keyAuthorization).toEqual(keyAuthorization)
1014
+ })
1015
+
1016
+ test('authorizationList (secp256k1)', () => {
1017
+ const authorizationList = [
1018
+ AuthorizationTempo.from({
1019
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
1020
+ chainId: 1,
1021
+ nonce: 40n,
1022
+ signature: SignatureEnvelope.from({
1023
+ r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
1024
+ s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
1025
+ yParity: 0,
1026
+ }),
1027
+ }),
1028
+ AuthorizationTempo.from({
1029
+ address: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1030
+ chainId: 1,
1031
+ nonce: 55n,
1032
+ signature: SignatureEnvelope.from({
1033
+ r: 12345678901234567890n,
1034
+ s: 98765432109876543210n,
1035
+ yParity: 1,
1036
+ }),
1037
+ }),
1038
+ ] as const
1039
+
1040
+ const transaction = TxEnvelopeTempo.from({
1041
+ chainId: 1,
1042
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1043
+ nonce: 0n,
1044
+ authorizationList,
1045
+ })
1046
+
1047
+ const serialized = TxEnvelopeTempo.serialize(transaction)
1048
+ expect(serialized).toMatchInlineSnapshot(
1049
+ `"0x76f8de01808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080f8b8f85a0194be95c3f554e9fc85ec51be69a3d807a0d55bcf2c28b8416e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81bf85a019470997970c51812dc3a010c7d01b50e0d17dc79c837b841000000000000000000000000000000000000000000000000ab54a98ceb1f0ad20000000000000000000000000000000000000000000000055aa54d38e5267eea1c"`,
1050
+ )
1051
+
1052
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
1053
+ expect(deserialized.authorizationList).toEqual(authorizationList)
1054
+ })
1055
+
1056
+ test('authorizationList (multiple types)', () => {
1057
+ const privateKey = P256.randomPrivateKey()
1058
+ const publicKey = P256.getPublicKey({ privateKey })
1059
+
1060
+ const authorization1 = AuthorizationTempo.from({
1061
+ address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
1062
+ chainId: 1,
1063
+ nonce: 40n,
1064
+ })
1065
+ const secp256k1Signature = Secp256k1.sign({
1066
+ payload: AuthorizationTempo.getSignPayload(authorization1),
1067
+ privateKey,
1068
+ })
1069
+
1070
+ const authorization2 = AuthorizationTempo.from({
1071
+ address: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1072
+ chainId: 1,
1073
+ nonce: 55n,
1074
+ })
1075
+ const p256Signature = P256.sign({
1076
+ payload: AuthorizationTempo.getSignPayload(authorization2),
1077
+ privateKey,
1078
+ })
1079
+
1080
+ const authorizationList = [
1081
+ AuthorizationTempo.from(authorization1, {
1082
+ signature: SignatureEnvelope.from({ signature: secp256k1Signature }),
1083
+ }),
1084
+ AuthorizationTempo.from(authorization2, {
1085
+ signature: SignatureEnvelope.from({
1086
+ signature: p256Signature,
1087
+ publicKey,
1088
+ prehash: true,
1089
+ }),
1090
+ }),
1091
+ ]
1092
+
1093
+ const transaction = TxEnvelopeTempo.from({
1094
+ chainId: 1,
1095
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1096
+ nonce: 0n,
1097
+ authorizationList,
1098
+ })
1099
+
1100
+ const serialized = TxEnvelopeTempo.serialize(transaction)
1101
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
1102
+
1103
+ expect(deserialized.authorizationList).toHaveLength(2)
1104
+ expect(deserialized.authorizationList?.[0]?.address).toBe(
1105
+ '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
1106
+ )
1107
+ expect(deserialized.authorizationList?.[0]?.signature?.type).toBe(
1108
+ 'secp256k1',
1109
+ )
1110
+ expect(deserialized.authorizationList?.[1]?.address).toBe(
1111
+ '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1112
+ )
1113
+ expect(deserialized.authorizationList?.[1]?.signature?.type).toBe('p256')
1114
+ })
1115
+
1116
+ test('authorizationList (empty)', () => {
1117
+ const transaction = TxEnvelopeTempo.from({
1118
+ chainId: 1,
1119
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1120
+ nonce: 0n,
1121
+ authorizationList: [],
1122
+ })
1123
+
1124
+ const serialized = TxEnvelopeTempo.serialize(transaction)
1125
+ expect(serialized).toMatchInlineSnapshot(
1126
+ `"0x76e501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0"`,
1127
+ )
1128
+
1129
+ const deserialized = TxEnvelopeTempo.deserialize(serialized)
1130
+ expect(deserialized.authorizationList).toBeUndefined()
1131
+ })
1132
+
1133
+ describe('with signature', () => {
1134
+ test('secp256k1', () => {
1135
+ const transaction = TxEnvelopeTempo.from({
1136
+ chainId: 1,
1137
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1138
+ nonce: 0n,
1139
+ })
1140
+ const signature = Secp256k1.sign({
1141
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1142
+ privateKey,
1143
+ })
1144
+ expect(
1145
+ TxEnvelopeTempo.serialize(transaction, {
1146
+ signature: SignatureEnvelope.from(signature),
1147
+ }),
1148
+ ).toMatchInlineSnapshot(
1149
+ `"0x76f86801808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0b8416b37e17bf41d92dfee5ffdce55431bf01dd7875b2229d6258350c5ee6fe6a54225b867dc1b19c9ec97833ebdccd830d2846c5b724b72dcd754d694d08b5e80ee1c"`,
1150
+ )
1151
+ })
1152
+
1153
+ test('p256', () => {
1154
+ const transaction = TxEnvelopeTempo.from({
1155
+ chainId: 1,
1156
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1157
+ nonce: 0n,
1158
+ })
1159
+ const privateKey = P256.randomPrivateKey()
1160
+ const publicKey = P256.getPublicKey({ privateKey })
1161
+ const signature = P256.sign({
1162
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1163
+ privateKey,
1164
+ })
1165
+ const serialized = TxEnvelopeTempo.serialize(transaction, {
1166
+ signature: SignatureEnvelope.from({
1167
+ signature,
1168
+ publicKey,
1169
+ prehash: true,
1170
+ }),
1171
+ })
1172
+ // biome-ignore lint/suspicious/noTsIgnore: _
1173
+ // @ts-ignore
1174
+ delete signature.yParity
1175
+ expect(TxEnvelopeTempo.deserialize(serialized)).toEqual({
1176
+ ...transaction,
1177
+ nonceKey: 0n,
1178
+ signature: { prehash: true, publicKey, signature, type: 'p256' },
1179
+ })
1180
+ })
1181
+ })
1182
+
1183
+ test('with feePayerSignature', () => {
1184
+ const transaction = TxEnvelopeTempo.from({
1185
+ chainId: 1,
1186
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1187
+ nonce: 0n,
1188
+ })
1189
+ expect(
1190
+ TxEnvelopeTempo.serialize(transaction, {
1191
+ feePayerSignature: {
1192
+ r: 1n,
1193
+ s: 2n,
1194
+ yParity: 0,
1195
+ },
1196
+ }),
1197
+ ).toMatchInlineSnapshot(
1198
+ `"0x76e801808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c08080808080c3800102c0"`,
1199
+ )
1200
+ })
1201
+
1202
+ test('with feePayerSignature (null)', () => {
1203
+ const transaction = TxEnvelopeTempo.from({
1204
+ chainId: 1,
1205
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1206
+ nonce: 0n,
1207
+ })
1208
+ expect(
1209
+ TxEnvelopeTempo.serialize(transaction, {
1210
+ feePayerSignature: null,
1211
+ }),
1212
+ ).toMatchInlineSnapshot(
1213
+ `"0x76e501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808000c0"`,
1214
+ )
1215
+ })
1216
+
1217
+ test('format: feePayer', () => {
1218
+ const transaction = TxEnvelopeTempo.from({
1219
+ chainId: 1,
1220
+ calls: [{ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' }],
1221
+ nonce: 0n,
1222
+ })
1223
+ expect(
1224
+ TxEnvelopeTempo.serialize(transaction, {
1225
+ sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1226
+ format: 'feePayer',
1227
+ }),
1228
+ ).toMatchInlineSnapshot(
1229
+ `"0x78f83901808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808094f39fd6e51aad88f6f4ce6ab8827279cfffb92266c0"`,
1230
+ )
1231
+ })
1232
+ })
1233
+
1234
+ describe('hash', () => {
1235
+ describe('default', () => {
1236
+ test('secp256k1', () => {
1237
+ const transaction = TxEnvelopeTempo.from({
1238
+ chainId: 1,
1239
+ calls: [
1240
+ {
1241
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1242
+ },
1243
+ ],
1244
+ nonce: 0n,
1245
+ })
1246
+ const signature = Secp256k1.sign({
1247
+ payload: TxEnvelopeTempo.getSignPayload(transaction),
1248
+ privateKey,
1249
+ })
1250
+ const signed = TxEnvelopeTempo.from(transaction, {
1251
+ signature: SignatureEnvelope.from(signature),
1252
+ })
1253
+ expect(TxEnvelopeTempo.hash(signed)).toMatchInlineSnapshot(
1254
+ `"0x04ad27d1607bc3fc37445724d8864b0843f88008bafd818814474e5ee94647eb"`,
1255
+ )
1256
+ })
1257
+ })
1258
+
1259
+ test('presign', () => {
1260
+ const transaction = TxEnvelopeTempo.from({
1261
+ chainId: 1,
1262
+ calls: [
1263
+ {
1264
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1265
+ },
1266
+ ],
1267
+ nonce: 0n,
1268
+ })
1269
+ expect(
1270
+ TxEnvelopeTempo.hash(transaction, { presign: true }),
1271
+ ).toMatchInlineSnapshot(
1272
+ `"0xe1222a45806457acbe3a13940aae4c34f3180659fa16613b5a45dc183adae07c"`,
1273
+ )
1274
+ })
1275
+ })
1276
+
1277
+ describe('getSignPayload', () => {
1278
+ test('default', () => {
1279
+ const transaction = TxEnvelopeTempo.from({
1280
+ chainId: 1,
1281
+ calls: [
1282
+ {
1283
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1284
+ },
1285
+ ],
1286
+ nonce: 0n,
1287
+ })
1288
+ expect(TxEnvelopeTempo.getSignPayload(transaction)).toMatchInlineSnapshot(
1289
+ `"0xe1222a45806457acbe3a13940aae4c34f3180659fa16613b5a45dc183adae07c"`,
1290
+ )
1291
+ })
1292
+ })
1293
+
1294
+ describe('getFeePayerSignPayload', () => {
1295
+ test('default', () => {
1296
+ const transaction = TxEnvelopeTempo.from({
1297
+ chainId: 1,
1298
+ calls: [
1299
+ {
1300
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1301
+ },
1302
+ ],
1303
+ nonce: 0n,
1304
+ })
1305
+ expect(
1306
+ TxEnvelopeTempo.getFeePayerSignPayload(transaction, {
1307
+ sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1308
+ }),
1309
+ ).toMatchInlineSnapshot(
1310
+ `"0xde7a88984d766d0f5aac705487b43e68261516d6e7c524698804d4970d39d77d"`,
1311
+ )
1312
+ })
1313
+
1314
+ test('with feeToken', () => {
1315
+ const transaction = TxEnvelopeTempo.from({
1316
+ chainId: 1,
1317
+ calls: [
1318
+ {
1319
+ to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
1320
+ },
1321
+ ],
1322
+ nonce: 0n,
1323
+ feeToken: '0x20c0000000000000000000000000000000000000',
1324
+ })
1325
+ const hash1 = TxEnvelopeTempo.getFeePayerSignPayload(transaction, {
1326
+ sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1327
+ })
1328
+
1329
+ // Change feeToken - hash should be different
1330
+ const transaction2 = TxEnvelopeTempo.from({
1331
+ ...transaction,
1332
+ feeToken: '0x20c0000000000000000000000000000000000001',
1333
+ })
1334
+ const hash2 = TxEnvelopeTempo.getFeePayerSignPayload(transaction2, {
1335
+ sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
1336
+ })
1337
+
1338
+ expect(hash1).not.toBe(hash2)
1339
+ })
1340
+ })
1341
+
1342
+ describe('validate', () => {
1343
+ test('valid', () => {
1344
+ expect(
1345
+ TxEnvelopeTempo.validate({
1346
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
1347
+ chainId: 1,
1348
+ }),
1349
+ ).toBe(true)
1350
+ })
1351
+
1352
+ test('invalid (empty calls)', () => {
1353
+ expect(
1354
+ TxEnvelopeTempo.validate({
1355
+ calls: [],
1356
+ chainId: 1,
1357
+ }),
1358
+ ).toBe(false)
1359
+ })
1360
+
1361
+ test('invalid (validity window)', () => {
1362
+ expect(
1363
+ TxEnvelopeTempo.validate({
1364
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
1365
+ chainId: 1,
1366
+ validBefore: 100,
1367
+ validAfter: 200,
1368
+ }),
1369
+ ).toBe(false)
1370
+ })
1371
+ })