ox 1.7.2 → 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 (103) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/tempo/KeyAuthorization.d.ts +1 -1
  3. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  4. package/dist/tempo/MultisigOperation.d.ts +42 -26
  5. package/dist/tempo/MultisigOperation.d.ts.map +1 -1
  6. package/dist/tempo/MultisigOperation.js +78 -48
  7. package/dist/tempo/MultisigOperation.js.map +1 -1
  8. package/dist/tempo/MultisigSimulation.d.ts +132 -0
  9. package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
  10. package/dist/tempo/MultisigSimulation.js +140 -0
  11. package/dist/tempo/MultisigSimulation.js.map +1 -0
  12. package/dist/tempo/RpcSchemaTempo.d.ts +20 -10
  13. package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
  14. package/dist/tempo/SignatureEnvelope.d.ts +9 -20
  15. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  16. package/dist/tempo/SignatureEnvelope.js +12 -18
  17. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  18. package/dist/tempo/TransactionRequest.d.ts +6 -6
  19. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  20. package/dist/tempo/TransactionRequest.js +7 -7
  21. package/dist/tempo/TransactionRequest.js.map +1 -1
  22. package/dist/tempo/index.d.ts +4 -4
  23. package/dist/tempo/index.d.ts.map +1 -1
  24. package/dist/tempo/index.js +4 -4
  25. package/dist/tempo/index.js.map +1 -1
  26. package/dist/zod/tempo/AuthorizationTempo.d.ts +10 -130
  27. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  28. package/dist/zod/tempo/KeyAuthorization.d.ts +5 -65
  29. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  30. package/dist/zod/tempo/MultisigOperation.d.ts +0 -10
  31. package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -1
  32. package/dist/zod/tempo/MultisigOperation.js +0 -3
  33. package/dist/zod/tempo/MultisigOperation.js.map +1 -1
  34. package/dist/zod/tempo/{MultisigWitness.d.ts → MultisigSimulation.d.ts} +17 -49
  35. package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
  36. package/dist/zod/tempo/{MultisigWitness.js → MultisigSimulation.js} +26 -24
  37. package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
  38. package/dist/zod/tempo/RpcSchemaTempo.d.ts +83 -204
  39. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  40. package/dist/zod/tempo/RpcSchemaTempo.js +19 -10
  41. package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
  42. package/dist/zod/tempo/SignatureEnvelope.d.ts +3 -39
  43. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  44. package/dist/zod/tempo/SignatureEnvelope.js +4 -14
  45. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  46. package/dist/zod/tempo/Transaction.d.ts +18 -234
  47. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  48. package/dist/zod/tempo/TransactionRequest.d.ts +36 -192
  49. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  50. package/dist/zod/tempo/TransactionRequest.js +10 -10
  51. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  52. package/dist/zod/tempo/z.d.ts +1 -1
  53. package/dist/zod/tempo/z.d.ts.map +1 -1
  54. package/dist/zod/tempo/z.js +1 -1
  55. package/dist/zod/tempo/z.js.map +1 -1
  56. package/package.json +9 -9
  57. package/src/tempo/AuthorizationTempo.test.ts +4 -0
  58. package/src/tempo/KeyAuthorization.test-d.ts +1 -12
  59. package/src/tempo/KeyAuthorization.test.ts +44 -3
  60. package/src/tempo/KeyAuthorization.ts +3 -3
  61. package/src/tempo/MultisigOperation.test-d.ts +8 -6
  62. package/src/tempo/MultisigOperation.test.ts +236 -169
  63. package/src/tempo/MultisigOperation.ts +122 -83
  64. package/src/tempo/MultisigSimulation.test-d.ts +33 -0
  65. package/src/tempo/MultisigSimulation.test.ts +283 -0
  66. package/src/tempo/MultisigSimulation.ts +266 -0
  67. package/src/tempo/RpcSchemaTempo.test-d.ts +45 -10
  68. package/src/tempo/RpcSchemaTempo.ts +15 -6
  69. package/src/tempo/SignatureEnvelope.test-d.ts +3 -59
  70. package/src/tempo/SignatureEnvelope.test.ts +61 -51
  71. package/src/tempo/SignatureEnvelope.ts +27 -44
  72. package/src/tempo/Transaction.test.ts +61 -0
  73. package/src/tempo/TransactionRequest.test-d.ts +7 -7
  74. package/src/tempo/TransactionRequest.test.ts +129 -208
  75. package/src/tempo/TransactionRequest.ts +16 -12
  76. package/src/tempo/index.ts +4 -4
  77. package/src/tempo/multisig.e2e.test.ts +51 -1
  78. package/src/version.ts +1 -1
  79. package/src/zod/tempo/MultisigOperation.ts +0 -3
  80. package/src/zod/tempo/{MultisigWitness.ts → MultisigSimulation.ts} +39 -42
  81. package/src/zod/tempo/RpcSchemaTempo.ts +20 -10
  82. package/src/zod/tempo/SignatureEnvelope.ts +11 -35
  83. package/src/zod/tempo/TransactionRequest.ts +14 -14
  84. package/src/zod/tempo/_test/KeyAuthorization.test.ts +2 -15
  85. package/src/zod/tempo/_test/MultisigOperation.test.ts +0 -2
  86. package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
  87. package/src/zod/tempo/_test/MultisigSimulation.test.ts +78 -0
  88. package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +19 -2
  89. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +46 -4
  90. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +58 -112
  91. package/src/zod/tempo/_test/TransactionRequest.test.ts +19 -160
  92. package/src/zod/tempo/z.ts +1 -1
  93. package/dist/tempo/MultisigWitness.d.ts +0 -106
  94. package/dist/tempo/MultisigWitness.d.ts.map +0 -1
  95. package/dist/tempo/MultisigWitness.js +0 -118
  96. package/dist/tempo/MultisigWitness.js.map +0 -1
  97. package/dist/zod/tempo/MultisigWitness.d.ts.map +0 -1
  98. package/dist/zod/tempo/MultisigWitness.js.map +0 -1
  99. package/src/tempo/MultisigWitness.test-d.ts +0 -42
  100. package/src/tempo/MultisigWitness.test.ts +0 -288
  101. package/src/tempo/MultisigWitness.ts +0 -210
  102. package/src/zod/tempo/_test/MultisigWitness.test-d.ts +0 -13
  103. package/src/zod/tempo/_test/MultisigWitness.test.ts +0 -149
