ox 1.7.1 → 1.7.4

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 (123) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/core/Hash.js +1 -1
  3. package/dist/core/Hash.js.map +1 -1
  4. package/dist/tempo/KeyAuthorization.d.ts +2 -2
  5. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  6. package/dist/tempo/MultisigConfig.d.ts +151 -83
  7. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  8. package/dist/tempo/MultisigConfig.js +164 -86
  9. package/dist/tempo/MultisigConfig.js.map +1 -1
  10. package/dist/tempo/MultisigOperation.d.ts +340 -0
  11. package/dist/tempo/MultisigOperation.d.ts.map +1 -0
  12. package/dist/tempo/MultisigOperation.js +839 -0
  13. package/dist/tempo/MultisigOperation.js.map +1 -0
  14. package/dist/tempo/MultisigSimulation.d.ts +132 -0
  15. package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
  16. package/dist/tempo/MultisigSimulation.js +140 -0
  17. package/dist/tempo/MultisigSimulation.js.map +1 -0
  18. package/dist/tempo/RpcSchemaTempo.d.ts +45 -0
  19. package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
  20. package/dist/tempo/SignatureEnvelope.d.ts +55 -86
  21. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  22. package/dist/tempo/SignatureEnvelope.js +93 -158
  23. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  24. package/dist/tempo/TransactionRequest.d.ts +9 -17
  25. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  26. package/dist/tempo/TransactionRequest.js +7 -7
  27. package/dist/tempo/TransactionRequest.js.map +1 -1
  28. package/dist/tempo/TxEnvelopeTempo.d.ts +3 -2
  29. package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  30. package/dist/tempo/TxEnvelopeTempo.js +3 -2
  31. package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
  32. package/dist/tempo/index.d.ts +22 -2
  33. package/dist/tempo/index.d.ts.map +1 -1
  34. package/dist/tempo/index.js +22 -2
  35. package/dist/tempo/index.js.map +1 -1
  36. package/dist/zod/tempo/AuthorizationTempo.d.ts +60 -180
  37. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  38. package/dist/zod/tempo/KeyAuthorization.d.ts +65 -118
  39. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  40. package/dist/zod/tempo/MultisigConfig.d.ts +31 -2
  41. package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
  42. package/dist/zod/tempo/MultisigConfig.js +26 -3
  43. package/dist/zod/tempo/MultisigConfig.js.map +1 -1
  44. package/dist/zod/tempo/MultisigOperation.d.ts +158 -0
  45. package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -0
  46. package/dist/zod/tempo/MultisigOperation.js +62 -0
  47. package/dist/zod/tempo/MultisigOperation.js.map +1 -0
  48. package/dist/zod/tempo/MultisigSimulation.d.ts +120 -0
  49. package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
  50. package/dist/zod/tempo/MultisigSimulation.js +78 -0
  51. package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
  52. package/dist/zod/tempo/RpcSchemaTempo.d.ts +528 -144
  53. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  54. package/dist/zod/tempo/RpcSchemaTempo.js +47 -0
  55. package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
  56. package/dist/zod/tempo/SignatureEnvelope.d.ts +18 -54
  57. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  58. package/dist/zod/tempo/SignatureEnvelope.js +5 -22
  59. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  60. package/dist/zod/tempo/Transaction.d.ts +138 -348
  61. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  62. package/dist/zod/tempo/TransactionRequest.d.ts +286 -239
  63. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  64. package/dist/zod/tempo/TransactionRequest.js +11 -22
  65. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  66. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +60 -48
  67. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  68. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +15 -12
  69. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  70. package/dist/zod/tempo/z.d.ts +2 -0
  71. package/dist/zod/tempo/z.d.ts.map +1 -1
  72. package/dist/zod/tempo/z.js +2 -0
  73. package/dist/zod/tempo/z.js.map +1 -1
  74. package/package.json +21 -1
  75. package/src/core/Hash.ts +1 -1
  76. package/src/core/_test/Hash.test.ts +15 -0
  77. package/src/tempo/AuthorizationTempo.test.ts +4 -0
  78. package/src/tempo/KeyAuthorization.test-d.ts +13 -12
  79. package/src/tempo/KeyAuthorization.test.ts +109 -3
  80. package/src/tempo/KeyAuthorization.ts +4 -4
  81. package/src/tempo/MultisigConfig.test-d.ts +52 -0
  82. package/src/tempo/MultisigConfig.test.ts +288 -227
  83. package/src/tempo/MultisigConfig.ts +275 -104
  84. package/src/tempo/MultisigOperation.test-d.ts +79 -0
  85. package/src/tempo/MultisigOperation.test.ts +1950 -0
  86. package/src/tempo/MultisigOperation.ts +1293 -0
  87. package/src/tempo/MultisigSimulation.test-d.ts +33 -0
  88. package/src/tempo/MultisigSimulation.test.ts +283 -0
  89. package/src/tempo/MultisigSimulation.ts +266 -0
  90. package/src/tempo/RpcSchemaTempo.test-d.ts +91 -0
  91. package/src/tempo/RpcSchemaTempo.ts +46 -0
  92. package/src/tempo/SignatureEnvelope.test-d.ts +33 -72
  93. package/src/tempo/SignatureEnvelope.test.ts +533 -500
  94. package/src/tempo/SignatureEnvelope.ts +171 -258
  95. package/src/tempo/Transaction.test.ts +61 -0
  96. package/src/tempo/TransactionRequest.test-d.ts +11 -0
  97. package/src/tempo/TransactionRequest.test.ts +214 -41
  98. package/src/tempo/TransactionRequest.ts +19 -20
  99. package/src/tempo/TxEnvelopeTempo.test-d.ts +7 -0
  100. package/src/tempo/TxEnvelopeTempo.test.ts +25 -0
  101. package/src/tempo/TxEnvelopeTempo.ts +5 -3
  102. package/src/tempo/index.ts +22 -2
  103. package/src/tempo/multisig.e2e.test.ts +164 -87
  104. package/src/version.ts +1 -1
  105. package/src/zod/tempo/MultisigConfig.ts +29 -3
  106. package/src/zod/tempo/MultisigOperation.ts +66 -0
  107. package/src/zod/tempo/MultisigSimulation.ts +98 -0
  108. package/src/zod/tempo/RpcSchemaTempo.ts +53 -0
  109. package/src/zod/tempo/SignatureEnvelope.ts +12 -54
  110. package/src/zod/tempo/TransactionRequest.ts +17 -26
  111. package/src/zod/tempo/_test/KeyAuthorization.test.ts +2 -4
  112. package/src/zod/tempo/_test/MultisigConfig.test-d.ts +13 -0
  113. package/src/zod/tempo/_test/MultisigConfig.test.ts +13 -2
  114. package/src/zod/tempo/_test/MultisigOperation.test-d.ts +27 -0
  115. package/src/zod/tempo/_test/MultisigOperation.test.ts +66 -0
  116. package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
  117. package/src/zod/tempo/_test/MultisigSimulation.test.ts +78 -0
  118. package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +48 -0
  119. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +83 -0
  120. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +1 -1
  121. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +57 -120
  122. package/src/zod/tempo/_test/TransactionRequest.test.ts +43 -16
  123. package/src/zod/tempo/z.ts +2 -0
