ox 0.14.41 → 0.14.43

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 (73) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/_cjs/tempo/MultisigOperation.js +27 -0
  3. package/_cjs/tempo/MultisigOperation.js.map +1 -1
  4. package/_cjs/tempo/MultisigSimulation.js +115 -0
  5. package/_cjs/tempo/MultisigSimulation.js.map +1 -0
  6. package/_cjs/tempo/SignatureEnvelope.js +12 -21
  7. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  8. package/_cjs/tempo/TransactionRequest.js +7 -7
  9. package/_cjs/tempo/TransactionRequest.js.map +1 -1
  10. package/_cjs/tempo/index.js +2 -2
  11. package/_cjs/tempo/index.js.map +1 -1
  12. package/_cjs/version.js +1 -1
  13. package/_esm/tempo/MultisigOperation.js +48 -0
  14. package/_esm/tempo/MultisigOperation.js.map +1 -1
  15. package/_esm/tempo/MultisigSimulation.js +145 -0
  16. package/_esm/tempo/MultisigSimulation.js.map +1 -0
  17. package/_esm/tempo/SignatureEnvelope.js +12 -21
  18. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  19. package/_esm/tempo/TransactionRequest.js +7 -7
  20. package/_esm/tempo/TransactionRequest.js.map +1 -1
  21. package/_esm/tempo/index.js +4 -4
  22. package/_esm/tempo/index.js.map +1 -1
  23. package/_esm/version.js +1 -1
  24. package/_types/tempo/KeyAuthorization.d.ts +1 -1
  25. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  26. package/_types/tempo/MultisigOperation.d.ts +36 -0
  27. package/_types/tempo/MultisigOperation.d.ts.map +1 -1
  28. package/_types/tempo/MultisigSimulation.d.ts +132 -0
  29. package/_types/tempo/MultisigSimulation.d.ts.map +1 -0
  30. package/_types/tempo/RpcSchemaTempo.d.ts +10 -0
  31. package/_types/tempo/RpcSchemaTempo.d.ts.map +1 -1
  32. package/_types/tempo/SignatureEnvelope.d.ts +10 -34
  33. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  34. package/_types/tempo/TransactionRequest.d.ts +9 -9
  35. package/_types/tempo/TransactionRequest.d.ts.map +1 -1
  36. package/_types/tempo/index.d.ts +4 -4
  37. package/_types/tempo/index.d.ts.map +1 -1
  38. package/_types/version.d.ts +1 -1
  39. package/package.json +9 -9
  40. package/tempo/AuthorizationTempo.test.ts +4 -0
  41. package/tempo/KeyAuthorization.test-d.ts +1 -12
  42. package/tempo/KeyAuthorization.test.ts +34 -6
  43. package/tempo/KeyAuthorization.ts +3 -3
  44. package/tempo/MultisigOperation.test.ts +94 -0
  45. package/tempo/MultisigOperation.ts +83 -0
  46. package/tempo/MultisigSimulation/package.json +6 -0
  47. package/tempo/MultisigSimulation.test-d/package.json +6 -0
  48. package/tempo/MultisigSimulation.test-d.ts +33 -0
  49. package/tempo/MultisigSimulation.test.ts +284 -0
  50. package/tempo/MultisigSimulation.ts +266 -0
  51. package/tempo/RpcSchemaTempo.test-d.ts +14 -4
  52. package/tempo/RpcSchemaTempo.ts +9 -0
  53. package/tempo/SignatureEnvelope.test-d.ts +8 -44
  54. package/tempo/SignatureEnvelope.test.ts +92 -51
  55. package/tempo/SignatureEnvelope.ts +27 -61
  56. package/tempo/Transaction.test.ts +61 -0
  57. package/tempo/TransactionRequest.test-d.ts +9 -9
  58. package/tempo/TransactionRequest.test.ts +122 -49
  59. package/tempo/TransactionRequest.ts +20 -16
  60. package/tempo/index.ts +4 -4
  61. package/tempo/multisig.e2e.test.ts +51 -1
  62. package/version.ts +1 -1
  63. package/_cjs/tempo/MultisigWitness.js +0 -92
  64. package/_cjs/tempo/MultisigWitness.js.map +0 -1
  65. package/_esm/tempo/MultisigWitness.js +0 -123
  66. package/_esm/tempo/MultisigWitness.js.map +0 -1
  67. package/_types/tempo/MultisigWitness.d.ts +0 -106
  68. package/_types/tempo/MultisigWitness.d.ts.map +0 -1
  69. package/tempo/MultisigWitness/package.json +0 -6
  70. package/tempo/MultisigWitness.test-d/package.json +0 -6
  71. package/tempo/MultisigWitness.test-d.ts +0 -42
  72. package/tempo/MultisigWitness.test.ts +0 -289
  73. package/tempo/MultisigWitness.ts +0 -210