@@ -0,0 +1,266 @@
1
+ import * as Address from '../core/Address.js'
2
+ import * as Errors from '../core/Errors.js'
3
+ import * as Hex from '../core/Hex.js'
4
+ import type { Compute } from '../core/internal/types.js'
5
+ import * as Rlp from '../core/Rlp.js'
6
+ import * as MultisigConfig from './MultisigConfig.js'
7
+ import type * as SignatureEnvelope from './SignatureEnvelope.js'
8
+
9
+ /** Native multisig owner approval used for RPC simulation. */
10
+ export type Approval = NestedApproval | PrimitiveApproval
11
+
12
+ /** JSON-RPC native multisig owner approval used for RPC simulation. */
13
+ export type ApprovalRpc = NestedApprovalRpc | PrimitiveApproval
14
+
15
+ /** Nested native multisig owner approval used for RPC simulation. */
16
+ export type NestedApproval = {
17
+ /** Depth-2 multisig simulation spec. */
18
+ spec: NestedSpec
19
+ /** Approval type. */
20
+ type: 'multisig'
21
+ }
22
+
23
+ /** JSON-RPC nested native multisig owner approval used for RPC simulation. */
24
+ export type NestedApprovalRpc = {
25
+ /** Depth-2 multisig simulation spec. */
26
+ spec: NestedSpecRpc
27
+ /** Approval type. */
28
+ type: 'multisig'
29
+ }
30
+
31
+ /** Primitive approval in a depth-2 multisig simulation spec. */
32
+ export type NestedPrimitiveApproval = {
33
+ /** Optional signature-specific gas-estimation data. */
34
+ keyData?: Hex.Hex | undefined
35
+ /** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
36
+ keyType?: SignatureEnvelope.Type | undefined
37
+ /** Configured owner address. */
38
+ owner: Address.Address
39
+ }
40
+
41
+ /** Depth-2 native multisig spec used for RPC simulation. */
42
+ export type NestedSpec = {
43
+ /** Nested multisig account. */
44
+ account: Address.Address
45
+ /** Primitive owner approvals to model. */
46
+ approvals: readonly NestedPrimitiveApproval[]
47
+ /** Complete applicable configuration. */
48
+ config: MultisigConfig.Config
49
+ }
50
+
51
+ /** JSON-RPC depth-2 native multisig spec used for RPC simulation. */
52
+ export type NestedSpecRpc = {
53
+ /** Nested multisig account. */
54
+ account: Address.Address
55
+ /** Primitive owner approvals to model. */
56
+ approvals: readonly NestedPrimitiveApproval[]
57
+ /** Canonical RLP-encoded applicable configuration. */
58
+ config: Hex.Hex
59
+ }
60
+
61
+ /** Primitive owner approval used for RPC simulation. */
62
+ export type PrimitiveApproval = {
63
+ /** Optional signature-specific gas-estimation data. */
64
+ keyData?: Hex.Hex | undefined
65
+ /** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
66
+ keyType?: SignatureEnvelope.Type | undefined
67
+ /** Configured owner address. */
68
+ owner: Address.Address
69
+ /** Approval type. */
70
+ type: 'primitive'
71
+ }
72
+
73
+ /** JSON-RPC representation of a native multisig simulation spec. */
74
+ export type Rpc = Compute<{
75
+ /** Account authorized by this spec. */
76
+ account: Address.Address
77
+ /** Owner approvals to model. */
78
+ approvals: readonly ApprovalRpc[]
79
+ /** Canonical RLP-encoded applicable configuration. */
80
+ config: Hex.Hex
81
+ }>
82
+
83
+ /** Native multisig spec used to construct an RPC simulation signature. */
84
+ export type Spec = Compute<{
85
+ /** Account authorized by this spec. */
86
+ account: Address.Address
87
+ /** Owner approvals to model. */
88
+ approvals: readonly Approval[]
89
+ /** Complete applicable configuration. */
90
+ config: MultisigConfig.Config
91
+ }>
92
+
93
+ /**
94
+ * Converts a JSON-RPC multisig simulation spec to its domain representation.
95
+ *
96
+ * @example
97
+ * ```ts twoslash
98
+ * // @noErrors
99
+ * import { MultisigSimulation } from 'ox/tempo'
100
+ *
101
+ * const spec = MultisigSimulation.fromRpc(specRpc)
102
+ * ```
103
+ *
104
+ * @param spec - JSON-RPC multisig simulation spec.
105
+ * @returns The multisig simulation spec with decoded configurations.
106
+ */
107
+ export function fromRpc(spec: Rpc): Spec {
108
+ const { account, approvals, config } = spec
109
+ assertApprovalCount(approvals)
110
+ return {
111
+ account,
112
+ approvals: approvals.map((approval) => {
113
+ if (approval.type === 'primitive') return approval
114
+ assertApprovalCount(approval.spec.approvals)
115
+ return {
116
+ spec: {
117
+ account: approval.spec.account,
118
+ approvals: approval.spec.approvals,
119
+ config: deserializeConfig(approval.spec.config),
120
+ },
121
+ type: 'multisig',
122
+ }
123
+ }),
124
+ config: deserializeConfig(config),
125
+ }
126
+ }
127
+
128
+ export declare namespace fromRpc {
129
+ /** Error type for `fromRpc`. */
130
+ export type ErrorType =
131
+ | Hex.isEqual.ErrorType
132
+ | InvalidSimulationError
133
+ | MultisigConfig.assert.ErrorType
134
+ | Rlp.fromHex.ErrorType
135
+ | Rlp.toHex.ErrorType
136
+ | Errors.GlobalErrorType
137
+ }
138
+
139
+ /**
140
+ * Converts a multisig simulation spec to its JSON-RPC representation.
141
+ *
142
+ * @example
143
+ * ```ts twoslash
144
+ * // @noErrors
145
+ * import { MultisigSimulation } from 'ox/tempo'
146
+ *
147
+ * const specRpc = MultisigSimulation.toRpc(spec)
148
+ * ```
149
+ *
150
+ * @param spec - Multisig simulation spec.
151
+ * @returns The JSON-RPC multisig simulation spec with encoded configurations.
152
+ */
153
+ export function toRpc(spec: Spec): Rpc {
154
+ const { account, approvals, config } = spec
155
+ assertApprovalCount(approvals)
156
+ return {
157
+ account,
158
+ approvals: approvals.map((approval) => {
159
+ if (approval.type === 'primitive')
160
+ return {
161
+ ...approval,
162
+ ...(typeof approval.keyData !== 'undefined'
163
+ ? { keyData: shimKeyData(approval.keyData) }
164
+ : {}),
165
+ }
166
+ assertApprovalCount(approval.spec.approvals)
167
+ return {
168
+ spec: {
169
+ account: approval.spec.account,
170
+ approvals: approval.spec.approvals.map((approval) => ({
171
+ ...approval,
172
+ ...(typeof approval.keyData !== 'undefined'
173
+ ? { keyData: shimKeyData(approval.keyData) }
174
+ : {}),
175
+ })),
176
+ config: serializeConfig(approval.spec.config),
177
+ },
178
+ type: 'multisig',
179
+ }
180
+ }),
181
+ config: serializeConfig(config),
182
+ }
183
+ }
184
+
185
+ export declare namespace toRpc {
186
+ /** Error type for `toRpc`. */
187
+ export type ErrorType =
188
+ | Hex.fromNumber.ErrorType
189
+ | InvalidSimulationError
190
+ | MultisigConfig.assert.ErrorType
191
+ | Rlp.fromHex.ErrorType
192
+ | Errors.GlobalErrorType
193
+ }
194
+
195
+ /** @internal */
196
+ function assertApprovalCount(approvals: readonly unknown[]) {
197
+ if (approvals.length > MultisigConfig.maxSignatures)
198
+ throw new InvalidSimulationError({
199
+ reason: `approval count exceeds ${MultisigConfig.maxSignatures}`,
200
+ })
201
+ }
202
+
203
+ /** @internal */
204
+ function deserializeConfig(config: Hex.Hex): MultisigConfig.Config {
205
+ const tuple = Rlp.toHex(config)
206
+ if (!Array.isArray(tuple) || tuple.length !== 4)
207
+ throw new InvalidSimulationError({ reason: 'invalid config encoding' })
208
+ const [salt, version, threshold, owners] = tuple
209
+ if (
210
+ Array.isArray(salt) ||
211
+ Hex.size(salt) !== 32 ||
212
+ Array.isArray(version) ||
213
+ Hex.size(version) > 8 ||
214
+ (version !== '0x' && Hex.slice(version, 0, 1) === '0x00') ||
215
+ Array.isArray(threshold) ||
216
+ Hex.size(threshold) > 1 ||
217
+ !Array.isArray(owners) ||
218
+ owners.some(
219
+ (owner) =>
220
+ !Array.isArray(owner) ||
221
+ owner.length !== 2 ||
222
+ owner.some(Array.isArray) ||
223
+ !Address.validate(owner[0]) ||
224
+ Hex.size(owner[1] as Hex.Hex) > 1,
225
+ )
226
+ )
227
+ throw new InvalidSimulationError({ reason: 'invalid config encoding' })
228
+ const value = MultisigConfig.fromTuple(
229
+ tuple as unknown as MultisigConfig.Tuple,
230
+ )
231
+ MultisigConfig.assert(value)
232
+ if (!Hex.isEqual(config, serializeConfig(value)))
233
+ throw new InvalidSimulationError({ reason: 'noncanonical config encoding' })
234
+ return value
235
+ }
236
+
237
+ /** @internal */
238
+ function serializeConfig(config: MultisigConfig.Input): Hex.Hex {
239
+ return Rlp.fromHex(MultisigConfig.toTuple(config))
240
+ }
241
+
242
+ /**
243
+ * Shims long key data into the length hint accepted by Tempo's gas estimator.
244
+ * @internal
245
+ */
246
+ function shimKeyData(data: Hex.Hex): Hex.Hex {
247
+ const size = Hex.size(data)
248
+ if (size <= 4) return data
249
+ return Hex.fromNumber(size, { size: 2 })
250
+ }
251
+
252
+ /** Thrown when a native multisig simulation spec is invalid. */
253
+ export class InvalidSimulationError extends Errors.BaseError {
254
+ override readonly name = 'MultisigSimulation.InvalidSimulationError'
255
+ constructor(options: InvalidSimulationError.Options) {
256
+ super(`Invalid multisig simulation: ${options.reason}.`)
257
+ }
258
+ }
259
+
260
+ export declare namespace InvalidSimulationError {
261
+ /** Error construction options. */
262
+ export type Options = {
263
+ /** Validation failure. */
264
+ reason: string
265
+ }
266
+ }
@@ -1,19 +1,41 @@
1
1
  import type { Provider } from 'ox'
