ox 1.7.1 → 1.7.4
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 +42 -0
- package/dist/core/Hash.js +1 -1
- package/dist/core/Hash.js.map +1 -1
- package/dist/tempo/KeyAuthorization.d.ts +2 -2
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +151 -83
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +164 -86
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/MultisigOperation.d.ts +340 -0
- package/dist/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/tempo/MultisigOperation.js +839 -0
- package/dist/tempo/MultisigOperation.js.map +1 -0
- package/dist/tempo/MultisigSimulation.d.ts +132 -0
- package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/tempo/MultisigSimulation.js +140 -0
- package/dist/tempo/MultisigSimulation.js.map +1 -0
- package/dist/tempo/RpcSchemaTempo.d.ts +45 -0
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +55 -86
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +93 -158
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +9 -17
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +7 -7
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.d.ts +3 -2
- package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.js +3 -2
- package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
- package/dist/tempo/index.d.ts +22 -2
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +22 -2
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +60 -180
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +65 -118
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigConfig.d.ts +31 -2
- package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigConfig.js +26 -3
- package/dist/zod/tempo/MultisigConfig.js.map +1 -1
- package/dist/zod/tempo/MultisigOperation.d.ts +158 -0
- package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigOperation.js +62 -0
- package/dist/zod/tempo/MultisigOperation.js.map +1 -0
- package/dist/zod/tempo/MultisigSimulation.d.ts +120 -0
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/zod/tempo/MultisigSimulation.js +78 -0
- package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +528 -144
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +47 -0
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +18 -54
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +5 -22
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +138 -348
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +286 -239
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +11 -22
- package/dist/zod/tempo/TransactionRequest.js.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +60 -48
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +15 -12
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
- package/dist/zod/tempo/z.d.ts +2 -0
- package/dist/zod/tempo/z.d.ts.map +1 -1
- package/dist/zod/tempo/z.js +2 -0
- package/dist/zod/tempo/z.js.map +1 -1
- package/package.json +21 -1
- package/src/core/Hash.ts +1 -1
- package/src/core/_test/Hash.test.ts +15 -0
- package/src/tempo/AuthorizationTempo.test.ts +4 -0
- package/src/tempo/KeyAuthorization.test-d.ts +13 -12
- package/src/tempo/KeyAuthorization.test.ts +109 -3
- package/src/tempo/KeyAuthorization.ts +4 -4
- package/src/tempo/MultisigConfig.test-d.ts +52 -0
- package/src/tempo/MultisigConfig.test.ts +288 -227
- package/src/tempo/MultisigConfig.ts +275 -104
- package/src/tempo/MultisigOperation.test-d.ts +79 -0
- package/src/tempo/MultisigOperation.test.ts +1950 -0
- package/src/tempo/MultisigOperation.ts +1293 -0
- package/src/tempo/MultisigSimulation.test-d.ts +33 -0
- package/src/tempo/MultisigSimulation.test.ts +283 -0
- package/src/tempo/MultisigSimulation.ts +266 -0
- package/src/tempo/RpcSchemaTempo.test-d.ts +91 -0
- package/src/tempo/RpcSchemaTempo.ts +46 -0
- package/src/tempo/SignatureEnvelope.test-d.ts +33 -72
- package/src/tempo/SignatureEnvelope.test.ts +533 -500
- package/src/tempo/SignatureEnvelope.ts +171 -258
- package/src/tempo/Transaction.test.ts +61 -0
- package/src/tempo/TransactionRequest.test-d.ts +11 -0
- package/src/tempo/TransactionRequest.test.ts +214 -41
- package/src/tempo/TransactionRequest.ts +19 -20
- package/src/tempo/TxEnvelopeTempo.test-d.ts +7 -0
- package/src/tempo/TxEnvelopeTempo.test.ts +25 -0
- package/src/tempo/TxEnvelopeTempo.ts +5 -3
- package/src/tempo/index.ts +22 -2
- package/src/tempo/multisig.e2e.test.ts +164 -87
- package/src/version.ts +1 -1
- package/src/zod/tempo/MultisigConfig.ts +29 -3
- package/src/zod/tempo/MultisigOperation.ts +66 -0
- package/src/zod/tempo/MultisigSimulation.ts +98 -0
- package/src/zod/tempo/RpcSchemaTempo.ts +53 -0
- package/src/zod/tempo/SignatureEnvelope.ts +12 -54
- package/src/zod/tempo/TransactionRequest.ts +17 -26
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +2 -4
- package/src/zod/tempo/_test/MultisigConfig.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigConfig.test.ts +13 -2
- package/src/zod/tempo/_test/MultisigOperation.test-d.ts +27 -0
- package/src/zod/tempo/_test/MultisigOperation.test.ts +66 -0
- package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigSimulation.test.ts +78 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +48 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +83 -0
- package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +1 -1
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +57 -120
- package/src/zod/tempo/_test/TransactionRequest.test.ts +43 -16
- package/src/zod/tempo/z.ts +2 -0
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
IsNarrowable,
|
|
9
9
|
OneOf,
|
|
10
10
|
PartialBy,
|
|
11
|
+
Undefined,
|
|
11
12
|
UnionPartialBy,
|
|
12
13
|
} from '../core/internal/types.js'
|
|
13
14
|
import * as Json from '../core/Json.js'
|
|
@@ -27,6 +28,15 @@ const serializedKeychainType = '0x03'
|
|
|
27
28
|
const serializedKeychainV2Type = '0x04'
|
|
28
29
|
const serializedMultisigType = '0x05'
|
|
29
30
|
|
|
31
|
+
type NonMultisig<numberType = number> = OneOf<
|
|
32
|
+
| Secp256k1<numberType>
|
|
33
|
+
| P256<numberType>
|
|
34
|
+
| WebAuthn<numberType>
|
|
35
|
+
| Keychain<numberType>
|
|
36
|
+
>
|
|
37
|
+
|
|
38
|
+
type NonMultisigRpc = OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc>
|
|
39
|
+
|
|
30
40
|
/** Serialized magic identifier for Tempo signature envelopes. */
|
|
31
41
|
export const magicBytes =
|
|
32
42
|
'0x7777777777777777777777777777777777777777777777777777777777777777' // 32 "T"s
|
|
@@ -84,15 +94,7 @@ export type GetType<
|
|
|
84
94
|
userAddress: Address.Address
|
|
85
95
|
}
|
|
86
96
|
? 'keychain'
|
|
87
|
-
: envelope extends
|
|
88
|
-
| {
|
|
89
|
-
account: Address.Address
|
|
90
|
-
signatures: any
|
|
91
|
-
}
|
|
92
|
-
| {
|
|
93
|
-
init: MultisigConfig.Config
|
|
94
|
-
signatures: any
|
|
95
|
-
}
|
|
97
|
+
: envelope extends { config: unknown; signatures: any }
|
|
96
98
|
? 'multisig'
|
|
97
99
|
: never
|
|
98
100
|
|
|
@@ -119,20 +121,12 @@ export type GetType<
|
|
|
119
121
|
*
|
|
120
122
|
* [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
121
123
|
*/
|
|
122
|
-
export type SignatureEnvelope<numberType = number> =
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
| WebAuthn<numberType>
|
|
126
|
-
| Keychain<numberType>
|
|
127
|
-
| Multisig<numberType>
|
|
128
|
-
>
|
|
124
|
+
export type SignatureEnvelope<numberType = number> =
|
|
125
|
+
| (NonMultisig<numberType> & Undefined<Omit<Multisig<numberType>, 'type'>>)
|
|
126
|
+
| MultisigOneOf<numberType>
|
|
129
127
|
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
export type SignatureEnvelopeRpc = OneOf<
|
|
134
|
-
Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc | MultisigRpc
|
|
135
|
-
>
|
|
128
|
+
/** RPC-formatted signature envelope. */
|
|
129
|
+
export type SignatureEnvelopeRpc = NonMultisigRpc | MultisigRpc
|
|
136
130
|
|
|
137
131
|
/** Primitive signature envelope accepted by protocol sidecars. */
|
|
138
132
|
export type Primitive<numberType = number> = OneOf<
|
|
@@ -182,42 +176,26 @@ export type KeychainRpc = {
|
|
|
182
176
|
* [TIP-1061](https://tips.sh/1061)
|
|
183
177
|
*/
|
|
184
178
|
export type Multisig<numberType = number> = {
|
|
185
|
-
type: 'multisig'
|
|
186
179
|
/** Native multisig account address. */
|
|
187
180
|
account: Address.Address
|
|
181
|
+
/** Complete applicable multisig configuration. */
|
|
182
|
+
config: MultisigConfig.Config<bigint, numberType>
|
|
188
183
|
/**
|
|
189
184
|
* Owner approvals over the multisig owner approval digest. Each approval is
|
|
190
185
|
* either a primitive signature or a nested multisig signature (keychain
|
|
191
186
|
* approvals are invalid).
|
|
192
187
|
*/
|
|
193
188
|
signatures: readonly SignatureEnvelope<numberType>[]
|
|
194
|
-
|
|
195
|
-
* Initial native multisig config for bootstrapping this account. Present only on
|
|
196
|
-
* the first (bootstrap) transaction from the derived account; absent on every
|
|
197
|
-
* subsequent transaction.
|
|
198
|
-
*/
|
|
199
|
-
init?: MultisigConfig.Config<numberType> | undefined
|
|
189
|
+
type: 'multisig'
|
|
200
190
|
}
|
|
201
191
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
/** Multisig RPC signatures are untagged. */
|
|
210
|
-
type?: undefined
|
|
211
|
-
}
|
|
212
|
-
| {
|
|
213
|
-
/** Initial config for bootstrapping a native multisig account. */
|
|
214
|
-
init: MultisigConfig.Config
|
|
215
|
-
/** Structured owner approvals. */
|
|
216
|
-
signatures: readonly SignatureEnvelopeRpc[]
|
|
217
|
-
/** Multisig RPC signatures are untagged. */
|
|
218
|
-
type?: undefined
|
|
219
|
-
}
|
|
220
|
-
>
|
|
192
|
+
interface MultisigOneOf<numberType = number>
|
|
193
|
+
extends
|
|
194
|
+
Multisig<numberType>,
|
|
195
|
+
Undefined<Omit<NonMultisig<numberType>, 'type'>> {}
|
|
196
|
+
|
|
197
|
+
/** RPC-formatted native multisig signature without the `0x05` type byte. */
|
|
198
|
+
export type MultisigRpc = Hex.Hex
|
|
221
199
|
|
|
222
200
|
export type P256<numberType = number> = {
|
|
223
201
|
prehash: boolean
|
|
@@ -380,6 +358,7 @@ export declare namespace assert {
|
|
|
380
358
|
function assertMultisig(envelope: Multisig, depth: number): void {
|
|
381
359
|
const missing: string[] = []
|
|
382
360
|
if (!envelope.account) missing.push('account')
|
|
361
|
+
if (!envelope.config) missing.push('config')
|
|
383
362
|
if (!Array.isArray(envelope.signatures)) missing.push('signatures')
|
|
384
363
|
if (missing.length > 0)
|
|
385
364
|
throw new MissingPropertiesError({
|
|
@@ -399,6 +378,25 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
399
378
|
throw new InvalidMultisigApprovalError({
|
|
400
379
|
reason: 'multisig account cannot be zero',
|
|
401
380
|
})
|
|
381
|
+
MultisigConfig.assert(envelope.config)
|
|
382
|
+
if (
|
|
383
|
+
envelope.config.owners.some((owner) =>
|
|
384
|
+
Address.isEqual(owner.owner, envelope.account),
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
throw new InvalidMultisigApprovalError({
|
|
388
|
+
reason: 'multisig account cannot be an owner',
|
|
389
|
+
})
|
|
390
|
+
if (
|
|
391
|
+
envelope.config.version === 0n &&
|
|
392
|
+
!Address.isEqual(
|
|
393
|
+
MultisigConfig.getAddress(envelope.config),
|
|
394
|
+
envelope.account,
|
|
395
|
+
)
|
|
396
|
+
)
|
|
397
|
+
throw new InvalidMultisigApprovalError({
|
|
398
|
+
reason: 'initial multisig config does not derive account',
|
|
399
|
+
})
|
|
402
400
|
if (envelope.signatures.length === 0)
|
|
403
401
|
throw new InvalidMultisigApprovalError({
|
|
404
402
|
reason: 'multisig signatures cannot be empty',
|
|
@@ -408,33 +406,14 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
408
406
|
reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
|
|
409
407
|
})
|
|
410
408
|
|
|
411
|
-
if (envelope.init) {
|
|
412
|
-
MultisigConfig.assert(envelope.init)
|
|
413
|
-
if (
|
|
414
|
-
!Address.isEqual(
|
|
415
|
-
MultisigConfig.getAddress(envelope.init),
|
|
416
|
-
envelope.account,
|
|
417
|
-
)
|
|
418
|
-
)
|
|
419
|
-
throw new InvalidMultisigApprovalError({
|
|
420
|
-
reason: 'multisig init does not derive account',
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
|
-
|
|
424
409
|
for (const inner of envelope.signatures) {
|
|
425
410
|
const type = getType(inner)
|
|
426
411
|
if (type === 'keychain')
|
|
427
412
|
throw new InvalidMultisigApprovalError({
|
|
428
413
|
reason: 'keychain owner approvals are not allowed',
|
|
429
414
|
})
|
|
430
|
-
if (type === 'multisig')
|
|
431
|
-
|
|
432
|
-
if (multisig.init)
|
|
433
|
-
throw new InvalidMultisigApprovalError({
|
|
434
|
-
reason: 'nested multisig owner approvals cannot carry `init`',
|
|
435
|
-
})
|
|
436
|
-
assertMultisig(multisig, depth + 1)
|
|
437
|
-
} else {
|
|
415
|
+
if (type === 'multisig') assertMultisig(inner as Multisig, depth + 1)
|
|
416
|
+
else {
|
|
438
417
|
assert(inner)
|
|
439
418
|
if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
|
|
440
419
|
throw new InvalidMultisigApprovalError({
|
|
@@ -724,16 +703,48 @@ function deserialize_(
|
|
|
724
703
|
serialized,
|
|
725
704
|
})
|
|
726
705
|
|
|
727
|
-
// The first field distinguishes the static wire shapes: a bootstrap init
|
|
728
|
-
// config is an RLP list, while an initialized account is a 20-byte string.
|
|
729
706
|
const decoded = Rlp.toHex(data)
|
|
730
|
-
if (!Array.isArray(decoded) || decoded.length !==
|
|
707
|
+
if (!Array.isArray(decoded) || decoded.length !== 3)
|
|
731
708
|
throw new InvalidSerializedError({
|
|
732
|
-
reason: 'invalid multisig wire shape: expected exactly
|
|
709
|
+
reason: 'invalid multisig wire shape: expected exactly three fields',
|
|
733
710
|
serialized,
|
|
734
711
|
})
|
|
735
712
|
|
|
736
|
-
const [
|
|
713
|
+
const [account, config, signatures] = decoded
|
|
714
|
+
if (!Address.validate(account))
|
|
715
|
+
throw new InvalidSerializedError({
|
|
716
|
+
reason: 'invalid multisig account',
|
|
717
|
+
serialized,
|
|
718
|
+
})
|
|
719
|
+
if (!Array.isArray(config))
|
|
720
|
+
throw new InvalidSerializedError({
|
|
721
|
+
reason: 'invalid multisig config',
|
|
722
|
+
serialized,
|
|
723
|
+
})
|
|
724
|
+
const [salt, version, threshold, owners] = config
|
|
725
|
+
if (
|
|
726
|
+
config.length !== 4 ||
|
|
727
|
+
Array.isArray(salt) ||
|
|
728
|
+
Hex.size(salt) !== 32 ||
|
|
729
|
+
Array.isArray(version) ||
|
|
730
|
+
Hex.size(version) > 8 ||
|
|
731
|
+
(version !== '0x' && Hex.slice(version, 0, 1) === '0x00') ||
|
|
732
|
+
Array.isArray(threshold) ||
|
|
733
|
+
Hex.size(threshold) > 1 ||
|
|
734
|
+
!Array.isArray(owners) ||
|
|
735
|
+
owners.some(
|
|
736
|
+
(owner) =>
|
|
737
|
+
!Array.isArray(owner) ||
|
|
738
|
+
owner.length !== 2 ||
|
|
739
|
+
owner.some(Array.isArray) ||
|
|
740
|
+
!Address.validate(owner[0]) ||
|
|
741
|
+
Hex.size(owner[1] as Hex.Hex) > 1,
|
|
742
|
+
)
|
|
743
|
+
)
|
|
744
|
+
throw new InvalidSerializedError({
|
|
745
|
+
reason: 'invalid multisig config',
|
|
746
|
+
serialized,
|
|
747
|
+
})
|
|
737
748
|
if (!Array.isArray(signatures) || signatures.some(Array.isArray))
|
|
738
749
|
throw new InvalidSerializedError({
|
|
739
750
|
reason: 'invalid multisig signatures list',
|
|
@@ -761,47 +772,15 @@ function deserialize_(
|
|
|
761
772
|
serialized,
|
|
762
773
|
})
|
|
763
774
|
|
|
764
|
-
if (!Array.isArray(address) && !Address.validate(address))
|
|
765
|
-
throw new InvalidSerializedError({
|
|
766
|
-
reason: 'invalid multisig account',
|
|
767
|
-
serialized,
|
|
768
|
-
})
|
|
769
|
-
if (Array.isArray(address)) {
|
|
770
|
-
const [salt, threshold, owners] = address
|
|
771
|
-
if (
|
|
772
|
-
address.length !== 3 ||
|
|
773
|
-
Array.isArray(salt) ||
|
|
774
|
-
Hex.size(salt) !== 32 ||
|
|
775
|
-
Array.isArray(threshold) ||
|
|
776
|
-
Hex.size(threshold) > 1 ||
|
|
777
|
-
!Array.isArray(owners) ||
|
|
778
|
-
owners.some(
|
|
779
|
-
(owner) =>
|
|
780
|
-
!Array.isArray(owner) ||
|
|
781
|
-
owner.length !== 2 ||
|
|
782
|
-
owner.some(Array.isArray) ||
|
|
783
|
-
Hex.size(owner[1] as Hex.Hex) > 1,
|
|
784
|
-
)
|
|
785
|
-
)
|
|
786
|
-
throw new InvalidSerializedError({
|
|
787
|
-
reason: 'invalid multisig init config',
|
|
788
|
-
serialized,
|
|
789
|
-
})
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
const init = Array.isArray(address)
|
|
793
|
-
? MultisigConfig.fromTuple(address as unknown as MultisigConfig.Tuple)
|
|
794
|
-
: undefined
|
|
795
|
-
const account = init
|
|
796
|
-
? MultisigConfig.getAddress(init)
|
|
797
|
-
: (address as Address.Address)
|
|
798
775
|
const envelope = {
|
|
799
|
-
|
|
800
|
-
|
|
776
|
+
account: account as Address.Address,
|
|
777
|
+
config: MultisigConfig.fromTuple(
|
|
778
|
+
config as unknown as MultisigConfig.Tuple,
|
|
779
|
+
),
|
|
801
780
|
signatures: signatures.map((signature) =>
|
|
802
781
|
deserialize_(signature as Hex.Hex, depth),
|
|
803
782
|
),
|
|
804
|
-
|
|
783
|
+
type: 'multisig',
|
|
805
784
|
} satisfies Multisig
|
|
806
785
|
assertMultisig(envelope, depth)
|
|
807
786
|
return envelope
|
|
@@ -939,24 +918,20 @@ function deserialize_(
|
|
|
939
918
|
* ```
|
|
940
919
|
*
|
|
941
920
|
* @example
|
|
942
|
-
* ### Multisig
|
|
943
|
-
*
|
|
944
|
-
* Pass `initialConfig` to derive `account` automatically. Set `init: true` to
|
|
945
|
-
* opt into bootstrap (uses `initialConfig` as the bootstrap `init`); omit
|
|
946
|
-
* `init` for subsequent (non-bootstrap) transactions.
|
|
921
|
+
* ### Multisig
|
|
947
922
|
*
|
|
948
923
|
* ```ts twoslash
|
|
949
924
|
* import { Secp256k1 } from 'ox'
|
|
950
925
|
* import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
|
|
951
926
|
*
|
|
952
|
-
* const
|
|
953
|
-
* threshold: 1,
|
|
927
|
+
* const config = MultisigConfig.from({
|
|
954
928
|
* owners: [
|
|
955
929
|
* {
|
|
956
930
|
* owner: '0x1111111111111111111111111111111111111111',
|
|
957
931
|
* weight: 1
|
|
958
932
|
* }
|
|
959
|
-
* ]
|
|
933
|
+
* ],
|
|
934
|
+
* threshold: 1
|
|
960
935
|
* })
|
|
961
936
|
*
|
|
962
937
|
* const privateKey = Secp256k1.randomPrivateKey()
|
|
@@ -964,16 +939,8 @@ function deserialize_(
|
|
|
964
939
|
* Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
|
|
965
940
|
* )
|
|
966
941
|
*
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
* initialConfig,
|
|
970
|
-
* signatures: [signature],
|
|
971
|
-
* init: true
|
|
972
|
-
* })
|
|
973
|
-
*
|
|
974
|
-
* // Subsequent (non-bootstrap) transactions
|
|
975
|
-
* const subsequent = SignatureEnvelope.from({
|
|
976
|
-
* initialConfig,
|
|
942
|
+
* const multisig = SignatureEnvelope.from({
|
|
943
|
+
* config,
|
|
977
944
|
* signatures: [signature]
|
|
978
945
|
* })
|
|
979
946
|
* ```
|
|
@@ -999,30 +966,17 @@ export function from<const value extends from.Value>(
|
|
|
999
966
|
const type = getType(value)
|
|
1000
967
|
|
|
1001
968
|
if (type === 'multisig') {
|
|
1002
|
-
const multisig = value as
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
const { initialConfig, init, ...rest } = multisig
|
|
1007
|
-
// Derive `account` from `initialConfig` when not provided explicitly.
|
|
1008
|
-
const account = (() => {
|
|
1009
|
-
if (rest.account) return rest.account
|
|
1010
|
-
if (initialConfig) return MultisigConfig.getAddress(initialConfig)
|
|
1011
|
-
return rest.account
|
|
1012
|
-
})()
|
|
1013
|
-
// `init: true` opts into bootstrap using the supplied `initialConfig`.
|
|
1014
|
-
// Otherwise, `init` is treated as the explicit bootstrap config (or
|
|
1015
|
-
// omitted).
|
|
1016
|
-
const initSource = init === true ? initialConfig : init || undefined
|
|
1017
|
-
return {
|
|
1018
|
-
...rest,
|
|
969
|
+
const multisig = value as from.MultisigFromConfig
|
|
970
|
+
const config = MultisigConfig.from(multisig.config)
|
|
971
|
+
const account = multisig.account ?? MultisigConfig.getAddress(config)
|
|
972
|
+
const envelope = {
|
|
1019
973
|
account,
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
// in-memory envelope matches what `deserialize` reconstructs.
|
|
1023
|
-
...(initSource ? { init: MultisigConfig.from(initSource) } : {}),
|
|
974
|
+
config,
|
|
975
|
+
signatures: multisig.signatures.map((signature) => from(signature)),
|
|
1024
976
|
type,
|
|
1025
|
-
}
|
|
977
|
+
} satisfies Multisig
|
|
978
|
+
assert(envelope)
|
|
979
|
+
return envelope as never
|
|
1026
980
|
}
|
|
1027
981
|
|
|
1028
982
|
return {
|
|
@@ -1067,24 +1021,32 @@ export declare namespace from {
|
|
|
1067
1021
|
payload?: Hex.Hex | Bytes.Bytes | undefined
|
|
1068
1022
|
}
|
|
1069
1023
|
|
|
1070
|
-
/**
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1024
|
+
/** Multisig signature input with an optional derived initial account. */
|
|
1025
|
+
type MultisigFromConfig =
|
|
1026
|
+
| {
|
|
1027
|
+
/** Initial native multisig account, derived from config when omitted. */
|
|
1028
|
+
account?: Address.Address | undefined
|
|
1029
|
+
/** Initial version-0 multisig configuration. */
|
|
1030
|
+
config: MultisigConfig.Input<0 | 0n>
|
|
1031
|
+
/** Primitive or nested owner approvals. */
|
|
1032
|
+
signatures: readonly SignatureEnvelope[]
|
|
1033
|
+
type?: 'multisig' | undefined
|
|
1034
|
+
}
|
|
1035
|
+
| {
|
|
1036
|
+
/** Native multisig account. */
|
|
1037
|
+
account: Address.Address
|
|
1038
|
+
/** Complete applicable multisig configuration. */
|
|
1039
|
+
config: MultisigConfig.Input
|
|
1040
|
+
/** Primitive or nested owner approvals. */
|
|
1041
|
+
signatures: readonly SignatureEnvelope[]
|
|
1042
|
+
type?: 'multisig' | undefined
|
|
1043
|
+
}
|
|
1082
1044
|
|
|
1083
1045
|
type Value =
|
|
1084
1046
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
1085
1047
|
| Secp256k1Flat
|
|
1086
1048
|
| Serialized
|
|
1087
|
-
|
|
|
1049
|
+
| MultisigFromConfig
|
|
1088
1050
|
|
|
1089
1051
|
type ReturnValue<value extends Value> = Compute<
|
|
1090
1052
|
OneOf<
|
|
@@ -1092,7 +1054,7 @@ export declare namespace from {
|
|
|
1092
1054
|
? SignatureEnvelope
|
|
1093
1055
|
: value extends Secp256k1Flat
|
|
1094
1056
|
? Secp256k1
|
|
1095
|
-
: value extends
|
|
1057
|
+
: value extends MultisigFromConfig
|
|
1096
1058
|
? Multisig
|
|
1097
1059
|
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
1098
1060
|
? SignatureEnvelope
|
|
@@ -1127,6 +1089,20 @@ export declare namespace from {
|
|
|
1127
1089
|
* @returns The signature envelope with bigint values.
|
|
1128
1090
|
*/
|
|
1129
1091
|
export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
1092
|
+
if (typeof envelope === 'string') {
|
|
1093
|
+
const serialized = Hex.concat(serializedMultisigType, envelope)
|
|
1094
|
+
const result = deserialize(serialized)
|
|
1095
|
+
if (
|
|
1096
|
+
result.type !== 'multisig' ||
|
|
1097
|
+
!Hex.isEqual(serialize(result), serialized)
|
|
1098
|
+
)
|
|
1099
|
+
throw new InvalidSerializedError({
|
|
1100
|
+
reason: 'invalid multisig RPC signature',
|
|
1101
|
+
serialized: envelope,
|
|
1102
|
+
})
|
|
1103
|
+
return result
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1130
1106
|
if (envelope.type === 'secp256k1')
|
|
1131
1107
|
return {
|
|
1132
1108
|
signature: Signature.fromRpc(envelope),
|
|
@@ -1209,31 +1185,6 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
|
1209
1185
|
}
|
|
1210
1186
|
}
|
|
1211
1187
|
|
|
1212
|
-
if (
|
|
1213
|
-
(envelope as { type?: string | undefined }).type === 'multisig' ||
|
|
1214
|
-
('signatures' in envelope && ('account' in envelope || 'init' in envelope))
|
|
1215
|
-
) {
|
|
1216
|
-
const multisig = envelope as MultisigRpc
|
|
1217
|
-
const hasAccount = typeof multisig.account !== 'undefined'
|
|
1218
|
-
const hasInit = typeof multisig.init !== 'undefined'
|
|
1219
|
-
if (hasAccount === hasInit)
|
|
1220
|
-
throw new InvalidMultisigApprovalError({
|
|
1221
|
-
reason: 'RPC multisig must contain exactly one of `account` or `init`',
|
|
1222
|
-
})
|
|
1223
|
-
const init = hasInit
|
|
1224
|
-
? MultisigConfig.from(multisig.init as MultisigConfig.Config)
|
|
1225
|
-
: undefined
|
|
1226
|
-
const account = init ? MultisigConfig.getAddress(init) : multisig.account
|
|
1227
|
-
const result = {
|
|
1228
|
-
type: 'multisig',
|
|
1229
|
-
account: account as Address.Address,
|
|
1230
|
-
signatures: multisig.signatures.map((signature) => fromRpc(signature)),
|
|
1231
|
-
...(init ? { init } : {}),
|
|
1232
|
-
} satisfies Multisig
|
|
1233
|
-
assert(result)
|
|
1234
|
-
return result
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
1188
|
throw new CoercionError({ envelope })
|
|
1238
1189
|
}
|
|
1239
1190
|
|
|
@@ -1241,8 +1192,10 @@ export declare namespace fromRpc {
|
|
|
1241
1192
|
type ErrorType =
|
|
1242
1193
|
| assert.ErrorType
|
|
1243
1194
|
| CoercionError
|
|
1195
|
+
| Hex.concat.ErrorType
|
|
1196
|
+
| Hex.isEqual.ErrorType
|
|
1244
1197
|
| InvalidSerializedError
|
|
1245
|
-
|
|
|
1198
|
+
| serialize.ErrorType
|
|
1246
1199
|
| Signature.fromRpc.ErrorType
|
|
1247
1200
|
| Errors.GlobalErrorType
|
|
1248
1201
|
}
|
|
@@ -1317,12 +1270,7 @@ export function getType<
|
|
|
1317
1270
|
return 'keychain' as never
|
|
1318
1271
|
|
|
1319
1272
|
// Detect Multisig signature
|
|
1320
|
-
if (
|
|
1321
|
-
('account' in envelope ||
|
|
1322
|
-
'initialConfig' in envelope ||
|
|
1323
|
-
'init' in envelope) &&
|
|
1324
|
-
'signatures' in envelope
|
|
1325
|
-
)
|
|
1273
|
+
if ('config' in envelope && 'signatures' in envelope)
|
|
1326
1274
|
return 'multisig' as never
|
|
1327
1275
|
|
|
1328
1276
|
throw new CoercionError({
|
|
@@ -1339,7 +1287,7 @@ export function getType<
|
|
|
1339
1287
|
* - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
|
|
1340
1288
|
* - Keychain V1: `0x03` + userAddress (20) + inner signature (recursive)
|
|
1341
1289
|
* - Keychain V2: `0x04` + userAddress (20) + inner signature (recursive)
|
|
1342
|
-
* - Multisig: `0x05` + RLP `[account
|
|
1290
|
+
* - Multisig: `0x05` + RLP `[account, config, signatures]`
|
|
1343
1291
|
*
|
|
1344
1292
|
* [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
1345
1293
|
*
|
|
@@ -1426,14 +1374,11 @@ export function serialize(
|
|
|
1426
1374
|
if (type === 'multisig') {
|
|
1427
1375
|
const multisig = envelope as Multisig
|
|
1428
1376
|
assert(multisig)
|
|
1429
|
-
// The first field is either the initialized account or the bootstrap init
|
|
1430
|
-
// config. Each owner approval is an encoded signature.
|
|
1431
1377
|
return Hex.concat(
|
|
1432
1378
|
serializedMultisigType,
|
|
1433
1379
|
Rlp.fromHex([
|
|
1434
|
-
multisig.
|
|
1435
|
-
|
|
1436
|
-
: multisig.account,
|
|
1380
|
+
multisig.account,
|
|
1381
|
+
MultisigConfig.toTuple(multisig.config),
|
|
1437
1382
|
multisig.signatures.map((signature) => serialize(signature)),
|
|
1438
1383
|
]),
|
|
1439
1384
|
options.magic ? magicBytes : '0x',
|
|
@@ -1474,9 +1419,6 @@ export declare namespace serialize {
|
|
|
1474
1419
|
* recovered owner address. Works for any owner key type (secp256k1, p256,
|
|
1475
1420
|
* webAuthn).
|
|
1476
1421
|
*
|
|
1477
|
-
* Config updates never change `account`, so the initial config is the correct
|
|
1478
|
-
* input even for post-update transactions.
|
|
1479
|
-
*
|
|
1480
1422
|
* @example
|
|
1481
1423
|
* ```ts twoslash
|
|
1482
1424
|
* import { Secp256k1 } from 'ox'
|
|
@@ -1486,8 +1428,7 @@ export declare namespace serialize {
|
|
|
1486
1428
|
* TxEnvelopeTempo
|
|
1487
1429
|
* } from 'ox/tempo'
|
|
1488
1430
|
*
|
|
1489
|
-
* const
|
|
1490
|
-
* threshold: 2,
|
|
1431
|
+
* const config = MultisigConfig.from({
|
|
1491
1432
|
* owners: [
|
|
1492
1433
|
* {
|
|
1493
1434
|
* owner: '0x1111111111111111111111111111111111111111',
|
|
@@ -1497,8 +1438,10 @@ export declare namespace serialize {
|
|
|
1497
1438
|
* owner: '0x2222222222222222222222222222222222222222',
|
|
1498
1439
|
* weight: 1
|
|
1499
1440
|
* }
|
|
1500
|
-
* ]
|
|
1441
|
+
* ],
|
|
1442
|
+
* threshold: 2
|
|
1501
1443
|
* })
|
|
1444
|
+
* const account = MultisigConfig.getAddress(config)
|
|
1502
1445
|
*
|
|
1503
1446
|
* const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
|
|
1504
1447
|
* const payload = TxEnvelopeTempo.getSignPayload(tx)
|
|
@@ -1508,8 +1451,9 @@ export declare namespace serialize {
|
|
|
1508
1451
|
* Secp256k1.randomPrivateKey()
|
|
1509
1452
|
* ]
|
|
1510
1453
|
* const digest = MultisigConfig.getSignPayload({
|
|
1511
|
-
*
|
|
1512
|
-
*
|
|
1454
|
+
* account,
|
|
1455
|
+
* config,
|
|
1456
|
+
* payload
|
|
1513
1457
|
* })
|
|
1514
1458
|
* const signatures = privateKeys.map((privateKey) =>
|
|
1515
1459
|
* SignatureEnvelope.from(
|
|
@@ -1519,7 +1463,8 @@ export declare namespace serialize {
|
|
|
1519
1463
|
*
|
|
1520
1464
|
* const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1521
1465
|
* // [!code focus]
|
|
1522
|
-
*
|
|
1466
|
+
* account, // [!code focus]
|
|
1467
|
+
* config, // [!code focus]
|
|
1523
1468
|
* payload, // [!code focus]
|
|
1524
1469
|
* signatures // [!code focus]
|
|
1525
1470
|
* }) // [!code focus]
|
|
@@ -1531,16 +1476,12 @@ export declare namespace serialize {
|
|
|
1531
1476
|
export function sortMultisigApprovals(
|
|
1532
1477
|
value: sortMultisigApprovals.Value,
|
|
1533
1478
|
): readonly SignatureEnvelope[] {
|
|
1534
|
-
const {
|
|
1535
|
-
const digest = MultisigConfig.getSignPayload(
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
account: (value as { account: Address.Address }).account,
|
|
1541
|
-
version,
|
|
1542
|
-
},
|
|
1543
|
-
)
|
|
1479
|
+
const { account, config, payload, signatures } = value
|
|
1480
|
+
const digest = MultisigConfig.getSignPayload({
|
|
1481
|
+
account,
|
|
1482
|
+
config: MultisigConfig.from(config),
|
|
1483
|
+
payload,
|
|
1484
|
+
})
|
|
1544
1485
|
// Recover each signer once (decorate–sort–undecorate) rather than inside the
|
|
1545
1486
|
// comparator.
|
|
1546
1487
|
return signatures
|
|
@@ -1554,25 +1495,15 @@ export function sortMultisigApprovals(
|
|
|
1554
1495
|
|
|
1555
1496
|
export declare namespace sortMultisigApprovals {
|
|
1556
1497
|
type Value = {
|
|
1498
|
+
/** The native multisig account address. */
|
|
1499
|
+
account: Address.Address
|
|
1500
|
+
/** Complete applicable multisig configuration. */
|
|
1501
|
+
config: MultisigConfig.Input
|
|
1557
1502
|
/** The inner transaction sign payload (`tx.signature_hash()`). */
|
|
1558
1503
|
payload: Hex.Hex | Bytes.Bytes
|
|
1559
1504
|
/** The owner approvals to order. */
|
|
1560
1505
|
signatures: readonly SignatureEnvelope[]
|
|
1561
|
-
|
|
1562
|
-
version?: bigint | undefined
|
|
1563
|
-
} & OneOf<
|
|
1564
|
-
| {
|
|
1565
|
-
/** The native multisig account address. */
|
|
1566
|
-
account: Address.Address
|
|
1567
|
-
}
|
|
1568
|
-
| {
|
|
1569
|
-
/**
|
|
1570
|
-
* The initial multisig config (the bootstrap config that derived the
|
|
1571
|
-
* permanent `account`). Used to derive the account automatically.
|
|
1572
|
-
*/
|
|
1573
|
-
initialConfig: MultisigConfig.Config
|
|
1574
|
-
}
|
|
1575
|
-
>
|
|
1506
|
+
}
|
|
1576
1507
|
|
|
1577
1508
|
type ErrorType =
|
|
1578
1509
|
| MultisigConfig.getSignPayload.ErrorType
|
|
@@ -1655,27 +1586,7 @@ export function toRpc<const envelope extends toRpc.Input>(
|
|
|
1655
1586
|
|
|
1656
1587
|
if (type === 'multisig') {
|
|
1657
1588
|
const multisig = envelope as Multisig
|
|
1658
|
-
|
|
1659
|
-
const signatures = multisig.signatures.map((signature) => toRpc(signature))
|
|
1660
|
-
if (multisig.init) {
|
|
1661
|
-
const init = {
|
|
1662
|
-
...multisig.init,
|
|
1663
|
-
salt: multisig.init.salt ?? MultisigConfig.zeroSalt,
|
|
1664
|
-
threshold: Number(multisig.init.threshold),
|
|
1665
|
-
owners: multisig.init.owners.map((owner) => ({
|
|
1666
|
-
...owner,
|
|
1667
|
-
weight: Number(owner.weight),
|
|
1668
|
-
})),
|
|
1669
|
-
}
|
|
1670
|
-
return {
|
|
1671
|
-
init,
|
|
1672
|
-
signatures,
|
|
1673
|
-
} as never
|
|
1674
|
-
}
|
|
1675
|
-
return {
|
|
1676
|
-
account: multisig.account,
|
|
1677
|
-
signatures,
|
|
1678
|
-
} as never
|
|
1589
|
+
return Hex.slice(serialize(multisig), 1) as never
|
|
1679
1590
|
}
|
|
1680
1591
|
|
|
1681
1592
|
throw new CoercionError({ envelope })
|
|
@@ -1702,6 +1613,8 @@ export declare namespace toRpc {
|
|
|
1702
1613
|
type ErrorType =
|
|
1703
1614
|
| assert.ErrorType
|
|
1704
1615
|
| CoercionError
|
|
1616
|
+
| Hex.slice.ErrorType
|
|
1617
|
+
| serialize.ErrorType
|
|
1705
1618
|
| Signature.toRpc.ErrorType
|
|
1706
1619
|
| Errors.GlobalErrorType
|
|
1707
1620
|
}
|