@@ -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,20 +1,23 @@
1
1
  import type { Provider } from 'ox'
2
2
  import type {
3
3
  KeyAuthorization,
4
+ MultisigConfig,
4
5
  MultisigOperation,
5
- MultisigWitness,
6
+ MultisigSimulation,
6
7
  RpcSchemaTempo,
7
8
  } from 'ox/tempo'
8
9
  import { expectTypeOf, test } from 'vitest'
10
+ import type * as Address from '../core/Address.js'
9
11
  import type * as Hex from '../core/Hex.js'
10
12
 
11
13
  declare const provider: Provider.Provider<{ schema: RpcSchemaTempo.Multisig }>
12
14
  declare const tempoProvider: Provider.Provider<{
13
15
  schema: RpcSchemaTempo.Tempo
14
16
  }>
17
+ declare const address: Address.Address
15
18
  declare const hash: Hex.Hex
16
19
  declare const keyAuthorization: KeyAuthorization.Rpc
17
- declare const multisigWitness: MultisigWitness.Rpc
20
+ declare const multisigSimulation: MultisigSimulation.Rpc
18
21
  declare const serializedTransaction: Hex.Hex
19
22
  declare const signature: Hex.Hex
20
23
 
@@ -52,6 +55,13 @@ test('multisig provider methods', () => {
52
55
  }),
53
56
  ).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
54
57
 