2
2
  import type {
3
3
  KeyAuthorization,
4
+ MultisigConfig,
4
5
  MultisigOperation,
6
+ MultisigSimulation,
5
7
  RpcSchemaTempo,
6
8
  } from 'ox/tempo'
7
9
  import { expectTypeOf, test } from 'vp/test'
10
+ import type * as Address from '../core/Address.js'
8
11
  import type * as Hex from '../core/Hex.js'
9
12
 
10
13
  declare const provider: Provider.Provider<{ schema: RpcSchemaTempo.Multisig }>
14
+ declare const tempoProvider: Provider.Provider<{
15
+ schema: RpcSchemaTempo.Tempo
16
+ }>
17
+ declare const address: Address.Address
11
18
  declare const hash: Hex.Hex
12
19
  declare const keyAuthorization: KeyAuthorization.Rpc
20
+ declare const multisigSimulation: MultisigSimulation.Rpc
13
21
  declare const serializedTransaction: Hex.Hex
14
22
  declare const signature: Hex.Hex
15
23
 
16
24
  test('multisig provider methods', () => {
25
+ expectTypeOf(
26
+ provider.request({
27
+ method: 'multisig_approveKeyAuthorization',
28
+ params: [{ keyAuthorization }],
29
+ }),
30
+ ).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
31
+
32
+ expectTypeOf(
33
+ provider.request({
34
+ method: 'multisig_approveKeyAuthorization',
35
+ params: [{ hash, signature }],
36
+ }),
37
+ ).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
38
+
17
39
  expectTypeOf(
18
40
  provider.request({
19
41
  method: 'multisig_approveRawTransaction',
@@ -35,17 +57,10 @@ test('multisig provider methods', () => {
35
57
 
36
58
  expectTypeOf(
37
59
  provider.request({
38
- method: 'multisig_approveKeyAuthorization',
39
- params: [{ keyAuthorization }],
60
+ method: 'multisig_getConfig',
61
+ params: [{ address }],
40
62
  }),
41
- ).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
42
-
43
- expectTypeOf(
44
- provider.request({
45
- method: 'multisig_approveKeyAuthorization',
46
- params: [{ hash, signature }],
47
- }),
48
- ).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
63
+ ).resolves.toEqualTypeOf<MultisigConfig.Rpc | null>()
49
64
 
50
65
  expectTypeOf(
51
66
  provider.request({
@@ -54,3 +69,23 @@ test('multisig provider methods', () => {
54
69
  }),
55
70
  ).resolves.toEqualTypeOf<MultisigOperation.Rpc | null>()
56
71
  })
72
+
73
+ test('tempo simulation accepts multisig specs', () => {
74
+ void tempoProvider.request({
75
+ method: 'tempo_simulateV1',
76
+ params: [
77
+ {
78
+ blockStateCalls: [
79
+ {
80
+ calls: [
81
+ {
82
+ multisigSimulation,
83
+ },
84
+ ],
85
+ },
86
+ ],
87
+ },
88
+ 'latest',
89
+ ],
90
+ })
91
+ })
@@ -1,3 +1,4 @@
1
+ import type * as Address from '../core/Address.js'
1
2
  import type * as Block from '../core/Block.js'
2
3
  import type * as BlockOverrides from '../core/BlockOverrides.js'
3
4
  import type * as Hex from '../core/Hex.js'
@@ -5,6 +6,7 @@ import type * as Log from '../core/Log.js'
5
6
  import type * as RpcSchema from '../core/RpcSchema.js'
6
7
  import type * as StateOverrides from '../core/StateOverrides.js'
7
8
  import type * as KeyAuthorization from './KeyAuthorization.js'
9
+ import type * as MultisigConfig from './MultisigConfig.js'
8
10
  import type * as MultisigOperation from './MultisigOperation.js'
9
11
  import type * as TransactionRequest from './TransactionRequest.js'
10
12
 
@@ -58,6 +60,16 @@ export type Tempo = RpcSchema.From<{
58
60
 
59
61
  /** Union of all JSON-RPC methods for the `multisig_` namespace. */
60
62
  export type Multisig = RpcSchema.From<
63
+ | {
64
+ Request: {
65
+ method: 'multisig_approveKeyAuthorization'
66
+ params: [
67
+ | { keyAuthorization: KeyAuthorization.Rpc }
68
+ | { hash: Hex.Hex; signature: Hex.Hex },
69
+ ]
70
+ }
71
+ ReturnType: MultisigOperation.KeyAuthorizationRpc
72
+ }
61
73
  | {
62
74
  Request: {
63
75
  method: 'multisig_approveRawTransaction'
@@ -74,13 +86,10 @@ export type Multisig = RpcSchema.From<
74
86
  }
75
87
  | {
76
88
  Request: {
77
- method: 'multisig_approveKeyAuthorization'
78
- params: [
79
- | { keyAuthorization: KeyAuthorization.Rpc }
80
- | { hash: Hex.Hex; signature: Hex.Hex },
81
- ]
89
+ method: 'multisig_getConfig'
90
+ params: [{ address: Address.Address }]
82
91
  }
83
- ReturnType: MultisigOperation.KeyAuthorizationRpc
92
+ ReturnType: MultisigConfig.Rpc | null
84
93
  }
85
94
  | {
86
95
  Request: {
@@ -2,13 +2,6 @@ import { expectTypeOf, test } from 'vp/test'
2
2
  import * as MultisigConfig from './MultisigConfig.js'
3
3
  import * as SignatureEnvelope from './SignatureEnvelope.js'
4
4
 
5
- const signatureRpc = {
6
- r: '0x01',
7
- s: '0x02',
8
- type: 'secp256k1',
9
- yParity: '0x0',
10
- } as const satisfies SignatureEnvelope.SignatureEnvelopeRpc
11
-
12
5
  const signature = {
13
6
  signature: {
14
7
  r: '0x01',
@@ -34,20 +27,9 @@ test('toRpc preserves the signature type', () => {
34
27
  ).toEqualTypeOf<SignatureEnvelope.Secp256k1Rpc>()
35
28
  })
36
29
 
37
- test('MultisigRpc carries one complete witness shape', () => {
38
- const rpc = {
39
- account: '0x1111111111111111111111111111111111111111',
40
- config: MultisigConfig.toRpc(config),
41
- signatures: [signatureRpc],
42
- } as const satisfies SignatureEnvelope.MultisigRpc
43
-
44
- expectTypeOf(rpc.config).toMatchTypeOf<MultisigConfig.Rpc>()
45
- expectTypeOf(rpc.signatures).toMatchTypeOf<
46
- readonly SignatureEnvelope.SignatureEnvelopeRpc[]
47
- >()
48
- expectTypeOf<
49
- SignatureEnvelope.GetType<typeof rpc>
50
- >().toEqualTypeOf<'multisig'>()
30
+ test('MultisigRpc contains an untagged serialized multisig signature', () => {
31
+ const rpc = '0xf8' as const satisfies SignatureEnvelope.MultisigRpc
32
+ expectTypeOf(rpc).toMatchTypeOf<`0x${string}`>()
51
33
  })
52
34
 
53
35
  test('from derives an initial account', () => {
@@ -74,41 +56,3 @@ test('from requires an account for a current config', () => {
74
56
  })
75
57
  expectTypeOf(multisig).toMatchTypeOf<SignatureEnvelope.Multisig>()
76
58
  })
77
-
78
- test('MultisigRpc rejects old witness shapes', () => {
79
- const accountOnly = {
80
- account: '0x1111111111111111111111111111111111111111',
81
- signatures: [signatureRpc],
82
- } as const
83
- // @ts-expect-error Multisig RPC signatures require config.
84
- const accountOnlyRpc: SignatureEnvelope.MultisigRpc = accountOnly
85
-
86
- const init = {
87
- init: config,
88
- signatures: [signatureRpc],
89
- } as const
90
- // @ts-expect-error Multisig RPC signatures no longer accept init.
91
- const initRpc: SignatureEnvelope.MultisigRpc = init
92
-
93
- const serialized = {
94
- account: '0x1111111111111111111111111111111111111111',
95
- config: MultisigConfig.toRpc(config),
96
- signatures: ['0x1234'],
97
- } as const
98
- // @ts-expect-error Owner approvals use structured RPC envelopes.
99
- const serializedRpc: SignatureEnvelope.MultisigRpc = serialized
100
-
101
- const tagged = {
102
- account: '0x1111111111111111111111111111111111111111',
103
- config: MultisigConfig.toRpc(config),
104
- signatures: [signatureRpc],
105
- type: 'multisig',
106
- } as const
107
- // @ts-expect-error Multisig RPC signatures are untagged.
108
- const taggedRpc: SignatureEnvelope.MultisigRpc = tagged
109
-
110
- expectTypeOf(accountOnlyRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
111
- expectTypeOf(initRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
112
- expectTypeOf(serializedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
113
- expectTypeOf(taggedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
114
- })
@@ -68,6 +68,9 @@ const signature_keychain_webauthn = SignatureEnvelope.from({
68
68
  version: 'v2',
69
69
  })
70
70
 
71
+ const signature_multisig_rpc =
72
+ '0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
73
+
71
74
  describe('assert', () => {
72
75
  describe('secp256k1', () => {
73
76
  test('behavior: validates valid signature', () => {
@@ -2492,6 +2495,41 @@ describe('fromRpc', () => {
2492
2495
  expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
2493
2496
  })
2494
2497
  })
2498
+ describe('multisig', () => {
2499
+ test('behavior: converts an untagged serialized multisig signature', () => {
2500
+ expect(
2501
+ SignatureEnvelope.serialize(
2502
+ SignatureEnvelope.fromRpc(signature_multisig_rpc),
2503
+ ),
2504
+ ).toMatchInlineSnapshot(`"0x05${signature_multisig_rpc.slice(2)}"`)
2505
+ })
2506
+
2507
+ test('error: rejects a payload with the transaction type byte', () => {
2508
+ expect(() =>
2509
+ SignatureEnvelope.fromRpc(`0x05${signature_multisig_rpc.slice(2)}`),
2510
+ ).toThrowError()
2511
+ })
2512
+
2513
+ test('error: rejects trailing bytes', () => {
2514
+ expect(() =>
2515
+ SignatureEnvelope.fromRpc(`${signature_multisig_rpc}00`),
2516
+ ).toThrowErrorMatchingInlineSnapshot(
2517
+ `[Rlp.TrailingBytesError: RLP payload encodes a single item, but \`1\` trailing byte remains.]`,
2518
+ )
2519
+ })
2520
+
2521
+ test('error: rejects a structured multisig signature', () => {
2522
+ expect(() =>
2523
+ SignatureEnvelope.fromRpc({
2524
+ account: '0x9dba7f426b711d4893c11611eacf7cc334e7146b',
2525
+ config: {},
2526
+ signatures: [],
2527
+ } as never),
2528
+ ).toThrowErrorMatchingInlineSnapshot(
2529
+ `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"account":"0x9dba7f426b711d4893c11611eacf7cc334e7146b","config":{},"signatures":[]}\`) to a valid signature envelope.]`,
2530
+ )
2531
+ })
2532
+ })
2495
2533
  })
2496
2534
 
2497
2535
  describe('toRpc', () => {
@@ -2589,6 +2627,7 @@ describe('toRpc', () => {
2589
2627
  const rpc = SignatureEnvelope.toRpc(
2590
2628
  signature_keychain_p256,
2591
2629
  ) as SignatureEnvelope.KeychainRpc
2630
+ if (typeof rpc.signature === 'string') throw new Error('unreachable')
2592
2631
 
2593
2632
  expect(rpc.type).toBe('keychain')
2594
2633
  expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
@@ -2601,6 +2640,7 @@ describe('toRpc', () => {
2601
2640
  const rpc = SignatureEnvelope.toRpc(
2602
2641
  signature_keychain_webauthn,
2603
2642
  ) as SignatureEnvelope.KeychainRpc
2643
+ if (typeof rpc.signature === 'string') throw new Error('unreachable')
2604
2644
 
2605
2645
  expect(rpc.type).toBe('keychain')
2606
2646
  expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
@@ -2644,6 +2684,18 @@ describe('toRpc', () => {
2644
2684
  expect(rpc.keyId).toBeUndefined()
2645
2685
  })
2646
2686
  })
2687
+
2688
+ describe('multisig', () => {
2689
+ test('behavior: removes the transaction type byte', () => {
2690
+ const envelope = SignatureEnvelope.deserialize(
2691
+ `0x05${signature_multisig_rpc.slice(2)}`,
2692
+ )
2693
+
2694
+ expect(SignatureEnvelope.toRpc(envelope)).toMatchInlineSnapshot(
2695
+ `"${signature_multisig_rpc}"`,
2696
+ )
2697
+ })
2698
+ })
2647
2699
  })
2648
2700
 
2649
2701
  describe('roundtrip: toRpc <-> fromRpc', () => {
@@ -2888,7 +2940,7 @@ describe('multisig', () => {
2888
2940
  const nested =
2889
2941
  '0x05f8f0949969e2243075b27a8eab61009c59b77ab13b83f6f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d6944f9f5b162a7464bcc260ce44d7ae0d935f9c583701f89cb89a05f897944f9f5b162a7464bcc260ce44d7ae0d935f9c5837f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
2890
2942
 
2891
- test('example: decodes and re-encodes the frozen initial witness', () => {
2943
+ test('example: decodes and re-encodes the frozen initial signature', () => {
2892
2944
  const envelope = SignatureEnvelope.deserialize(initial)
2893
2945
 
2894
2946
  expect(envelope).toMatchInlineSnapshot(`
@@ -2921,7 +2973,7 @@ describe('multisig', () => {
2921
2973
  expect(SignatureEnvelope.serialize(envelope)).toBe(initial)
2922
2974
  })
2923
2975
 
2924
- test('example: decodes and re-encodes the frozen current witness', () => {
2976
+ test('example: decodes and re-encodes the frozen current signature', () => {
2925
2977
  const envelope = SignatureEnvelope.deserialize(current)
2926
2978
 
2927
2979
  expect(envelope).toMatchInlineSnapshot(`
@@ -2954,7 +3006,7 @@ describe('multisig', () => {
2954
3006
  expect(SignatureEnvelope.serialize(envelope)).toBe(current)
2955
3007
  })
2956
3008
 
2957
- test('example: decodes and re-encodes the frozen nested witness', () => {
3009
+ test('example: decodes and re-encodes the frozen nested signature', () => {
2958
3010
  const envelope = SignatureEnvelope.deserialize(nested)
2959
3011
 
2960
3012
  expect(envelope).toMatchInlineSnapshot(`
@@ -3042,7 +3094,7 @@ describe('multisig', () => {
3042
3094
  `)
3043
3095
  })
3044
3096
 
3045
- test('behavior: round trips initial and current RPC witnesses', () => {
3097
+ test('behavior: round trips initial and current RPC signatures', () => {
3046
3098
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3047
3099
  const currentEnvelope = SignatureEnvelope.deserialize(current)
3048
3100
  const rpc = {
@@ -3052,50 +3104,8 @@ describe('multisig', () => {
3052
3104
 
3053
3105
  expect(rpc).toMatchInlineSnapshot(`
3054
3106
  {
3055
- "current": {
3056
- "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
3057
- "config": {
3058
- "owners": [
3059
- {
3060
- "owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
3061
- "weight": 1,
3062
- },
3063
- ],
3064
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
3065
- "threshold": 1,
3066
- "version": "0x1",
3067
- },
3068
- "signatures": [
3069
- {
3070
- "r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
3071
- "s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
3072
- "type": "secp256k1",
3073
- "yParity": "0x0",
3074
- },
3075
- ],
3076
- },
3077
- "initial": {
3078
- "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
3079
- "config": {
3080
- "owners": [
3081
- {
3082
- "owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
3083
- "weight": 1,
3084
- },
3085
- ],
3086
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
3087
- "threshold": 1,
3088
- "version": "0x0",
3089
- },
3090
- "signatures": [
3091
- {
3092
- "r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
3093
- "s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
3094
- "type": "secp256k1",
3095
- "yParity": "0x0",
3096
- },
3097
- ],
3098
- },
3107
+ "current": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b",
3108
+ "initial": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
3099
3109
  }
3100
3110
  `)
3101
3111
  expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
@@ -3106,7 +3116,7 @@ describe('multisig', () => {
3106
3116
  )
3107
3117
  })
3108
3118
 
3109
- test('behavior: derives the account only for an initial witness', () => {
3119
+ test('behavior: derives the account only for an initial config', () => {
3110
3120
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3111
3121
  if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
3112
3122
  if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
@@ -3177,7 +3187,7 @@ describe('multisig', () => {
3177
3187
  `)
3178
3188
  })
3179
3189
 
3180
- test('behavior: accepts initial and current nested witnesses', () => {
3190
+ test('behavior: accepts initial and current nested signatures', () => {
3181
3191
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3182
3192
  const currentEnvelope = SignatureEnvelope.deserialize(current)
3183
3193
  if (
@@ -3219,7 +3229,7 @@ describe('multisig', () => {
3219
3229
  )
3220
3230
  })
3221
3231
 
3222
- test('error: rejects a self-owned current witness', () => {
3232
+ test('error: rejects a self-owned current signature', () => {
3223
3233
  const envelope = SignatureEnvelope.deserialize(current)
3224
3234
  if (envelope.type !== 'multisig') throw new Error('unreachable')
3225
3235
  const account = envelope.config.owners[0]!.owner