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
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
IsNarrowable,
|
|
9
9
|
OneOf,
|
|
10
10
|
PartialBy,
|
|
11
|
-
Undefined,
|
|
12
11
|
UnionPartialBy,
|
|
13
12
|
} from '../core/internal/types.js'
|
|
14
13
|
import * as Json from '../core/Json.js'
|
|
@@ -28,15 +27,6 @@ const serializedKeychainType = '0x03'
|
|
|
28
27
|
const serializedKeychainV2Type = '0x04'
|
|
29
28
|
const serializedMultisigType = '0x05'
|
|
30
29
|
|
|
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
|
-
|
|
40
30
|
/** Serialized magic identifier for Tempo signature envelopes. */
|
|
41
31
|
export const magicBytes =
|
|
42
32
|
'0x7777777777777777777777777777777777777777777777777777777777777777' // 32 "T"s
|
|
@@ -65,15 +55,15 @@ export type GetType<
|
|
|
65
55
|
: envelope extends { type: infer T extends Type }
|
|
66
56
|
? T
|
|
67
57
|
: envelope extends {
|
|
68
|
-
signature: { r: `0x${string}`; s: `0x${string}` }
|
|
69
58
|
prehash: boolean
|
|
70
59
|
publicKey: PublicKey.PublicKey
|
|
60
|
+
signature: { r: `0x${string}`; s: `0x${string}` }
|
|
71
61
|
}
|
|
72
62
|
? 'p256'
|
|
73
63
|
: envelope extends {
|
|
74
|
-
signature: { r: `0x${string}`; s: `0x${string}` }
|
|
75
64
|
metadata: any
|
|
76
65
|
publicKey: PublicKey.PublicKey
|
|
66
|
+
signature: { r: `0x${string}`; s: `0x${string}` }
|
|
77
67
|
}
|
|
78
68
|
? 'webAuthn'
|
|
79
69
|
: envelope extends {
|
|
@@ -94,7 +84,11 @@ export type GetType<
|
|
|
94
84
|
userAddress: Address.Address
|
|
95
85
|
}
|
|
96
86
|
? 'keychain'
|
|
97
|
-
: envelope extends {
|
|
87
|
+
: envelope extends {
|
|
88
|
+
account: Address.Address
|
|
89
|
+
config: MultisigConfig.Input
|
|
90
|
+
signatures: any
|
|
91
|
+
}
|
|
98
92
|
? 'multisig'
|
|
99
93
|
: never
|
|
100
94
|
|
|
@@ -121,12 +115,20 @@ export type GetType<
|
|
|
121
115
|
*
|
|
122
116
|
* [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
123
117
|
*/
|
|
124
|
-
export type SignatureEnvelope<numberType = number> =
|
|
125
|
-
|
|
|
126
|
-
|
|
|
118
|
+
export type SignatureEnvelope<numberType = number> = OneOf<
|
|
119
|
+
| Secp256k1<numberType>
|
|
120
|
+
| P256<numberType>
|
|
121
|
+
| WebAuthn<numberType>
|
|
122
|
+
| Keychain<numberType>
|
|
123
|
+
| Multisig<numberType>
|
|
124
|
+
>
|
|
127
125
|
|
|
128
|
-
/**
|
|
129
|
-
|
|
126
|
+
/**
|
|
127
|
+
* RPC-formatted signature envelope.
|
|
128
|
+
*/
|
|
129
|
+
export type SignatureEnvelopeRpc =
|
|
130
|
+
| OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc>
|
|
131
|
+
| MultisigRpc
|
|
130
132
|
|
|
131
133
|
/** Primitive signature envelope accepted by protocol sidecars. */
|
|
132
134
|
export type Primitive<numberType = number> = OneOf<
|
|
@@ -146,55 +148,43 @@ export type PrimitiveRpc = OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc>
|
|
|
146
148
|
export type KeychainVersion = 'v1' | 'v2'
|
|
147
149
|
|
|
148
150
|
export type Keychain<numberType = number> = {
|
|
149
|
-
/** Root account address that this transaction is being executed for */
|
|
150
|
-
userAddress: Address.Address
|
|
151
151
|
/** The actual signature from the access key (can be Secp256k1, P256, or WebAuthn) */
|
|
152
152
|
inner: SignatureEnvelope<numberType>
|
|
153
153
|
/** The access key address (recovered address of the access key signer). */
|
|
154
154
|
keyId?: Address.Address | undefined
|
|
155
155
|
type: 'keychain'
|
|
156
|
+
/** Root account address that this transaction is being executed for */
|
|
157
|
+
userAddress: Address.Address
|
|
156
158
|
/** Keychain signature version. @default 'v1' */
|
|
157
159
|
version?: KeychainVersion | undefined
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
export type KeychainRpc = {
|
|
161
|
-
type: 'keychain'
|
|
162
|
-
userAddress: Address.Address
|
|
163
163
|
keyId?: Address.Address | undefined
|
|
164
164
|
signature: SignatureEnvelopeRpc
|
|
165
|
+
type: 'keychain'
|
|
166
|
+
userAddress: Address.Address
|
|
165
167
|
version?: KeychainVersion | undefined
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
/**
|
|
169
171
|
* Native multisig signature (type `0x05`).
|
|
170
172
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* configured threshold.
|
|
175
|
-
*
|
|
176
|
-
* [TIP-1061](https://tips.sh/1061)
|
|
173
|
+
* Carries the account's complete current configuration and primitive owner
|
|
174
|
+
* approvals over its version-bound digest. The node validates the account
|
|
175
|
+
* identity or stored commitment and the owner quorum against chain state.
|
|
177
176
|
*/
|
|
178
177
|
export type Multisig<numberType = number> = {
|
|
179
|
-
/**
|
|
178
|
+
/** Permanent native multisig account address. */
|
|
180
179
|
account: Address.Address
|
|
181
|
-
/** Complete
|
|
180
|
+
/** Complete current configuration, included in every signature. */
|
|
182
181
|
config: MultisigConfig.Config<bigint, numberType>
|
|
183
|
-
/**
|
|
184
|
-
|
|
185
|
-
* either a primitive signature or a nested multisig signature (keychain
|
|
186
|
-
* approvals are invalid).
|
|
187
|
-
*/
|
|
188
|
-
signatures: readonly SignatureEnvelope<numberType>[]
|
|
182
|
+
/** Primitive owner approvals in ascending recovered-address order. */
|
|
183
|
+
signatures: readonly Primitive<numberType>[]
|
|
189
184
|
type: 'multisig'
|
|
190
185
|
}
|
|
191
186
|
|
|
192
|
-
|
|
193
|
-
extends
|
|
194
|
-
Multisig<numberType>,
|
|
195
|
-
Undefined<Omit<NonMultisig<numberType>, 'type'>> {}
|
|
196
|
-
|
|
197
|
-
/** RPC-formatted native multisig signature without the `0x05` type byte. */
|
|
187
|
+
/** RLP-encoded `[account, config, signatures]`, without the `0x05` type byte. */
|
|
198
188
|
export type MultisigRpc = Hex.Hex
|
|
199
189
|
|
|
200
190
|
export type P256<numberType = number> = {
|
|
@@ -220,8 +210,8 @@ export type Secp256k1<numberType = number> = {
|
|
|
220
210
|
|
|
221
211
|
export type Secp256k1Rpc = Compute<
|
|
222
212
|
Signature.Rpc<true> & {
|
|
223
|
-
v?: Hex.Hex | undefined
|
|
224
213
|
type: 'secp256k1'
|
|
214
|
+
v?: Hex.Hex | undefined
|
|
225
215
|
}
|
|
226
216
|
>
|
|
227
217
|
|
|
@@ -237,8 +227,8 @@ export type WebAuthn<numberType = number> = {
|
|
|
237
227
|
WebAuthnP256.SignMetadata,
|
|
238
228
|
'authenticatorData' | 'clientDataJSON'
|
|
239
229
|
>
|
|
240
|
-
signature: Signature.Signature<false, numberType>
|
|
241
230
|
publicKey: PublicKey.PublicKey
|
|
231
|
+
signature: Signature.Signature<false, numberType>
|
|
242
232
|
type: 'webAuthn'
|
|
243
233
|
}
|
|
244
234
|
|
|
@@ -333,13 +323,14 @@ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
|
|
|
333
323
|
|
|
334
324
|
if (type === 'keychain') {
|
|
335
325
|
const keychain = envelope as Keychain
|
|
326
|
+
assertKeychainVersion(keychain)
|
|
336
327
|
assert(keychain.inner)
|
|
337
328
|
return
|
|
338
329
|
}
|
|
339
330
|
|
|
340
331
|
if (type === 'multisig') {
|
|
341
332
|
const multisig = envelope as Multisig
|
|
342
|
-
assertMultisig(multisig
|
|
333
|
+
assertMultisig(multisig)
|
|
343
334
|
return
|
|
344
335
|
}
|
|
345
336
|
}
|
|
@@ -355,7 +346,17 @@ export declare namespace assert {
|
|
|
355
346
|
| Errors.GlobalErrorType
|
|
356
347
|
}
|
|
357
348
|
|
|
358
|
-
function
|
|
349
|
+
function assertKeychainVersion(keychain: Keychain): void {
|
|
350
|
+
if (keychain.version !== 'v1') return
|
|
351
|
+
let inner = keychain.inner
|
|
352
|
+
while (getType(inner) === 'keychain') inner = (inner as Keychain).inner
|
|
353
|
+
if (getType(inner) === 'multisig')
|
|
354
|
+
throw new InvalidMultisigApprovalError({
|
|
355
|
+
reason: 'multisig access keys require keychain V2',
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function assertMultisig(envelope: Multisig): void {
|
|
359
360
|
const missing: string[] = []
|
|
360
361
|
if (!envelope.account) missing.push('account')
|
|
361
362
|
if (!envelope.config) missing.push('config')
|
|
@@ -366,10 +367,6 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
366
367
|
missing,
|
|
367
368
|
type: 'multisig',
|
|
368
369
|
})
|
|
369
|
-
if (depth > MultisigConfig.maxNestingDepth)
|
|
370
|
-
throw new InvalidMultisigApprovalError({
|
|
371
|
-
reason: `multisig nesting depth exceeds ${MultisigConfig.maxNestingDepth}`,
|
|
372
|
-
})
|
|
373
370
|
if (!Address.validate(envelope.account))
|
|
374
371
|
throw new InvalidMultisigApprovalError({
|
|
375
372
|
reason: 'multisig account is invalid',
|
|
@@ -378,25 +375,6 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
378
375
|
throw new InvalidMultisigApprovalError({
|
|
379
376
|
reason: 'multisig account cannot be zero',
|
|
380
377
|
})
|
|
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
|
-
})
|
|
400
378
|
if (envelope.signatures.length === 0)
|
|
401
379
|
throw new InvalidMultisigApprovalError({
|
|
402
380
|
reason: 'multisig signatures cannot be empty',
|
|
@@ -406,20 +384,29 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
406
384
|
reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
|
|
407
385
|
})
|
|
408
386
|
|
|
387
|
+
MultisigConfig.assert(envelope.config)
|
|
388
|
+
if (
|
|
389
|
+
(envelope.config.version ?? 0n) === 0n &&
|
|
390
|
+
envelope.config.owners.some((owner) =>
|
|
391
|
+
Address.isEqual(owner.owner, envelope.account),
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
throw new InvalidMultisigApprovalError({
|
|
395
|
+
reason: 'initial account cannot be an owner',
|
|
396
|
+
})
|
|
397
|
+
|
|
409
398
|
for (const inner of envelope.signatures) {
|
|
410
|
-
const type = getType(inner)
|
|
411
|
-
if (type === 'keychain')
|
|
399
|
+
const type = getType(inner as SignatureEnvelope)
|
|
400
|
+
if (type === 'keychain' || type === 'multisig')
|
|
412
401
|
throw new InvalidMultisigApprovalError({
|
|
413
|
-
reason: '
|
|
402
|
+
reason: 'owner approvals must be primitive signatures',
|
|
403
|
+
})
|
|
404
|
+
assert(inner)
|
|
405
|
+
|
|
406
|
+
if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
|
|
407
|
+
throw new InvalidMultisigApprovalError({
|
|
408
|
+
reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
|
|
414
409
|
})
|
|
415
|
-
if (type === 'multisig') assertMultisig(inner as Multisig, depth + 1)
|
|
416
|
-
else {
|
|
417
|
-
assert(inner)
|
|
418
|
-
if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
|
|
419
|
-
throw new InvalidMultisigApprovalError({
|
|
420
|
-
reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
410
|
}
|
|
424
411
|
}
|
|
425
412
|
|
|
@@ -470,10 +457,10 @@ export declare namespace extractAddress {
|
|
|
470
457
|
type Options = {
|
|
471
458
|
/** The sign payload that was signed (only required for secp256k1 signatures). */
|
|
472
459
|
payload: Hex.Hex | Bytes.Bytes
|
|
473
|
-
/** The signature envelope. */
|
|
474
|
-
signature: SignatureEnvelope
|
|
475
460
|
/** Whether to return the root `userAddress` for keychain signatures instead of extracting from the inner signature. */
|
|
476
461
|
root?: boolean | undefined
|
|
462
|
+
/** The signature envelope. */
|
|
463
|
+
signature: SignatureEnvelope
|
|
477
464
|
}
|
|
478
465
|
|
|
479
466
|
type ReturnType = Address.Address
|
|
@@ -576,13 +563,10 @@ export declare namespace extractPublicKey {
|
|
|
576
563
|
* @throws `CoercionError` if the serialized value cannot be coerced to a valid signature envelope.
|
|
577
564
|
*/
|
|
578
565
|
export function deserialize(value: Serialized): SignatureEnvelope {
|
|
579
|
-
return deserialize_(value
|
|
566
|
+
return deserialize_(value)
|
|
580
567
|
}
|
|
581
568
|
|
|
582
|
-
function deserialize_(
|
|
583
|
-
value: Serialized,
|
|
584
|
-
multisigDepth: number,
|
|
585
|
-
): SignatureEnvelope {
|
|
569
|
+
function deserialize_(value: Serialized): SignatureEnvelope {
|
|
586
570
|
const serialized = value.endsWith(magicBytes.slice(2))
|
|
587
571
|
? Hex.slice(value, 0, -Hex.size(magicBytes))
|
|
588
572
|
: value
|
|
@@ -685,33 +669,27 @@ function deserialize_(
|
|
|
685
669
|
typeId === serializedKeychainV2Type
|
|
686
670
|
) {
|
|
687
671
|
const userAddress = Hex.slice(data, 0, 20)
|
|
688
|
-
const inner = deserialize_(Hex.slice(data, 20)
|
|
672
|
+
const inner = deserialize_(Hex.slice(data, 20))
|
|
689
673
|
|
|
690
|
-
|
|
674
|
+
const envelope = {
|
|
691
675
|
userAddress,
|
|
692
676
|
inner,
|
|
693
677
|
type: 'keychain',
|
|
694
678
|
version: typeId === serializedKeychainV2Type ? 'v2' : 'v1',
|
|
695
679
|
} satisfies Keychain
|
|
680
|
+
assert(envelope)
|
|
681
|
+
return envelope
|
|
696
682
|
}
|
|
697
683
|
|
|
698
684
|
if (typeId === serializedMultisigType) {
|
|
699
|
-
const depth = multisigDepth + 1
|
|
700
|
-
if (depth > MultisigConfig.maxNestingDepth)
|
|
701
|
-
throw new InvalidSerializedError({
|
|
702
|
-
reason: `multisig nesting depth exceeds ${MultisigConfig.maxNestingDepth}`,
|
|
703
|
-
serialized,
|
|
704
|
-
})
|
|
705
|
-
|
|
706
685
|
const decoded = Rlp.toHex(data)
|
|
707
686
|
if (!Array.isArray(decoded) || decoded.length !== 3)
|
|
708
687
|
throw new InvalidSerializedError({
|
|
709
688
|
reason: 'invalid multisig wire shape: expected exactly three fields',
|
|
710
689
|
serialized,
|
|
711
690
|
})
|
|
712
|
-
|
|
713
691
|
const [account, config, signatures] = decoded
|
|
714
|
-
if (!Address.validate(account))
|
|
692
|
+
if (typeof account !== 'string' || !Address.validate(account))
|
|
715
693
|
throw new InvalidSerializedError({
|
|
716
694
|
reason: 'invalid multisig account',
|
|
717
695
|
serialized,
|
|
@@ -721,68 +699,43 @@ function deserialize_(
|
|
|
721
699
|
reason: 'invalid multisig config',
|
|
722
700
|
serialized,
|
|
723
701
|
})
|
|
724
|
-
const [salt, version, threshold, owners] = config
|
|
725
702
|
if (
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
)
|
|
703
|
+
!Array.isArray(signatures) ||
|
|
704
|
+
signatures.length === 0 ||
|
|
705
|
+
signatures.length > MultisigConfig.maxSignatures
|
|
743
706
|
)
|
|
744
|
-
throw new InvalidSerializedError({
|
|
745
|
-
reason: 'invalid multisig config',
|
|
746
|
-
serialized,
|
|
747
|
-
})
|
|
748
|
-
if (!Array.isArray(signatures) || signatures.some(Array.isArray))
|
|
749
707
|
throw new InvalidSerializedError({
|
|
750
708
|
reason: 'invalid multisig signatures list',
|
|
751
709
|
serialized,
|
|
752
710
|
})
|
|
753
|
-
if (signatures.length === 0)
|
|
754
|
-
throw new InvalidSerializedError({
|
|
755
|
-
reason: 'multisig signatures cannot be empty',
|
|
756
|
-
serialized,
|
|
757
|
-
})
|
|
758
|
-
if (signatures.length > MultisigConfig.maxSignatures)
|
|
759
|
-
throw new InvalidSerializedError({
|
|
760
|
-
reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
|
|
761
|
-
serialized,
|
|
762
|
-
})
|
|
763
|
-
for (const signature of signatures)
|
|
764
|
-
if (
|
|
765
|
-
Hex.size(signature as Hex.Hex) >
|
|
766
|
-
MultisigConfig.maxOwnerSignatureBytes &&
|
|
767
|
-
Hex.slice(signature as Hex.Hex, 0, 1) !==
|
|
768
|
-
MultisigConfig.signatureTypeByte
|
|
769
|
-
)
|
|
770
|
-
throw new InvalidSerializedError({
|
|
771
|
-
reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
|
|
772
|
-
serialized,
|
|
773
|
-
})
|
|
774
|
-
|
|
775
711
|
const envelope = {
|
|
776
|
-
|
|
712
|
+
type: 'multisig',
|
|
713
|
+
account,
|
|
777
714
|
config: MultisigConfig.fromTuple(
|
|
778
715
|
config as unknown as MultisigConfig.Tuple,
|
|
779
716
|
),
|
|
780
|
-
signatures: signatures.map((signature) =>
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
717
|
+
signatures: signatures.map((signature) => {
|
|
718
|
+
if (
|
|
719
|
+
!Hex.validate(signature) ||
|
|
720
|
+
Hex.size(signature) > MultisigConfig.maxOwnerSignatureBytes
|
|
721
|
+
)
|
|
722
|
+
throw new InvalidSerializedError({
|
|
723
|
+
reason: 'invalid multisig owner signature',
|
|
724
|
+
serialized,
|
|
725
|
+
})
|
|
726
|
+
// Reject recursive envelopes before decoding their contents.
|
|
727
|
+
if (
|
|
728
|
+
Hex.size(signature) !== 65 &&
|
|
729
|
+
!['0x01', '0x02'].includes(Hex.slice(signature, 0, 1))
|
|
730
|
+
)
|
|
731
|
+
throw new InvalidSerializedError({
|
|
732
|
+
reason: 'owner approvals must be primitive signatures',
|
|
733
|
+
serialized,
|
|
734
|
+
})
|
|
735
|
+
return deserialize(signature) as Primitive
|
|
736
|
+
}),
|
|
784
737
|
} satisfies Multisig
|
|
785
|
-
assertMultisig(envelope
|
|
738
|
+
assertMultisig(envelope)
|
|
786
739
|
return envelope
|
|
787
740
|
}
|
|
788
741
|
|
|
@@ -920,28 +873,30 @@ function deserialize_(
|
|
|
920
873
|
* @example
|
|
921
874
|
* ### Multisig
|
|
922
875
|
*
|
|
923
|
-
*
|
|
924
|
-
* import { Secp256k1 } from 'ox'
|
|
925
|
-
* import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
|
|
926
|
-
*
|
|
927
|
-
* const config = MultisigConfig.from({
|
|
928
|
-
* owners: [
|
|
929
|
-
* {
|
|
930
|
-
* owner: '0x1111111111111111111111111111111111111111',
|
|
931
|
-
* weight: 1
|
|
932
|
-
* }
|
|
933
|
-
* ],
|
|
934
|
-
* threshold: 1
|
|
935
|
-
* })
|
|
876
|
+
* Include the permanent account and current configuration on every transaction.
|
|
936
877
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
* Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
|
|
940
|
-
* )
|
|
878
|
+
* ```ts twoslash
|
|
879
|
+
* import { SignatureEnvelope } from 'ox/tempo'
|
|
941
880
|
*
|
|
942
|
-
* const
|
|
943
|
-
*
|
|
944
|
-
*
|
|
881
|
+
* const envelope = SignatureEnvelope.from({
|
|
882
|
+
* account: '0x2222222222222222222222222222222222222222',
|
|
883
|
+
* config: {
|
|
884
|
+
* version: 0n,
|
|
885
|
+
* threshold: 1,
|
|
886
|
+
* owners: [
|
|
887
|
+
* {
|
|
888
|
+
* owner: '0x1111111111111111111111111111111111111111',
|
|
889
|
+
* weight: 1
|
|
890
|
+
* }
|
|
891
|
+
* ]
|
|
892
|
+
* },
|
|
893
|
+
* signatures: [
|
|
894
|
+
* SignatureEnvelope.from({
|
|
895
|
+
* r: '0x01',
|
|
896
|
+
* s: '0x02',
|
|
897
|
+
* yParity: 0
|
|
898
|
+
* })
|
|
899
|
+
* ]
|
|
945
900
|
* })
|
|
946
901
|
* ```
|
|
947
902
|
*
|
|
@@ -966,17 +921,13 @@ export function from<const value extends from.Value>(
|
|
|
966
921
|
const type = getType(value)
|
|
967
922
|
|
|
968
923
|
if (type === 'multisig') {
|
|
969
|
-
const multisig = value as
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
account,
|
|
974
|
-
config,
|
|
924
|
+
const multisig = value as Multisig
|
|
925
|
+
return {
|
|
926
|
+
...multisig,
|
|
927
|
+
config: MultisigConfig.from(multisig.config),
|
|
975
928
|
signatures: multisig.signatures.map((signature) => from(signature)),
|
|
976
929
|
type,
|
|
977
|
-
}
|
|
978
|
-
assert(envelope)
|
|
979
|
-
return envelope as never
|
|
930
|
+
} as never
|
|
980
931
|
}
|
|
981
932
|
|
|
982
933
|
return {
|
|
@@ -1021,32 +972,17 @@ export declare namespace from {
|
|
|
1021
972
|
payload?: Hex.Hex | Bytes.Bytes | undefined
|
|
1022
973
|
}
|
|
1023
974
|
|
|
1024
|
-
/** Multisig
|
|
1025
|
-
type MultisigFromConfig =
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
-
}
|
|
975
|
+
/** Multisig input with an explicit account and a normalizable configuration. */
|
|
976
|
+
type MultisigFromConfig = Omit<Multisig, 'config' | 'type'> & {
|
|
977
|
+
config: MultisigConfig.Input
|
|
978
|
+
type?: 'multisig' | undefined
|
|
979
|
+
}
|
|
1044
980
|
|
|
1045
981
|
type Value =
|
|
982
|
+
| MultisigFromConfig
|
|
1046
983
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
1047
984
|
| Secp256k1Flat
|
|
1048
985
|
| Serialized
|
|
1049
|
-
| MultisigFromConfig
|
|
1050
986
|
|
|
1051
987
|
type ReturnValue<value extends Value> = Compute<
|
|
1052
988
|
OneOf<
|
|
@@ -1055,7 +991,7 @@ export declare namespace from {
|
|
|
1055
991
|
: value extends Secp256k1Flat
|
|
1056
992
|
? Secp256k1
|
|
1057
993
|
: value extends MultisigFromConfig
|
|
1058
|
-
?
|
|
994
|
+
? Extract<SignatureEnvelope, { type: 'multisig' }>
|
|
1059
995
|
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
1060
996
|
? SignatureEnvelope
|
|
1061
997
|
: Assign<
|
|
@@ -1090,19 +1026,10 @@ export declare namespace from {
|
|
|
1090
1026
|
*/
|
|
1091
1027
|
export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
1092
1028
|
if (typeof envelope === 'string') {
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
|
1029
|
+
const signature = deserialize(Hex.concat(serializedMultisigType, envelope))
|
|
1030
|
+
if (signature.type !== 'multisig') throw new CoercionError({ envelope })
|
|
1031
|
+
return signature
|
|
1104
1032
|
}
|
|
1105
|
-
|
|
1106
1033
|
if (envelope.type === 'secp256k1')
|
|
1107
1034
|
return {
|
|
1108
1035
|
signature: Signature.fromRpc(envelope),
|
|
@@ -1176,13 +1103,15 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
|
1176
1103
|
('userAddress' in envelope && 'signature' in envelope)
|
|
1177
1104
|
) {
|
|
1178
1105
|
const keychain = envelope as KeychainRpc
|
|
1179
|
-
|
|
1106
|
+
const result = {
|
|
1180
1107
|
type: 'keychain',
|
|
1181
1108
|
userAddress: keychain.userAddress,
|
|
1182
1109
|
inner: fromRpc(keychain.signature),
|
|
1183
1110
|
...(keychain.keyId ? { keyId: keychain.keyId } : {}),
|
|
1184
1111
|
...(keychain.version ? { version: keychain.version } : {}),
|
|
1185
|
-
}
|
|
1112
|
+
} satisfies Keychain
|
|
1113
|
+
assert(result)
|
|
1114
|
+
return result
|
|
1186
1115
|
}
|
|
1187
1116
|
|
|
1188
1117
|
throw new CoercionError({ envelope })
|
|
@@ -1192,10 +1121,8 @@ export declare namespace fromRpc {
|
|
|
1192
1121
|
type ErrorType =
|
|
1193
1122
|
| assert.ErrorType
|
|
1194
1123
|
| CoercionError
|
|
1195
|
-
| Hex.concat.ErrorType
|
|
1196
|
-
| Hex.isEqual.ErrorType
|
|
1197
1124
|
| InvalidSerializedError
|
|
1198
|
-
|
|
|
1125
|
+
| MultisigConfig.getAddress.ErrorType
|
|
1199
1126
|
| Signature.fromRpc.ErrorType
|
|
1200
1127
|
| Errors.GlobalErrorType
|
|
1201
1128
|
}
|
|
@@ -1270,7 +1197,7 @@ export function getType<
|
|
|
1270
1197
|
return 'keychain' as never
|
|
1271
1198
|
|
|
1272
1199
|
// Detect Multisig signature
|
|
1273
|
-
if ('config' in envelope && 'signatures' in envelope)
|
|
1200
|
+
if ('account' in envelope && 'config' in envelope && 'signatures' in envelope)
|
|
1274
1201
|
return 'multisig' as never
|
|
1275
1202
|
|
|
1276
1203
|
throw new CoercionError({
|
|
@@ -1359,6 +1286,7 @@ export function serialize(
|
|
|
1359
1286
|
|
|
1360
1287
|
if (type === 'keychain') {
|
|
1361
1288
|
const keychain = envelope as Keychain
|
|
1289
|
+
assert(keychain)
|
|
1362
1290
|
const keychainTypeId =
|
|
1363
1291
|
keychain.version === 'v1'
|
|
1364
1292
|
? serializedKeychainType
|
|
@@ -1409,79 +1337,31 @@ export declare namespace serialize {
|
|
|
1409
1337
|
}
|
|
1410
1338
|
|
|
1411
1339
|
/**
|
|
1412
|
-
*
|
|
1413
|
-
* recovered-owner order the Tempo node requires for the multisig `signatures`
|
|
1414
|
-
* array (the node enforces "recovered owners must be strictly ascending").
|
|
1340
|
+
* Sorts primitive owner approvals by recovered address.
|
|
1415
1341
|
*
|
|
1416
|
-
*
|
|
1417
|
-
*
|
|
1418
|
-
* every approval is recovered against that digest and the list is sorted by the
|
|
1419
|
-
* recovered owner address. Works for any owner key type (secp256k1, p256,
|
|
1420
|
-
* webAuthn).
|
|
1342
|
+
* Recovery uses the account and current config version's multisig digest.
|
|
1343
|
+
* Duplicate or non-owner approvals must still be rejected by the node.
|
|
1421
1344
|
*
|
|
1422
1345
|
* @example
|
|
1423
1346
|
* ```ts twoslash
|
|
1424
|
-
* import {
|
|
1425
|
-
* import {
|
|
1426
|
-
* MultisigConfig,
|
|
1427
|
-
* SignatureEnvelope,
|
|
1428
|
-
* TxEnvelopeTempo
|
|
1429
|
-
* } from 'ox/tempo'
|
|
1430
|
-
*
|
|
1431
|
-
* const config = MultisigConfig.from({
|
|
1432
|
-
* owners: [
|
|
1433
|
-
* {
|
|
1434
|
-
* owner: '0x1111111111111111111111111111111111111111',
|
|
1435
|
-
* weight: 1
|
|
1436
|
-
* },
|
|
1437
|
-
* {
|
|
1438
|
-
* owner: '0x2222222222222222222222222222222222222222',
|
|
1439
|
-
* weight: 1
|
|
1440
|
-
* }
|
|
1441
|
-
* ],
|
|
1442
|
-
* threshold: 2
|
|
1443
|
-
* })
|
|
1444
|
-
* const account = MultisigConfig.getAddress(config)
|
|
1445
|
-
*
|
|
1446
|
-
* const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
|
|
1447
|
-
* const payload = TxEnvelopeTempo.getSignPayload(tx)
|
|
1448
|
-
*
|
|
1449
|
-
* const privateKeys = [
|
|
1450
|
-
* Secp256k1.randomPrivateKey(),
|
|
1451
|
-
* Secp256k1.randomPrivateKey()
|
|
1452
|
-
* ]
|
|
1453
|
-
* const digest = MultisigConfig.getSignPayload({
|
|
1454
|
-
* account,
|
|
1455
|
-
* config,
|
|
1456
|
-
* payload
|
|
1457
|
-
* })
|
|
1458
|
-
* const signatures = privateKeys.map((privateKey) =>
|
|
1459
|
-
* SignatureEnvelope.from(
|
|
1460
|
-
* Secp256k1.sign({ payload: digest, privateKey })
|
|
1461
|
-
* )
|
|
1462
|
-
* )
|
|
1347
|
+
* import { SignatureEnvelope } from 'ox/tempo'
|
|
1463
1348
|
*
|
|
1464
1349
|
* const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
*
|
|
1469
|
-
*
|
|
1470
|
-
* }) // [!code focus]
|
|
1350
|
+
* account: '0x2222222222222222222222222222222222222222',
|
|
1351
|
+
* config: { version: 0n },
|
|
1352
|
+
* payload: `0x${'ab'.repeat(32)}`,
|
|
1353
|
+
* signatures: []
|
|
1354
|
+
* })
|
|
1471
1355
|
* ```
|
|
1472
1356
|
*
|
|
1473
|
-
* @param value -
|
|
1474
|
-
* @returns The
|
|
1357
|
+
* @param value - Account, version, payload, and primitive approvals.
|
|
1358
|
+
* @returns The approvals in ascending recovered-address order.
|
|
1475
1359
|
*/
|
|
1476
1360
|
export function sortMultisigApprovals(
|
|
1477
1361
|
value: sortMultisigApprovals.Value,
|
|
1478
|
-
): readonly
|
|
1479
|
-
const {
|
|
1480
|
-
const digest = MultisigConfig.getSignPayload(
|
|
1481
|
-
account,
|
|
1482
|
-
config: MultisigConfig.from(config),
|
|
1483
|
-
payload,
|
|
1484
|
-
})
|
|
1362
|
+
): readonly Primitive[] {
|
|
1363
|
+
const { signatures } = value
|
|
1364
|
+
const digest = MultisigConfig.getSignPayload(value)
|
|
1485
1365
|
// Recover each signer once (decorate–sort–undecorate) rather than inside the
|
|
1486
1366
|
// comparator.
|
|
1487
1367
|
return signatures
|
|
@@ -1494,15 +1374,9 @@ export function sortMultisigApprovals(
|
|
|
1494
1374
|
}
|
|
1495
1375
|
|
|
1496
1376
|
export declare namespace sortMultisigApprovals {
|
|
1497
|
-
type Value = {
|
|
1498
|
-
/**
|
|
1499
|
-
|
|
1500
|
-
/** Complete applicable multisig configuration. */
|
|
1501
|
-
config: MultisigConfig.Input
|
|
1502
|
-
/** The inner transaction sign payload (`tx.signature_hash()`). */
|
|
1503
|
-
payload: Hex.Hex | Bytes.Bytes
|
|
1504
|
-
/** The owner approvals to order. */
|
|
1505
|
-
signatures: readonly SignatureEnvelope[]
|
|
1377
|
+
type Value = MultisigConfig.getSignPayload.Value & {
|
|
1378
|
+
/** Primitive owner approvals to order. */
|
|
1379
|
+
signatures: readonly Primitive[]
|
|
1506
1380
|
}
|
|
1507
1381
|
|
|
1508
1382
|
type ErrorType =
|
|
@@ -1575,6 +1449,7 @@ export function toRpc<const envelope extends toRpc.Input>(
|
|
|
1575
1449
|
|
|
1576
1450
|
if (type === 'keychain') {
|
|
1577
1451
|
const keychain = envelope as Keychain
|
|
1452
|
+
assertKeychainVersion(keychain)
|
|
1578
1453
|
return {
|
|
1579
1454
|
type: 'keychain',
|
|
1580
1455
|
userAddress: keychain.userAddress,
|
|
@@ -1585,8 +1460,38 @@ export function toRpc<const envelope extends toRpc.Input>(
|
|
|
1585
1460
|
}
|
|
1586
1461
|
|
|
1587
1462
|
if (type === 'multisig') {
|
|
1588
|
-
const multisig = envelope as Multisig
|
|
1589
|
-
return Hex.slice(
|
|
1463
|
+
const multisig = envelope as Multisig<Hex.Hex | number>
|
|
1464
|
+
return Hex.slice(
|
|
1465
|
+
serialize({
|
|
1466
|
+
...multisig,
|
|
1467
|
+
config: {
|
|
1468
|
+
...multisig.config,
|
|
1469
|
+
threshold: Number(multisig.config.threshold),
|
|
1470
|
+
owners: multisig.config.owners.map((owner) => ({
|
|
1471
|
+
...owner,
|
|
1472
|
+
weight: Number(owner.weight),
|
|
1473
|
+
})),
|
|
1474
|
+
},
|
|
1475
|
+
signatures: multisig.signatures.map((signature) =>
|
|
1476
|
+
signature.type === 'secp256k1'
|
|
1477
|
+
? {
|
|
1478
|
+
...signature,
|
|
1479
|
+
signature: {
|
|
1480
|
+
...signature.signature,
|
|
1481
|
+
yParity: Number(signature.signature.yParity),
|
|
1482
|
+
},
|
|
1483
|
+
}
|
|
1484
|
+
: {
|
|
1485
|
+
...signature,
|
|
1486
|
+
signature: {
|
|
1487
|
+
r: signature.signature.r,
|
|
1488
|
+
s: signature.signature.s,
|
|
1489
|
+
},
|
|
1490
|
+
},
|
|
1491
|
+
),
|
|
1492
|
+
}),
|
|
1493
|
+
1,
|
|
1494
|
+
) as never
|
|
1590
1495
|
}
|
|
1591
1496
|
|
|
1592
1497
|
throw new CoercionError({ envelope })
|