58
+ expectTypeOf(
59
+ provider.request({
60
+ method: 'multisig_getConfig',
61
+ params: [{ address }],
62
+ }),
63
+ ).resolves.toEqualTypeOf<MultisigConfig.Rpc | null>()
64
+
55
65
  expectTypeOf(
56
66
  provider.request({
57
67
  method: 'multisig_getOperation',
@@ -60,7 +70,7 @@ test('multisig provider methods', () => {
60
70
  ).resolves.toEqualTypeOf<MultisigOperation.Rpc | null>()
61
71
  })
62
72
 
63
- test('tempo simulation accepts multisig witnesses', () => {
73
+ test('tempo simulation accepts multisig specs', () => {
64
74
  tempoProvider.request({
65
75
  method: 'tempo_simulateV1',
66
76
  params: [
@@ -69,7 +79,7 @@ test('tempo simulation accepts multisig witnesses', () => {
69
79
  {
70
80
  calls: [
71
81
  {
72
- multisigWitness,
82
+ multisigSimulation,
73
83
  },
74
84
  ],
75
85
  },
@@ -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
 
@@ -82,6 +84,13 @@ export type Multisig = RpcSchema.From<
82
84
  }
83
85
  ReturnType: MultisigOperation.KeyAuthorizationRpc
84
86
  }
87
+ | {
88
+ Request: {
89
+ method: 'multisig_getConfig'
90
+ params: [{ address: Address.Address }]
91
+ }
92
+ ReturnType: MultisigConfig.Rpc | null
93
+ }
85
94
  | {
86
95
  Request: {
87
96
  method: 'multisig_getOperation'
@@ -34,20 +34,10 @@ test('toRpc preserves the signature type', () => {
34
34
  ).toEqualTypeOf<SignatureEnvelope.Secp256k1Rpc>()
35
35
  })
36
36
 
37
- test('MultisigRpc carries one complete witness shape', () => {
38
- const rpc = {
39
- account: '0x1111111111111111111111111111111111111111',
40
- config: { ...config, version: 0 },
41
- signatures: [signatureRpc],
42
- } as const satisfies SignatureEnvelope.MultisigRpc
37
+ test('MultisigRpc contains an untagged serialized multisig signature', () => {
38
+ const rpc = '0xf8' as const satisfies SignatureEnvelope.MultisigRpc
43
39
 
44
- expectTypeOf(rpc.config).toMatchTypeOf<MultisigConfig.Config<number>>()
45
- expectTypeOf(rpc.signatures).toMatchTypeOf<
46
- readonly SignatureEnvelope.SignatureEnvelopeRpc[]
47
- >()
48
- expectTypeOf<
49
- SignatureEnvelope.GetType<typeof rpc>
50
- >().toEqualTypeOf<'multisig'>()
40
+ expectTypeOf(rpc).toMatchTypeOf<`0x${string}`>()
51
41
  })
52
42
 
53
43
  test('from derives an initial account', () => {
@@ -75,40 +65,14 @@ test('from requires an account for a current config', () => {
75
65
  expectTypeOf(multisig).toMatchTypeOf<SignatureEnvelope.Multisig>()
76
66
  })
77
67
 
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: { ...config, version: 0 },
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 = {
68
+ test('MultisigRpc rejects structured signatures', () => {
69
+ const structured = {
102
70
  account: '0x1111111111111111111111111111111111111111',
103
71
  config: { ...config, version: 0 },
104
72
  signatures: [signatureRpc],
105
- type: 'multisig',
106
73
  } as const
107
- // @ts-expect-error Multisig RPC signatures are untagged.
108
- const taggedRpc: SignatureEnvelope.MultisigRpc = tagged
74
+ // @ts-expect-error Multisig RPC signatures are serialized hex.
75
+ const rpc: SignatureEnvelope.MultisigRpc = structured
109
76
 
110
- expectTypeOf(accountOnlyRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
111
- expectTypeOf(initRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
112
- expectTypeOf(serializedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
113
- expectTypeOf(taggedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
77
+ expectTypeOf(rpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
114
78
  })
@@ -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', () => {
@@ -2468,6 +2471,72 @@ describe('fromRpc', () => {
2468
2471
  expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
2469
2472
  })
2470
2473
  })
2474
+
2475
+ describe('multisig', () => {
2476
+ test('behavior: converts an untagged serialized multisig signature', () => {
2477
+ expect(
2478
+ SignatureEnvelope.fromRpc(signature_multisig_rpc),
2479
+ ).toMatchInlineSnapshot(`
2480
+ {
2481
+ "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
2482
+ "config": {
2483
+ "owners": [
2484
+ {
2485
+ "owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
2486
+ "weight": 1,
2487
+ },
2488
+ ],
2489
+ "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
2490
+ "threshold": 1,
2491
+ "version": 0n,
2492
+ },
2493
+ "signatures": [
2494
+ {
2495
+ "signature": {
2496
+ "r": 60871800714128149016591846789173983752390955456021923556141688418770269698437n,
2497
+ "s": 24367763726302312398084372528434045669788111037623467391175388828437596076276n,
2498
+ "yParity": 0,
2499
+ },
2500
+ "type": "secp256k1",
2501
+ },
2502
+ ],
2503
+ "type": "multisig",
2504
+ }
2505
+ `)
2506
+ })
2507
+
2508
+ test('error: rejects a payload with the transaction type byte', () => {
2509
+ expect(() =>
2510
+ SignatureEnvelope.fromRpc(`0x05${signature_multisig_rpc.slice(2)}`),
2511
+ ).toThrowErrorMatchingInlineSnapshot(`
2512
+ [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: invalid multisig wire shape: expected exactly three fields
2513
+
2514
+ Serialized: 0x0505f897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b]
2515
+ `)
2516
+ })
2517
+
2518
+ test('error: rejects trailing bytes', () => {
2519
+ expect(() =>
2520
+ SignatureEnvelope.fromRpc(`${signature_multisig_rpc}00`),
2521
+ ).toThrowErrorMatchingInlineSnapshot(`
2522
+ [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: invalid multisig RPC signature
2523
+
2524
+ Serialized: 0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b00]
2525
+ `)
2526
+ })
2527
+
2528
+ test('error: rejects a structured multisig signature', () => {
2529
+ expect(() =>
2530
+ SignatureEnvelope.fromRpc({
2531
+ account: '0x9dba7f426b711d4893c11611eacf7cc334e7146b',
2532
+ config: {},
2533
+ signatures: [],
2534
+ } as never),
2535
+ ).toThrowErrorMatchingInlineSnapshot(
2536
+ `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"account":"0x9dba7f426b711d4893c11611eacf7cc334e7146b","config":{},"signatures":[]}\`) to a valid signature envelope.]`,
2537
+ )
2538
+ })
2539
+ })
2471
2540
  })
2472
2541
 
2473
2542
  describe('toRpc', () => {
@@ -2565,6 +2634,7 @@ describe('toRpc', () => {
2565
2634
  const rpc = SignatureEnvelope.toRpc(
2566
2635
  signature_keychain_p256,
2567
2636
  ) as SignatureEnvelope.KeychainRpc
2637
+ if (typeof rpc.signature === 'string') throw new Error('unreachable')
2568
2638
 
2569
2639
  expect(rpc.type).toBe('keychain')
2570
2640
  expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
@@ -2577,6 +2647,7 @@ describe('toRpc', () => {
2577
2647
  const rpc = SignatureEnvelope.toRpc(
2578
2648
  signature_keychain_webauthn,
2579
2649
  ) as SignatureEnvelope.KeychainRpc
2650
+ if (typeof rpc.signature === 'string') throw new Error('unreachable')
2580
2651
 
2581
2652
  expect(rpc.type).toBe('keychain')
2582
2653
  expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
@@ -2620,6 +2691,18 @@ describe('toRpc', () => {
2620
2691
  expect(rpc.keyId).toBeUndefined()
2621
2692
  })
2622
2693
  })
2694
+
2695
+ describe('multisig', () => {
2696
+ test('behavior: removes the transaction type byte', () => {
2697
+ const envelope = SignatureEnvelope.deserialize(
2698
+ `0x05${signature_multisig_rpc.slice(2)}`,
2699
+ )
2700
+
2701
+ expect(SignatureEnvelope.toRpc(envelope)).toMatchInlineSnapshot(
2702
+ `"${signature_multisig_rpc}"`,
2703
+ )
2704
+ })
2705
+ })
2623
2706
  })
2624
2707
 
2625
2708
  describe('roundtrip: toRpc <-> fromRpc', () => {
@@ -2860,7 +2943,7 @@ describe('multisig', () => {
2860
2943
  const nested =
2861
2944
  '0x05f8f0949969e2243075b27a8eab61009c59b77ab13b83f6f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d6944f9f5b162a7464bcc260ce44d7ae0d935f9c583701f89cb89a05f897944f9f5b162a7464bcc260ce44d7ae0d935f9c5837f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
2862
2945
 
2863
- test('example: decodes and re-encodes the frozen initial witness', () => {
2946
+ test('example: decodes and re-encodes the frozen initial signature', () => {
2864
2947
  const envelope = SignatureEnvelope.deserialize(initial)
2865
2948
 
2866
2949
  expect(envelope).toMatchInlineSnapshot(`
@@ -2893,7 +2976,7 @@ describe('multisig', () => {
2893
2976
  expect(SignatureEnvelope.serialize(envelope)).toBe(initial)
2894
2977
  })
2895
2978
 
2896
- test('example: decodes and re-encodes the frozen current witness', () => {
2979
+ test('example: decodes and re-encodes the frozen current signature', () => {
2897
2980
  const envelope = SignatureEnvelope.deserialize(current)
2898
2981
 
2899
2982
  expect(envelope).toMatchInlineSnapshot(`
@@ -2926,7 +3009,7 @@ describe('multisig', () => {
2926
3009
  expect(SignatureEnvelope.serialize(envelope)).toBe(current)
2927
3010
  })
2928
3011
 
2929
- test('example: decodes and re-encodes the frozen nested witness', () => {
3012
+ test('example: decodes and re-encodes the frozen nested signature', () => {
2930
3013
  const envelope = SignatureEnvelope.deserialize(nested)
2931
3014
 
2932
3015
  expect(envelope).toMatchInlineSnapshot(`
@@ -3014,7 +3097,7 @@ describe('multisig', () => {
3014
3097
  `)
3015
3098
  })
3016
3099
 
3017
- test('behavior: round trips initial and current RPC witnesses', () => {
3100
+ test('behavior: round trips initial and current RPC signatures', () => {
3018
3101
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3019
3102
  const currentEnvelope = SignatureEnvelope.deserialize(current)
3020
3103
  const rpc = {
@@ -3024,50 +3107,8 @@ describe('multisig', () => {
3024
3107
 
3025
3108
  expect(rpc).toMatchInlineSnapshot(`
3026
3109
  {
3027
- "current": {
3028
- "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
3029
- "config": {
3030
- "owners": [
3031
- {
3032
- "owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
3033
- "weight": 1,
3034
- },
3035
- ],
3036
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
3037
- "threshold": 1,
3038
- "version": 1,
3039
- },
3040
- "signatures": [
3041
- {
3042
- "r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
3043
- "s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
3044
- "type": "secp256k1",
3045
- "yParity": "0x0",
3046
- },
3047
- ],
3048
- },
3049
- "initial": {
3050
- "account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
3051
- "config": {
3052
- "owners": [
3053
- {
3054
- "owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
3055
- "weight": 1,
3056
- },
3057
- ],
3058
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
3059
- "threshold": 1,
3060
- "version": 0,
3061
- },
3062
- "signatures": [
3063
- {
3064
- "r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
3065
- "s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
3066
- "type": "secp256k1",
3067
- "yParity": "0x0",
3068
- },
3069
- ],
3070
- },
3110
+ "current": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b",
3111
+ "initial": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
3071
3112
  }
3072
3113
  `)
3073
3114
  expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
@@ -3078,7 +3119,7 @@ describe('multisig', () => {
3078
3119
  )
3079
3120
  })
3080
3121
 
3081
- test('behavior: derives the account only for an initial witness', () => {
3122
+ test('behavior: derives the account only for an initial config', () => {
3082
3123
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3083
3124
  if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
3084
3125
  if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
@@ -3149,7 +3190,7 @@ describe('multisig', () => {
3149
3190
  `)
3150
3191
  })
3151
3192
 
3152
- test('behavior: accepts initial and current nested witnesses', () => {
3193
+ test('behavior: accepts initial and current nested signatures', () => {
3153
3194
  const initialEnvelope = SignatureEnvelope.deserialize(initial)
3154
3195
  const currentEnvelope = SignatureEnvelope.deserialize(current)
3155
3196
  if (
@@ -3191,7 +3232,7 @@ describe('multisig', () => {
3191
3232
  )
3192
3233
  })
3193
3234
 
3194
- test('error: rejects a self-owned current witness', () => {
3235
+ test('error: rejects a self-owned current signature', () => {
3195
3236
  const envelope = SignatureEnvelope.deserialize(current)
3196
3237
  if (envelope.type !== 'multisig') throw new Error('unreachable')
3197
3238
  const account = envelope.config.owners[0]!.owner