ox 0.14.44 → 0.14.45
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 +6 -0
- package/_cjs/tempo/KeyAuthorization.js +43 -10
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/MultisigConfig.js +33 -11
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +18 -94
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/MultisigSimulation.js +18 -46
- package/_cjs/tempo/MultisigSimulation.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +72 -109
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TransactionRequest.js +5 -1
- package/_cjs/tempo/TransactionRequest.js.map +1 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/KeyAuthorization.js +42 -9
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/MultisigConfig.js +41 -22
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/MultisigOperation.js +20 -100
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/MultisigSimulation.js +18 -46
- package/_esm/tempo/MultisigSimulation.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +107 -169
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TransactionRequest.js +5 -1
- package/_esm/tempo/TransactionRequest.js.map +1 -1
- package/_esm/tempo/index.js +3 -1
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +48 -38
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +15 -14
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/MultisigOperation.d.ts +3 -2
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/MultisigSimulation.d.ts +2 -49
- package/_types/tempo/MultisigSimulation.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +69 -128
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TransactionRequest.d.ts +5 -2
- package/_types/tempo/TransactionRequest.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +3 -1
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/package.json +1 -1
- package/tempo/AuthorizationTempo.test.ts +8 -4
- package/tempo/KeyAuthorization.test-d.ts +68 -33
- package/tempo/KeyAuthorization.test.ts +89 -8
- package/tempo/KeyAuthorization.ts +124 -61
- package/tempo/MultisigConfig.test.ts +34 -26
- package/tempo/MultisigConfig.ts +54 -24
- package/tempo/MultisigOperation.test.ts +79 -550
- package/tempo/MultisigOperation.ts +31 -146
- package/tempo/MultisigSimulation.test-d.ts +2 -2
- package/tempo/MultisigSimulation.test.ts +21 -110
- package/tempo/MultisigSimulation.ts +25 -99
- package/tempo/SignatureEnvelope.test-d.ts +29 -67
- package/tempo/SignatureEnvelope.test.ts +301 -568
- package/tempo/SignatureEnvelope.ts +156 -274
- package/tempo/TransactionRequest.test-d.ts +6 -5
- package/tempo/TransactionRequest.test.ts +17 -80
- package/tempo/TransactionRequest.ts +23 -3
- package/tempo/index.ts +3 -1
- package/tempo/multisig.e2e.test.ts +322 -861
- package/version.ts +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Hash, Hex, Rlp } from 'ox'
|
|
2
2
|
import { MultisigConfig } from 'ox/tempo'
|
|
3
3
|
import { describe, expect, test } from 'vitest'
|
|
4
|
+
import { factory } from '../../test/tempo/multisig.js'
|
|
4
5
|
|
|
5
6
|
const owner_1 = '0x1111111111111111111111111111111111111111'
|
|
6
7
|
const owner_2 = '0x2222222222222222222222222222222222222222'
|
|
@@ -9,7 +10,7 @@ const config = MultisigConfig.from({
|
|
|
9
10
|
owners: [{ owner: owner_1, weight: 1 }],
|
|
10
11
|
threshold: 1,
|
|
11
12
|
})
|
|
12
|
-
const account = MultisigConfig.getAddress(config)
|
|
13
|
+
const account = MultisigConfig.getAddress(config, { factory })
|
|
13
14
|
|
|
14
15
|
describe('from', () => {
|
|
15
16
|
test('behavior: normalizes an initial configuration', () => {
|
|
@@ -101,49 +102,57 @@ describe('fromRpc/toRpc', () => {
|
|
|
101
102
|
describe('getAddress', () => {
|
|
102
103
|
test('example: matches the frozen CREATE2 vector', () => {
|
|
103
104
|
expect(account).toMatchInlineSnapshot(
|
|
104
|
-
`"
|
|
105
|
+
`"0x90ba71cb7534da990a1182cf28c9bf415055e03d"`,
|
|
105
106
|
)
|
|
106
107
|
expect(account).not.toBe('0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da')
|
|
107
108
|
})
|
|
108
109
|
|
|
109
110
|
test('behavior: includes salt, threshold, and owners', () => {
|
|
110
111
|
expect(
|
|
111
|
-
MultisigConfig.getAddress(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
MultisigConfig.getAddress(
|
|
113
|
+
{
|
|
114
|
+
owners: [
|
|
115
|
+
{ owner: owner_1, weight: 1 },
|
|
116
|
+
{ owner: owner_2, weight: 2 },
|
|
117
|
+
],
|
|
118
|
+
salt: `0x${'42'.repeat(32)}`,
|
|
119
|
+
threshold: 2,
|
|
120
|
+
},
|
|
121
|
+
{ factory },
|
|
122
|
+
),
|
|
123
|
+
).toMatchInlineSnapshot(`"0x7898a7d1dd96946d2a968de1540ca884a244320c"`)
|
|
120
124
|
})
|
|
121
125
|
|
|
122
126
|
test('behavior: is stable and binds the salt', () => {
|
|
123
127
|
expect({
|
|
124
|
-
repeated: MultisigConfig.getAddress(config),
|
|
125
|
-
salted: MultisigConfig.getAddress(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
repeated: MultisigConfig.getAddress(config, { factory }),
|
|
129
|
+
salted: MultisigConfig.getAddress(
|
|
130
|
+
{
|
|
131
|
+
...config,
|
|
132
|
+
salt: `0x${'42'.repeat(32)}`,
|
|
133
|
+
},
|
|
134
|
+
{ factory },
|
|
135
|
+
),
|
|
129
136
|
}).toMatchInlineSnapshot(`
|
|
130
137
|
{
|
|
131
|
-
"repeated": "
|
|
132
|
-
"salted": "
|
|
138
|
+
"repeated": "0x90ba71cb7534da990a1182cf28c9bf415055e03d",
|
|
139
|
+
"salted": "0xa1a8472f4a01772db729d8ebc73fe46120750b83",
|
|
133
140
|
}
|
|
134
141
|
`)
|
|
135
142
|
})
|
|
136
143
|
|
|
137
144
|
test('error: rejects a current configuration', () => {
|
|
138
145
|
expect(() =>
|
|
139
|
-
MultisigConfig.getAddress({ ...config, version: 1n }),
|
|
146
|
+
MultisigConfig.getAddress({ ...config, version: 1n }, { factory }),
|
|
140
147
|
).toThrowErrorMatchingInlineSnapshot(
|
|
141
148
|
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
142
149
|
)
|
|
143
150
|
})
|
|
144
151
|
|
|
145
152
|
test('behavior: accepts numeric zero for an initial configuration', () => {
|
|
146
|
-
expect(
|
|
153
|
+
expect(
|
|
154
|
+
MultisigConfig.getAddress({ ...config, version: 0 }, { factory }),
|
|
155
|
+
).toBe(account)
|
|
147
156
|
})
|
|
148
157
|
})
|
|
149
158
|
|
|
@@ -168,7 +177,7 @@ describe('getSignPayload', () => {
|
|
|
168
177
|
expect(
|
|
169
178
|
MultisigConfig.getSignPayload({ account, config, payload }),
|
|
170
179
|
).toMatchInlineSnapshot(
|
|
171
|
-
`"
|
|
180
|
+
`"0x728efe90611b01c91c2be0da56e7eb8e0d2c274c84f97182e274187dde0b8a20"`,
|
|
172
181
|
)
|
|
173
182
|
})
|
|
174
183
|
|
|
@@ -223,14 +232,14 @@ describe('assert/validate', () => {
|
|
|
223
232
|
})
|
|
224
233
|
const rlp = Rlp.fromHex(MultisigConfig.toTuple(boundary))
|
|
225
234
|
expect({
|
|
226
|
-
account: MultisigConfig.getAddress(boundary),
|
|
235
|
+
account: MultisigConfig.getAddress(boundary, { factory }),
|
|
227
236
|
commitment: MultisigConfig.getCommitment(boundary),
|
|
228
237
|
rlpHash: Hash.keccak256(rlp),
|
|
229
238
|
rlpLength: Hex.size(rlp),
|
|
230
239
|
valid: MultisigConfig.validate(boundary),
|
|
231
240
|
}).toMatchInlineSnapshot(`
|
|
232
241
|
{
|
|
233
|
-
"account": "
|
|
242
|
+
"account": "0x6acb25715a2b0cf05c098f753a87d9b38471e99f",
|
|
234
243
|
"commitment": "0x0dc47a7ab45ffa21a01bfd115427e26617b5a57d7ccbea57db2fd4537ba96f56",
|
|
235
244
|
"rlpHash": "0xbaf0d030add91caaa10815d2e99c942f1e39b0d199216973781adb4fc1af6955",
|
|
236
245
|
"rlpLength": 1145,
|
|
@@ -246,7 +255,7 @@ describe('assert/validate', () => {
|
|
|
246
255
|
{ owner: owner_1, weight: 128 },
|
|
247
256
|
{ owner: owner_2, weight: 127 },
|
|
248
257
|
],
|
|
249
|
-
threshold:
|
|
258
|
+
threshold: 8,
|
|
250
259
|
}),
|
|
251
260
|
).toBe(true)
|
|
252
261
|
})
|
|
@@ -312,7 +321,7 @@ describe('assert/validate', () => {
|
|
|
312
321
|
{ owner: owner_1, weight: 128 },
|
|
313
322
|
{ owner: owner_2, weight: 128 },
|
|
314
323
|
],
|
|
315
|
-
threshold:
|
|
324
|
+
threshold: 8,
|
|
316
325
|
},
|
|
317
326
|
name: 'weight overflow',
|
|
318
327
|
},
|
|
@@ -359,7 +368,6 @@ describe('assert/validate', () => {
|
|
|
359
368
|
test('exports', () => {
|
|
360
369
|
expect(Object.keys(MultisigConfig)).toMatchInlineSnapshot(`
|
|
361
370
|
[
|
|
362
|
-
"maxNestingDepth",
|
|
363
371
|
"maxOwnerSignatureBytes",
|
|
364
372
|
"maxOwners",
|
|
365
373
|
"maxSignatures",
|
package/tempo/MultisigConfig.ts
CHANGED
|
@@ -6,12 +6,6 @@ import * as Hash from '../core/Hash.js'
|
|
|
6
6
|
import * as Hex from '../core/Hex.js'
|
|
7
7
|
import type { Compute } from '../core/internal/types.js'
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Maximum number of native multisig signatures in one nested authorization
|
|
11
|
-
* path, including the top-level transaction signature.
|
|
12
|
-
*/
|
|
13
|
-
export const maxNestingDepth = 2
|
|
14
|
-
|
|
15
9
|
/** Maximum encoded byte length for one primitive owner approval. */
|
|
16
10
|
export const maxOwnerSignatureBytes = 2049
|
|
17
11
|
|
|
@@ -22,7 +16,7 @@ export const maxOwners = 48
|
|
|
22
16
|
export const maxSignatures = 8
|
|
23
17
|
|
|
24
18
|
/** Maximum threshold accepted by a native multisig config. */
|
|
25
|
-
export const maxThreshold =
|
|
19
|
+
export const maxThreshold = 8
|
|
26
20
|
|
|
27
21
|
/** Maximum version accepted by a native multisig config. */
|
|
28
22
|
export const maxVersion = 2n ** 64n - 1n
|
|
@@ -39,12 +33,9 @@ const accountDomain = 'tempo:multisig:account'
|
|
|
39
33
|
/** Domain prefix for native multisig configuration commitments. */
|
|
40
34
|
const configDomain = 'tempo:multisig:config'
|
|
41
35
|
|
|
42
|
-
/** Canonical CREATE2 factory for multisig recovery wallets. */
|
|
43
|
-
const recoveryFactory = '0x8a196A227C48Ae8A3E36EebD4E106675CC0f6E64'
|
|
44
|
-
|
|
45
36
|
/** Keccak-256 of the canonical recovery wallet creation code. */
|
|
46
37
|
const recoveryWalletInitCodeHash =
|
|
47
|
-
'
|
|
38
|
+
'0x583cc63a2e37f645b43eac911b1a6d6de08b83abdc308c61364edda8cfc3bd37'
|
|
48
39
|
|
|
49
40
|
/** Domain prefix for native multisig owner approvals. */
|
|
50
41
|
const signatureDomain = 'tempo:multisig:signature'
|
|
@@ -300,7 +291,33 @@ export declare namespace fromRpc {
|
|
|
300
291
|
*/
|
|
301
292
|
export function fromTuple(tuple: Tuple): Config {
|
|
302
293
|
const [salt, version, threshold, owners] = tuple
|
|
303
|
-
|
|
294
|
+
if (
|
|
295
|
+
tuple.length !== 4 ||
|
|
296
|
+
typeof salt !== 'string' ||
|
|
297
|
+
Hex.size(salt) !== 32 ||
|
|
298
|
+
typeof version !== 'string' ||
|
|
299
|
+
Hex.size(version) > 8 ||
|
|
300
|
+
version.startsWith('0x00') ||
|
|
301
|
+
typeof threshold !== 'string' ||
|
|
302
|
+
Hex.size(threshold) > 1 ||
|
|
303
|
+
threshold.startsWith('0x00') ||
|
|
304
|
+
!Array.isArray(owners) ||
|
|
305
|
+
owners.some(
|
|
306
|
+
(owner) =>
|
|
307
|
+
!Array.isArray(owner) ||
|
|
308
|
+
owner.length !== 2 ||
|
|
309
|
+
typeof owner[0] !== 'string' ||
|
|
310
|
+
!Address.validate(owner[0]) ||
|
|
311
|
+
typeof owner[1] !== 'string' ||
|
|
312
|
+
Hex.size(owner[1] as Hex.Hex) > 1 ||
|
|
313
|
+
owner[1].startsWith('0x00'),
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
throw new InvalidConfigError({ reason: 'invalid config RLP tuple' })
|
|
317
|
+
const config = {
|
|
318
|
+
salt: salt && salt !== '0x' ? Hex.padLeft(salt, 32) : zeroSalt,
|
|
319
|
+
version: version === '0x' ? 0n : Hex.toBigInt(version),
|
|
320
|
+
threshold: threshold === '0x' ? 0 : Hex.toNumber(threshold),
|
|
304
321
|
owners: owners.map((owner) => {
|
|
305
322
|
const [ownerAddress, weight] = owner as readonly Hex.Hex[]
|
|
306
323
|
return {
|
|
@@ -308,18 +325,17 @@ export function fromTuple(tuple: Tuple): Config {
|
|
|
308
325
|
weight: !weight || weight === '0x' ? 0 : Hex.toNumber(weight),
|
|
309
326
|
}
|
|
310
327
|
}),
|
|
311
|
-
salt: salt && salt !== '0x' ? Hex.padLeft(salt, 32) : zeroSalt,
|
|
312
|
-
threshold: threshold === '0x' ? 0 : Hex.toNumber(threshold),
|
|
313
|
-
version: version === '0x' ? 0n : Hex.toBigInt(version),
|
|
314
328
|
}
|
|
329
|
+
assert(config)
|
|
330
|
+
return config
|
|
315
331
|
}
|
|
316
332
|
|
|
317
333
|
/**
|
|
318
334
|
* Derives the stable native multisig account address.
|
|
319
335
|
*
|
|
320
336
|
* The initial config is hashed into a CREATE2 salt using fixed-width
|
|
321
|
-
* big-endian fields, not RLP. The account uses the
|
|
322
|
-
* and wallet init-code hash.
|
|
337
|
+
* big-endian fields, not RLP. The account uses the chain-configured recovery
|
|
338
|
+
* factory and wallet init-code hash.
|
|
323
339
|
*
|
|
324
340
|
* The address is derived once from the initial version-0 config. Config
|
|
325
341
|
* updates do not change it.
|
|
@@ -335,13 +351,20 @@ export function fromTuple(tuple: Tuple): Config {
|
|
|
335
351
|
* threshold: 1,
|
|
336
352
|
* })
|
|
337
353
|
*
|
|
338
|
-
* const address = MultisigConfig.getAddress(initialConfig
|
|
354
|
+
* const address = MultisigConfig.getAddress(initialConfig, {
|
|
355
|
+
* factory: '0x7171717171717171717171717171717171717171'
|
|
356
|
+
* })
|
|
339
357
|
* ```
|
|
340
358
|
*
|
|
341
359
|
* @param config - The initial multisig config.
|
|
360
|
+
* @param options - The recovery factory configured by the chain.
|
|
342
361
|
* @returns The multisig account address.
|
|
343
362
|
*/
|
|
344
|
-
export function getAddress(
|
|
363
|
+
export function getAddress(
|
|
364
|
+
config: Input,
|
|
365
|
+
options: getAddress.Options,
|
|
366
|
+
): Address.Address {
|
|
367
|
+
Address.assert(options.factory)
|
|
345
368
|
assert(config)
|
|
346
369
|
if (BigInt(config.version ?? 0) !== 0n)
|
|
347
370
|
throw new InvalidConfigError({
|
|
@@ -361,7 +384,7 @@ export function getAddress(config: Input): Address.Address {
|
|
|
361
384
|
)
|
|
362
385
|
const account = ContractAddress.fromCreate2({
|
|
363
386
|
bytecodeHash: recoveryWalletInitCodeHash,
|
|
364
|
-
from:
|
|
387
|
+
from: options.factory,
|
|
365
388
|
salt: accountSalt,
|
|
366
389
|
})
|
|
367
390
|
if (Hex.toBigInt(account) === 0n)
|
|
@@ -374,6 +397,11 @@ export function getAddress(config: Input): Address.Address {
|
|
|
374
397
|
}
|
|
375
398
|
|
|
376
399
|
export declare namespace getAddress {
|
|
400
|
+
type Options = {
|
|
401
|
+
/** Recovery factory configured by the chain. */
|
|
402
|
+
factory: Address.Address
|
|
403
|
+
}
|
|
404
|
+
|
|
377
405
|
type ErrorType =
|
|
378
406
|
| assert.ErrorType
|
|
379
407
|
| ContractAddress.fromCreate2.ErrorType
|
|
@@ -444,9 +472,6 @@ export declare namespace getCommitment {
|
|
|
444
472
|
* version. Initial approvals use version `0n`; each config update increments
|
|
445
473
|
* it.
|
|
446
474
|
*
|
|
447
|
-
* For a nested multisig owner approval, the parent digest becomes the nested
|
|
448
|
-
* approval's `payload`, with the nested multisig `account`.
|
|
449
|
-
*
|
|
450
475
|
* @example
|
|
451
476
|
* ```ts twoslash
|
|
452
477
|
* import { MultisigConfig, TxEnvelopeTempo } from 'ox/tempo'
|
|
@@ -464,7 +489,9 @@ export declare namespace getCommitment {
|
|
|
464
489
|
* })
|
|
465
490
|
*
|
|
466
491
|
* const digest = MultisigConfig.getSignPayload({
|
|
467
|
-
* account: MultisigConfig.getAddress(config
|
|
492
|
+
* account: MultisigConfig.getAddress(config, {
|
|
493
|
+
* factory: '0x7171717171717171717171717171717171717171'
|
|
494
|
+
* }),
|
|
468
495
|
* config,
|
|
469
496
|
* payload: TxEnvelopeTempo.getSignPayload(envelope),
|
|
470
497
|
* })
|
|
@@ -476,6 +503,9 @@ export declare namespace getCommitment {
|
|
|
476
503
|
export function getSignPayload(value: getSignPayload.Value): Hex.Hex {
|
|
477
504
|
const { account, config, payload } = value
|
|
478
505
|
assertVersion(config.version)
|
|
506
|
+
Address.assert(account)
|
|
507
|
+
if (Hex.size(Hex.from(payload)) !== 32)
|
|
508
|
+
throw new InvalidConfigError({ reason: 'payload must be 32 bytes' })
|
|
479
509
|
return Hash.keccak256(
|
|
480
510
|
Hex.concat(
|
|
481
511
|
Hex.fromString(signatureDomain),
|