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
|
@@ -144,7 +144,8 @@ export declare namespace getHash {
|
|
|
144
144
|
*
|
|
145
145
|
* The function retains one canonical approval per owner. It selects the
|
|
146
146
|
* smallest deterministic quorum by owner weight, then orders the selected
|
|
147
|
-
* approvals by owner address for serialization.
|
|
147
|
+
* approvals by owner address for serialization. The caller supplies the
|
|
148
|
+
* account and config; the chain validates their factory-derived identity.
|
|
148
149
|
*
|
|
149
150
|
* @example
|
|
150
151
|
* ```ts twoslash
|
|
@@ -171,22 +172,12 @@ export async function selectApprovals(
|
|
|
171
172
|
if (!Hash.validate(hash))
|
|
172
173
|
throw new InvalidApprovalError({ reason: 'hash is invalid' })
|
|
173
174
|
const config = MultisigConfig.from(options.config)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
})
|
|
181
|
-
return selectApprovals_internal(
|
|
182
|
-
{
|
|
183
|
-
account,
|
|
184
|
-
approvals,
|
|
185
|
-
config,
|
|
186
|
-
hash,
|
|
187
|
-
},
|
|
188
|
-
[account.toLowerCase()],
|
|
189
|
-
)
|
|
175
|
+
return selectApprovals_internal({
|
|
176
|
+
account,
|
|
177
|
+
approvals,
|
|
178
|
+
config,
|
|
179
|
+
hash,
|
|
180
|
+
})
|
|
190
181
|
}
|
|
191
182
|
|
|
192
183
|
export declare namespace selectApprovals {
|
|
@@ -194,7 +185,7 @@ export declare namespace selectApprovals {
|
|
|
194
185
|
export type Options = {
|
|
195
186
|
/** Root multisig account. */
|
|
196
187
|
account: Address.Address
|
|
197
|
-
/** Serialized primitive
|
|
188
|
+
/** Serialized primitive owner approvals. */
|
|
198
189
|
approvals: readonly SignatureEnvelope.Serialized[]
|
|
199
190
|
/** Current root multisig configuration. */
|
|
200
191
|
config: MultisigConfig.Config
|
|
@@ -272,7 +263,7 @@ export function serializeKeyAuthorization(
|
|
|
272
263
|
config,
|
|
273
264
|
payload: KeyAuthorization_.getSignPayload(authorization),
|
|
274
265
|
signatures: options.approvals.map((approval) =>
|
|
275
|
-
|
|
266
|
+
assertApproval(options.account, approval, config),
|
|
276
267
|
),
|
|
277
268
|
})
|
|
278
269
|
return KeyAuthorization_.serialize(
|
|
@@ -337,7 +328,7 @@ export function serializeTransaction(
|
|
|
337
328
|
value.transaction as TxEnvelopeTempo.Serialized,
|
|
338
329
|
)
|
|
339
330
|
const approvals = options.approvals.map((approval) =>
|
|
340
|
-
|
|
331
|
+
assertApproval(value.account, approval, value.config),
|
|
341
332
|
)
|
|
342
333
|
assertRetainedApprovals(value, approvals)
|
|
343
334
|
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
@@ -522,7 +513,6 @@ export declare namespace toRpc {
|
|
|
522
513
|
*/
|
|
523
514
|
async function selectApprovals_internal(
|
|
524
515
|
options: selectApprovals.Options,
|
|
525
|
-
path: readonly string[],
|
|
526
516
|
): Promise<selectApprovals.ReturnValue> {
|
|
527
517
|
const owners = new Map(
|
|
528
518
|
options.config.owners.map((owner) => [
|
|
@@ -533,17 +523,14 @@ async function selectApprovals_internal(
|
|
|
533
523
|
const groups = new Map<string, ApprovalGroup>()
|
|
534
524
|
for (const serialized of options.approvals) {
|
|
535
525
|
const signature = SignatureEnvelope.from(serialized)
|
|
536
|
-
if (signature.type === 'keychain')
|
|
526
|
+
if (signature.type === 'keychain' || signature.type === 'multisig')
|
|
537
527
|
throw new InvalidApprovalError({
|
|
538
|
-
reason: '
|
|
528
|
+
reason: 'only primitive signatures can approve a multisig operation',
|
|
539
529
|
})
|
|
540
|
-
const address =
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
payload: options.hash,
|
|
545
|
-
signature,
|
|
546
|
-
})
|
|
530
|
+
const address = SignatureEnvelope.extractAddress({
|
|
531
|
+
payload: options.hash,
|
|
532
|
+
signature,
|
|
533
|
+
})
|
|
547
534
|
const owner = owners.get(address.toLowerCase())
|
|
548
535
|
if (!owner)
|
|
549
536
|
throw new InvalidApprovalError({
|
|
@@ -563,79 +550,6 @@ async function selectApprovals_internal(
|
|
|
563
550
|
const valid: SelectedApproval[] = []
|
|
564
551
|
const retained: RetainedApproval[] = []
|
|
565
552
|
for (const group of groups.values()) {
|
|
566
|
-
const nested = group.signatures.filter(
|
|
567
|
-
(signature) => signature.type === 'multisig',
|
|
568
|
-
)
|
|
569
|
-
if (nested.length > 0) {
|
|
570
|
-
if (nested.length !== group.signatures.length)
|
|
571
|
-
throw new InvalidApprovalError({
|
|
572
|
-
reason: `owner ${group.address} has conflicting signature types`,
|
|
573
|
-
})
|
|
574
|
-
if (
|
|
575
|
-
path.length >= MultisigConfig.maxNestingDepth ||
|
|
576
|
-
path.includes(group.address.toLowerCase())
|
|
577
|
-
)
|
|
578
|
-
throw new InvalidApprovalError({
|
|
579
|
-
reason: `nested multisig owner ${group.address} is invalid`,
|
|
580
|
-
})
|
|
581
|
-
const config = MultisigConfig.from(nested[0]!.config)
|
|
582
|
-
if (nested.some((signature) => !sameConfig(signature.config, config)))
|
|
583
|
-
throw new InvalidApprovalError({
|
|
584
|
-
reason: `nested multisig owner ${group.address} has conflicting config witnesses`,
|
|
585
|
-
})
|
|
586
|
-
if (
|
|
587
|
-
config.version === 0n &&
|
|
588
|
-
!Address.isEqual(MultisigConfig.getAddress(config), group.address)
|
|
589
|
-
)
|
|
590
|
-
throw new InvalidApprovalError({
|
|
591
|
-
reason: `initial config does not derive nested multisig owner ${group.address}`,
|
|
592
|
-
})
|
|
593
|
-
const selected = await selectApprovals_internal(
|
|
594
|
-
{
|
|
595
|
-
account: group.address,
|
|
596
|
-
approvals: nested.flatMap((signature) =>
|
|
597
|
-
signature.signatures.map((approval) =>
|
|
598
|
-
SignatureEnvelope.serialize(approval),
|
|
599
|
-
),
|
|
600
|
-
),
|
|
601
|
-
config,
|
|
602
|
-
hash: MultisigConfig.getSignPayload({
|
|
603
|
-
account: group.address,
|
|
604
|
-
config,
|
|
605
|
-
payload: options.hash,
|
|
606
|
-
}),
|
|
607
|
-
},
|
|
608
|
-
[...path, group.address.toLowerCase()],
|
|
609
|
-
)
|
|
610
|
-
retained.push({
|
|
611
|
-
address: group.address,
|
|
612
|
-
signature: SignatureEnvelope.serialize(
|
|
613
|
-
SignatureEnvelope.from({
|
|
614
|
-
account: group.address,
|
|
615
|
-
config,
|
|
616
|
-
signatures: selected.approvals.map((approval) =>
|
|
617
|
-
SignatureEnvelope.from(approval),
|
|
618
|
-
),
|
|
619
|
-
}),
|
|
620
|
-
),
|
|
621
|
-
})
|
|
622
|
-
if (selected.weight >= selected.threshold)
|
|
623
|
-
valid.push({
|
|
624
|
-
address: group.address,
|
|
625
|
-
signature: SignatureEnvelope.serialize(
|
|
626
|
-
SignatureEnvelope.from({
|
|
627
|
-
account: group.address,
|
|
628
|
-
config,
|
|
629
|
-
signatures: selected.selectedApprovals.map((approval) =>
|
|
630
|
-
SignatureEnvelope.from(approval),
|
|
631
|
-
),
|
|
632
|
-
}),
|
|
633
|
-
),
|
|
634
|
-
weight: group.weight,
|
|
635
|
-
})
|
|
636
|
-
continue
|
|
637
|
-
}
|
|
638
|
-
|
|
639
553
|
const signatures = group.signatures.map((signature) => {
|
|
640
554
|
if (
|
|
641
555
|
!SignatureEnvelope.verify(signature, {
|
|
@@ -704,7 +618,7 @@ type ApprovalGroup = {
|
|
|
704
618
|
type RetainedApproval = {
|
|
705
619
|
/** Configured owner address. */
|
|
706
620
|
address: Address.Address
|
|
707
|
-
/** Serialized primitive
|
|
621
|
+
/** Serialized primitive approval. */
|
|
708
622
|
signature: SignatureEnvelope.Serialized
|
|
709
623
|
}
|
|
710
624
|
|
|
@@ -824,13 +738,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
824
738
|
reason:
|
|
825
739
|
'weight is not reachable by signatureCount retained owner approvals',
|
|
826
740
|
})
|
|
827
|
-
if (
|
|
828
|
-
config.version === 0n &&
|
|
829
|
-
!Address.isEqual(MultisigConfig.getAddress(config), operation.account)
|
|
830
|
-
)
|
|
831
|
-
throw new InvalidOperationError({
|
|
832
|
-
reason: 'initial config does not derive the operation account',
|
|
833
|
-
})
|
|
834
741
|
}
|
|
835
742
|
|
|
836
743
|
/**
|
|
@@ -1018,8 +925,12 @@ function assertApproval(
|
|
|
1018
925
|
account: Address.Address,
|
|
1019
926
|
serialized: SignatureEnvelope.Serialized,
|
|
1020
927
|
config: MultisigConfig.Config,
|
|
1021
|
-
): SignatureEnvelope.
|
|
928
|
+
): SignatureEnvelope.Primitive {
|
|
1022
929
|
const approval = SignatureEnvelope.deserialize(serialized)
|
|
930
|
+
if (approval.type === 'keychain' || approval.type === 'multisig')
|
|
931
|
+
throw new InvalidOperationError({
|
|
932
|
+
reason: 'only primitive owner approvals are allowed',
|
|
933
|
+
})
|
|
1023
934
|
SignatureEnvelope.assert({
|
|
1024
935
|
account,
|
|
1025
936
|
config,
|
|
@@ -1047,8 +958,10 @@ function assertRetainedApprovals(
|
|
|
1047
958
|
SignatureEnvelope.deserialize(approval),
|
|
1048
959
|
)
|
|
1049
960
|
for (const approval of selected) {
|
|
1050
|
-
const index = retained.findIndex(
|
|
1051
|
-
|
|
961
|
+
const index = retained.findIndex(
|
|
962
|
+
(candidate) =>
|
|
963
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
964
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1052
965
|
)
|
|
1053
966
|
if (index === -1)
|
|
1054
967
|
throw new InvalidOperationError({
|
|
@@ -1072,8 +985,10 @@ function assertSelectedApprovals(
|
|
|
1072
985
|
SignatureEnvelope.deserialize(approval),
|
|
1073
986
|
)
|
|
1074
987
|
for (const approval of selected) {
|
|
1075
|
-
const index = retained.findIndex(
|
|
1076
|
-
|
|
988
|
+
const index = retained.findIndex(
|
|
989
|
+
(candidate) =>
|
|
990
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
991
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1077
992
|
)
|
|
1078
993
|
if (index === -1)
|
|
1079
994
|
throw new InvalidOperationError({
|
|
@@ -1104,36 +1019,6 @@ function assertSelectedApprovals(
|
|
|
1104
1019
|
}
|
|
1105
1020
|
}
|
|
1106
1021
|
|
|
1107
|
-
/**
|
|
1108
|
-
* Checks whether a selected approval is contained in a retained approval tree.
|
|
1109
|
-
*
|
|
1110
|
-
* @internal
|
|
1111
|
-
*/
|
|
1112
|
-
function includesApproval(
|
|
1113
|
-
retained: SignatureEnvelope.SignatureEnvelope,
|
|
1114
|
-
selected: SignatureEnvelope.SignatureEnvelope,
|
|
1115
|
-
): boolean {
|
|
1116
|
-
if (retained.type !== 'multisig' || selected.type !== 'multisig')
|
|
1117
|
-
return (
|
|
1118
|
-
SignatureEnvelope.serialize(retained).toLowerCase() ===
|
|
1119
|
-
SignatureEnvelope.serialize(selected).toLowerCase()
|
|
1120
|
-
)
|
|
1121
|
-
if (retained.account.toLowerCase() !== selected.account.toLowerCase())
|
|
1122
|
-
return false
|
|
1123
|
-
if (!sameConfig(retained.config, selected.config)) return false
|
|
1124
|
-
let index = 0
|
|
1125
|
-
for (const approval of selected.signatures) {
|
|
1126
|
-
while (
|
|
1127
|
-
index < retained.signatures.length &&
|
|
1128
|
-
!includesApproval(retained.signatures[index]!, approval)
|
|
1129
|
-
)
|
|
1130
|
-
index++
|
|
1131
|
-
if (index === retained.signatures.length) return false
|
|
1132
|
-
index++
|
|
1133
|
-
}
|
|
1134
|
-
return true
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
1022
|
/**
|
|
1138
1023
|
* Checks whether exactly `signatureCount` retained owners can produce `weight`.
|
|
1139
1024
|
*
|
|
@@ -2,11 +2,11 @@ import { expectTypeOf, test } from 'vitest'
|
|
|
2
2
|
import * as MultisigSimulation from './MultisigSimulation.js'
|
|
3
3
|
|
|
4
4
|
const rpc = {
|
|
5
|
-
account: '0x2222222222222222222222222222222222222222',
|
|
6
5
|
approvals: [
|
|
7
6
|
{
|
|
7
|
+
keyType: 'webAuthn',
|
|
8
|
+
keyData: '0x0102030405',
|
|
8
9
|
owner: '0x1111111111111111111111111111111111111111',
|
|
9
|
-
type: 'primitive',
|
|
10
10
|
},
|
|
11
11
|
],
|
|
12
12
|
config:
|
|
@@ -3,70 +3,26 @@ import { describe, expect, test } from 'vitest'
|
|
|
3
3
|
|
|
4
4
|
const config =
|
|
5
5
|
'0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01' as const
|
|
6
|
-
const nestedConfig =
|
|
7
|
-
'0xf83ba022222222222222222222222222222222222222222222222222222222222222220101d7d694222222222222222222222222222222222222222201' as const
|
|
8
6
|
const rpc = {
|
|
9
|
-
account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
10
7
|
approvals: [
|
|
11
8
|
{
|
|
12
|
-
keyData: '0x0102030405',
|
|
13
9
|
keyType: 'webAuthn',
|
|
10
|
+
keyData: '0x0102030405',
|
|
14
11
|
owner: '0x1111111111111111111111111111111111111111',
|
|
15
|
-
type: 'primitive',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
spec: {
|
|
19
|
-
account: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
20
|
-
approvals: [
|
|
21
|
-
{
|
|
22
|
-
keyData: '0x010203040506',
|
|
23
|
-
keyType: 'secp256k1',
|
|
24
|
-
owner: '0x2222222222222222222222222222222222222222',
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
config: nestedConfig,
|
|
28
|
-
},
|
|
29
|
-
type: 'multisig',
|
|
30
12
|
},
|
|
31
13
|
],
|
|
32
14
|
config,
|
|
33
15
|
} as const satisfies MultisigSimulation.Rpc
|
|
34
16
|
|
|
35
17
|
describe('fromRpc', () => {
|
|
36
|
-
test('behavior: decodes
|
|
18
|
+
test('behavior: decodes the configuration', () => {
|
|
37
19
|
expect(MultisigSimulation.fromRpc(rpc)).toMatchInlineSnapshot(`
|
|
38
20
|
{
|
|
39
|
-
"account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
40
21
|
"approvals": [
|
|
41
22
|
{
|
|
42
23
|
"keyData": "0x0102030405",
|
|
43
24
|
"keyType": "webAuthn",
|
|
44
25
|
"owner": "0x1111111111111111111111111111111111111111",
|
|
45
|
-
"type": "primitive",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"spec": {
|
|
49
|
-
"account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
50
|
-
"approvals": [
|
|
51
|
-
{
|
|
52
|
-
"keyData": "0x010203040506",
|
|
53
|
-
"keyType": "secp256k1",
|
|
54
|
-
"owner": "0x2222222222222222222222222222222222222222",
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
"config": {
|
|
58
|
-
"owners": [
|
|
59
|
-
{
|
|
60
|
-
"owner": "0x2222222222222222222222222222222222222222",
|
|
61
|
-
"weight": 1,
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
"salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
|
|
65
|
-
"threshold": 1,
|
|
66
|
-
"version": 1n,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
"type": "multisig",
|
|
70
26
|
},
|
|
71
27
|
],
|
|
72
28
|
"config": {
|
|
@@ -108,26 +64,14 @@ describe('fromRpc', () => {
|
|
|
108
64
|
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
|
|
109
65
|
)
|
|
110
66
|
})
|
|
111
|
-
|
|
112
|
-
test('error: rejects excess nested approvals', () => {
|
|
67
|
+
test('error: rejects nested approvals', () => {
|
|
113
68
|
expect(() =>
|
|
114
69
|
MultisigSimulation.fromRpc({
|
|
115
70
|
...rpc,
|
|
116
|
-
approvals: [
|
|
117
|
-
|
|
118
|
-
...rpc.approvals[1],
|
|
119
|
-
spec: {
|
|
120
|
-
...rpc.approvals[1].spec,
|
|
121
|
-
approvals: Array.from(
|
|
122
|
-
{ length: 9 },
|
|
123
|
-
() => rpc.approvals[1].spec.approvals[0],
|
|
124
|
-
),
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
}),
|
|
71
|
+
approvals: [{ type: 'multisig', spec: rpc }],
|
|
72
|
+
} as never),
|
|
129
73
|
).toThrowErrorMatchingInlineSnapshot(
|
|
130
|
-
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation:
|
|
74
|
+
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
|
|
131
75
|
)
|
|
132
76
|
})
|
|
133
77
|
|
|
@@ -173,33 +117,17 @@ describe('toRpc', () => {
|
|
|
173
117
|
expect(
|
|
174
118
|
MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)),
|
|
175
119
|
).toMatchInlineSnapshot(`
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"owner": "0x1111111111111111111111111111111111111111",
|
|
183
|
-
"type": "primitive",
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
"spec": {
|
|
187
|
-
"account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
188
|
-
"approvals": [
|
|
189
|
-
{
|
|
190
|
-
"keyData": "0x0006",
|
|
191
|
-
"keyType": "secp256k1",
|
|
192
|
-
"owner": "0x2222222222222222222222222222222222222222",
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
"config": "${nestedConfig}",
|
|
120
|
+
{
|
|
121
|
+
"approvals": [
|
|
122
|
+
{
|
|
123
|
+
"keyData": "0x0005",
|
|
124
|
+
"keyType": "webAuthn",
|
|
125
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
196
126
|
},
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
`)
|
|
127
|
+
],
|
|
128
|
+
"config": "0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01",
|
|
129
|
+
}
|
|
130
|
+
`)
|
|
203
131
|
})
|
|
204
132
|
|
|
205
133
|
test('behavior: preserves short key data', () => {
|
|
@@ -214,7 +142,6 @@ describe('toRpc', () => {
|
|
|
214
142
|
"keyData": "0x0102",
|
|
215
143
|
"keyType": "webAuthn",
|
|
216
144
|
"owner": "0x1111111111111111111111111111111111111111",
|
|
217
|
-
"type": "primitive",
|
|
218
145
|
},
|
|
219
146
|
]
|
|
220
147
|
`)
|
|
@@ -255,30 +182,14 @@ describe('toRpc', () => {
|
|
|
255
182
|
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
|
|
256
183
|
)
|
|
257
184
|
})
|
|
258
|
-
|
|
259
|
-
test('error: rejects excess nested approvals', () => {
|
|
260
|
-
const spec = MultisigSimulation.fromRpc(rpc)
|
|
261
|
-
const nested = spec.approvals[1]!
|
|
262
|
-
if (nested.type !== 'multisig') throw new Error('unreachable')
|
|
263
|
-
|
|
185
|
+
test('error: rejects nested approvals', () => {
|
|
264
186
|
expect(() =>
|
|
265
187
|
MultisigSimulation.toRpc({
|
|
266
|
-
...
|
|
267
|
-
approvals: [
|
|
268
|
-
|
|
269
|
-
...nested,
|
|
270
|
-
spec: {
|
|
271
|
-
...nested.spec,
|
|
272
|
-
approvals: Array.from(
|
|
273
|
-
{ length: 9 },
|
|
274
|
-
() => nested.spec.approvals[0]!,
|
|
275
|
-
),
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
],
|
|
279
|
-
}),
|
|
188
|
+
...rpc,
|
|
189
|
+
approvals: [{ type: 'multisig', spec: rpc }],
|
|
190
|
+
} as never),
|
|
280
191
|
).toThrowErrorMatchingInlineSnapshot(
|
|
281
|
-
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation:
|
|
192
|
+
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
|
|
282
193
|
)
|
|
283
194
|
})
|
|
284
195
|
})
|
|
@@ -7,56 +7,10 @@ import * as MultisigConfig from './MultisigConfig.js'
|
|
|
7
7
|
import type * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
8
8
|
|
|
9
9
|
/** Native multisig owner approval used for RPC simulation. */
|
|
10
|
-
export type Approval =
|
|
10
|
+
export type Approval = PrimitiveApproval
|
|
11
11
|
|
|
12
12
|
/** JSON-RPC native multisig owner approval used for RPC simulation. */
|
|
13
|
-
export type ApprovalRpc =
|
|
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
|
-
}
|
|
13
|
+
export type ApprovalRpc = PrimitiveApproval
|
|
60
14
|
|
|
61
15
|
/** Primitive owner approval used for RPC simulation. */
|
|
62
16
|
export type PrimitiveApproval = {
|
|
@@ -66,14 +20,10 @@ export type PrimitiveApproval = {
|
|
|
66
20
|
keyType?: SignatureEnvelope.Type | undefined
|
|
67
21
|
/** Configured owner address. */
|
|
68
22
|
owner: Address.Address
|
|
69
|
-
/** Approval type. */
|
|
70
|
-
type: 'primitive'
|
|
71
23
|
}
|
|
72
24
|
|
|
73
25
|
/** JSON-RPC representation of a native multisig simulation spec. */
|
|
74
26
|
export type Rpc = Compute<{
|
|
75
|
-
/** Account authorized by this spec. */
|
|
76
|
-
account: Address.Address
|
|
77
27
|
/** Owner approvals to model. */
|
|
78
28
|
approvals: readonly ApprovalRpc[]
|
|
79
29
|
/** Canonical RLP-encoded applicable configuration. */
|
|
@@ -82,8 +32,6 @@ export type Rpc = Compute<{
|
|
|
82
32
|
|
|
83
33
|
/** Native multisig spec used to construct an RPC simulation signature. */
|
|
84
34
|
export type Spec = Compute<{
|
|
85
|
-
/** Account authorized by this spec. */
|
|
86
|
-
account: Address.Address
|
|
87
35
|
/** Owner approvals to model. */
|
|
88
36
|
approvals: readonly Approval[]
|
|
89
37
|
/** Complete applicable configuration. */
|
|
@@ -105,24 +53,9 @@ export type Spec = Compute<{
|
|
|
105
53
|
* @returns The multisig simulation spec with decoded configurations.
|
|
106
54
|
*/
|
|
107
55
|
export function fromRpc(spec: Rpc): Spec {
|
|
108
|
-
const {
|
|
109
|
-
|
|
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
|
-
}
|
|
56
|
+
const { approvals, config } = spec
|
|
57
|
+
assertApprovals(approvals)
|
|
58
|
+
return { approvals, config: deserializeConfig(config) }
|
|
126
59
|
}
|
|
127
60
|
|
|
128
61
|
export declare namespace fromRpc {
|
|
@@ -151,33 +84,14 @@ export declare namespace fromRpc {
|
|
|
151
84
|
* @returns The JSON-RPC multisig simulation spec with encoded configurations.
|
|
152
85
|
*/
|
|
153
86
|
export function toRpc(spec: Spec): Rpc {
|
|
154
|
-
const {
|
|
155
|
-
|
|
87
|
+
const { approvals, config } = spec
|
|
88
|
+
assertApprovals(approvals)
|
|
156
89
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
}),
|
|
90
|
+
approvals: approvals.map(({ keyData, keyType, owner }) => ({
|
|
91
|
+
...(keyData !== undefined ? { keyData: shimKeyData(keyData) } : {}),
|
|
92
|
+
...(keyType !== undefined ? { keyType } : {}),
|
|
93
|
+
owner,
|
|
94
|
+
})),
|
|
181
95
|
config: serializeConfig(config),
|
|
182
96
|
}
|
|
183
97
|
}
|
|
@@ -193,7 +107,19 @@ export declare namespace toRpc {
|
|
|
193
107
|
}
|
|
194
108
|
|
|
195
109
|
/** @internal */
|
|
196
|
-
function
|
|
110
|
+
function assertApprovals(approvals: readonly unknown[]) {
|
|
111
|
+
if (
|
|
112
|
+
approvals.some(
|
|
113
|
+
(approval) =>
|
|
114
|
+
typeof approval !== 'object' ||
|
|
115
|
+
approval === null ||
|
|
116
|
+
'spec' in approval ||
|
|
117
|
+
'type' in approval,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
throw new InvalidSimulationError({
|
|
121
|
+
reason: 'only untagged primitive owner approvals are allowed',
|
|
122
|
+
})
|
|
197
123
|
if (approvals.length > MultisigConfig.maxSignatures)
|
|
198
124
|
throw new InvalidSimulationError({
|
|
199
125
|
reason: `approval count exceeds ${MultisigConfig.maxSignatures}`,
|