@@ -1,6 +1,9 @@
1
1
  import { Transaction } from 'ox/tempo'
2
2
  import { describe, expect, test } from 'vp/test'
3
3
 
4
+ const multisigRpcSignature =
5
+ '0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
6
+
4
7
  describe('fromRpc', () => {
5
8
  test('default', () => {
6
9
  const transaction = Transaction.fromRpc({
@@ -512,3 +515,61 @@ describe('toRpc', () => {
512
515
  `)
513
516
  })
514
517
  })
518
+
519
+ describe('roundtrip', () => {
520
+ test('behavior: multisig RPC signature', () => {
521
+ const rpc = {
522
+ accessList: [],
523
+ blockHash:
524
+ '0xc350d807505fb835650f0013632c5515592987ba169bbc6626d9fc54d91f0f0b',
525
+ blockNumber: '0x12f296f',
526
+ calls: [],
527
+ chainId: '0x1',
528
+ feeToken: '0x20c0000000000000000000000000000000000000',
529
+ from: '0x9dba7f426b711d4893c11611eacf7cc334e7146b',
530
+ gas: '0x43f5d',
531
+ hash: '0x353fdfc38a2f26115daadee9f5b8392ce62b84f410957967e2ed56b35338cdd0',
532
+ maxFeePerGas: '0x2',
533
+ maxPriorityFeePerGas: '0x1',
534
+ nonce: '0x357',
535
+ signature: multisigRpcSignature,
536
+ transactionIndex: '0x2',
537
+ type: '0x76',
538
+ } as const satisfies Transaction.TempoRpc
539
+ const transaction = Transaction.fromRpc(rpc)
540
+
541
+ expect({
542
+ rpc: Transaction.toRpc(transaction).signature,
543
+ signature: transaction.signature,
544
+ }).toMatchInlineSnapshot(`
545
+ {
546
+ "rpc": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
547
+ "signature": {
548
+ "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
549
+ "config": {
550
+ "owners": [
551
+ {
552
+ "owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
553
+ "weight": 1,
554
+ },
555
+ ],
556
+ "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
557
+ "threshold": 1,
558
+ "version": 0n,
559
+ },
560
+ "signatures": [
561
+ {
562
+ "signature": {
563
+ "r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
564
+ "s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
565
+ "yParity": 0,
566
+ },
567
+ "type": "secp256k1",
568
+ },
569
+ ],
570
+ "type": "multisig",
571
+ },
572
+ }
573
+ `)
574
+ })
575
+ })
@@ -1,6 +1,7 @@
1
1
  import type * as ox_TransactionRequest from '../core/TransactionRequest.js'
2
2
  import * as Signature from '../core/Signature.js'
3
3
  import { describe, expectTypeOf, test } from 'vp/test'
4
+ import * as MultisigSimulation from './MultisigSimulation.js'
4
5
  import * as SignatureEnvelope from './SignatureEnvelope.js'
5
6
  import * as TransactionRequest from './TransactionRequest.js'
6
7
  import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
@@ -47,6 +48,15 @@ describe('TransactionRequest type', () => {
47
48
  Signature.Rpc | null | undefined
48
49
  >()
49
50
  })
51
+
52
+ test('carries domain and RPC multisig simulations', () => {
53
+ expectTypeOf<
54
+ TransactionRequest.TransactionRequest['multisigSimulation']
55
+ >().toEqualTypeOf<MultisigSimulation.Spec | undefined>()
56
+ expectTypeOf<TransactionRequest.Rpc['multisigSimulation']>().toEqualTypeOf<
57
+ MultisigSimulation.Rpc | undefined
58
+ >()
59
+ })
50
60
  })
51
61
 
52
62
  describe('toEnvelope', () => {
@@ -77,6 +87,7 @@ describe('toEnvelope', () => {
77
87
  keyType: 'secp256k1',
78
88
  maxFeePerGas: 1n,
79
89
  maxPriorityFeePerGas: 1n,
90
+ multisigSimulation: undefined,
80
91
  nonce: 0n,
81
92
  nonceKey: 'random',
82
93
  signature: undefined,
@@ -1,6 +1,77 @@
1
- import { TransactionRequest } from 'ox/tempo'
1
+ import {
2
+ MultisigConfig,
3
+ type MultisigSimulation,
4
+ TransactionRequest,
5
+ } from 'ox/tempo'
2
6
  import { describe, expect, test } from 'vp/test'
3
7
 
8
+ const multisigSimulationRpc = {
9
+ account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
10
+ approvals: [
11
+ {
12
+ keyType: 'secp256k1',
13
+ owner: '0x1111111111111111111111111111111111111111',
14
+ type: 'primitive',
15
+ },
16
+ {
17
+ spec: {
18
+ account: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
19
+ approvals: [
20
+ {
21
+ keyData: '0x0578',
22
+ keyType: 'webAuthn',
23
+ owner: '0x2222222222222222222222222222222222222222',
24
+ },
25
+ ],
26
+ config:
27
+ '0xf83ba022222222222222222222222222222222222222222222222222222222222222220101d7d694222222222222222222222222222222222222222201',
28
+ },
29
+ type: 'multisig',
30
+ },
31
+ ],
32
+ config:
33
+ '0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01',
34
+ } as const satisfies MultisigSimulation.Rpc
35
+
36
+ const multisigSimulation = {
37
+ account: multisigSimulationRpc.account,
38
+ approvals: [
39
+ multisigSimulationRpc.approvals[0],
40
+ {
41
+ spec: {
42
+ account: multisigSimulationRpc.approvals[1].spec.account,
43
+ approvals: multisigSimulationRpc.approvals[1].spec.approvals,
44
+ config: MultisigConfig.from({
45
+ owners: [
46
+ {
47
+ owner: '0x2222222222222222222222222222222222222222',
48
+ weight: 1,
49
+ },
50
+ ],
51
+ salt: `0x${'22'.repeat(32)}`,
52
+ threshold: 1,
53
+ version: 1n,
54
+ }),
55
+ },
56
+ type: 'multisig',
57
+ },
58
+ ],
59
+ config: MultisigConfig.from({
60
+ owners: [
61
+ {
62
+ owner: '0x1111111111111111111111111111111111111111',
63
+ weight: 1,
64
+ },
65
+ {
66
+ owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
67
+ weight: 1,
68
+ },
69
+ ],
70
+ salt: `0x${'11'.repeat(32)}`,
71
+ threshold: 2,
72
+ }),
73
+ } as const satisfies MultisigSimulation.Spec
74
+
4
75
  describe('fromRpc', () => {
5
76
  test('default', () => {
6
77
  const request = TransactionRequest.fromRpc({
@@ -69,7 +140,7 @@ describe('fromRpc', () => {
69
140
  expect(request.nonceKey).toBe(255n)
70
141
  })
71
142
 
72
- test('behavior: gas-model hints and capabilities pass through', () => {
143
+ test('behavior: capabilities and key hints pass through', () => {
73
144
  const request = TransactionRequest.fromRpc({
74
145
  calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
75
146
  capabilities: { balanceDiffs: true },
@@ -77,30 +148,86 @@ describe('fromRpc', () => {
77
148
  keyData: '0x0578',
78
149
  keyId: '0xcccccccccccccccccccccccccccccccccccccccc',
79
150
  keyType: 'webAuthn',
80
- multisigInit: {
81
- salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
82
- threshold: 2,
83
- owners: [
84
- {
85
- owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
86
- weight: 1,
87
- },
151
+ type: '0x76',
152
+ })
153
+ expect(request).toMatchInlineSnapshot(`
154
+ {
155
+ "calls": [
88
156
  {
89
- owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
90
- weight: 1,
157
+ "data": undefined,
158
+ "to": "0xcafebabecafebabecafebabecafebabecafebabe",
91
159
  },
92
160
  ],
93
- },
94
- multisigSignatureCount: 2,
161
+ "capabilities": {
162
+ "balanceDiffs": true,
163
+ },
164
+ "feePayer": true,
165
+ "keyData": "0x0578",
166
+ "keyId": "0xcccccccccccccccccccccccccccccccccccccccc",
167
+ "keyType": "webAuthn",
168
+ "type": "tempo",
169
+ }
170
+ `)
171
+ })
172
+
173
+ test('behavior: multisig simulation', () => {
174
+ const request = TransactionRequest.fromRpc({
175
+ from: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
176
+ multisigSimulation: multisigSimulationRpc,
95
177
  type: '0x76',
96
178
  })
97
- expect(request.capabilities).toEqual({ balanceDiffs: true })
98
- expect(request.feePayer).toBe(true)
99
- expect(request.keyData).toBe('0x0578')
100
- expect(request.keyId).toBe('0xcccccccccccccccccccccccccccccccccccccccc')
101
- expect(request.keyType).toBe('webAuthn')
102
- expect(request.multisigInit).toMatchObject({ threshold: 2 })
103
- expect(request.multisigSignatureCount).toBe(2)
179
+
180
+ expect(request.multisigSimulation).toMatchInlineSnapshot(`
181
+ {
182
+ "account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
183
+ "approvals": [
184
+ {
185
+ "keyType": "secp256k1",
186
+ "owner": "0x1111111111111111111111111111111111111111",
187
+ "type": "primitive",
188
+ },
189
+ {
190
+ "spec": {
191
+ "account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
192
+ "approvals": [
193
+ {
194
+ "keyData": "0x0578",
195
+ "keyType": "webAuthn",
196
+ "owner": "0x2222222222222222222222222222222222222222",
197
+ },
198
+ ],
199
+ "config": {
200
+ "owners": [
201
+ {
202
+ "owner": "0x2222222222222222222222222222222222222222",
203
+ "weight": 1,
204
+ },
205
+ ],
206
+ "salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
207
+ "threshold": 1,
208
+ "version": 1n,
209
+ },
210
+ },
211
+ "type": "multisig",
212
+ },
213
+ ],
214
+ "config": {
215
+ "owners": [
216
+ {
217
+ "owner": "0x1111111111111111111111111111111111111111",
218
+ "weight": 1,
219
+ },
220
+ {
221
+ "owner": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
222
+ "weight": 1,
223
+ },
224
+ ],
225
+ "salt": "0x1111111111111111111111111111111111111111111111111111111111111111",
226
+ "threshold": 2,
227
+ "version": 0n,
228
+ },
229
+ }
230
+ `)
104
231
  })
105
232
 
106
233
  test('behavior: empty', () => {
@@ -157,7 +284,7 @@ describe('toRpc', () => {
157
284
  `)
158
285
  })
159
286
 
160
- test('behavior: gas-model hints and capabilities are carried', () => {
287
+ test('behavior: capabilities and key hints are carried', () => {
161
288
  const request = TransactionRequest.toRpc({
162
289
  calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe' }],
163
290
  capabilities: { balanceDiffs: true },
@@ -165,30 +292,72 @@ describe('toRpc', () => {
165
292
  keyData: '0x0578',
166
293
  keyId: '0xcccccccccccccccccccccccccccccccccccccccc',
167
294
  keyType: 'webAuthn',
168
- multisigInit: {
169
- salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
170
- threshold: 2,
171
- owners: [
295
+ })
296
+ expect(request).toMatchInlineSnapshot(`
297
+ {
298
+ "calls": [
172
299
  {
173
- owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
174
- weight: 1,
300
+ "data": "0x",
301
+ "to": "0xcafebabecafebabecafebabecafebabecafebabe",
302
+ "value": "0x",
175
303
  },
304
+ ],
305
+ "capabilities": {
306
+ "balanceDiffs": true,
307
+ },
308
+ "feePayer": false,
309
+ "keyData": "0x0578",
310
+ "keyId": "0xcccccccccccccccccccccccccccccccccccccccc",
311
+ "keyType": "webAuthn",
312
+ "type": "0x76",
313
+ }
314
+ `)
315
+ })
316
+
317
+ test('behavior: multisig simulation', () => {
318
+ const request = TransactionRequest.toRpc({
319
+ from: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
320
+ multisigSimulation,
321
+ })
322
+
323
+ expect(request).toMatchInlineSnapshot(`
324
+ {
325
+ "calls": [
176
326
  {
177
- owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
178
- weight: 1,
327
+ "data": "0x",
328
+ "to": "0x0000000000000000000000000000000000000000",
329
+ "value": "0x",
179
330
  },
180
331
  ],
181
- },
182
- multisigSignatureCount: 2,
183
- })
184
- expect(request.capabilities).toEqual({ balanceDiffs: true })
185
- expect(request.feePayer).toBe(false)
186
- expect(request.keyData).toBe('0x0578')
187
- expect(request.keyId).toBe('0xcccccccccccccccccccccccccccccccccccccccc')
188
- expect(request.keyType).toBe('webAuthn')
189
- expect(request.multisigInit).toMatchObject({ threshold: 2 })
190
- expect(request.multisigSignatureCount).toBe(2)
191
- expect(request.type).toBe('0x76')
332
+ "from": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
333
+ "multisigSimulation": {
334
+ "account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
335
+ "approvals": [
336
+ {
337
+ "keyType": "secp256k1",
338
+ "owner": "0x1111111111111111111111111111111111111111",
339
+ "type": "primitive",
340
+ },
341
+ {
342
+ "spec": {
343
+ "account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
344
+ "approvals": [
345
+ {
346
+ "keyData": "0x0578",
347
+ "keyType": "webAuthn",
348
+ "owner": "0x2222222222222222222222222222222222222222",
349
+ },
350
+ ],
351
+ "config": "0xf83ba022222222222222222222222222222222222222222222222222222222222222220101d7d694222222222222222222222222222222222222222201",
352
+ },
353
+ "type": "multisig",
354
+ },
355
+ ],
356
+ "config": "0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01",
357
+ },
358
+ "type": "0x76",
359
+ }
360
+ `)
192
361
  })
193
362
 
194
363
  test('behavior: key data longer than 4 bytes shims into a length hint', () => {
@@ -288,6 +457,7 @@ describe('roundtrip', () => {
288
457
  nonceKey: 255n,
289
458
  gas: 100000n,
290
459
  maxFeePerGas: 1000000000n,
460
+ multisigSimulation,
291
461
  }
292
462
 
293
463
  const rpc = TransactionRequest.toRpc(original)
@@ -308,6 +478,7 @@ describe('roundtrip', () => {
308
478
  expect(converted.nonceKey).toBe(original.nonceKey)
309
479
  expect(converted.gas).toBe(original.gas)
310
480
  expect(converted.maxFeePerGas).toBe(original.maxFeePerGas)
481
+ expect(converted.multisigSimulation).toEqual(original.multisigSimulation)
311
482
  expect(converted.type).toBe('tempo')
312
483
  })
313
484
 
@@ -325,6 +496,7 @@ describe('roundtrip', () => {
325
496
  validAfter: '0x32',
326
497
  nonceKey: '0xff',
327
498
  gas: '0x186a0',
499
+ multisigSimulation: multisigSimulationRpc,
328
500
  type: '0x76',
329
501
  }
330
502
 
@@ -337,6 +509,7 @@ describe('roundtrip', () => {
337
509
  expect(rpc.validAfter).toBe(original.validAfter)
338
510
  expect(rpc.nonceKey).toBe(original.nonceKey)
339
511
  expect(rpc.gas).toBe(original.gas)
512
+ expect(rpc.multisigSimulation).toEqual(original.multisigSimulation)
340
513
  expect(rpc.type).toBe('0x76')
341
514
  })
342
515
  })
@@ -7,7 +7,7 @@ import * as Signature from '../core/Signature.js'
7
7
  import * as ox_TransactionRequest from '../core/TransactionRequest.js'
8
8
  import * as AuthorizationTempo from './AuthorizationTempo.js'
9
9
  import * as KeyAuthorization from './KeyAuthorization.js'
10
- import type * as MultisigConfig from './MultisigConfig.js'
10
+ import * as MultisigSimulation from './MultisigSimulation.js'
11
11
  import * as SignatureEnvelope from './SignatureEnvelope.js'
12
12
  import * as Transaction from './Transaction.js'
13
13
  import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
@@ -15,13 +15,6 @@ import type { Call } from './TxEnvelopeTempo.js'
15
15
 
16
16
  type KeyType = 'secp256k1' | 'p256' | 'webAuthn'
17
17
 
18
- /**
19
- * Bootstrap multisig config hint for node-side gas modeling (TIP-1061).
20
- * The node prices multisig gas from it during simulation, and ignores it
21
- * for registered senders.
22
- */
23
- export type MultisigInit = Compute<MultisigConfig.Config & { salt: Hex.Hex }>
24
-
25
18
  /**
26
19
  * A Transaction Request that is generic to all transaction types.
27
20
  *
@@ -44,19 +37,18 @@ export type TransactionRequest<
44
37
  | undefined
45
38
  calls?: readonly Call<bigintType>[] | undefined
46
39
  capabilities?: Record<string, unknown> | undefined
40
+ feePayer?: boolean | undefined
47
41
  feePayerSignature?: Signature.Signature<true, numberType> | null | undefined
42
+ feeToken?: Address.Address | undefined
48
43
  keyAuthorization?: KeyAuthorization.KeyAuthorization<true> | undefined
49
44
  keyData?: Hex.Hex | undefined
50
45
  keyId?: Address.Address | undefined
51
46
  keyType?: KeyType | undefined
52
- feePayer?: boolean | undefined
53
- feeToken?: Address.Address | undefined
54
- multisigInit?: MultisigInit | undefined
55
- multisigSignatureCount?: number | undefined
47
+ multisigSimulation?: MultisigSimulation.Spec | undefined
56
48
  nonceKey?: 'random' | bigintType | undefined
57
49
  signature?: SignatureEnvelope.SignatureEnvelope<numberType> | undefined
58
- validBefore?: numberType | undefined
59
50
  validAfter?: numberType | undefined
51
+ validBefore?: numberType | undefined
60
52
  }
61
53
  >
62
54
 
@@ -67,12 +59,14 @@ export type Rpc = Omit<
67
59
  | 'feePayerSignature'
68
60
  | 'feeToken'
69
61
  | 'keyAuthorization'
62
+ | 'multisigSimulation'
70
63
  | 'signature'
71
64
  > & {
72
65
  authorizationList?: AuthorizationTempo.ListRpc | undefined
73
66
  feePayerSignature?: Signature.Rpc | null | undefined
74
67
  feeToken?: Hex.Hex | undefined
75
68
  keyAuthorization?: KeyAuthorization.Rpc | undefined
69
+ multisigSimulation?: MultisigSimulation.Rpc | undefined
76
70
  nonceKey?: Hex.Hex | undefined
77
71
  signature?: SignatureEnvelope.SignatureEnvelopeRpc | undefined
78
72
  }
@@ -100,7 +94,7 @@ export type Rpc = Omit<
100
94
  * @returns A transaction request.
101
95
  */
102
96
  export function fromRpc(request: Rpc): TransactionRequest {
103
- const { authorizationList: _, ...rest } = request
97
+ const { authorizationList: _, multisigSimulation: __, ...rest } = request
104
98
  const request_ = ox_TransactionRequest.fromRpc(
105
99
  rest as any,
106
100
  ) as TransactionRequest
@@ -135,6 +129,10 @@ export function fromRpc(request: Rpc): TransactionRequest {
135
129
  request_.keyAuthorization = KeyAuthorization.fromRpc(
136
130
  request.keyAuthorization,
137
131
  )
132
+ if (request.multisigSimulation)
133
+ request_.multisigSimulation = MultisigSimulation.fromRpc(
134
+ request.multisigSimulation,
135
+ )
138
136
  if (typeof request.validBefore !== 'undefined')
139
137
  request_.validBefore = Hex.toNumber(request.validBefore as Hex.Hex)
140
138
  if (typeof request.validAfter !== 'undefined')
@@ -150,6 +148,7 @@ export declare namespace fromRpc {
150
148
  | AuthorizationTempo.fromRpcList.ErrorType
151
149
  | Hex.toNumber.ErrorType
152
150
  | Hex.toBigInt.ErrorType
151
+ | MultisigSimulation.fromRpc.ErrorType
153
152
  | Errors.GlobalErrorType
154
153
  }
155
154
 
@@ -223,8 +222,7 @@ export function toRpc(request: toRpc.Input): Rpc {
223
222
  typeof request.keyData !== 'undefined' ||
224
223
  typeof request.keyId !== 'undefined' ||
225
224
  typeof request.keyType !== 'undefined' ||
226
- typeof request.multisigInit !== 'undefined' ||
227
- typeof request.multisigSignatureCount !== 'undefined' ||
225
+ typeof request.multisigSimulation !== 'undefined' ||
228
226
  typeof request.nonceKey !== 'undefined' ||
229
227
  typeof request.validBefore !== 'undefined' ||
230
228
  typeof request.validAfter !== 'undefined' ||
@@ -274,10 +272,10 @@ export function toRpc(request: toRpc.Input): Rpc {
274
272
  if (typeof request.keyId !== 'undefined') request_rpc.keyId = request.keyId
275
273
  if (typeof request.keyType !== 'undefined')
276
274
  request_rpc.keyType = request.keyType
277
- if (typeof request.multisigInit !== 'undefined')
278
- request_rpc.multisigInit = request.multisigInit
279
- if (typeof request.multisigSignatureCount !== 'undefined')
280
- request_rpc.multisigSignatureCount = request.multisigSignatureCount
275
+ if (typeof request.multisigSimulation !== 'undefined')
276
+ request_rpc.multisigSimulation = MultisigSimulation.toRpc(
277
+ request.multisigSimulation,
278
+ )
281
279
  if (typeof request.validBefore !== 'undefined')
282
280
  request_rpc.validBefore = Quantity.fromNumberish(request.validBefore)
283
281
  if (typeof request.validAfter !== 'undefined')
@@ -312,6 +310,7 @@ export declare namespace toRpc {
312
310
  export type ErrorType =
313
311
  | AuthorizationTempo.toRpcList.ErrorType
314
312
  | Hex.fromNumber.ErrorType
313
+ | MultisigSimulation.toRpc.ErrorType
315
314
  | Errors.GlobalErrorType
316
315
  }
317
316
 
@@ -0,0 +1,7 @@
1
+ import { expectTypeOf, test } from 'vp/test'
2
+ import type * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
3
+
4
+ test('Serialized', () => {
5
+ expectTypeOf<'0x76'>().toExtend<TxEnvelopeTempo.Serialized>()
6
+ expectTypeOf<'0x78'>().toExtend<TxEnvelopeTempo.Serialized>()
7
+ })
@@ -138,6 +138,31 @@ describe('deserialize', () => {
138
138
  expect(deserialized).toEqual(transaction)
139
139
  })
140
140
 
141
+ test('fee payer', () => {
142
+ const serialized = TxEnvelopeTempo.serialize(transaction, {
143
+ format: 'feePayer',
144
+ sender: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
145
+ })
146
+
147
+ expect(TxEnvelopeTempo.deserialize(serialized)).toMatchInlineSnapshot(`
148
+ {
149
+ "calls": [
150
+ {
151
+ "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
152
+ },
153
+ ],
154
+ "chainId": 1,
155
+ "feePayerSignature": null,
156
+ "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
157
+ "maxFeePerGas": 2000000000n,
158
+ "maxPriorityFeePerGas": 2000000000n,
159
+ "nonce": 785n,
160
+ "nonceKey": 0n,
161
+ "type": "tempo",
162
+ }
163
+ `)
164
+ })
165
+
141
166
  test('minimal', () => {
142
167
  const transaction = TxEnvelopeTempo.from({
143
168
  chainId: 1,
@@ -134,7 +134,8 @@ export type Rpc<signed extends boolean = boolean> = TxEnvelopeTempo<
134
134
  export const feePayerMagic = '0x78' as const
135
135
  export type FeePayerMagic = typeof feePayerMagic
136
136
 
137
- export type Serialized = `${SerializedType}${string}`
137
+ /** Serialized standard or fee-payer Tempo transaction. */
138
+ export type Serialized = `${SerializedType | FeePayerMagic}${string}`
138
139
 
139
140
  export type Signed = TxEnvelopeTempo<true>
140
141
 
@@ -232,7 +233,7 @@ export declare namespace assert {
232
233
  }
233
234
 
234
235
  /**
235
- * Deserializes a {@link ox#TxEnvelopeTempo.TxEnvelopeTempo} from its serialized form.
236
+ * Deserializes a standard or fee-payer {@link ox#TxEnvelopeTempo.TxEnvelopeTempo}.
236
237
  *
237
238
  * @example
238
239
  * ```ts twoslash
@@ -254,7 +255,8 @@ export declare namespace assert {
254
255
  * @returns Deserialized Transaction Envelope.
255
256
  */
256
257
  export function deserialize(serialized: Serialized): Compute<TxEnvelopeTempo> {
257
- if (Hex.slice(serialized, 0, 1) !== serializedType)
258
+ const prefix = Hex.slice(serialized, 0, 1)
259
+ if (prefix !== serializedType && prefix !== feePayerMagic)
258
260
  throw new TransactionEnvelope.InvalidSerializedError({
259
261
  attributes: {},
260
262
  serialized,
@@ -173,6 +173,24 @@ export * as KeyAuthorization from './KeyAuthorization.js'
173
173
  * @category Reference
174
174
  */
175
175
  export * as MultisigConfig from './MultisigConfig.js'
176
+ /**
177
+ * Offchain multisig transaction and key authorization operation utilities.
178
+ *
179
+ * Derives operation hashes, selects owner approvals, serializes transactions,
180
+ * validates operation state, and converts JSON-RPC representations.
181
+ *
182
+ * @category Reference
183
+ */
184
+ export * as MultisigOperation from './MultisigOperation.js'
185
+ /**
186
+ * Native multisig RPC simulation utilities.
187
+ *
188
+ * Converts complete root and nested owner specs between their domain and Tempo
189
+ * JSON-RPC representations.
190
+ *
191
+ * @category Reference
192
+ */
193
+ export * as MultisigSimulation from './MultisigSimulation.js'
176
194
  /**
177
195
  * Utilities for constructing period durations (in seconds) for recurring spending limits.
178
196
  *
@@ -251,7 +269,7 @@ export * as PoolId from './PoolId.js'
251
269
  */
252
270
  export * as ReceivePolicyReceipt from './ReceivePolicyReceipt.js'
253
271
  /**
254
- * Union of all JSON-RPC Methods for the `tempo_` namespace.
272
+ * JSON-RPC schemas for the `tempo_` and `multisig_` namespaces.
255
273
  *
256
274
  * @example
257
275
  * ```ts twoslash
@@ -260,7 +278,9 @@ export * as ReceivePolicyReceipt from './ReceivePolicyReceipt.js'
260
278
  * import { RpcSchemaTempo } from 'ox/tempo'
261
279
  *
262
280
  * const schema = RpcSchema.from<
263
- * RpcSchema.Default | RpcSchemaTempo.Tempo
281
+ * | RpcSchema.Default
282
+ * | RpcSchemaTempo.Multisig
283
+ * | RpcSchemaTempo.Tempo
264
284
  * >()
265
285
  *
266
286
  * const provider = Provider.from(window.ethereum!, { schema })