ox 0.14.38 → 0.14.39
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 +14 -0
- package/_cjs/tempo/MultisigConfig.js +71 -15
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +42 -36
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +71 -108
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/MultisigConfig.js +140 -71
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/MultisigOperation.js +42 -37
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +84 -143
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +1 -1
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +137 -73
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/MultisigOperation.d.ts +1 -1
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +81 -75
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/package.json +6 -1
- package/tempo/KeyAuthorization.test-d.ts +12 -0
- package/tempo/KeyAuthorization.test.ts +54 -0
- package/tempo/KeyAuthorization.ts +1 -1
- package/tempo/MultisigConfig.test-d/package.json +6 -0
- package/tempo/MultisigConfig.test-d.ts +52 -0
- package/tempo/MultisigConfig.test.ts +291 -263
- package/tempo/MultisigConfig.ts +250 -95
- package/tempo/MultisigOperation.test-d.ts +6 -0
- package/tempo/MultisigOperation.test.ts +113 -72
- package/tempo/MultisigOperation.ts +41 -35
- package/tempo/SignatureEnvelope.test-d.ts +57 -40
- package/tempo/SignatureEnvelope.test.ts +523 -480
- package/tempo/SignatureEnvelope.ts +250 -257
- package/tempo/multisig.e2e.test.ts +113 -86
- package/version.ts +1 -1
|
@@ -27,6 +27,50 @@ const serializedKeychainType = '0x03'
|
|
|
27
27
|
const serializedKeychainV2Type = '0x04'
|
|
28
28
|
const serializedMultisigType = '0x05'
|
|
29
29
|
|
|
30
|
+
type EnvelopeOneOf<
|
|
31
|
+
union extends object,
|
|
32
|
+
keys extends PropertyKey,
|
|
33
|
+
> = union extends union
|
|
34
|
+
? Compute<
|
|
35
|
+
union & {
|
|
36
|
+
[key in Exclude<keys, keyof union>]?: undefined
|
|
37
|
+
}
|
|
38
|
+
>
|
|
39
|
+
: never
|
|
40
|
+
|
|
41
|
+
type SignatureEnvelopeKey =
|
|
42
|
+
| 'account'
|
|
43
|
+
| 'config'
|
|
44
|
+
| 'inner'
|
|
45
|
+
| 'keyId'
|
|
46
|
+
| 'metadata'
|
|
47
|
+
| 'prehash'
|
|
48
|
+
| 'publicKey'
|
|
49
|
+
| 'signature'
|
|
50
|
+
| 'signatures'
|
|
51
|
+
| 'type'
|
|
52
|
+
| 'userAddress'
|
|
53
|
+
| 'version'
|
|
54
|
+
|
|
55
|
+
type SignatureEnvelopeRpcKey =
|
|
56
|
+
| 'account'
|
|
57
|
+
| 'config'
|
|
58
|
+
| 'keyId'
|
|
59
|
+
| 'metadata'
|
|
60
|
+
| 'preHash'
|
|
61
|
+
| 'pubKeyX'
|
|
62
|
+
| 'pubKeyY'
|
|
63
|
+
| 'r'
|
|
64
|
+
| 's'
|
|
65
|
+
| 'signature'
|
|
66
|
+
| 'signatures'
|
|
67
|
+
| 'type'
|
|
68
|
+
| 'userAddress'
|
|
69
|
+
| 'v'
|
|
70
|
+
| 'version'
|
|
71
|
+
| 'webauthnData'
|
|
72
|
+
| 'yParity'
|
|
73
|
+
|
|
30
74
|
/** Serialized magic identifier for Tempo signature envelopes. */
|
|
31
75
|
export const magicBytes =
|
|
32
76
|
'0x7777777777777777777777777777777777777777777777777777777777777777' // 32 "T"s
|
|
@@ -72,15 +116,7 @@ export type GetType<
|
|
|
72
116
|
userAddress: Address.Address
|
|
73
117
|
}
|
|
74
118
|
? 'keychain'
|
|
75
|
-
: envelope extends
|
|
76
|
-
| {
|
|
77
|
-
account: Address.Address
|
|
78
|
-
signatures: any
|
|
79
|
-
}
|
|
80
|
-
| {
|
|
81
|
-
init: MultisigConfig.Config
|
|
82
|
-
signatures: any
|
|
83
|
-
}
|
|
119
|
+
: envelope extends { config: unknown; signatures: any }
|
|
84
120
|
? 'multisig'
|
|
85
121
|
: never
|
|
86
122
|
|
|
@@ -107,20 +143,25 @@ export type GetType<
|
|
|
107
143
|
*
|
|
108
144
|
* [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
109
145
|
*/
|
|
110
|
-
export type SignatureEnvelope<bigintType = bigint, numberType = number> =
|
|
111
|
-
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
146
|
+
export type SignatureEnvelope<bigintType = bigint, numberType = number> =
|
|
147
|
+
| EnvelopeOneOf<
|
|
148
|
+
| Secp256k1<bigintType, numberType>
|
|
149
|
+
| P256<bigintType, numberType>
|
|
150
|
+
| WebAuthn<bigintType, numberType>
|
|
151
|
+
| Keychain<bigintType, numberType>,
|
|
152
|
+
SignatureEnvelopeKey
|
|
153
|
+
>
|
|
154
|
+
| MultisigOneOf<bigintType, numberType>
|
|
117
155
|
|
|
118
156
|
/**
|
|
119
157
|
* RPC-formatted signature envelope.
|
|
120
158
|
*/
|
|
121
|
-
export type SignatureEnvelopeRpc =
|
|
122
|
-
|
|
123
|
-
|
|
159
|
+
export type SignatureEnvelopeRpc =
|
|
160
|
+
| EnvelopeOneOf<
|
|
161
|
+
Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc,
|
|
162
|
+
SignatureEnvelopeRpcKey
|
|
163
|
+
>
|
|
164
|
+
| MultisigRpcOneOf
|
|
124
165
|
|
|
125
166
|
/** Primitive signature envelope accepted by protocol sidecars. */
|
|
126
167
|
export type Primitive<bigintType = bigint, numberType = number> = OneOf<
|
|
@@ -171,43 +212,61 @@ export type KeychainRpc = {
|
|
|
171
212
|
*
|
|
172
213
|
* [TIP-1061](https://tips.sh/1061)
|
|
173
214
|
*/
|
|
174
|
-
export
|
|
175
|
-
type: 'multisig'
|
|
215
|
+
export interface Multisig<bigintType = bigint, numberType = number> {
|
|
176
216
|
/** Native multisig account address. */
|
|
177
217
|
account: Address.Address
|
|
218
|
+
/** Complete applicable multisig configuration witness. */
|
|
219
|
+
config: MultisigConfig.Config<bigintType, numberType>
|
|
178
220
|
/**
|
|
179
221
|
* Owner approvals over the multisig owner approval digest. Each approval is
|
|
180
222
|
* either a primitive signature or a nested multisig signature (keychain
|
|
181
223
|
* approvals are invalid).
|
|
182
224
|
*/
|
|
183
225
|
signatures: readonly SignatureEnvelope<bigintType, numberType>[]
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
226
|
+
type: 'multisig'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
type MultisigOneOf<bigintType = bigint, numberType = number> = Multisig<
|
|
230
|
+
bigintType,
|
|
231
|
+
numberType
|
|
232
|
+
> & {
|
|
233
|
+
inner?: undefined
|
|
234
|
+
keyId?: undefined
|
|
235
|
+
metadata?: undefined
|
|
236
|
+
prehash?: undefined
|
|
237
|
+
publicKey?: undefined
|
|
238
|
+
signature?: undefined
|
|
239
|
+
userAddress?: undefined
|
|
240
|
+
version?: undefined
|
|
190
241
|
}
|
|
191
242
|
|
|
192
243
|
/** RPC-formatted native multisig signature. */
|
|
193
|
-
export type MultisigRpc =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
244
|
+
export type MultisigRpc = {
|
|
245
|
+
/** Native multisig account address. */
|
|
246
|
+
account: Address.Address
|
|
247
|
+
/** Complete applicable multisig configuration witness. */
|
|
248
|
+
config: MultisigConfig.Rpc
|
|
249
|
+
/** Structured owner approvals. */
|
|
250
|
+
signatures: readonly SignatureEnvelopeRpc[]
|
|
251
|
+
/** Multisig RPC signatures are untagged. */
|
|
252
|
+
type?: undefined
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
type MultisigRpcOneOf = MultisigRpc & {
|
|
256
|
+
keyId?: undefined
|
|
257
|
+
metadata?: undefined
|
|
258
|
+
preHash?: undefined
|
|
259
|
+
pubKeyX?: undefined
|
|
260
|
+
pubKeyY?: undefined
|
|
261
|
+
r?: undefined
|
|
262
|
+
s?: undefined
|
|
263
|
+
signature?: undefined
|
|
264
|
+
userAddress?: undefined
|
|
265
|
+
v?: undefined
|
|
266
|
+
version?: undefined
|
|
267
|
+
webauthnData?: undefined
|
|
268
|
+
yParity?: undefined
|
|
269
|
+
}
|
|
211
270
|
|
|
212
271
|
export type P256<bigintType = bigint, numberType = number> = {
|
|
213
272
|
prehash: boolean
|
|
@@ -370,6 +429,7 @@ export declare namespace assert {
|
|
|
370
429
|
function assertMultisig(envelope: Multisig, depth: number): void {
|
|
371
430
|
const missing: string[] = []
|
|
372
431
|
if (!envelope.account) missing.push('account')
|
|
432
|
+
if (!envelope.config) missing.push('config')
|
|
373
433
|
if (!Array.isArray(envelope.signatures)) missing.push('signatures')
|
|
374
434
|
if (missing.length > 0)
|
|
375
435
|
throw new MissingPropertiesError({
|
|
@@ -389,6 +449,25 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
389
449
|
throw new InvalidMultisigApprovalError({
|
|
390
450
|
reason: 'multisig account cannot be zero',
|
|
391
451
|
})
|
|
452
|
+
MultisigConfig.assert(envelope.config)
|
|
453
|
+
if (
|
|
454
|
+
envelope.config.owners.some((owner) =>
|
|
455
|
+
Address.isEqual(owner.owner, envelope.account),
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
throw new InvalidMultisigApprovalError({
|
|
459
|
+
reason: 'multisig account cannot be an owner',
|
|
460
|
+
})
|
|
461
|
+
if (
|
|
462
|
+
envelope.config.version === 0n &&
|
|
463
|
+
!Address.isEqual(
|
|
464
|
+
MultisigConfig.getAddress(envelope.config),
|
|
465
|
+
envelope.account,
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
throw new InvalidMultisigApprovalError({
|
|
469
|
+
reason: 'initial multisig config does not derive account',
|
|
470
|
+
})
|
|
392
471
|
if (envelope.signatures.length === 0)
|
|
393
472
|
throw new InvalidMultisigApprovalError({
|
|
394
473
|
reason: 'multisig signatures cannot be empty',
|
|
@@ -398,33 +477,14 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
398
477
|
reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
|
|
399
478
|
})
|
|
400
479
|
|
|
401
|
-
if (envelope.init) {
|
|
402
|
-
MultisigConfig.assert(envelope.init)
|
|
403
|
-
if (
|
|
404
|
-
!Address.isEqual(
|
|
405
|
-
MultisigConfig.getAddress(envelope.init),
|
|
406
|
-
envelope.account,
|
|
407
|
-
)
|
|
408
|
-
)
|
|
409
|
-
throw new InvalidMultisigApprovalError({
|
|
410
|
-
reason: 'multisig init does not derive account',
|
|
411
|
-
})
|
|
412
|
-
}
|
|
413
|
-
|
|
414
480
|
for (const inner of envelope.signatures) {
|
|
415
481
|
const type = getType(inner)
|
|
416
482
|
if (type === 'keychain')
|
|
417
483
|
throw new InvalidMultisigApprovalError({
|
|
418
484
|
reason: 'keychain owner approvals are not allowed',
|
|
419
485
|
})
|
|
420
|
-
if (type === 'multisig')
|
|
421
|
-
|
|
422
|
-
if (multisig.init)
|
|
423
|
-
throw new InvalidMultisigApprovalError({
|
|
424
|
-
reason: 'nested multisig owner approvals cannot carry `init`',
|
|
425
|
-
})
|
|
426
|
-
assertMultisig(multisig, depth + 1)
|
|
427
|
-
} else {
|
|
486
|
+
if (type === 'multisig') assertMultisig(inner as Multisig, depth + 1)
|
|
487
|
+
else {
|
|
428
488
|
assert(inner)
|
|
429
489
|
if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
|
|
430
490
|
throw new InvalidMultisigApprovalError({
|
|
@@ -714,16 +774,48 @@ function deserialize_(
|
|
|
714
774
|
serialized,
|
|
715
775
|
})
|
|
716
776
|
|
|
717
|
-
// The first field distinguishes the static wire shapes: a bootstrap init
|
|
718
|
-
// config is an RLP list, while an initialized account is a 20-byte string.
|
|
719
777
|
const decoded = Rlp.toHex(data)
|
|
720
|
-
if (!Array.isArray(decoded) || decoded.length !==
|
|
778
|
+
if (!Array.isArray(decoded) || decoded.length !== 3)
|
|
721
779
|
throw new InvalidSerializedError({
|
|
722
|
-
reason: 'invalid multisig wire shape: expected exactly
|
|
780
|
+
reason: 'invalid multisig wire shape: expected exactly three fields',
|
|
723
781
|
serialized,
|
|
724
782
|
})
|
|
725
783
|
|
|
726
|
-
const [
|
|
784
|
+
const [account, config, signatures] = decoded
|
|
785
|
+
if (!Address.validate(account))
|
|
786
|
+
throw new InvalidSerializedError({
|
|
787
|
+
reason: 'invalid multisig account',
|
|
788
|
+
serialized,
|
|
789
|
+
})
|
|
790
|
+
if (!Array.isArray(config))
|
|
791
|
+
throw new InvalidSerializedError({
|
|
792
|
+
reason: 'invalid multisig config',
|
|
793
|
+
serialized,
|
|
794
|
+
})
|
|
795
|
+
const [salt, version, threshold, owners] = config
|
|
796
|
+
if (
|
|
797
|
+
config.length !== 4 ||
|
|
798
|
+
Array.isArray(salt) ||
|
|
799
|
+
Hex.size(salt) !== 32 ||
|
|
800
|
+
Array.isArray(version) ||
|
|
801
|
+
Hex.size(version) > 8 ||
|
|
802
|
+
(version !== '0x' && Hex.slice(version, 0, 1) === '0x00') ||
|
|
803
|
+
Array.isArray(threshold) ||
|
|
804
|
+
Hex.size(threshold) > 1 ||
|
|
805
|
+
!Array.isArray(owners) ||
|
|
806
|
+
owners.some(
|
|
807
|
+
(owner) =>
|
|
808
|
+
!Array.isArray(owner) ||
|
|
809
|
+
owner.length !== 2 ||
|
|
810
|
+
owner.some(Array.isArray) ||
|
|
811
|
+
!Address.validate(owner[0]) ||
|
|
812
|
+
Hex.size(owner[1] as Hex.Hex) > 1,
|
|
813
|
+
)
|
|
814
|
+
)
|
|
815
|
+
throw new InvalidSerializedError({
|
|
816
|
+
reason: 'invalid multisig config',
|
|
817
|
+
serialized,
|
|
818
|
+
})
|
|
727
819
|
if (!Array.isArray(signatures) || signatures.some(Array.isArray))
|
|
728
820
|
throw new InvalidSerializedError({
|
|
729
821
|
reason: 'invalid multisig signatures list',
|
|
@@ -751,47 +843,15 @@ function deserialize_(
|
|
|
751
843
|
serialized,
|
|
752
844
|
})
|
|
753
845
|
|
|
754
|
-
if (!Array.isArray(address) && !Address.validate(address))
|
|
755
|
-
throw new InvalidSerializedError({
|
|
756
|
-
reason: 'invalid multisig account',
|
|
757
|
-
serialized,
|
|
758
|
-
})
|
|
759
|
-
if (Array.isArray(address)) {
|
|
760
|
-
const [salt, threshold, owners] = address
|
|
761
|
-
if (
|
|
762
|
-
address.length !== 3 ||
|
|
763
|
-
Array.isArray(salt) ||
|
|
764
|
-
Hex.size(salt) !== 32 ||
|
|
765
|
-
Array.isArray(threshold) ||
|
|
766
|
-
Hex.size(threshold) > 1 ||
|
|
767
|
-
!Array.isArray(owners) ||
|
|
768
|
-
owners.some(
|
|
769
|
-
(owner) =>
|
|
770
|
-
!Array.isArray(owner) ||
|
|
771
|
-
owner.length !== 2 ||
|
|
772
|
-
owner.some(Array.isArray) ||
|
|
773
|
-
Hex.size(owner[1] as Hex.Hex) > 1,
|
|
774
|
-
)
|
|
775
|
-
)
|
|
776
|
-
throw new InvalidSerializedError({
|
|
777
|
-
reason: 'invalid multisig init config',
|
|
778
|
-
serialized,
|
|
779
|
-
})
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
const init = Array.isArray(address)
|
|
783
|
-
? MultisigConfig.fromTuple(address as unknown as MultisigConfig.Tuple)
|
|
784
|
-
: undefined
|
|
785
|
-
const account = init
|
|
786
|
-
? MultisigConfig.getAddress(init)
|
|
787
|
-
: (address as Address.Address)
|
|
788
846
|
const envelope = {
|
|
789
|
-
|
|
790
|
-
|
|
847
|
+
account: account as Address.Address,
|
|
848
|
+
config: MultisigConfig.fromTuple(
|
|
849
|
+
config as unknown as MultisigConfig.Tuple,
|
|
850
|
+
),
|
|
791
851
|
signatures: signatures.map((signature) =>
|
|
792
852
|
deserialize_(signature as Hex.Hex, depth),
|
|
793
853
|
),
|
|
794
|
-
|
|
854
|
+
type: 'multisig',
|
|
795
855
|
} satisfies Multisig
|
|
796
856
|
assertMultisig(envelope, depth)
|
|
797
857
|
return envelope
|
|
@@ -916,21 +976,17 @@ function deserialize_(
|
|
|
916
976
|
* ```
|
|
917
977
|
*
|
|
918
978
|
* @example
|
|
919
|
-
* ### Multisig
|
|
920
|
-
*
|
|
921
|
-
* Pass `initialConfig` to derive `account` automatically. Set `init: true` to
|
|
922
|
-
* opt into bootstrap (uses `initialConfig` as the bootstrap `init`); omit
|
|
923
|
-
* `init` for subsequent (non-bootstrap) transactions.
|
|
979
|
+
* ### Multisig
|
|
924
980
|
*
|
|
925
981
|
* ```ts twoslash
|
|
926
982
|
* import { Secp256k1 } from 'ox'
|
|
927
983
|
* import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
|
|
928
984
|
*
|
|
929
|
-
* const
|
|
930
|
-
* threshold: 1,
|
|
985
|
+
* const config = MultisigConfig.from({
|
|
931
986
|
* owners: [
|
|
932
987
|
* { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
|
|
933
988
|
* ],
|
|
989
|
+
* threshold: 1,
|
|
934
990
|
* })
|
|
935
991
|
*
|
|
936
992
|
* const privateKey = Secp256k1.randomPrivateKey()
|
|
@@ -938,16 +994,8 @@ function deserialize_(
|
|
|
938
994
|
* Secp256k1.sign({ payload: '0xdeadbeef', privateKey }),
|
|
939
995
|
* )
|
|
940
996
|
*
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
* initialConfig,
|
|
944
|
-
* signatures: [signature],
|
|
945
|
-
* init: true,
|
|
946
|
-
* })
|
|
947
|
-
*
|
|
948
|
-
* // Subsequent (non-bootstrap) transactions
|
|
949
|
-
* const subsequent = SignatureEnvelope.from({
|
|
950
|
-
* initialConfig,
|
|
997
|
+
* const multisig = SignatureEnvelope.from({
|
|
998
|
+
* config,
|
|
951
999
|
* signatures: [signature],
|
|
952
1000
|
* })
|
|
953
1001
|
* ```
|
|
@@ -973,30 +1021,17 @@ export function from<const value extends from.Value>(
|
|
|
973
1021
|
const type = getType(value)
|
|
974
1022
|
|
|
975
1023
|
if (type === 'multisig') {
|
|
976
|
-
const multisig = value as
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const { initialConfig, init, ...rest } = multisig
|
|
981
|
-
// Derive `account` from `initialConfig` when not provided explicitly.
|
|
982
|
-
const account = (() => {
|
|
983
|
-
if (rest.account) return rest.account
|
|
984
|
-
if (initialConfig) return MultisigConfig.getAddress(initialConfig)
|
|
985
|
-
return rest.account
|
|
986
|
-
})()
|
|
987
|
-
// `init: true` opts into bootstrap using the supplied `initialConfig`.
|
|
988
|
-
// Otherwise, `init` is treated as the explicit bootstrap config (or
|
|
989
|
-
// omitted).
|
|
990
|
-
const initSource = init === true ? initialConfig : init || undefined
|
|
991
|
-
return {
|
|
992
|
-
...rest,
|
|
1024
|
+
const multisig = value as from.MultisigFromConfig
|
|
1025
|
+
const config = MultisigConfig.from(multisig.config)
|
|
1026
|
+
const account = multisig.account ?? MultisigConfig.getAddress(config)
|
|
1027
|
+
const envelope = {
|
|
993
1028
|
account,
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
// in-memory envelope matches what `deserialize` reconstructs.
|
|
997
|
-
...(initSource ? { init: MultisigConfig.from(initSource) } : {}),
|
|
1029
|
+
config,
|
|
1030
|
+
signatures: multisig.signatures.map((signature) => from(signature)),
|
|
998
1031
|
type,
|
|
999
|
-
}
|
|
1032
|
+
} satisfies Multisig
|
|
1033
|
+
assert(envelope)
|
|
1034
|
+
return envelope as never
|
|
1000
1035
|
}
|
|
1001
1036
|
|
|
1002
1037
|
return {
|
|
@@ -1041,45 +1076,49 @@ export declare namespace from {
|
|
|
1041
1076
|
payload?: Hex.Hex | Bytes.Bytes | undefined
|
|
1042
1077
|
}
|
|
1043
1078
|
|
|
1044
|
-
/**
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1079
|
+
/** Multisig signature input with an optional derived initial account. */
|
|
1080
|
+
type MultisigFromConfig =
|
|
1081
|
+
| {
|
|
1082
|
+
/** Initial native multisig account, derived from config when omitted. */
|
|
1083
|
+
account?: Address.Address | undefined
|
|
1084
|
+
/** Initial version-0 multisig configuration witness. */
|
|
1085
|
+
config: MultisigConfig.Input<0 | 0n>
|
|
1086
|
+
/** Primitive or nested owner approvals. */
|
|
1087
|
+
signatures: readonly SignatureEnvelope[]
|
|
1088
|
+
type?: 'multisig' | undefined
|
|
1089
|
+
}
|
|
1090
|
+
| {
|
|
1091
|
+
/** Native multisig account. */
|
|
1092
|
+
account: Address.Address
|
|
1093
|
+
/** Complete applicable multisig configuration witness. */
|
|
1094
|
+
config: MultisigConfig.Input
|
|
1095
|
+
/** Primitive or nested owner approvals. */
|
|
1096
|
+
signatures: readonly SignatureEnvelope[]
|
|
1097
|
+
type?: 'multisig' | undefined
|
|
1098
|
+
}
|
|
1056
1099
|
|
|
1057
1100
|
type Value =
|
|
1058
1101
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
1059
1102
|
| Secp256k1Flat
|
|
1060
1103
|
| Serialized
|
|
1061
|
-
|
|
|
1062
|
-
|
|
1063
|
-
type ReturnValue<value extends Value> =
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
: {})
|
|
1080
|
-
>
|
|
1081
|
-
>
|
|
1082
|
-
>
|
|
1104
|
+
| MultisigFromConfig
|
|
1105
|
+
|
|
1106
|
+
type ReturnValue<value extends Value> = value extends Serialized
|
|
1107
|
+
? SignatureEnvelope
|
|
1108
|
+
: value extends Secp256k1Flat
|
|
1109
|
+
? Secp256k1
|
|
1110
|
+
: value extends MultisigFromConfig
|
|
1111
|
+
? MultisigOneOf
|
|
1112
|
+
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
1113
|
+
? SignatureEnvelope
|
|
1114
|
+
: Assign<
|
|
1115
|
+
value,
|
|
1116
|
+
{
|
|
1117
|
+
readonly type: GetType<value>
|
|
1118
|
+
} & (GetType<value> extends 'keychain'
|
|
1119
|
+
? { keyId?: Address.Address | undefined }
|
|
1120
|
+
: {})
|
|
1121
|
+
>
|
|
1083
1122
|
}
|
|
1084
1123
|
|
|
1085
1124
|
/**
|
|
@@ -1185,24 +1224,14 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
|
1185
1224
|
|
|
1186
1225
|
if (
|
|
1187
1226
|
(envelope as { type?: string | undefined }).type === 'multisig' ||
|
|
1188
|
-
('
|
|
1227
|
+
('config' in envelope && 'signatures' in envelope)
|
|
1189
1228
|
) {
|
|
1190
1229
|
const multisig = envelope as MultisigRpc
|
|
1191
|
-
const hasAccount = typeof multisig.account !== 'undefined'
|
|
1192
|
-
const hasInit = typeof multisig.init !== 'undefined'
|
|
1193
|
-
if (hasAccount === hasInit)
|
|
1194
|
-
throw new InvalidMultisigApprovalError({
|
|
1195
|
-
reason: 'RPC multisig must contain exactly one of `account` or `init`',
|
|
1196
|
-
})
|
|
1197
|
-
const init = hasInit
|
|
1198
|
-
? MultisigConfig.from(multisig.init as MultisigConfig.Config)
|
|
1199
|
-
: undefined
|
|
1200
|
-
const account = init ? MultisigConfig.getAddress(init) : multisig.account
|
|
1201
1230
|
const result = {
|
|
1202
|
-
|
|
1203
|
-
|
|
1231
|
+
account: multisig.account,
|
|
1232
|
+
config: MultisigConfig.fromRpc(multisig.config),
|
|
1204
1233
|
signatures: multisig.signatures.map((signature) => fromRpc(signature)),
|
|
1205
|
-
|
|
1234
|
+
type: 'multisig',
|
|
1206
1235
|
} satisfies Multisig
|
|
1207
1236
|
assert(result)
|
|
1208
1237
|
return result
|
|
@@ -1216,7 +1245,7 @@ export declare namespace fromRpc {
|
|
|
1216
1245
|
| assert.ErrorType
|
|
1217
1246
|
| CoercionError
|
|
1218
1247
|
| InvalidSerializedError
|
|
1219
|
-
| MultisigConfig.
|
|
1248
|
+
| MultisigConfig.fromRpc.ErrorType
|
|
1220
1249
|
| Signature.fromRpc.ErrorType
|
|
1221
1250
|
| Errors.GlobalErrorType
|
|
1222
1251
|
}
|
|
@@ -1287,12 +1316,7 @@ export function getType<
|
|
|
1287
1316
|
return 'keychain' as never
|
|
1288
1317
|
|
|
1289
1318
|
// Detect Multisig signature
|
|
1290
|
-
if (
|
|
1291
|
-
('account' in envelope ||
|
|
1292
|
-
'initialConfig' in envelope ||
|
|
1293
|
-
'init' in envelope) &&
|
|
1294
|
-
'signatures' in envelope
|
|
1295
|
-
)
|
|
1319
|
+
if ('config' in envelope && 'signatures' in envelope)
|
|
1296
1320
|
return 'multisig' as never
|
|
1297
1321
|
|
|
1298
1322
|
throw new CoercionError({
|
|
@@ -1309,7 +1333,7 @@ export function getType<
|
|
|
1309
1333
|
* - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
|
|
1310
1334
|
* - Keychain V1: `0x03` + userAddress (20) + inner signature (recursive)
|
|
1311
1335
|
* - Keychain V2: `0x04` + userAddress (20) + inner signature (recursive)
|
|
1312
|
-
* - Multisig: `0x05` + RLP `[account
|
|
1336
|
+
* - Multisig: `0x05` + RLP `[account, config, signatures]`
|
|
1313
1337
|
*
|
|
1314
1338
|
* [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
1315
1339
|
*
|
|
@@ -1392,14 +1416,11 @@ export function serialize(
|
|
|
1392
1416
|
if (type === 'multisig') {
|
|
1393
1417
|
const multisig = envelope as Multisig
|
|
1394
1418
|
assert(multisig)
|
|
1395
|
-
// The first field is either the initialized account or the bootstrap init
|
|
1396
|
-
// config. Each owner approval is an encoded signature.
|
|
1397
1419
|
return Hex.concat(
|
|
1398
1420
|
serializedMultisigType,
|
|
1399
1421
|
Rlp.fromHex([
|
|
1400
|
-
multisig.
|
|
1401
|
-
|
|
1402
|
-
: multisig.account,
|
|
1422
|
+
multisig.account,
|
|
1423
|
+
MultisigConfig.toTuple(multisig.config),
|
|
1403
1424
|
multisig.signatures.map((signature) => serialize(signature)),
|
|
1404
1425
|
]),
|
|
1405
1426
|
options.magic ? magicBytes : '0x',
|
|
@@ -1440,21 +1461,19 @@ export declare namespace serialize {
|
|
|
1440
1461
|
* recovered owner address. Works for any owner key type (secp256k1, p256,
|
|
1441
1462
|
* webAuthn).
|
|
1442
1463
|
*
|
|
1443
|
-
* Config updates never change `account`, so the initial config is the correct
|
|
1444
|
-
* input even for post-update transactions.
|
|
1445
|
-
*
|
|
1446
1464
|
* @example
|
|
1447
1465
|
* ```ts twoslash
|
|
1448
1466
|
* import { Secp256k1 } from 'ox'
|
|
1449
1467
|
* import { MultisigConfig, SignatureEnvelope, TxEnvelopeTempo } from 'ox/tempo'
|
|
1450
1468
|
*
|
|
1451
|
-
* const
|
|
1452
|
-
* threshold: 2,
|
|
1469
|
+
* const config = MultisigConfig.from({
|
|
1453
1470
|
* owners: [
|
|
1454
1471
|
* { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
|
|
1455
1472
|
* { owner: '0x2222222222222222222222222222222222222222', weight: 1 },
|
|
1456
1473
|
* ],
|
|
1474
|
+
* threshold: 2,
|
|
1457
1475
|
* })
|
|
1476
|
+
* const account = MultisigConfig.getAddress(config)
|
|
1458
1477
|
*
|
|
1459
1478
|
* const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
|
|
1460
1479
|
* const payload = TxEnvelopeTempo.getSignPayload(tx)
|
|
@@ -1464,15 +1483,17 @@ export declare namespace serialize {
|
|
|
1464
1483
|
* Secp256k1.randomPrivateKey(),
|
|
1465
1484
|
* ]
|
|
1466
1485
|
* const digest = MultisigConfig.getSignPayload({
|
|
1486
|
+
* account,
|
|
1487
|
+
* config,
|
|
1467
1488
|
* payload,
|
|
1468
|
-
* initialConfig,
|
|
1469
1489
|
* })
|
|
1470
1490
|
* const signatures = privateKeys.map((privateKey) =>
|
|
1471
1491
|
* SignatureEnvelope.from(Secp256k1.sign({ payload: digest, privateKey })),
|
|
1472
1492
|
* )
|
|
1473
1493
|
*
|
|
1474
1494
|
* const ordered = SignatureEnvelope.sortMultisigApprovals({ // [!code focus]
|
|
1475
|
-
*
|
|
1495
|
+
* account, // [!code focus]
|
|
1496
|
+
* config, // [!code focus]
|
|
1476
1497
|
* payload, // [!code focus]
|
|
1477
1498
|
* signatures, // [!code focus]
|
|
1478
1499
|
* }) // [!code focus]
|
|
@@ -1484,16 +1505,12 @@ export declare namespace serialize {
|
|
|
1484
1505
|
export function sortMultisigApprovals(
|
|
1485
1506
|
value: sortMultisigApprovals.Value,
|
|
1486
1507
|
): readonly SignatureEnvelope[] {
|
|
1487
|
-
const {
|
|
1488
|
-
const digest = MultisigConfig.getSignPayload(
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
account: (value as { account: Address.Address }).account,
|
|
1494
|
-
version,
|
|
1495
|
-
},
|
|
1496
|
-
)
|
|
1508
|
+
const { account, config, payload, signatures } = value
|
|
1509
|
+
const digest = MultisigConfig.getSignPayload({
|
|
1510
|
+
account,
|
|
1511
|
+
config: MultisigConfig.from(config),
|
|
1512
|
+
payload,
|
|
1513
|
+
})
|
|
1497
1514
|
// Recover each signer once (decorate–sort–undecorate) rather than inside the
|
|
1498
1515
|
// comparator.
|
|
1499
1516
|
return signatures
|
|
@@ -1507,25 +1524,15 @@ export function sortMultisigApprovals(
|
|
|
1507
1524
|
|
|
1508
1525
|
export declare namespace sortMultisigApprovals {
|
|
1509
1526
|
type Value = {
|
|
1527
|
+
/** The native multisig account address. */
|
|
1528
|
+
account: Address.Address
|
|
1529
|
+
/** Complete applicable multisig configuration witness. */
|
|
1530
|
+
config: MultisigConfig.Input
|
|
1510
1531
|
/** The inner transaction sign payload (`tx.signature_hash()`). */
|
|
1511
1532
|
payload: Hex.Hex | Bytes.Bytes
|
|
1512
1533
|
/** The owner approvals to order. */
|
|
1513
1534
|
signatures: readonly SignatureEnvelope[]
|
|
1514
|
-
|
|
1515
|
-
version?: bigint | undefined
|
|
1516
|
-
} & OneOf<
|
|
1517
|
-
| {
|
|
1518
|
-
/** The native multisig account address. */
|
|
1519
|
-
account: Address.Address
|
|
1520
|
-
}
|
|
1521
|
-
| {
|
|
1522
|
-
/**
|
|
1523
|
-
* The initial multisig config (the bootstrap config that derived the
|
|
1524
|
-
* permanent `account`). Used to derive the account automatically.
|
|
1525
|
-
*/
|
|
1526
|
-
initialConfig: MultisigConfig.Config
|
|
1527
|
-
}
|
|
1528
|
-
>
|
|
1535
|
+
}
|
|
1529
1536
|
|
|
1530
1537
|
type ErrorType =
|
|
1531
1538
|
| MultisigConfig.getSignPayload.ErrorType
|
|
@@ -1605,25 +1612,10 @@ export function toRpc<const envelope extends toRpc.Input>(
|
|
|
1605
1612
|
if (type === 'multisig') {
|
|
1606
1613
|
const multisig = envelope as Multisig
|
|
1607
1614
|
assert(multisig)
|
|
1608
|
-
const signatures = multisig.signatures.map((signature) => toRpc(signature))
|
|
1609
|
-
if (multisig.init) {
|
|
1610
|
-
const init = {
|
|
1611
|
-
...multisig.init,
|
|
1612
|
-
salt: multisig.init.salt ?? MultisigConfig.zeroSalt,
|
|
1613
|
-
threshold: Number(multisig.init.threshold),
|
|
1614
|
-
owners: multisig.init.owners.map((owner) => ({
|
|
1615
|
-
...owner,
|
|
1616
|
-
weight: Number(owner.weight),
|
|
1617
|
-
})),
|
|
1618
|
-
}
|
|
1619
|
-
return {
|
|
1620
|
-
init,
|
|
1621
|
-
signatures,
|
|
1622
|
-
} as never
|
|
1623
|
-
}
|
|
1624
1615
|
return {
|
|
1625
1616
|
account: multisig.account,
|
|
1626
|
-
|
|
1617
|
+
config: MultisigConfig.toRpc(multisig.config),
|
|
1618
|
+
signatures: multisig.signatures.map((signature) => toRpc(signature)),
|
|
1627
1619
|
} as never
|
|
1628
1620
|
}
|
|
1629
1621
|
|
|
@@ -1651,6 +1643,7 @@ export declare namespace toRpc {
|
|
|
1651
1643
|
type ErrorType =
|
|
1652
1644
|
| assert.ErrorType
|
|
1653
1645
|
| CoercionError
|
|
1646
|
+
| MultisigConfig.toRpc.ErrorType
|
|
1654
1647
|
| Signature.toRpc.ErrorType
|
|
1655
1648
|
| Errors.GlobalErrorType
|
|
1656
1649
|
}
|