ox 1.7.4 → 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 +16 -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 +42 -38
- 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 +3 -2
- package/dist/tempo/MultisigOperation.d.ts.map +1 -1
- package/dist/tempo/MultisigOperation.js +20 -100
- package/dist/tempo/MultisigOperation.js.map +1 -1
- package/dist/tempo/MultisigSimulation.d.ts +2 -49
- package/dist/tempo/MultisigSimulation.d.ts.map +1 -1
- package/dist/tempo/MultisigSimulation.js +18 -46
- package/dist/tempo/MultisigSimulation.js.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +72 -140
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +130 -181
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +4 -2
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +6 -1
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/index.d.ts +3 -1
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +3 -1
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +350 -10
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +242 -422
- 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/MultisigSimulation.d.ts +8 -76
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigSimulation.js +3 -37
- package/dist/zod/tempo/MultisigSimulation.js.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +284 -228
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +105 -3
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +21 -9
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +676 -448
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +590 -473
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +8 -0
- 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/package.json +1 -1
- 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 -4
- package/src/tempo/KeyAuthorization.test-d.ts +16 -63
- package/src/tempo/KeyAuthorization.test.ts +69 -11
- package/src/tempo/KeyAuthorization.ts +107 -63
- package/src/tempo/MultisigConfig.test.ts +44 -20
- package/src/tempo/MultisigConfig.ts +65 -23
- package/src/tempo/MultisigOperation.test.ts +79 -550
- package/src/tempo/MultisigOperation.ts +31 -146
- package/src/tempo/MultisigSimulation.test-d.ts +2 -2
- package/src/tempo/MultisigSimulation.test.ts +21 -110
- package/src/tempo/MultisigSimulation.ts +25 -99
- package/src/tempo/SignatureEnvelope.test-d.ts +29 -47
- package/src/tempo/SignatureEnvelope.test.ts +328 -541
- package/src/tempo/SignatureEnvelope.ts +196 -291
- package/src/tempo/Transaction.test.ts +4 -4
- package/src/tempo/TransactionRequest.test.ts +17 -80
- package/src/tempo/TransactionRequest.ts +19 -2
- package/src/tempo/index.ts +3 -1
- package/src/tempo/multisig.e2e.test.ts +321 -860
- package/src/version.ts +1 -1
- package/src/zod/tempo/KeyAuthorization.ts +50 -39
- package/src/zod/tempo/MultisigSimulation.ts +3 -48
- package/src/zod/tempo/SignatureEnvelope.ts +46 -35
- package/src/zod/tempo/TransactionRequest.ts +14 -0
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +69 -20
- package/src/zod/tempo/_test/MultisigSimulation.test.ts +6 -31
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +118 -85
- package/src/zod/tempo/_test/TransactionRequest.test.ts +9 -19
|
@@ -142,7 +142,8 @@ export declare namespace getHash {
|
|
|
142
142
|
*
|
|
143
143
|
* The function retains one canonical approval per owner. It selects the
|
|
144
144
|
* smallest deterministic quorum by owner weight, then orders the selected
|
|
145
|
-
* 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.
|
|
146
147
|
*
|
|
147
148
|
* @example
|
|
148
149
|
* ```ts twoslash
|
|
@@ -169,22 +170,12 @@ export async function selectApprovals(
|
|
|
169
170
|
if (!Hash.validate(hash))
|
|
170
171
|
throw new InvalidApprovalError({ reason: 'hash is invalid' })
|
|
171
172
|
const config = MultisigConfig.from(options.config)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
})
|
|
179
|
-
return selectApprovals_internal(
|
|
180
|
-
{
|
|
181
|
-
account,
|
|
182
|
-
approvals,
|
|
183
|
-
config,
|
|
184
|
-
hash,
|
|
185
|
-
},
|
|
186
|
-
[account.toLowerCase()],
|
|
187
|
-
)
|
|
173
|
+
return selectApprovals_internal({
|
|
174
|
+
account,
|
|
175
|
+
approvals,
|
|
176
|
+
config,
|
|
177
|
+
hash,
|
|
178
|
+
})
|
|
188
179
|
}
|
|
189
180
|
|
|
190
181
|
export declare namespace selectApprovals {
|
|
@@ -192,7 +183,7 @@ export declare namespace selectApprovals {
|
|
|
192
183
|
export type Options = {
|
|
193
184
|
/** Root multisig account. */
|
|
194
185
|
account: Address.Address
|
|
195
|
-
/** Serialized primitive
|
|
186
|
+
/** Serialized primitive owner approvals. */
|
|
196
187
|
approvals: readonly SignatureEnvelope.Serialized[]
|
|
197
188
|
/** Current root multisig configuration. */
|
|
198
189
|
config: MultisigConfig.Config
|
|
@@ -271,7 +262,7 @@ export function serializeKeyAuthorization(
|
|
|
271
262
|
config,
|
|
272
263
|
payload: KeyAuthorization_.getSignPayload(authorization),
|
|
273
264
|
signatures: options.approvals.map((approval) =>
|
|
274
|
-
|
|
265
|
+
assertApproval(options.account, approval, config),
|
|
275
266
|
),
|
|
276
267
|
})
|
|
277
268
|
return KeyAuthorization_.serialize(
|
|
@@ -339,7 +330,7 @@ export function serializeTransaction(
|
|
|
339
330
|
value.transaction as TxEnvelopeTempo.Serialized,
|
|
340
331
|
)
|
|
341
332
|
const approvals = options.approvals.map((approval) =>
|
|
342
|
-
|
|
333
|
+
assertApproval(value.account, approval, value.config),
|
|
343
334
|
)
|
|
344
335
|
assertRetainedApprovals(value, approvals)
|
|
345
336
|
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
@@ -524,7 +515,6 @@ export declare namespace toRpc {
|
|
|
524
515
|
*/
|
|
525
516
|
async function selectApprovals_internal(
|
|
526
517
|
options: selectApprovals.Options,
|
|
527
|
-
path: readonly string[],
|
|
528
518
|
): Promise<selectApprovals.ReturnValue> {
|
|
529
519
|
const owners = new Map(
|
|
530
520
|
options.config.owners.map((owner) => [
|
|
@@ -535,17 +525,14 @@ async function selectApprovals_internal(
|
|
|
535
525
|
const groups = new Map<string, ApprovalGroup>()
|
|
536
526
|
for (const serialized of options.approvals) {
|
|
537
527
|
const signature = SignatureEnvelope.from(serialized)
|
|
538
|
-
if (signature.type === 'keychain')
|
|
528
|
+
if (signature.type === 'keychain' || signature.type === 'multisig')
|
|
539
529
|
throw new InvalidApprovalError({
|
|
540
|
-
reason: '
|
|
530
|
+
reason: 'only primitive signatures can approve a multisig operation',
|
|
541
531
|
})
|
|
542
|
-
const address =
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
payload: options.hash,
|
|
547
|
-
signature,
|
|
548
|
-
})
|
|
532
|
+
const address = SignatureEnvelope.extractAddress({
|
|
533
|
+
payload: options.hash,
|
|
534
|
+
signature,
|
|
535
|
+
})
|
|
549
536
|
const owner = owners.get(address.toLowerCase())
|
|
550
537
|
if (!owner)
|
|
551
538
|
throw new InvalidApprovalError({
|
|
@@ -565,79 +552,6 @@ async function selectApprovals_internal(
|
|
|
565
552
|
const valid: SelectedApproval[] = []
|
|
566
553
|
const retained: RetainedApproval[] = []
|
|
567
554
|
for (const group of groups.values()) {
|
|
568
|
-
const nested = group.signatures.filter(
|
|
569
|
-
(signature) => signature.type === 'multisig',
|
|
570
|
-
)
|
|
571
|
-
if (nested.length > 0) {
|
|
572
|
-
if (nested.length !== group.signatures.length)
|
|
573
|
-
throw new InvalidApprovalError({
|
|
574
|
-
reason: `owner ${group.address} has conflicting signature types`,
|
|
575
|
-
})
|
|
576
|
-
if (
|
|
577
|
-
path.length >= MultisigConfig.maxNestingDepth ||
|
|
578
|
-
path.includes(group.address.toLowerCase())
|
|
579
|
-
)
|
|
580
|
-
throw new InvalidApprovalError({
|
|
581
|
-
reason: `nested multisig owner ${group.address} is invalid`,
|
|
582
|
-
})
|
|
583
|
-
const config = MultisigConfig.from(nested[0]!.config)
|
|
584
|
-
if (nested.some((signature) => !sameConfig(signature.config, config)))
|
|
585
|
-
throw new InvalidApprovalError({
|
|
586
|
-
reason: `nested multisig owner ${group.address} has conflicting config witnesses`,
|
|
587
|
-
})
|
|
588
|
-
if (
|
|
589
|
-
config.version === 0n &&
|
|
590
|
-
!Address.isEqual(MultisigConfig.getAddress(config), group.address)
|
|
591
|
-
)
|
|
592
|
-
throw new InvalidApprovalError({
|
|
593
|
-
reason: `initial config does not derive nested multisig owner ${group.address}`,
|
|
594
|
-
})
|
|
595
|
-
const selected = await selectApprovals_internal(
|
|
596
|
-
{
|
|
597
|
-
account: group.address,
|
|
598
|
-
approvals: nested.flatMap((signature) =>
|
|
599
|
-
signature.signatures.map((approval) =>
|
|
600
|
-
SignatureEnvelope.serialize(approval),
|
|
601
|
-
),
|
|
602
|
-
),
|
|
603
|
-
config,
|
|
604
|
-
hash: MultisigConfig.getSignPayload({
|
|
605
|
-
account: group.address,
|
|
606
|
-
config,
|
|
607
|
-
payload: options.hash,
|
|
608
|
-
}),
|
|
609
|
-
},
|
|
610
|
-
[...path, group.address.toLowerCase()],
|
|
611
|
-
)
|
|
612
|
-
retained.push({
|
|
613
|
-
address: group.address,
|
|
614
|
-
signature: SignatureEnvelope.serialize(
|
|
615
|
-
SignatureEnvelope.from({
|
|
616
|
-
account: group.address,
|
|
617
|
-
config,
|
|
618
|
-
signatures: selected.approvals.map((approval) =>
|
|
619
|
-
SignatureEnvelope.from(approval),
|
|
620
|
-
),
|
|
621
|
-
}),
|
|
622
|
-
),
|
|
623
|
-
})
|
|
624
|
-
if (selected.weight >= selected.threshold)
|
|
625
|
-
valid.push({
|
|
626
|
-
address: group.address,
|
|
627
|
-
signature: SignatureEnvelope.serialize(
|
|
628
|
-
SignatureEnvelope.from({
|
|
629
|
-
account: group.address,
|
|
630
|
-
config,
|
|
631
|
-
signatures: selected.selectedApprovals.map((approval) =>
|
|
632
|
-
SignatureEnvelope.from(approval),
|
|
633
|
-
),
|
|
634
|
-
}),
|
|
635
|
-
),
|
|
636
|
-
weight: group.weight,
|
|
637
|
-
})
|
|
638
|
-
continue
|
|
639
|
-
}
|
|
640
|
-
|
|
641
555
|
const signatures = group.signatures.map((signature) => {
|
|
642
556
|
if (
|
|
643
557
|
!SignatureEnvelope.verify(signature, {
|
|
@@ -706,7 +620,7 @@ type ApprovalGroup = {
|
|
|
706
620
|
type RetainedApproval = {
|
|
707
621
|
/** Configured owner address. */
|
|
708
622
|
address: Address.Address
|
|
709
|
-
/** Serialized primitive
|
|
623
|
+
/** Serialized primitive approval. */
|
|
710
624
|
signature: SignatureEnvelope.Serialized
|
|
711
625
|
}
|
|
712
626
|
|
|
@@ -826,13 +740,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
826
740
|
reason:
|
|
827
741
|
'weight is not reachable by signatureCount retained owner approvals',
|
|
828
742
|
})
|
|
829
|
-
if (
|
|
830
|
-
config.version === 0n &&
|
|
831
|
-
!Address.isEqual(MultisigConfig.getAddress(config), operation.account)
|
|
832
|
-
)
|
|
833
|
-
throw new InvalidOperationError({
|
|
834
|
-
reason: 'initial config does not derive the operation account',
|
|
835
|
-
})
|
|
836
743
|
}
|
|
837
744
|
|
|
838
745
|
/**
|
|
@@ -1020,8 +927,12 @@ function assertApproval(
|
|
|
1020
927
|
account: Address.Address,
|
|
1021
928
|
serialized: SignatureEnvelope.Serialized,
|
|
1022
929
|
config: MultisigConfig.Config,
|
|
1023
|
-
): SignatureEnvelope.
|
|
930
|
+
): SignatureEnvelope.Primitive {
|
|
1024
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
|
+
})
|
|
1025
936
|
SignatureEnvelope.assert({
|
|
1026
937
|
account,
|
|
1027
938
|
config,
|
|
@@ -1049,8 +960,10 @@ function assertRetainedApprovals(
|
|
|
1049
960
|
SignatureEnvelope.deserialize(approval),
|
|
1050
961
|
)
|
|
1051
962
|
for (const approval of selected) {
|
|
1052
|
-
const index = retained.findIndex(
|
|
1053
|
-
|
|
963
|
+
const index = retained.findIndex(
|
|
964
|
+
(candidate) =>
|
|
965
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
966
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1054
967
|
)
|
|
1055
968
|
if (index === -1)
|
|
1056
969
|
throw new InvalidOperationError({
|
|
@@ -1074,8 +987,10 @@ function assertSelectedApprovals(
|
|
|
1074
987
|
SignatureEnvelope.deserialize(approval),
|
|
1075
988
|
)
|
|
1076
989
|
for (const approval of selected) {
|
|
1077
|
-
const index = retained.findIndex(
|
|
1078
|
-
|
|
990
|
+
const index = retained.findIndex(
|
|
991
|
+
(candidate) =>
|
|
992
|
+
SignatureEnvelope.serialize(candidate).toLowerCase() ===
|
|
993
|
+
SignatureEnvelope.serialize(approval).toLowerCase(),
|
|
1079
994
|
)
|
|
1080
995
|
if (index === -1)
|
|
1081
996
|
throw new InvalidOperationError({
|
|
@@ -1106,36 +1021,6 @@ function assertSelectedApprovals(
|
|
|
1106
1021
|
}
|
|
1107
1022
|
}
|
|
1108
1023
|
|
|
1109
|
-
/**
|
|
1110
|
-
* Checks whether a selected approval is contained in a retained approval tree.
|
|
1111
|
-
*
|
|
1112
|
-
* @internal
|
|
1113
|
-
*/
|
|
1114
|
-
function includesApproval(
|
|
1115
|
-
retained: SignatureEnvelope.SignatureEnvelope,
|
|
1116
|
-
selected: SignatureEnvelope.SignatureEnvelope,
|
|
1117
|
-
): boolean {
|
|
1118
|
-
if (retained.type !== 'multisig' || selected.type !== 'multisig')
|
|
1119
|
-
return (
|
|
1120
|
-
SignatureEnvelope.serialize(retained).toLowerCase() ===
|
|
1121
|
-
SignatureEnvelope.serialize(selected).toLowerCase()
|
|
1122
|
-
)
|
|
1123
|
-
if (retained.account.toLowerCase() !== selected.account.toLowerCase())
|
|
1124
|
-
return false
|
|
1125
|
-
if (!sameConfig(retained.config, selected.config)) return false
|
|
1126
|
-
let index = 0
|
|
1127
|
-
for (const approval of selected.signatures) {
|
|
1128
|
-
while (
|
|
1129
|
-
index < retained.signatures.length &&
|
|
1130
|
-
!includesApproval(retained.signatures[index]!, approval)
|
|
1131
|
-
)
|
|
1132
|
-
index++
|
|
1133
|
-
if (index === retained.signatures.length) return false
|
|
1134
|
-
index++
|
|
1135
|
-
}
|
|
1136
|
-
return true
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
1024
|
/**
|
|
1140
1025
|
* Checks whether exactly `signatureCount` retained owners can produce `weight`.
|
|
1141
1026
|
*
|
|
@@ -2,11 +2,11 @@ import { expectTypeOf, test } from 'vp/test'
|
|
|
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 'vp/test'
|
|
|
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
|
|
|
@@ -172,33 +116,17 @@ describe('toRpc', () => {
|
|
|
172
116
|
test('behavior: encodes configurations and shims long key data', () => {
|
|
173
117
|
expect(MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)))
|
|
174
118
|
.toMatchInlineSnapshot(`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"owner": "0x1111111111111111111111111111111111111111",
|
|
182
|
-
"type": "primitive",
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
"spec": {
|
|
186
|
-
"account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
187
|
-
"approvals": [
|
|
188
|
-
{
|
|
189
|
-
"keyData": "0x0006",
|
|
190
|
-
"keyType": "secp256k1",
|
|
191
|
-
"owner": "0x2222222222222222222222222222222222222222",
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
"config": "${nestedConfig}",
|
|
119
|
+
{
|
|
120
|
+
"approvals": [
|
|
121
|
+
{
|
|
122
|
+
"keyData": "0x0005",
|
|
123
|
+
"keyType": "webAuthn",
|
|
124
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
195
125
|
},
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
`)
|
|
126
|
+
],
|
|
127
|
+
"config": "0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01",
|
|
128
|
+
}
|
|
129
|
+
`)
|
|
202
130
|
})
|
|
203
131
|
|
|
204
132
|
test('behavior: preserves short key data', () => {
|
|
@@ -213,7 +141,6 @@ describe('toRpc', () => {
|
|
|
213
141
|
"keyData": "0x0102",
|
|
214
142
|
"keyType": "webAuthn",
|
|
215
143
|
"owner": "0x1111111111111111111111111111111111111111",
|
|
216
|
-
"type": "primitive",
|
|
217
144
|
},
|
|
218
145
|
]
|
|
219
146
|
`)
|
|
@@ -254,30 +181,14 @@ describe('toRpc', () => {
|
|
|
254
181
|
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
|
|
255
182
|
)
|
|
256
183
|
})
|
|
257
|
-
|
|
258
|
-
test('error: rejects excess nested approvals', () => {
|
|
259
|
-
const spec = MultisigSimulation.fromRpc(rpc)
|
|
260
|
-
const nested = spec.approvals[1]!
|
|
261
|
-
if (nested.type !== 'multisig') throw new Error('unreachable')
|
|
262
|
-
|
|
184
|
+
test('error: rejects nested approvals', () => {
|
|
263
185
|
expect(() =>
|
|
264
186
|
MultisigSimulation.toRpc({
|
|
265
|
-
...
|
|
266
|
-
approvals: [
|
|
267
|
-
|
|
268
|
-
...nested,
|
|
269
|
-
spec: {
|
|
270
|
-
...nested.spec,
|
|
271
|
-
approvals: Array.from(
|
|
272
|
-
{ length: 9 },
|
|
273
|
-
() => nested.spec.approvals[0]!,
|
|
274
|
-
),
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
],
|
|
278
|
-
}),
|
|
187
|
+
...rpc,
|
|
188
|
+
approvals: [{ type: 'multisig', spec: rpc }],
|
|
189
|
+
} as never),
|
|
279
190
|
).toThrowErrorMatchingInlineSnapshot(
|
|
280
|
-
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation:
|
|
191
|
+
`[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
|
|
281
192
|
)
|
|
282
193
|
})
|
|
283
194
|
})
|
|
@@ -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}`,
|