ox 0.14.38 → 0.14.40

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 (69) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/_cjs/tempo/MultisigConfig.js +71 -15
  3. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  4. package/_cjs/tempo/MultisigOperation.js +43 -35
  5. package/_cjs/tempo/MultisigOperation.js.map +1 -1
  6. package/_cjs/tempo/MultisigWitness.js +92 -0
  7. package/_cjs/tempo/MultisigWitness.js.map +1 -0
  8. package/_cjs/tempo/SignatureEnvelope.js +71 -108
  9. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  10. package/_cjs/tempo/TransactionRequest.js +7 -1
  11. package/_cjs/tempo/TransactionRequest.js.map +1 -1
  12. package/_cjs/tempo/index.js +2 -1
  13. package/_cjs/tempo/index.js.map +1 -1
  14. package/_cjs/version.js +1 -1
  15. package/_esm/tempo/MultisigConfig.js +140 -71
  16. package/_esm/tempo/MultisigConfig.js.map +1 -1
  17. package/_esm/tempo/MultisigOperation.js +43 -36
  18. package/_esm/tempo/MultisigOperation.js.map +1 -1
  19. package/_esm/tempo/MultisigWitness.js +123 -0
  20. package/_esm/tempo/MultisigWitness.js.map +1 -0
  21. package/_esm/tempo/SignatureEnvelope.js +84 -143
  22. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  23. package/_esm/tempo/TransactionRequest.js +7 -1
  24. package/_esm/tempo/TransactionRequest.js.map +1 -1
  25. package/_esm/tempo/index.js +9 -0
  26. package/_esm/tempo/index.js.map +1 -1
  27. package/_esm/version.js +1 -1
  28. package/_types/tempo/KeyAuthorization.d.ts +1 -1
  29. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  30. package/_types/tempo/MultisigConfig.d.ts +137 -73
  31. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  32. package/_types/tempo/MultisigOperation.d.ts +1 -1
  33. package/_types/tempo/MultisigOperation.d.ts.map +1 -1
  34. package/_types/tempo/MultisigWitness.d.ts +106 -0
  35. package/_types/tempo/MultisigWitness.d.ts.map +1 -0
  36. package/_types/tempo/SignatureEnvelope.d.ts +81 -75
  37. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  38. package/_types/tempo/TransactionRequest.d.ts +6 -3
  39. package/_types/tempo/TransactionRequest.d.ts.map +1 -1
  40. package/_types/tempo/index.d.ts +9 -0
  41. package/_types/tempo/index.d.ts.map +1 -1
  42. package/_types/version.d.ts +1 -1
  43. package/package.json +21 -1
  44. package/tempo/KeyAuthorization.test-d.ts +12 -0
  45. package/tempo/KeyAuthorization.test.ts +54 -0
  46. package/tempo/KeyAuthorization.ts +1 -1
  47. package/tempo/MultisigConfig.test-d/package.json +6 -0
  48. package/tempo/MultisigConfig.test-d.ts +52 -0
  49. package/tempo/MultisigConfig.test.ts +291 -263
  50. package/tempo/MultisigConfig.ts +250 -95
  51. package/tempo/MultisigOperation.test-d.ts +6 -0
  52. package/tempo/MultisigOperation.test.ts +200 -81
  53. package/tempo/MultisigOperation.ts +42 -34
  54. package/tempo/MultisigWitness/package.json +6 -0
  55. package/tempo/MultisigWitness.test-d/package.json +6 -0
  56. package/tempo/MultisigWitness.test-d.ts +42 -0
  57. package/tempo/MultisigWitness.test.ts +289 -0
  58. package/tempo/MultisigWitness.ts +210 -0
  59. package/tempo/RpcSchemaTempo.test-d.ts +25 -0
  60. package/tempo/SignatureEnvelope.test-d.ts +57 -40
  61. package/tempo/SignatureEnvelope.test.ts +523 -480
  62. package/tempo/SignatureEnvelope.ts +250 -257
  63. package/tempo/TransactionRequest.test-d/package.json +6 -0
  64. package/tempo/TransactionRequest.test-d.ts +24 -0
  65. package/tempo/TransactionRequest.test.ts +102 -1
  66. package/tempo/TransactionRequest.ts +12 -2
  67. package/tempo/index.ts +9 -0
  68. package/tempo/multisig.e2e.test.ts +113 -86
  69. package/version.ts +1 -1
