ox 1.7.2 → 1.7.5
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.
- package/CHANGELOG.md +43 -0
- package/dist/core/AbiEvent.d.ts.map +1 -1
- package/dist/core/AbiEvent.js +10 -9
- package/dist/core/AbiEvent.js.map +1 -1
- package/dist/core/Blobs.d.ts.map +1 -1
- package/dist/core/Blobs.js +6 -2
- package/dist/core/Blobs.js.map +1 -1
- package/dist/core/Signature.d.ts +6 -5
- package/dist/core/Signature.d.ts.map +1 -1
- package/dist/core/Signature.js +17 -7
- package/dist/core/Signature.js.map +1 -1
- package/dist/core/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +21 -1
- package/dist/core/Siwe.js.map +1 -1
- package/dist/tempo/KeyAuthorization.d.ts +43 -39
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +24 -4
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +18 -15
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +49 -20
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/MultisigOperation.d.ts +45 -28
- package/dist/tempo/MultisigOperation.d.ts.map +1 -1
- package/dist/tempo/MultisigOperation.js +81 -131
- package/dist/tempo/MultisigOperation.js.map +1 -1
- package/dist/tempo/MultisigSimulation.d.ts +85 -0
- package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/tempo/MultisigSimulation.js +112 -0
- package/dist/tempo/MultisigSimulation.js.map +1 -0
- package/dist/tempo/RpcSchemaTempo.d.ts +20 -10
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +74 -153
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +130 -187
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +9 -7
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +12 -7
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/index.d.ts +7 -5
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +7 -5
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +360 -140
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +247 -487
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.js +29 -31
- package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
- package/dist/zod/tempo/MultisigOperation.d.ts +0 -10
- package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigOperation.js +0 -3
- package/dist/zod/tempo/MultisigOperation.js.map +1 -1
- package/dist/zod/tempo/MultisigSimulation.d.ts +52 -0
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigSimulation.js +44 -0
- package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +357 -422
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +19 -10
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +108 -42
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +21 -19
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +678 -666
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +580 -619
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +18 -10
- package/dist/zod/tempo/TransactionRequest.js.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +327 -213
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +105 -3
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
- package/dist/zod/tempo/z.d.ts +1 -1
- package/dist/zod/tempo/z.d.ts.map +1 -1
- package/dist/zod/tempo/z.js +1 -1
- package/dist/zod/tempo/z.js.map +1 -1
- package/package.json +9 -9
- package/src/core/AbiEvent.ts +11 -9
- package/src/core/Blobs.ts +6 -2
- package/src/core/Signature.ts +41 -11
- package/src/core/Siwe.ts +20 -1
- package/src/core/_test/AbiEvent.test.ts +45 -0
- package/src/core/_test/Blobs.test.ts +22 -0
- package/src/core/_test/Signature.test.ts +166 -0
- package/src/core/_test/Siwe.test.ts +100 -1
- package/src/tempo/AuthorizationTempo.test.ts +8 -0
- package/src/tempo/KeyAuthorization.test-d.ts +16 -74
- package/src/tempo/KeyAuthorization.test.ts +113 -14
- package/src/tempo/KeyAuthorization.ts +110 -66
- package/src/tempo/MultisigConfig.test.ts +44 -20
- package/src/tempo/MultisigConfig.ts +65 -23
- package/src/tempo/MultisigOperation.test-d.ts +8 -6
- package/src/tempo/MultisigOperation.test.ts +187 -591
- package/src/tempo/MultisigOperation.ts +129 -205
- package/src/tempo/MultisigSimulation.test-d.ts +33 -0
- package/src/tempo/MultisigSimulation.test.ts +194 -0
- package/src/tempo/MultisigSimulation.ts +192 -0
- package/src/tempo/RpcSchemaTempo.test-d.ts +45 -10
- package/src/tempo/RpcSchemaTempo.ts +15 -6
- package/src/tempo/SignatureEnvelope.test-d.ts +27 -101
- package/src/tempo/SignatureEnvelope.test.ts +330 -533
- package/src/tempo/SignatureEnvelope.ts +198 -310
- package/src/tempo/Transaction.test.ts +61 -0
- package/src/tempo/TransactionRequest.test-d.ts +7 -7
- package/src/tempo/TransactionRequest.test.ts +81 -223
- package/src/tempo/TransactionRequest.ts +34 -13
- package/src/tempo/index.ts +7 -5
- package/src/tempo/multisig.e2e.test.ts +329 -818
- package/src/version.ts +1 -1
- package/src/zod/tempo/KeyAuthorization.ts +50 -39
- package/src/zod/tempo/MultisigOperation.ts +0 -3
- package/src/zod/tempo/MultisigSimulation.ts +53 -0
- package/src/zod/tempo/RpcSchemaTempo.ts +20 -10
- package/src/zod/tempo/SignatureEnvelope.ts +46 -59
- package/src/zod/tempo/TransactionRequest.ts +28 -14
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +66 -30
- package/src/zod/tempo/_test/MultisigOperation.test.ts +0 -2
- package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigSimulation.test.ts +53 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +19 -2
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +46 -4
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +108 -129
- package/src/zod/tempo/_test/TransactionRequest.test.ts +17 -168
- package/src/zod/tempo/z.ts +1 -1
- package/dist/tempo/MultisigWitness.d.ts +0 -106
- package/dist/tempo/MultisigWitness.d.ts.map +0 -1
- package/dist/tempo/MultisigWitness.js +0 -118
- package/dist/tempo/MultisigWitness.js.map +0 -1
- package/dist/zod/tempo/MultisigWitness.d.ts +0 -152
- package/dist/zod/tempo/MultisigWitness.d.ts.map +0 -1
- package/dist/zod/tempo/MultisigWitness.js +0 -76
- package/dist/zod/tempo/MultisigWitness.js.map +0 -1
- package/src/tempo/MultisigWitness.test-d.ts +0 -42
- package/src/tempo/MultisigWitness.test.ts +0 -288
- package/src/tempo/MultisigWitness.ts +0 -210
- package/src/zod/tempo/MultisigWitness.ts +0 -101
- package/src/zod/tempo/_test/MultisigWitness.test-d.ts +0 -13
- package/src/zod/tempo/_test/MultisigWitness.test.ts +0 -149
|
@@ -15,14 +15,10 @@ export type Base<quantity = bigint> = {
|
|
|
15
15
|
approvals: readonly Hex.Hex[]
|
|
16
16
|
/** Root configuration used to verify approvals. */
|
|
17
17
|
config: MultisigConfig.Config<quantity>
|
|
18
|
-
/** Root configuration version. */
|
|
19
|
-
configVersion: quantity
|
|
20
18
|
/** Unix creation time in milliseconds. */
|
|
21
19
|
createdAt: number
|
|
22
20
|
/** Deterministic multisig operation hash. */
|
|
23
21
|
hash: Hex.Hex
|
|
24
|
-
/** Whether the operation initializes the root multisig account. */
|
|
25
|
-
init: boolean
|
|
26
22
|
/** Number of approvals selected for quorum evaluation. */
|
|
27
23
|
signatureCount: number
|
|
28
24
|
/** Required root owner weight. */
|
|
@@ -73,9 +69,6 @@ export type KeyAuthorizationRpc = KeyAuthorizationOperation<Hex.Hex>
|
|
|
73
69
|
/** JSON-RPC multisig operation. */
|
|
74
70
|
export type Rpc = Operation<Hex.Hex>
|
|
75
71
|
|
|
76
|
-
/** Maximum supported multisig configuration version. */
|
|
77
|
-
const maxConfigVersion = 2n ** 64n - 1n
|
|
78
|
-
|
|
79
72
|
/**
|
|
80
73
|
* Derives the deterministic hash for a multisig operation.
|
|
81
74
|
*
|
|
@@ -86,7 +79,7 @@ const maxConfigVersion = 2n ** 64n - 1n
|
|
|
86
79
|
*
|
|
87
80
|
* const hash = MultisigOperation.getHash({
|
|
88
81
|
* account,
|
|
89
|
-
*
|
|
82
|
+
* config,
|
|
90
83
|
* transaction,
|
|
91
84
|
* type: 'transaction'
|
|
92
85
|
* })
|
|
@@ -96,7 +89,7 @@ const maxConfigVersion = 2n ** 64n - 1n
|
|
|
96
89
|
* @returns The operation hash signed by each owner.
|
|
97
90
|
*/
|
|
98
91
|
export function getHash(options: getHash.Options): Hex.Hex {
|
|
99
|
-
const { account,
|
|
92
|
+
const { account, config } = options
|
|
100
93
|
const payload =
|
|
101
94
|
options.type === 'transaction'
|
|
102
95
|
? TxEnvelopeTempo.getSignPayload(
|
|
@@ -107,7 +100,7 @@ export function getHash(options: getHash.Options): Hex.Hex {
|
|
|
107
100
|
)
|
|
108
101
|
return MultisigConfig.getSignPayload({
|
|
109
102
|
account,
|
|
110
|
-
config
|
|
103
|
+
config,
|
|
111
104
|
payload,
|
|
112
105
|
})
|
|
113
106
|
}
|
|
@@ -117,8 +110,8 @@ export declare namespace getHash {
|
|
|
117
110
|
export type Options = {
|
|
118
111
|
/** Root multisig account. */
|
|
119
112
|
account: Address.Address
|
|
120
|
-
/**
|
|
121
|
-
|
|
113
|
+
/** Complete root multisig configuration witness. */
|
|
114
|
+
config: MultisigConfig.Config
|
|
122
115
|
} & (
|
|
123
116
|
| {
|
|
124
117
|
/** Canonical serialized key authorization. */
|
|
@@ -149,7 +142,8 @@ export declare namespace getHash {
|
|
|
149
142
|
*
|
|
150
143
|
* The function retains one canonical approval per owner. It selects the
|
|
151
144
|
* smallest deterministic quorum by owner weight, then orders the selected
|
|
152
|
-
* approvals by owner address for serialization.
|
|
145
|
+
* approvals by owner address for serialization. The caller supplies the
|
|
146
|
+
* account and config; the chain validates their factory-derived identity.
|
|
153
147
|
*
|
|
154
148
|
* @example
|
|
155
149
|
* ```ts twoslash
|
|
@@ -160,8 +154,7 @@ export declare namespace getHash {
|
|
|
160
154
|
* account,
|
|
161
155
|
* approvals,
|
|
162
156
|
* config,
|
|
163
|
-
* hash
|
|
164
|
-
* resolveConfig
|
|
157
|
+
* hash
|
|
165
158
|
* })
|
|
166
159
|
* ```
|
|
167
160
|
*
|
|
@@ -171,21 +164,18 @@ export declare namespace getHash {
|
|
|
171
164
|
export async function selectApprovals(
|
|
172
165
|
options: selectApprovals.Options,
|
|
173
166
|
): Promise<selectApprovals.ReturnValue> {
|
|
174
|
-
const { account, approvals, hash
|
|
167
|
+
const { account, approvals, hash } = options
|
|
175
168
|
if (!Address.validate(account) || Hex.toBigInt(account) === 0n)
|
|
176
169
|
throw new InvalidApprovalError({ reason: 'account is invalid' })
|
|
177
170
|
if (!Hash.validate(hash))
|
|
178
171
|
throw new InvalidApprovalError({ reason: 'hash is invalid' })
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
[account.toLowerCase()],
|
|
188
|
-
)
|
|
172
|
+
const config = MultisigConfig.from(options.config)
|
|
173
|
+
return selectApprovals_internal({
|
|
174
|
+
account,
|
|
175
|
+
approvals,
|
|
176
|
+
config,
|
|
177
|
+
hash,
|
|
178
|
+
})
|
|
189
179
|
}
|
|
190
180
|
|
|
191
181
|
export declare namespace selectApprovals {
|
|
@@ -193,33 +183,12 @@ export declare namespace selectApprovals {
|
|
|
193
183
|
export type Options = {
|
|
194
184
|
/** Root multisig account. */
|
|
195
185
|
account: Address.Address
|
|
196
|
-
/** Serialized primitive
|
|
186
|
+
/** Serialized primitive owner approvals. */
|
|
197
187
|
approvals: readonly SignatureEnvelope.Serialized[]
|
|
198
188
|
/** Current root multisig configuration. */
|
|
199
189
|
config: MultisigConfig.Config
|
|
200
190
|
/** Deterministic operation hash approved by root owners. */
|
|
201
191
|
hash: Hex.Hex
|
|
202
|
-
/** Resolves the current configuration of a nested multisig owner. */
|
|
203
|
-
resolveConfig?: ResolveConfig | undefined
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Resolves an initialized nested multisig configuration. */
|
|
207
|
-
export type ResolveConfig = (
|
|
208
|
-
options: ResolveConfigOptions,
|
|
209
|
-
) => ResolvedConfig | Promise<ResolvedConfig>
|
|
210
|
-
|
|
211
|
-
/** Nested multisig configuration lookup parameters. */
|
|
212
|
-
export type ResolveConfigOptions = {
|
|
213
|
-
/** Nested multisig account. */
|
|
214
|
-
account: Address.Address
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** Resolved nested multisig configuration. */
|
|
218
|
-
export type ResolvedConfig = {
|
|
219
|
-
/** Current nested multisig configuration. */
|
|
220
|
-
config: MultisigConfig.Config
|
|
221
|
-
/** Current nested multisig configuration version. */
|
|
222
|
-
version: bigint
|
|
223
192
|
}
|
|
224
193
|
|
|
225
194
|
/** Result of validating and selecting approvals. */
|
|
@@ -248,6 +217,90 @@ export declare namespace selectApprovals {
|
|
|
248
217
|
| Errors.GlobalErrorType
|
|
249
218
|
}
|
|
250
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Serializes a key authorization with selected multisig owner approvals.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts twoslash
|
|
225
|
+
* // @noErrors
|
|
226
|
+
* import { MultisigOperation } from 'ox/tempo'
|
|
227
|
+
*
|
|
228
|
+
* const authorization =
|
|
229
|
+
* MultisigOperation.serializeKeyAuthorization(
|
|
230
|
+
* keyAuthorization,
|
|
231
|
+
* {
|
|
232
|
+
* account,
|
|
233
|
+
* approvals: selection.selectedApprovals,
|
|
234
|
+
* config
|
|
235
|
+
* }
|
|
236
|
+
* )
|
|
237
|
+
* ```
|
|
238
|
+
*
|
|
239
|
+
* @param keyAuthorization - Canonical serialized unsigned key authorization.
|
|
240
|
+
* @param options - Multisig account, config, and selected approvals.
|
|
241
|
+
* @returns The signed serialized key authorization.
|
|
242
|
+
*/
|
|
243
|
+
export function serializeKeyAuthorization(
|
|
244
|
+
keyAuthorization: Hex.Hex,
|
|
245
|
+
options: serializeKeyAuthorization.Options,
|
|
246
|
+
): Hex.Hex {
|
|
247
|
+
const authorization = KeyAuthorization_.deserialize(keyAuthorization)
|
|
248
|
+
if (authorization.signature)
|
|
249
|
+
throw new InvalidOperationError({
|
|
250
|
+
reason: 'keyAuthorization must not contain a signature',
|
|
251
|
+
})
|
|
252
|
+
if (
|
|
253
|
+
KeyAuthorization_.serialize(authorization).toLowerCase() !==
|
|
254
|
+
keyAuthorization.toLowerCase()
|
|
255
|
+
)
|
|
256
|
+
throw new InvalidOperationError({
|
|
257
|
+
reason: 'keyAuthorization is not canonically serialized',
|
|
258
|
+
})
|
|
259
|
+
const config = MultisigConfig.from(options.config)
|
|
260
|
+
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
261
|
+
account: options.account,
|
|
262
|
+
config,
|
|
263
|
+
payload: KeyAuthorization_.getSignPayload(authorization),
|
|
264
|
+
signatures: options.approvals.map((approval) =>
|
|
265
|
+
assertApproval(options.account, approval, config),
|
|
266
|
+
),
|
|
267
|
+
})
|
|
268
|
+
return KeyAuthorization_.serialize(
|
|
269
|
+
KeyAuthorization_.from(authorization, {
|
|
270
|
+
signature: SignatureEnvelope.from({
|
|
271
|
+
account: options.account,
|
|
272
|
+
config,
|
|
273
|
+
signatures,
|
|
274
|
+
}),
|
|
275
|
+
}),
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export declare namespace serializeKeyAuthorization {
|
|
280
|
+
/** Options for `serializeKeyAuthorization`. */
|
|
281
|
+
export type Options = {
|
|
282
|
+
/** Root multisig account. */
|
|
283
|
+
account: Address.Address
|
|
284
|
+
/** Selected serialized owner approvals. */
|
|
285
|
+
approvals: readonly SignatureEnvelope.Serialized[]
|
|
286
|
+
/** Complete applicable root multisig config. */
|
|
287
|
+
config: MultisigConfig.Config
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Error type for `serializeKeyAuthorization`. */
|
|
291
|
+
export type ErrorType =
|
|
292
|
+
| InvalidOperationError
|
|
293
|
+
| KeyAuthorization_.deserialize.ErrorType
|
|
294
|
+
| KeyAuthorization_.from.ErrorType
|
|
295
|
+
| KeyAuthorization_.getSignPayload.ErrorType
|
|
296
|
+
| KeyAuthorization_.serialize.ErrorType
|
|
297
|
+
| MultisigConfig.assert.ErrorType
|
|
298
|
+
| SignatureEnvelope.assert.ErrorType
|
|
299
|
+
| SignatureEnvelope.InvalidSerializedError
|
|
300
|
+
| SignatureEnvelope.sortMultisigApprovals.ErrorType
|
|
301
|
+
| Errors.GlobalErrorType
|
|
302
|
+
}
|
|
303
|
+
|
|
251
304
|
/**
|
|
252
305
|
* Serializes a multisig transaction operation with selected owner approvals.
|
|
253
306
|
*
|
|
@@ -277,7 +330,7 @@ export function serializeTransaction(
|
|
|
277
330
|
value.transaction as TxEnvelopeTempo.Serialized,
|
|
278
331
|
)
|
|
279
332
|
const approvals = options.approvals.map((approval) =>
|
|
280
|
-
|
|
333
|
+
assertApproval(value.account, approval, value.config),
|
|
281
334
|
)
|
|
282
335
|
assertRetainedApprovals(value, approvals)
|
|
283
336
|
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
@@ -340,10 +393,6 @@ export function from<const operation extends Operation>(
|
|
|
340
393
|
): from.ReturnValue<operation> {
|
|
341
394
|
try {
|
|
342
395
|
const config = MultisigConfig.from(operation.config)
|
|
343
|
-
if (config.version !== operation.configVersion)
|
|
344
|
-
throw new InvalidOperationError({
|
|
345
|
-
reason: 'config.version must equal configVersion',
|
|
346
|
-
})
|
|
347
396
|
if (
|
|
348
397
|
typeof config.threshold !== 'number' ||
|
|
349
398
|
config.owners.some((owner) => typeof owner.weight !== 'number')
|
|
@@ -393,22 +442,19 @@ export function fromRpc<const operation extends Rpc>(
|
|
|
393
442
|
operation: operation,
|
|
394
443
|
): fromRpc.ReturnValue<operation> {
|
|
395
444
|
try {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
!Hex.validate(operation.configVersion)
|
|
399
|
-
)
|
|
445
|
+
const version = operation.config?.version
|
|
446
|
+
if (typeof version !== 'string' || !Hex.validate(version))
|
|
400
447
|
throw new InvalidOperationError({
|
|
401
|
-
reason: '
|
|
448
|
+
reason: 'config.version must be a hexadecimal quantity',
|
|
402
449
|
})
|
|
403
|
-
const
|
|
404
|
-
if (Hex.fromNumber(
|
|
450
|
+
const version_ = Hex.toBigInt(version)
|
|
451
|
+
if (Hex.fromNumber(version_) !== version)
|
|
405
452
|
throw new InvalidOperationError({
|
|
406
|
-
reason: '
|
|
453
|
+
reason: 'config.version must use canonical quantity encoding',
|
|
407
454
|
})
|
|
408
455
|
return from({
|
|
409
456
|
...operation,
|
|
410
457
|
config: MultisigConfig.fromRpc(operation.config),
|
|
411
|
-
configVersion,
|
|
412
458
|
} as Operation) as never
|
|
413
459
|
} catch (cause) {
|
|
414
460
|
if (cause instanceof InvalidOperationError) throw cause
|
|
@@ -448,7 +494,6 @@ export function toRpc<const operation extends Operation>(
|
|
|
448
494
|
return {
|
|
449
495
|
...value,
|
|
450
496
|
config: MultisigConfig.toRpc(value.config),
|
|
451
|
-
configVersion: Hex.fromNumber(value.configVersion),
|
|
452
497
|
} as never
|
|
453
498
|
}
|
|
454
499
|
|
|
@@ -460,7 +505,7 @@ export declare namespace toRpc {
|
|
|
460
505
|
: KeyAuthorizationRpc
|
|
461
506
|
|
|
462
507
|
/** Error type for `toRpc`. */
|
|
463
|
-
export type ErrorType = from.ErrorType |
|
|
508
|
+
export type ErrorType = from.ErrorType | MultisigConfig.toRpc.ErrorType
|
|
464
509
|
}
|
|
465
510
|
|
|
466
511
|
/**
|
|
@@ -470,7 +515,6 @@ export declare namespace toRpc {
|
|
|
470
515
|
*/
|
|
471
516
|
async function selectApprovals_internal(
|
|
472
517
|
options: selectApprovals.Options,
|
|
473
|
-
path: readonly string[],
|
|
474
518
|
): Promise<selectApprovals.ReturnValue> {
|
|
475
519
|
const owners = new Map(
|
|
476
520
|
options.config.owners.map((owner) => [
|
|
@@ -481,17 +525,14 @@ async function selectApprovals_internal(
|
|
|
481
525
|
const groups = new Map<string, ApprovalGroup>()
|
|
482
526
|
for (const serialized of options.approvals) {
|
|
483
527
|
const signature = SignatureEnvelope.from(serialized)
|
|
484
|
-
if (signature.type === 'keychain')
|
|
528
|
+
if (signature.type === 'keychain' || signature.type === 'multisig')
|
|
485
529
|
throw new InvalidApprovalError({
|
|
486
|
-
reason: '
|
|
530
|
+
reason: 'only primitive signatures can approve a multisig operation',
|
|
487
531
|
})
|
|
488
|
-
const address =
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
payload: options.hash,
|
|
493
|
-
signature,
|
|
494
|
-
})
|
|
532
|
+
const address = SignatureEnvelope.extractAddress({
|
|
533
|
+
payload: options.hash,
|
|
534
|
+
signature,
|
|
535
|
+
})
|
|
495
536
|
const owner = owners.get(address.toLowerCase())
|
|
496
537
|
if (!owner)
|
|
497
538
|
throw new InvalidApprovalError({
|
|
@@ -511,78 +552,6 @@ async function selectApprovals_internal(
|
|
|
511
552
|
const valid: SelectedApproval[] = []
|
|
512
553
|
const retained: RetainedApproval[] = []
|
|
513
554
|
for (const group of groups.values()) {
|
|
514
|
-
const nested = group.signatures.filter(
|
|
515
|
-
(signature) => signature.type === 'multisig',
|
|
516
|
-
)
|
|
517
|
-
if (nested.length > 0) {
|
|
518
|
-
if (nested.length !== group.signatures.length)
|
|
519
|
-
throw new InvalidApprovalError({
|
|
520
|
-
reason: `owner ${group.address} has conflicting signature types`,
|
|
521
|
-
})
|
|
522
|
-
if (
|
|
523
|
-
path.length >= MultisigConfig.maxNestingDepth ||
|
|
524
|
-
path.includes(group.address.toLowerCase())
|
|
525
|
-
)
|
|
526
|
-
throw new InvalidApprovalError({
|
|
527
|
-
reason: `nested multisig owner ${group.address} is invalid`,
|
|
528
|
-
})
|
|
529
|
-
if (!options.resolveConfig)
|
|
530
|
-
throw new InvalidApprovalError({
|
|
531
|
-
reason: `nested multisig owner ${group.address} requires a config resolver`,
|
|
532
|
-
})
|
|
533
|
-
const resolved = await options.resolveConfig({ account: group.address })
|
|
534
|
-
const config = MultisigConfig.from(resolved.config)
|
|
535
|
-
if (config.version !== resolved.version)
|
|
536
|
-
throw new InvalidApprovalError({
|
|
537
|
-
reason: `resolved config.version must equal version for nested multisig owner ${group.address}`,
|
|
538
|
-
})
|
|
539
|
-
const selected = await selectApprovals_internal(
|
|
540
|
-
{
|
|
541
|
-
account: group.address,
|
|
542
|
-
approvals: nested.flatMap((signature) =>
|
|
543
|
-
signature.signatures.map((approval) =>
|
|
544
|
-
SignatureEnvelope.serialize(approval),
|
|
545
|
-
),
|
|
546
|
-
),
|
|
547
|
-
config,
|
|
548
|
-
hash: MultisigConfig.getSignPayload({
|
|
549
|
-
account: group.address,
|
|
550
|
-
config,
|
|
551
|
-
payload: options.hash,
|
|
552
|
-
}),
|
|
553
|
-
resolveConfig: options.resolveConfig,
|
|
554
|
-
},
|
|
555
|
-
[...path, group.address.toLowerCase()],
|
|
556
|
-
)
|
|
557
|
-
retained.push({
|
|
558
|
-
address: group.address,
|
|
559
|
-
signature: SignatureEnvelope.serialize(
|
|
560
|
-
SignatureEnvelope.from({
|
|
561
|
-
account: group.address,
|
|
562
|
-
config,
|
|
563
|
-
signatures: selected.approvals.map((approval) =>
|
|
564
|
-
SignatureEnvelope.from(approval),
|
|
565
|
-
),
|
|
566
|
-
}),
|
|
567
|
-
),
|
|
568
|
-
})
|
|
569
|
-
if (selected.weight >= selected.threshold)
|
|
570
|
-
valid.push({
|
|
571
|
-
address: group.address,
|
|
572
|
-
signature: SignatureEnvelope.serialize(
|
|
573
|
-
SignatureEnvelope.from({
|
|
574
|
-
account: group.address,
|
|
575
|
-
config,
|
|
576
|
-
signatures: selected.selectedApprovals.map((approval) =>
|
|
577
|
-
SignatureEnvelope.from(approval),
|
|
578
|
-
),
|
|
579
|
-
}),
|
|
580
|
-
),
|
|
581
|
-
weight: group.weight,
|
|
582
|
-
})
|
|
583
|
-
continue
|
|
584
|
-
}
|
|
585
|
-
|
|
586
555
|
const signatures = group.signatures.map((signature) => {
|
|
587
556
|
if (
|
|
588
557
|
!SignatureEnvelope.verify(signature, {
|
|
@@ -651,7 +620,7 @@ type ApprovalGroup = {
|
|
|
651
620
|
type RetainedApproval = {
|
|
652
621
|
/** Configured owner address. */
|
|
653
622
|
address: Address.Address
|
|
654
|
-
/** Serialized primitive
|
|
623
|
+
/** Serialized primitive approval. */
|
|
655
624
|
signature: SignatureEnvelope.Serialized
|
|
656
625
|
}
|
|
657
626
|
|
|
@@ -692,16 +661,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
692
661
|
throw new InvalidOperationError({ reason: 'account cannot be zero' })
|
|
693
662
|
if (!Hash.validate(operation.hash))
|
|
694
663
|
throw new InvalidOperationError({ reason: 'hash is invalid' })
|
|
695
|
-
if (typeof operation.init !== 'boolean')
|
|
696
|
-
throw new InvalidOperationError({ reason: 'init must be a boolean' })
|
|
697
|
-
if (
|
|
698
|
-
typeof operation.configVersion !== 'bigint' ||
|
|
699
|
-
operation.configVersion < 0n ||
|
|
700
|
-
operation.configVersion > maxConfigVersion
|
|
701
|
-
)
|
|
702
|
-
throw new InvalidOperationError({
|
|
703
|
-
reason: 'configVersion must be an unsigned 64-bit integer',
|
|
704
|
-
})
|
|
705
664
|
assertInteger(operation.createdAt, 'createdAt')
|
|
706
665
|
assertInteger(operation.updatedAt, 'updatedAt')
|
|
707
666
|
if (operation.updatedAt < operation.createdAt)
|
|
@@ -781,19 +740,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
781
740
|
reason:
|
|
782
741
|
'weight is not reachable by signatureCount retained owner approvals',
|
|
783
742
|
})
|
|
784
|
-
if (operation.init) {
|
|
785
|
-
if (operation.configVersion !== 0n)
|
|
786
|
-
throw new InvalidOperationError({
|
|
787
|
-
reason: 'bootstrap operations must use config version zero',
|
|
788
|
-
})
|
|
789
|
-
if (
|
|
790
|
-
MultisigConfig.getAddress(config).toLowerCase() !==
|
|
791
|
-
operation.account.toLowerCase()
|
|
792
|
-
)
|
|
793
|
-
throw new InvalidOperationError({
|
|
794
|
-
reason: 'bootstrap config does not derive the operation account',
|
|
795
|
-
})
|
|
796
|
-
}
|
|
797
743
|
}
|
|
798
744
|
|
|
799
745
|
/**
|
|
@@ -981,8 +927,12 @@ function assertApproval(
|
|
|
981
927
|
account: Address.Address,
|
|
982
928
|
serialized: SignatureEnvelope.Serialized,
|
|
983
929
|
config: MultisigConfig.Config,
|
|
984
|
-
): SignatureEnvelope.
|
|
930
|
+
): SignatureEnvelope.Primitive {
|
|
985
931
|
const approval = SignatureEnvelope.deserialize(serialized)
|
|
932
|
+
if (approval.type === 'keychain' || approval.type === 'multisig')
|
|
933
|
+
throw new InvalidOperationError({
|
|
934
|
+
reason: 'only primitive owner approvals are allowed',
|
|
935
|
+
})
|
|
986
936
|
SignatureEnvelope.assert({
|
|
987
937
|
account,
|
|
988
938
|
config,
|
|
@@ -1010,8 +960,10 @@ function assertRetainedApprovals(
|
|
|
1010
960
|
SignatureEnvelope.deserialize(approval),
|
|
1011
961
|
)
|
|
1012
962
|
for (const approval of selected) {
|
|
1013
|
-
const index = retained.findIndex(
|
|
1014
|
-
|
|
963
|
+
const index = retained.findIndex(
|
|
964
|
+
(candidate) =>
|
|
965
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
966
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1015
967
|
)
|
|
1016
968
|
if (index === -1)
|
|
1017
969
|
throw new InvalidOperationError({
|
|
@@ -1035,8 +987,10 @@ function assertSelectedApprovals(
|
|
|
1035
987
|
SignatureEnvelope.deserialize(approval),
|
|
1036
988
|
)
|
|
1037
989
|
for (const approval of selected) {
|
|
1038
|
-
const index = retained.findIndex(
|
|
1039
|
-
|
|
990
|
+
const index = retained.findIndex(
|
|
991
|
+
(candidate) =>
|
|
992
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
993
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1040
994
|
)
|
|
1041
995
|
if (index === -1)
|
|
1042
996
|
throw new InvalidOperationError({
|
|
@@ -1067,36 +1021,6 @@ function assertSelectedApprovals(
|
|
|
1067
1021
|
}
|
|
1068
1022
|
}
|
|
1069
1023
|
|
|
1070
|
-
/**
|
|
1071
|
-
* Checks whether a selected approval is contained in a retained approval tree.
|
|
1072
|
-
*
|
|
1073
|
-
* @internal
|
|
1074
|
-
*/
|
|
1075
|
-
function includesApproval(
|
|
1076
|
-
retained: SignatureEnvelope.SignatureEnvelope,
|
|
1077
|
-
selected: SignatureEnvelope.SignatureEnvelope,
|
|
1078
|
-
): boolean {
|
|
1079
|
-
if (retained.type !== 'multisig' || selected.type !== 'multisig')
|
|
1080
|
-
return (
|
|
1081
|
-
SignatureEnvelope.serialize(retained).toLowerCase() ===
|
|
1082
|
-
SignatureEnvelope.serialize(selected).toLowerCase()
|
|
1083
|
-
)
|
|
1084
|
-
if (retained.account.toLowerCase() !== selected.account.toLowerCase())
|
|
1085
|
-
return false
|
|
1086
|
-
if (!sameConfig(retained.config, selected.config)) return false
|
|
1087
|
-
let index = 0
|
|
1088
|
-
for (const approval of selected.signatures) {
|
|
1089
|
-
while (
|
|
1090
|
-
index < retained.signatures.length &&
|
|
1091
|
-
!includesApproval(retained.signatures[index]!, approval)
|
|
1092
|
-
)
|
|
1093
|
-
index++
|
|
1094
|
-
if (index === retained.signatures.length) return false
|
|
1095
|
-
index++
|
|
1096
|
-
}
|
|
1097
|
-
return true
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
1024
|
/**
|
|
1101
1025
|
* Checks whether exactly `signatureCount` retained owners can produce `weight`.
|
|
1102
1026
|
*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
2
|
+
import * as MultisigSimulation from './MultisigSimulation.js'
|
|
3
|
+
|
|
4
|
+
const rpc = {
|
|
5
|
+
approvals: [
|
|
6
|
+
{
|
|
7
|
+
keyType: 'webAuthn',
|
|
8
|
+
keyData: '0x0102030405',
|
|
9
|
+
owner: '0x1111111111111111111111111111111111111111',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
config:
|
|
13
|
+
'0xf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d694111111111111111111111111111111111111111101',
|
|
14
|
+
} as const satisfies MultisigSimulation.Rpc
|
|
15
|
+
|
|
16
|
+
test('fromRpc returns a domain spec', () => {
|
|
17
|
+
const spec = MultisigSimulation.fromRpc(rpc)
|
|
18
|
+
|
|
19
|
+
expectTypeOf(spec).toEqualTypeOf<MultisigSimulation.Spec>()
|
|
20
|
+
expectTypeOf(spec.config.version).toEqualTypeOf<bigint>()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('RPC specs use encoded configurations', () => {
|
|
24
|
+
expectTypeOf<
|
|
25
|
+
MultisigSimulation.Rpc['config']
|
|
26
|
+
>().toEqualTypeOf<`0x${string}`>()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('toRpc returns an RPC spec', () => {
|
|
30
|
+
expectTypeOf(
|
|
31
|
+
MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)),
|
|
32
|
+
).toEqualTypeOf<MultisigSimulation.Rpc>()
|
|
33
|
+
})
|