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