@@ -0,0 +1,210 @@
1
+ import type * 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 MultisigConfig from './MultisigConfig.js'
6
+ import type * as SignatureEnvelope from './SignatureEnvelope.js'
7
+
8
+ /** Native multisig owner approval used for RPC simulation. */
9
+ export type Approval<versionType = bigint> =
10
+ | NestedApproval<versionType>
11
+ | PrimitiveApproval
12
+
13
+ /** Native multisig witness used to construct an RPC simulation signature. */
14
+ export type MultisigWitness<versionType = bigint> = Compute<{
15
+ /** Account authorized by this witness. */
16
+ account: Address.Address
17
+ /** Owner approvals to model. */
18
+ approvals: readonly Approval<versionType>[]
19
+ /** Complete applicable configuration. */
20
+ config: MultisigConfig.Config<versionType>
21
+ }>
22
+
23
+ /** Nested native multisig owner approval used for RPC simulation. */
24
+ export type NestedApproval<versionType = bigint> = {
25
+ /** Approval type. */
26
+ type: 'multisig'
27
+ /** Depth-2 multisig witness. */
28
+ witness: NestedWitness<versionType>
29
+ }
30
+
31
+ /** Primitive approval in a depth-2 multisig simulation witness. */
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 witness used for RPC simulation. */
42
+ export type NestedWitness<versionType = bigint> = {
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<versionType>
49
+ }
50
+
51
+ /** Primitive owner approval used for RPC simulation. */
52
+ export type PrimitiveApproval = {
53
+ /** Optional signature-specific gas-estimation data. */
54
+ keyData?: Hex.Hex | undefined
55
+ /** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
56
+ keyType?: SignatureEnvelope.Type | undefined
57
+ /** Configured owner address. */
58
+ owner: Address.Address
59
+ /** Approval type. */
60
+ type: 'primitive'
61
+ }
62
+
63
+ /** JSON-RPC representation of a native multisig simulation witness. */
64
+ export type Rpc = MultisigWitness<number>
65
+
66
+ /**
67
+ * Converts a JSON-RPC multisig witness to its domain representation.
68
+ *
69
+ * @example
70
+ * ```ts twoslash
71
+ * // @noErrors
72
+ * import { MultisigWitness } from 'ox/tempo'
73
+ *
74
+ * const witness = MultisigWitness.fromRpc(witnessRpc)
75
+ * ```
76
+ *
77
+ * @param witness - JSON-RPC multisig witness.
78
+ * @returns The multisig witness with bigint configuration versions.
79
+ */
80
+ export function fromRpc(witness: Rpc): MultisigWitness {
81
+ const { account, approvals, config } = witness
82
+ assertApprovalCount(approvals)
83
+ return {
84
+ account,
85
+ approvals: approvals.map((approval) => {
86
+ if (approval.type === 'primitive') return approval
87
+ assertApprovalCount(approval.witness.approvals)
88
+ return {
89
+ type: 'multisig',
90
+ witness: {
91
+ account: approval.witness.account,
92
+ approvals: approval.witness.approvals,
93
+ config: MultisigConfig.from(approval.witness.config),
94
+ },
95
+ }
96
+ }),
97
+ config: MultisigConfig.from(config),
98
+ }
99
+ }
100
+
101
+ export declare namespace fromRpc {
102
+ /** Error type for `fromRpc`. */
103
+ export type ErrorType =
104
+ | InvalidWitnessError
105
+ | MultisigConfig.assert.ErrorType
106
+ | Errors.GlobalErrorType
107
+ }
108
+
109
+ /**
110
+ * Converts a multisig witness to its JSON-RPC representation.
111
+ *
112
+ * @example
113
+ * ```ts twoslash
114
+ * // @noErrors
115
+ * import { MultisigWitness } from 'ox/tempo'
116
+ *
117
+ * const witnessRpc = MultisigWitness.toRpc(witness)
118
+ * ```
119
+ *
120
+ * @param witness - Multisig witness.
121
+ * @returns The JSON-RPC multisig witness with numeric configuration versions.
122
+ */
123
+ export function toRpc(witness: MultisigWitness): Rpc {
124
+ const { account, approvals, config } = witness
125
+ assertApprovalCount(approvals)
126
+ return {
127
+ account,
128
+ approvals: approvals.map((approval) => {
129
+ if (approval.type === 'primitive')
130
+ return {
131
+ ...approval,
132
+ ...(typeof approval.keyData !== 'undefined'
133
+ ? { keyData: shimKeyData(approval.keyData) }
134
+ : {}),
135
+ }
136
+ assertApprovalCount(approval.witness.approvals)
137
+ return {
138
+ type: 'multisig',
139
+ witness: {
140
+ account: approval.witness.account,
141
+ approvals: approval.witness.approvals.map((approval) => ({
142
+ ...approval,
143
+ ...(typeof approval.keyData !== 'undefined'
144
+ ? { keyData: shimKeyData(approval.keyData) }
145
+ : {}),
146
+ })),
147
+ config: configToRpc(approval.witness.config),
148
+ },
149
+ }
150
+ }),
151
+ config: configToRpc(config),
152
+ }
153
+ }
154
+
155
+ export declare namespace toRpc {
156
+ /** Error type for `toRpc`. */
157
+ export type ErrorType =
158
+ | Hex.fromNumber.ErrorType
159
+ | Hex.toNumber.ErrorType
160
+ | InvalidWitnessError
161
+ | MultisigConfig.assert.ErrorType
162
+ | Errors.GlobalErrorType
163
+ }
164
+
165
+ /** @internal */
166
+ function assertApprovalCount(approvals: readonly unknown[]) {
167
+ if (approvals.length > MultisigConfig.maxSignatures)
168
+ throw new InvalidWitnessError({
169
+ reason: `approval count exceeds ${MultisigConfig.maxSignatures}`,
170
+ })
171
+ }
172
+
173
+ /** @internal */
174
+ function configToRpc(
175
+ config: MultisigConfig.Config,
176
+ ): MultisigConfig.Config<number> {
177
+ const value = MultisigConfig.from(config)
178
+ return {
179
+ ...value,
180
+ version: Hex.toNumber(Hex.fromNumber(Number(value.version))),
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Shims key data longer than 4 bytes into a 2-byte big-endian length hint.
186
+ * The node's gas estimator only accepts 1, 2, or 4-byte key data as a
187
+ * signature-size hint; anything else silently falls back to the default.
188
+ * @internal
189
+ */
190
+ function shimKeyData(data: Hex.Hex): Hex.Hex {
191
+ const size = Hex.size(data)
192
+ if (size <= 4) return data
193
+ return Hex.fromNumber(size, { size: 2 })
194
+ }
195
+
196
+ /** Thrown when a native multisig witness is invalid. */
197
+ export class InvalidWitnessError extends Errors.BaseError {
198
+ override readonly name = 'MultisigWitness.InvalidWitnessError'
199
+ constructor(options: InvalidWitnessError.Options) {
200
+ super(`Invalid multisig witness: ${options.reason}.`)
201
+ }
202
+ }
203
+
204
+ export declare namespace InvalidWitnessError {
205
+ /** Error construction options. */
206
+ export type Options = {
207
+ /** Validation failure. */
208
+ reason: string
209
+ }
210
+ }
@@ -2,14 +2,19 @@ import type { Provider } from 'ox'
2
2
  import type {
3
3
  KeyAuthorization,
4
4
  MultisigOperation,
5
+ MultisigWitness,
5
6
  RpcSchemaTempo,
6
7
  } from 'ox/tempo'
7
8
  import { expectTypeOf, test } from 'vitest'
8
9
  import type * as Hex from '../core/Hex.js'
9
10
 
10
11
  declare const provider: Provider.Provider<{ schema: RpcSchemaTempo.Multisig }>
12
+ declare const tempoProvider: Provider.Provider<{
13
+ schema: RpcSchemaTempo.Tempo
14
+ }>
11
15
  declare const hash: Hex.Hex
12
16
  declare const keyAuthorization: KeyAuthorization.Rpc
17
+ declare const multisigWitness: MultisigWitness.Rpc
13
18
  declare const serializedTransaction: Hex.Hex
14
19
  declare const signature: Hex.Hex
15
20
 
@@ -54,3 +59,23 @@ test('multisig provider methods', () => {
54
59
  }),
55
60
  ).resolves.toEqualTypeOf<MultisigOperation.Rpc | null>()
56
61
  })
