ox 1.0.0-beta.11 → 1.0.0-beta.13
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/dist/core/internal/rpcSchemas/eth.d.ts +16 -0
- package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +37 -85
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +39 -83
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +32 -30
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +56 -51
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/index.d.ts +2 -2
- package/dist/tempo/index.js +2 -2
- package/dist/zod/RpcSchema.d.ts +1 -0
- package/dist/zod/RpcSchema.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.d.ts +1 -0
- package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.js +5 -0
- package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +0 -20
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +0 -11
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +0 -12
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +0 -6
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +0 -2
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +0 -24
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +0 -21
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -9
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +0 -3
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/internal/rpcSchemas/eth.ts +16 -0
- package/src/tempo/MultisigConfig.test.ts +54 -37
- package/src/tempo/MultisigConfig.ts +59 -134
- package/src/tempo/SignatureEnvelope.test.ts +118 -17
- package/src/tempo/SignatureEnvelope.ts +77 -72
- package/src/tempo/e2e.test.ts +221 -1
- package/src/tempo/index.ts +2 -2
- package/src/version.ts +1 -1
- package/src/zod/internal/rpcSchemas/Eth.ts +6 -0
- package/src/zod/tempo/SignatureEnvelope.ts +0 -2
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +0 -2
|
@@ -30,31 +30,51 @@ describe('from', () => {
|
|
|
30
30
|
})
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
describe('
|
|
33
|
+
describe('getAddress', () => {
|
|
34
34
|
test('matches independent ground truth', () => {
|
|
35
|
-
expect(MultisigConfig.
|
|
36
|
-
`"
|
|
35
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
|
|
36
|
+
`"0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da"`,
|
|
37
37
|
)
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
+
test('matches independent ground truth (salt + weights)', () => {
|
|
41
|
+
expect(
|
|
42
|
+
MultisigConfig.getAddress({
|
|
43
|
+
salt: `0x${'42'.repeat(32)}`,
|
|
44
|
+
threshold: 2,
|
|
45
|
+
owners: [
|
|
46
|
+
{ owner: owner1, weight: 1 },
|
|
47
|
+
{ owner: owner2, weight: 2 },
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
).toMatchInlineSnapshot(`"0x0773e28146400643e42cb28f6659b74e7c0b451d"`)
|
|
51
|
+
})
|
|
52
|
+
|
|
40
53
|
test('is stable across calls', () => {
|
|
41
|
-
expect(MultisigConfig.
|
|
42
|
-
MultisigConfig.
|
|
54
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).toBe(
|
|
55
|
+
MultisigConfig.getAddress(singleOwnerConfig),
|
|
43
56
|
)
|
|
44
57
|
})
|
|
45
58
|
|
|
46
59
|
test('differs for a different salt', () => {
|
|
47
|
-
expect(MultisigConfig.
|
|
48
|
-
MultisigConfig.
|
|
60
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).not.toBe(
|
|
61
|
+
MultisigConfig.getAddress({
|
|
49
62
|
...singleOwnerConfig,
|
|
50
63
|
salt: `0x${'42'.repeat(32)}`,
|
|
51
64
|
}),
|
|
52
65
|
)
|
|
53
66
|
})
|
|
54
67
|
|
|
68
|
+
test('address is chain-independent', () => {
|
|
69
|
+
// Derivation does not include chain ID; identical config → identical address.
|
|
70
|
+
const a = MultisigConfig.getAddress(singleOwnerConfig)
|
|
71
|
+
const b = MultisigConfig.getAddress(MultisigConfig.from(singleOwnerConfig))
|
|
72
|
+
expect(a).toBe(b)
|
|
73
|
+
})
|
|
74
|
+
|
|
55
75
|
test('throws on invalid config', () => {
|
|
56
76
|
expect(() =>
|
|
57
|
-
MultisigConfig.
|
|
77
|
+
MultisigConfig.getAddress({
|
|
58
78
|
threshold: 5,
|
|
59
79
|
owners: singleOwnerConfig.owners,
|
|
60
80
|
}),
|
|
@@ -62,50 +82,27 @@ describe('genesisConfigId', () => {
|
|
|
62
82
|
})
|
|
63
83
|
})
|
|
64
84
|
|
|
65
|
-
describe('getAddress', () => {
|
|
66
|
-
test('matches independent ground truth', () => {
|
|
67
|
-
expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
|
|
68
|
-
`"0x6ca655065b1de473d903eebd50e5cb4996e10468"`,
|
|
69
|
-
)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test('derives from positional config or `{ genesisConfigId }` identically', () => {
|
|
73
|
-
const genesisConfigId = MultisigConfig.toId(singleOwnerConfig)
|
|
74
|
-
expect(MultisigConfig.getAddress({ genesisConfigId })).toBe(
|
|
75
|
-
MultisigConfig.getAddress(singleOwnerConfig),
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('config ID and address are chain-independent', () => {
|
|
80
|
-
// Derivation does not include chain ID; identical config → identical id/address.
|
|
81
|
-
const a = MultisigConfig.toId(singleOwnerConfig)
|
|
82
|
-
const b = MultisigConfig.toId(MultisigConfig.from(singleOwnerConfig))
|
|
83
|
-
expect(a).toBe(b)
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
85
|
describe('getSignPayload', () => {
|
|
88
86
|
test('matches independent ground truth', () => {
|
|
89
87
|
expect(
|
|
90
88
|
MultisigConfig.getSignPayload({
|
|
91
|
-
payload: `0x${'
|
|
89
|
+
payload: `0x${'de'.repeat(32)}`,
|
|
92
90
|
genesisConfig: singleOwnerConfig,
|
|
93
91
|
}),
|
|
94
92
|
).toMatchInlineSnapshot(
|
|
95
|
-
`"
|
|
93
|
+
`"0x7df8cb9fef4fc2aeb271b617d1a4c6178b720ae1d7564f48a363069b7d77a079"`,
|
|
96
94
|
)
|
|
97
95
|
})
|
|
98
96
|
|
|
99
|
-
test('behavior: `genesisConfig` and `{account
|
|
100
|
-
const
|
|
101
|
-
const account = MultisigConfig.getAddress({ genesisConfigId })
|
|
97
|
+
test('behavior: `genesisConfig` and `{ account }` produce identical digests', () => {
|
|
98
|
+
const account = MultisigConfig.getAddress(singleOwnerConfig)
|
|
102
99
|
const payload = `0x${'42'.repeat(32)}` as const
|
|
103
100
|
expect(
|
|
104
101
|
MultisigConfig.getSignPayload({
|
|
105
102
|
payload,
|
|
106
103
|
genesisConfig: singleOwnerConfig,
|
|
107
104
|
}),
|
|
108
|
-
).toBe(MultisigConfig.getSignPayload({ payload, account
|
|
105
|
+
).toBe(MultisigConfig.getSignPayload({ payload, account }))
|
|
109
106
|
})
|
|
110
107
|
})
|
|
111
108
|
|
|
@@ -152,8 +149,16 @@ describe('assert / validate', () => {
|
|
|
152
149
|
expect(MultisigConfig.validate({ threshold: 1, owners: [] })).toBe(false)
|
|
153
150
|
})
|
|
154
151
|
|
|
152
|
+
test('accepts 255 owners', () => {
|
|
153
|
+
const owners = Array.from({ length: 255 }, (_, i) => ({
|
|
154
|
+
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
155
|
+
weight: 1,
|
|
156
|
+
}))
|
|
157
|
+
expect(MultisigConfig.validate({ threshold: 255, owners })).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
|
|
155
160
|
test('too many owners', () => {
|
|
156
|
-
const owners = Array.from({ length:
|
|
161
|
+
const owners = Array.from({ length: 256 }, (_, i) => ({
|
|
157
162
|
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
158
163
|
weight: 1,
|
|
159
164
|
}))
|
|
@@ -178,6 +183,18 @@ describe('assert / validate', () => {
|
|
|
178
183
|
).toBe(false)
|
|
179
184
|
})
|
|
180
185
|
|
|
186
|
+
test('total weight exceeds u8 max', () => {
|
|
187
|
+
expect(
|
|
188
|
+
MultisigConfig.validate({
|
|
189
|
+
threshold: 255,
|
|
190
|
+
owners: [
|
|
191
|
+
{ owner: owner1, weight: 128 },
|
|
192
|
+
{ owner: owner2, weight: 128 },
|
|
193
|
+
],
|
|
194
|
+
}),
|
|
195
|
+
).toBe(false)
|
|
196
|
+
})
|
|
197
|
+
|
|
181
198
|
test('zero owner weight', () => {
|
|
182
199
|
expect(
|
|
183
200
|
MultisigConfig.validate({
|
|
@@ -6,7 +6,13 @@ import * as Hex from '../core/Hex.js'
|
|
|
6
6
|
import type { Compute, OneOf } from '../core/internal/types.js'
|
|
7
7
|
|
|
8
8
|
/** Maximum number of owners allowed in a native multisig config. */
|
|
9
|
-
export const maxOwners =
|
|
9
|
+
export const maxOwners = 255
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of native multisig signatures in one nested authorization
|
|
13
|
+
* path, including the top-level transaction signature.
|
|
14
|
+
*/
|
|
15
|
+
export const maxNestingDepth = 3
|
|
10
16
|
|
|
11
17
|
/** Maximum encoded byte length for one primitive owner approval. */
|
|
12
18
|
export const maxOwnerSignatureBytes = 2049
|
|
@@ -20,20 +26,17 @@ export const zeroSalt = `0x${'00'.repeat(32)}` as const
|
|
|
20
26
|
/** Domain prefix for the native multisig account address derivation. */
|
|
21
27
|
const accountDomain = 'tempo:multisig:account'
|
|
22
28
|
|
|
23
|
-
/** Domain prefix for the native multisig config ID derivation. */
|
|
24
|
-
const configDomain = 'tempo:multisig:config'
|
|
25
|
-
|
|
26
29
|
/** Domain prefix for native multisig owner approvals. */
|
|
27
30
|
const signatureDomain = 'tempo:multisig:signature'
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
|
-
* Native multisig configuration. Determines the
|
|
31
|
-
*
|
|
33
|
+
* Native multisig configuration. Determines the stable multisig account
|
|
34
|
+
* address.
|
|
32
35
|
*/
|
|
33
36
|
export type Config<numberType = number> = Compute<{
|
|
34
37
|
/**
|
|
35
|
-
* Caller-chosen 32-byte salt mixed into the
|
|
36
|
-
* the zero salt (`MultisigConfig.zeroSalt`) when omitted.
|
|
38
|
+
* Caller-chosen 32-byte salt mixed into the derived account address.
|
|
39
|
+
* Defaults to the zero salt (`MultisigConfig.zeroSalt`) when omitted.
|
|
37
40
|
*/
|
|
38
41
|
salt?: Hex.Hex | undefined
|
|
39
42
|
/** Minimum total owner weight required to authorize a transaction. */
|
|
@@ -44,7 +47,7 @@ export type Config<numberType = number> = Compute<{
|
|
|
44
47
|
|
|
45
48
|
/** Native multisig owner entry. */
|
|
46
49
|
export type Owner<numberType = number> = {
|
|
47
|
-
/** Owner address (recovered from the owner's
|
|
50
|
+
/** Owner address (recovered from the owner's approval). */
|
|
48
51
|
owner: Address.Address
|
|
49
52
|
/** Nonzero owner weight. */
|
|
50
53
|
weight: numberType
|
|
@@ -60,9 +63,9 @@ export type Tuple = readonly [
|
|
|
60
63
|
/**
|
|
61
64
|
* Asserts that a native multisig {@link ox#MultisigConfig.Config} is valid.
|
|
62
65
|
*
|
|
63
|
-
* Mirrors the Tempo `
|
|
66
|
+
* Mirrors the Tempo `InitMultisig::validate` rules: owners non-empty and
|
|
64
67
|
* `<= maxOwners`, strictly ascending unique nonzero owner addresses, nonzero
|
|
65
|
-
* owner weights, `threshold >= 1`, total weight `<=
|
|
68
|
+
* owner weights, `threshold >= 1`, total weight `<= 255` (u8 max), and
|
|
66
69
|
* `threshold <= total weight`.
|
|
67
70
|
*
|
|
68
71
|
* @example
|
|
@@ -112,9 +115,9 @@ export function assert<numberType = number>(config: Config<numberType>): void {
|
|
|
112
115
|
totalWeight += Number(owner.weight)
|
|
113
116
|
}
|
|
114
117
|
|
|
115
|
-
if (totalWeight >
|
|
118
|
+
if (totalWeight > 0xff)
|
|
116
119
|
throw new InvalidConfigError({
|
|
117
|
-
reason: 'total owner weight exceeds
|
|
120
|
+
reason: 'total owner weight exceeds u8 max',
|
|
118
121
|
})
|
|
119
122
|
if (Number(threshold) > totalWeight)
|
|
120
123
|
throw new InvalidConfigError({
|
|
@@ -130,7 +133,7 @@ export declare namespace assert {
|
|
|
130
133
|
* Normalizes a native multisig {@link ox#MultisigConfig.Config}.
|
|
131
134
|
*
|
|
132
135
|
* Sorts owners into strictly ascending `owner` address order (the canonical
|
|
133
|
-
* form required for
|
|
136
|
+
* form required for account derivation) and asserts the config is valid.
|
|
134
137
|
*
|
|
135
138
|
* @example
|
|
136
139
|
* ```ts twoslash
|
|
@@ -206,7 +209,11 @@ export function fromTuple(tuple: Tuple): Config {
|
|
|
206
209
|
/**
|
|
207
210
|
* Derives the stable native multisig account address.
|
|
208
211
|
*
|
|
209
|
-
*
|
|
212
|
+
* Preimage (fixed-width big-endian, **not** RLP):
|
|
213
|
+
* `keccak256("tempo:multisig:account" || salt || u8(threshold) || u8(owners.length) || (owner || u8(weight)) for each owner)[12:32]`.
|
|
214
|
+
*
|
|
215
|
+
* The address is derived once from the initial (bootstrap) config and never
|
|
216
|
+
* changes — config updates do not affect it.
|
|
210
217
|
*
|
|
211
218
|
* @example
|
|
212
219
|
* ```ts twoslash
|
|
@@ -225,50 +232,37 @@ export function fromTuple(tuple: Tuple): Config {
|
|
|
225
232
|
* const address = MultisigConfig.getAddress(genesisConfig)
|
|
226
233
|
* ```
|
|
227
234
|
*
|
|
228
|
-
* @
|
|
229
|
-
* ### From an genesis config ID
|
|
230
|
-
*
|
|
231
|
-
* If you already have the permanent `genesisConfigId`, pass it directly:
|
|
232
|
-
*
|
|
233
|
-
* ```ts twoslash
|
|
234
|
-
* import { MultisigConfig } from 'ox/tempo'
|
|
235
|
-
*
|
|
236
|
-
* const genesisConfigId = `0x${'00'.repeat(32)}` as const
|
|
237
|
-
*
|
|
238
|
-
* const address = MultisigConfig.getAddress({
|
|
239
|
-
* genesisConfigId
|
|
240
|
-
* })
|
|
241
|
-
* ```
|
|
242
|
-
*
|
|
243
|
-
* @param value - The genesis config (positional) or `{ genesisConfigId }`.
|
|
235
|
+
* @param config - The initial (bootstrap) multisig config.
|
|
244
236
|
* @returns The multisig account address.
|
|
245
237
|
*/
|
|
246
|
-
export function getAddress(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
238
|
+
export function getAddress(config: Config): Address.Address {
|
|
239
|
+
assert(config)
|
|
240
|
+
const hash = Hash.keccak256(
|
|
241
|
+
Hex.concat(
|
|
242
|
+
Hex.fromString(accountDomain),
|
|
243
|
+
Hex.padLeft(config.salt ?? zeroSalt, 32),
|
|
244
|
+
Hex.fromNumber(config.threshold, { size: 1 }),
|
|
245
|
+
Hex.fromNumber(config.owners.length, { size: 1 }),
|
|
246
|
+
...config.owners.flatMap((owner) => [
|
|
247
|
+
owner.owner,
|
|
248
|
+
Hex.fromNumber(owner.weight, { size: 1 }),
|
|
249
|
+
]),
|
|
250
|
+
),
|
|
251
|
+
)
|
|
252
|
+
const account = Address.from(Hex.slice(hash, 12, 32))
|
|
253
|
+
if (Hex.toBigInt(account) === 0n)
|
|
254
|
+
throw new InvalidConfigError({ reason: 'derived account cannot be zero' })
|
|
255
|
+
return account
|
|
253
256
|
}
|
|
254
257
|
|
|
255
258
|
export declare namespace getAddress {
|
|
256
|
-
type Value =
|
|
257
|
-
| Config
|
|
258
|
-
| {
|
|
259
|
-
/**
|
|
260
|
-
* The permanent genesis config ID (`MultisigConfig.toId(genesisConfig)`).
|
|
261
|
-
* Config updates never change this value, so it identifies the
|
|
262
|
-
* account permanently.
|
|
263
|
-
*/
|
|
264
|
-
genesisConfigId: Hex.Hex
|
|
265
|
-
}
|
|
266
|
-
|
|
267
259
|
type ErrorType =
|
|
268
|
-
|
|
|
260
|
+
| assert.ErrorType
|
|
269
261
|
| Address.from.ErrorType
|
|
270
262
|
| Hash.keccak256.ErrorType
|
|
271
263
|
| Hex.concat.ErrorType
|
|
264
|
+
| Hex.fromNumber.ErrorType
|
|
265
|
+
| Hex.fromString.ErrorType
|
|
272
266
|
| Hex.slice.ErrorType
|
|
273
267
|
| Errors.GlobalErrorType
|
|
274
268
|
}
|
|
@@ -276,14 +270,16 @@ export declare namespace getAddress {
|
|
|
276
270
|
/**
|
|
277
271
|
* Computes the digest a native multisig owner approves (signs).
|
|
278
272
|
*
|
|
279
|
-
* `keccak256("tempo:multisig:signature" || inner_digest || account
|
|
273
|
+
* `keccak256("tempo:multisig:signature" || inner_digest || account)`,
|
|
280
274
|
* where `inner_digest` is the transaction sign payload
|
|
281
275
|
* ({@link ox#TxEnvelopeTempo.(getSignPayload:function)}).
|
|
282
276
|
*
|
|
283
|
-
* The digest is
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
277
|
+
* The digest is keyed on the permanent `account` derived from the genesis
|
|
278
|
+
* (bootstrap) config — config updates never change it, so the genesis config
|
|
279
|
+
* is the correct input even for post-update transactions.
|
|
280
|
+
*
|
|
281
|
+
* For a nested multisig owner approval, the parent digest becomes the nested
|
|
282
|
+
* approval's `payload`, with the nested multisig `account`.
|
|
287
283
|
*
|
|
288
284
|
* @example
|
|
289
285
|
* ```ts twoslash
|
|
@@ -311,10 +307,10 @@ export declare namespace getAddress {
|
|
|
311
307
|
* ```
|
|
312
308
|
*
|
|
313
309
|
* @example
|
|
314
|
-
* ### From `account`
|
|
310
|
+
* ### From `account`
|
|
315
311
|
*
|
|
316
|
-
* If you already have the permanent `account`
|
|
317
|
-
*
|
|
312
|
+
* If you already have the permanent `account` (for example, recovered from a
|
|
313
|
+
* stored envelope), pass it directly:
|
|
318
314
|
*
|
|
319
315
|
* ```ts twoslash
|
|
320
316
|
* import { MultisigConfig, TxEnvelopeTempo } from 'ox/tempo'
|
|
@@ -328,7 +324,6 @@ export declare namespace getAddress {
|
|
|
328
324
|
* }
|
|
329
325
|
* ]
|
|
330
326
|
* })
|
|
331
|
-
* const genesisConfigId = MultisigConfig.toId(genesisConfig)
|
|
332
327
|
* const account = MultisigConfig.getAddress(genesisConfig)
|
|
333
328
|
*
|
|
334
329
|
* const envelope = TxEnvelopeTempo.from({
|
|
@@ -338,8 +333,7 @@ export declare namespace getAddress {
|
|
|
338
333
|
*
|
|
339
334
|
* const digest = MultisigConfig.getSignPayload({
|
|
340
335
|
* payload: TxEnvelopeTempo.getSignPayload(envelope),
|
|
341
|
-
* account
|
|
342
|
-
* genesisConfigId
|
|
336
|
+
* account
|
|
343
337
|
* })
|
|
344
338
|
* ```
|
|
345
339
|
*
|
|
@@ -352,17 +346,8 @@ export function getSignPayload(value: getSignPayload.Value): Hex.Hex {
|
|
|
352
346
|
'account' in value && value.account
|
|
353
347
|
? value.account
|
|
354
348
|
: getAddress((value as { genesisConfig: Config }).genesisConfig)
|
|
355
|
-
const genesisConfigId =
|
|
356
|
-
'genesisConfigId' in value && value.genesisConfigId
|
|
357
|
-
? value.genesisConfigId
|
|
358
|
-
: toId((value as { genesisConfig: Config }).genesisConfig)
|
|
359
349
|
return Hash.keccak256(
|
|
360
|
-
Hex.concat(
|
|
361
|
-
Hex.fromString(signatureDomain),
|
|
362
|
-
Hex.from(payload),
|
|
363
|
-
account,
|
|
364
|
-
genesisConfigId,
|
|
365
|
-
),
|
|
350
|
+
Hex.concat(Hex.fromString(signatureDomain), Hex.from(payload), account),
|
|
366
351
|
)
|
|
367
352
|
}
|
|
368
353
|
|
|
@@ -374,16 +359,13 @@ export declare namespace getSignPayload {
|
|
|
374
359
|
| {
|
|
375
360
|
/** The native multisig account address. */
|
|
376
361
|
account: Address.Address
|
|
377
|
-
/** The permanent config ID. */
|
|
378
|
-
genesisConfigId: Hex.Hex
|
|
379
362
|
}
|
|
380
363
|
| {
|
|
381
364
|
/**
|
|
382
365
|
* The initial multisig config (the bootstrap config that derived the
|
|
383
|
-
* permanent `account`
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* transactions.
|
|
366
|
+
* permanent `account`). Used to derive the account automatically.
|
|
367
|
+
* Config updates never change `account`, so the genesis config is
|
|
368
|
+
* also the correct input for post-update transactions.
|
|
387
369
|
*/
|
|
388
370
|
genesisConfig: Config
|
|
389
371
|
}
|
|
@@ -391,69 +373,12 @@ export declare namespace getSignPayload {
|
|
|
391
373
|
|
|
392
374
|
type ErrorType =
|
|
393
375
|
| getAddress.ErrorType
|
|
394
|
-
| toId.ErrorType
|
|
395
376
|
| Hash.keccak256.ErrorType
|
|
396
377
|
| Hex.concat.ErrorType
|
|
397
378
|
| Hex.from.ErrorType
|
|
398
379
|
| Errors.GlobalErrorType
|
|
399
380
|
}
|
|
400
381
|
|
|
401
|
-
/**
|
|
402
|
-
* Derives the permanent config ID for a native multisig
|
|
403
|
-
* {@link ox#MultisigConfig.Config}.
|
|
404
|
-
*
|
|
405
|
-
* Preimage (fixed-width big-endian, **not** RLP):
|
|
406
|
-
* `keccak256("tempo:multisig:config" || salt || be_u32(threshold) || be_u32(owners.length) || (owner || be_u32(weight)) for each owner)`.
|
|
407
|
-
*
|
|
408
|
-
* @example
|
|
409
|
-
* ```ts twoslash
|
|
410
|
-
* import { MultisigConfig } from 'ox/tempo'
|
|
411
|
-
*
|
|
412
|
-
* const config = MultisigConfig.from({
|
|
413
|
-
* threshold: 1,
|
|
414
|
-
* owners: [
|
|
415
|
-
* {
|
|
416
|
-
* owner: '0x1111111111111111111111111111111111111111',
|
|
417
|
-
* weight: 1
|
|
418
|
-
* }
|
|
419
|
-
* ]
|
|
420
|
-
* })
|
|
421
|
-
*
|
|
422
|
-
* const genesisConfigId = MultisigConfig.toId(config)
|
|
423
|
-
* ```
|
|
424
|
-
*
|
|
425
|
-
* @param config - The multisig config.
|
|
426
|
-
* @returns The 32-byte config ID.
|
|
427
|
-
*/
|
|
428
|
-
export function toId(config: Config): Hex.Hex {
|
|
429
|
-
assert(config)
|
|
430
|
-
const id = Hash.keccak256(
|
|
431
|
-
Hex.concat(
|
|
432
|
-
Hex.fromString(configDomain),
|
|
433
|
-
Hex.padLeft(config.salt ?? zeroSalt, 32),
|
|
434
|
-
Hex.fromNumber(config.threshold, { size: 4 }),
|
|
435
|
-
Hex.fromNumber(config.owners.length, { size: 4 }),
|
|
436
|
-
...config.owners.flatMap((owner) => [
|
|
437
|
-
owner.owner,
|
|
438
|
-
Hex.fromNumber(owner.weight, { size: 4 }),
|
|
439
|
-
]),
|
|
440
|
-
),
|
|
441
|
-
)
|
|
442
|
-
if (Hex.toBigInt(id) === 0n)
|
|
443
|
-
throw new InvalidConfigError({ reason: 'config ID cannot be zero' })
|
|
444
|
-
return id
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export declare namespace toId {
|
|
448
|
-
type ErrorType =
|
|
449
|
-
| assert.ErrorType
|
|
450
|
-
| Hash.keccak256.ErrorType
|
|
451
|
-
| Hex.concat.ErrorType
|
|
452
|
-
| Hex.fromNumber.ErrorType
|
|
453
|
-
| Hex.fromString.ErrorType
|
|
454
|
-
| Errors.GlobalErrorType
|
|
455
|
-
}
|
|
456
|
-
|
|
457
382
|
/**
|
|
458
383
|
* Converts a {@link ox#MultisigConfig.Config} to its RLP tuple form (carried
|
|
459
384
|
* by the multisig signature `init`).
|