62
+
63
+ test('tempo simulation accepts multisig witnesses', () => {
64
+ tempoProvider.request({
65
+ method: 'tempo_simulateV1',
66
+ params: [
67
+ {
68
+ blockStateCalls: [
69
+ {
70
+ calls: [
71
+ {
72
+ multisigWitness,
73
+ },
74
+ ],
75
+ },
76
+ ],
77
+ },
78
+ 'latest',
79
+ ],
80
+ })
81
+ })
@@ -1,4 +1,5 @@
1
1
  import { expectTypeOf, test } from 'vitest'
2
+ import * as MultisigConfig from './MultisigConfig.js'
2
3
  import * as SignatureEnvelope from './SignatureEnvelope.js'
3
4
 
4
5
  const signatureRpc = {
@@ -17,67 +18,81 @@ const signature = {
17
18
  type: 'secp256k1',
18
19
  } as const satisfies SignatureEnvelope.Secp256k1
19
20
 
21
+ const config = MultisigConfig.from({
22
+ owners: [
23
+ {
24
+ owner: '0x1111111111111111111111111111111111111111',
25
+ weight: 1,
26
+ },
27
+ ],
28
+ threshold: 1,
29
+ })
30
+
20
31
  test('toRpc preserves the signature type', () => {
21
32
  expectTypeOf(
22
33
  SignatureEnvelope.toRpc(signature),
23
34
  ).toEqualTypeOf<SignatureEnvelope.Secp256k1Rpc>()
24
35
  })
25
36
 
26
- test('MultisigRpc uses static initialized and bootstrap shapes', () => {
27
- const initialized = {
37
+ test('MultisigRpc carries one complete witness shape', () => {
38
+ const rpc = {
28
39
  account: '0x1111111111111111111111111111111111111111',
40
+ config: MultisigConfig.toRpc(config),
29
41
  signatures: [signatureRpc],
30
42
  } as const satisfies SignatureEnvelope.MultisigRpc
31
- const bootstrap = {
32
- init: {
33
- owners: [
34
- {
35
- owner: '0x1111111111111111111111111111111111111111',
36
- weight: 1,
37
- },
38
- ],
39
- threshold: 1,
40
- },
41
- signatures: [signatureRpc],
42
- } as const satisfies SignatureEnvelope.MultisigRpc
43
- const bootstrapWithAccountUndefined: SignatureEnvelope.MultisigRpc = {
44
- ...bootstrap,
45
- account: undefined,
46
- }
47
43
 
48
- expectTypeOf(initialized.signatures).toMatchTypeOf<
49
- readonly SignatureEnvelope.SignatureEnvelopeRpc[]
50
- >()
51
- expectTypeOf(bootstrap.signatures).toMatchTypeOf<
44
+ expectTypeOf(rpc.config).toMatchTypeOf<MultisigConfig.Rpc>()
45
+ expectTypeOf(rpc.signatures).toMatchTypeOf<
52
46
  readonly SignatureEnvelope.SignatureEnvelopeRpc[]
53
47
  >()
54
- expectTypeOf(
55
- bootstrapWithAccountUndefined,
56
- ).toMatchTypeOf<SignatureEnvelope.MultisigRpc>()
57
48
  expectTypeOf<
58
- SignatureEnvelope.GetType<typeof bootstrap>
49
+ SignatureEnvelope.GetType<typeof rpc>
59
50
  >().toEqualTypeOf<'multisig'>()
60
51
  })
61
52
 
62
- test('MultisigRpc rejects legacy shapes', () => {
63
- const combined = {
53
+ test('from derives an initial account', () => {
54
+ const multisig = SignatureEnvelope.from({
55
+ config,
56
+ signatures: [signature],
57
+ })
58
+
59
+ expectTypeOf(multisig).toMatchTypeOf<SignatureEnvelope.Multisig>()
60
+ expectTypeOf(multisig.config.version).toEqualTypeOf<bigint>()
61
+ })
62
+
63
+ test('from requires an account for a current config', () => {
64
+ // @ts-expect-error Current configurations require an explicit account.
65
+ SignatureEnvelope.from({
66
+ config: { ...config, version: 1 },
67
+ signatures: [signature],
68
+ })
69
+
70
+ const multisig = SignatureEnvelope.from({
71
+ account: '0x2222222222222222222222222222222222222222',
72
+ config: { ...config, version: 1 },
73
+ signatures: [signature],
74
+ })
75
+ expectTypeOf(multisig).toMatchTypeOf<SignatureEnvelope.Multisig>()
76
+ })
77
+
78
+ test('MultisigRpc rejects old witness shapes', () => {
79
+ const accountOnly = {
64
80
  account: '0x1111111111111111111111111111111111111111',
65
- init: {
66
- owners: [
67
- {
68
- owner: '0x1111111111111111111111111111111111111111',
69
- weight: 1,
70
- },
71
- ],
72
- threshold: 1,
73
- },
74
81
  signatures: [signatureRpc],
75
82
  } as const
76
- // @ts-expect-error Bootstrap RPC signatures omit `account`.
77
- const combinedRpc: SignatureEnvelope.MultisigRpc = combined
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
78
92
 
79
93
  const serialized = {
80
94
  account: '0x1111111111111111111111111111111111111111',
95
+ config: MultisigConfig.toRpc(config),
81
96
  signatures: ['0x1234'],
82
97
  } as const
83
98
  // @ts-expect-error Owner approvals use structured RPC envelopes.
@@ -85,13 +100,15 @@ test('MultisigRpc rejects legacy shapes', () => {
85
100
 
86
101
  const tagged = {
87
102
  account: '0x1111111111111111111111111111111111111111',
103
+ config: MultisigConfig.toRpc(config),
88
104
  signatures: [signatureRpc],
89
105
  type: 'multisig',
90
106
  } as const
91
107
  // @ts-expect-error Multisig RPC signatures are untagged.
92
108
  const taggedRpc: SignatureEnvelope.MultisigRpc = tagged
93
109
 
94
- expectTypeOf(combinedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
110
+ expectTypeOf(accountOnlyRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
111
+ expectTypeOf(initRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
95
112
  expectTypeOf(serializedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
96
113
  expectTypeOf(taggedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
97
114
  })