ox 1.0.2 → 1.0.3

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/tempo/KeyAuthorization.d.ts +22 -16
  3. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  4. package/dist/tempo/KeyAuthorization.js +31 -7
  5. package/dist/tempo/KeyAuthorization.js.map +1 -1
  6. package/dist/tempo/MultisigConfig.d.ts +8 -4
  7. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  8. package/dist/tempo/MultisigConfig.js +16 -4
  9. package/dist/tempo/MultisigConfig.js.map +1 -1
  10. package/dist/tempo/SignatureEnvelope.d.ts +31 -13
  11. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  12. package/dist/tempo/SignatureEnvelope.js +172 -55
  13. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  14. package/dist/zod/tempo/AuthorizationTempo.d.ts +70 -60
  15. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  16. package/dist/zod/tempo/KeyAuthorization.d.ts +0 -306
  17. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  18. package/dist/zod/tempo/KeyAuthorization.js +2 -2
  19. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  20. package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
  21. package/dist/zod/tempo/MultisigConfig.js +5 -2
  22. package/dist/zod/tempo/MultisigConfig.js.map +1 -1
  23. package/dist/zod/tempo/RpcSchemaTempo.d.ts +28 -132
  24. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  25. package/dist/zod/tempo/SignatureEnvelope.d.ts +79 -18
  26. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  27. package/dist/zod/tempo/SignatureEnvelope.js +39 -11
  28. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  29. package/dist/zod/tempo/Transaction.d.ts +84 -396
  30. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  31. package/dist/zod/tempo/TransactionRequest.d.ts +42 -234
  32. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  33. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -108
  34. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/tempo/KeyAuthorization.test-d.ts +62 -0
  37. package/src/tempo/KeyAuthorization.test.ts +53 -0
  38. package/src/tempo/KeyAuthorization.ts +69 -27
  39. package/src/tempo/MultisigConfig.test.ts +34 -2
  40. package/src/tempo/MultisigConfig.ts +18 -4
  41. package/src/tempo/SignatureEnvelope.test-d.ts +97 -0
  42. package/src/tempo/SignatureEnvelope.test.ts +247 -36
  43. package/src/tempo/SignatureEnvelope.ts +271 -85
  44. package/src/tempo/e2e.test.ts +0 -416
  45. package/src/tempo/multisig.e2e.test.ts +582 -0
  46. package/src/version.ts +1 -1
  47. package/src/zod/tempo/KeyAuthorization.ts +2 -2
  48. package/src/zod/tempo/MultisigConfig.ts +13 -5
  49. package/src/zod/tempo/SignatureEnvelope.ts +81 -18
  50. package/src/zod/tempo/_test/KeyAuthorization.test.ts +25 -0
  51. package/src/zod/tempo/_test/MultisigConfig.test.ts +44 -0
  52. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +9 -0
  53. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +142 -6
@@ -84,10 +84,15 @@ export type GetType<
84
84
  userAddress: Address.Address
85
85
  }
86
86
  ? 'keychain'
87
- : envelope extends {
88
- account: Address.Address
89
- signatures: any
90
- }
87
+ : envelope extends
88
+ | {
89
+ account: Address.Address
90
+ signatures: any
91
+ }
92
+ | {
93
+ init: MultisigConfig.Config
94
+ signatures: any
95
+ }
91
96
  ? 'multisig'
92
97
  : never
93
98
 
@@ -129,6 +134,14 @@ export type SignatureEnvelopeRpc = OneOf<
129
134
  Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc | MultisigRpc
130
135
  >
131
136
 
137
+ /** Primitive signature envelope accepted by protocol sidecars. */
138
+ export type Primitive<numberType = number> = OneOf<
139
+ Secp256k1<numberType> | P256<numberType> | WebAuthn<numberType>
140
+ >
141
+
142
+ /** RPC-formatted primitive signature envelope. */
143
+ export type PrimitiveRpc = OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc>
144
+
132
145
  /**
133
146
  * Keychain signature version.
134
147
  *
@@ -186,16 +199,25 @@ export type Multisig<numberType = number> = {
186
199
  init?: MultisigConfig.Config<numberType> | undefined
187
200
  }
188
201
 
189
- export type MultisigRpc = {
190
- type: 'multisig'
191
- account: Address.Address
192
- /**
193
- * Encoded owner approvals (raw serialized signatures), matching the node's
194
- * `Vec<Bytes>` representation.
195
- */
196
- signatures: readonly Serialized[]
197
- init?: MultisigConfig.Config | undefined
198
- }
202
+ /** RPC-formatted native multisig signature. */
203
+ export type MultisigRpc = OneOf<
204
+ | {
205
+ /** Existing native multisig account. */
206
+ account: Address.Address
207
+ /** Structured owner approvals. */
208
+ signatures: readonly SignatureEnvelopeRpc[]
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
+ >
199
221
 
200
222
  export type P256<numberType = number> = {
201
223
  prehash: boolean
@@ -339,27 +361,7 @@ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
339
361
 
340
362
  if (type === 'multisig') {
341
363
  const multisig = envelope as Multisig
342
- const missing: string[] = []
343
- if (!multisig.account) missing.push('account')
344
- if (!Array.isArray(multisig.signatures)) missing.push('signatures')
345
- if (missing.length > 0)
346
- throw new MissingPropertiesError({ envelope, missing, type: 'multisig' })
347
- if (getNestingDepth(multisig) > MultisigConfig.maxNestingDepth)
348
- throw new InvalidMultisigApprovalError({
349
- reason: `multisig nesting depth exceeds ${MultisigConfig.maxNestingDepth}`,
350
- })
351
- for (const inner of multisig.signatures) {
352
- if (inner.type === 'keychain')
353
- throw new InvalidMultisigApprovalError({
354
- reason: 'keychain owner approvals are not allowed',
355
- })
356
- if (inner.type === 'multisig' && inner.init)
357
- throw new InvalidMultisigApprovalError({
358
- reason: 'nested multisig owner approvals cannot carry `init`',
359
- })
360
- assert(inner)
361
- }
362
- if (multisig.init) MultisigConfig.assert(multisig.init)
364
+ assertMultisig(multisig, 1)
363
365
  return
364
366
  }
365
367
  }
@@ -370,17 +372,75 @@ export declare namespace assert {
370
372
  | InvalidMultisigApprovalError
371
373
  | MissingPropertiesError
372
374
  | MultisigConfig.assert.ErrorType
375
+ | MultisigConfig.getAddress.ErrorType
373
376
  | Signature.assert.ErrorType
374
377
  | Errors.GlobalErrorType
375
378
  }
376
379
 
377
- // Returns the deepest multisig path length within a multisig envelope.
378
- function getNestingDepth(envelope: Multisig): number {
379
- let deepest = 0
380
- for (const inner of envelope.signatures)
381
- if (inner.type === 'multisig')
382
- deepest = Math.max(deepest, getNestingDepth(inner))
383
- return 1 + deepest
380
+ function assertMultisig(envelope: Multisig, depth: number): void {
381
+ const missing: string[] = []
382
+ if (!envelope.account) missing.push('account')
383
+ if (!Array.isArray(envelope.signatures)) missing.push('signatures')
384
+ if (missing.length > 0)
385
+ throw new MissingPropertiesError({
386
+ envelope,
387
+ missing,
388
+ type: 'multisig',
389
+ })
390
+ if (depth > MultisigConfig.maxNestingDepth)
391
+ throw new InvalidMultisigApprovalError({
392
+ reason: `multisig nesting depth exceeds ${MultisigConfig.maxNestingDepth}`,
393
+ })
394
+ if (!Address.validate(envelope.account))
395
+ throw new InvalidMultisigApprovalError({
396
+ reason: 'multisig account is invalid',
397
+ })
398
+ if (Hex.toBigInt(envelope.account) === 0n)
399
+ throw new InvalidMultisigApprovalError({
400
+ reason: 'multisig account cannot be zero',
401
+ })
402
+ if (envelope.signatures.length === 0)
403
+ throw new InvalidMultisigApprovalError({
404
+ reason: 'multisig signatures cannot be empty',
405
+ })
406
+ if (envelope.signatures.length > MultisigConfig.maxSignatures)
407
+ throw new InvalidMultisigApprovalError({
408
+ reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
409
+ })
410
+
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
+ for (const inner of envelope.signatures) {
425
+ const type = getType(inner)
426
+ if (type === 'keychain')
427
+ throw new InvalidMultisigApprovalError({
428
+ reason: 'keychain owner approvals are not allowed',
429
+ })
430
+ if (type === 'multisig') {
431
+ const multisig = inner as Multisig
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 assert(inner)
438
+
439
+ if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
440
+ throw new InvalidMultisigApprovalError({
441
+ reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
442
+ })
443
+ }
384
444
  }
385
445
 
386
446
  /**
@@ -536,6 +596,13 @@ export declare namespace extractPublicKey {
536
596
  * @throws `CoercionError` if the serialized value cannot be coerced to a valid signature envelope.
537
597
  */
538
598
  export function deserialize(value: Serialized): SignatureEnvelope {
599
+ return deserialize_(value, 0)
600
+ }
601
+
602
+ function deserialize_(
603
+ value: Serialized,
604
+ multisigDepth: number,
605
+ ): SignatureEnvelope {
539
606
  const serialized = value.endsWith(magicBytes.slice(2))
540
607
  ? Hex.slice(value, 0, -Hex.size(magicBytes))
541
608
  : value
@@ -638,7 +705,7 @@ export function deserialize(value: Serialized): SignatureEnvelope {
638
705
  typeId === serializedKeychainV2Type
639
706
  ) {
640
707
  const userAddress = Hex.slice(data, 0, 20)
641
- const inner = deserialize(Hex.slice(data, 20))
708
+ const inner = deserialize_(Hex.slice(data, 20), multisigDepth)
642
709
 
643
710
  return {
644
711
  userAddress,
@@ -649,28 +716,91 @@ export function deserialize(value: Serialized): SignatureEnvelope {
649
716
  }
650
717
 
651
718
  if (typeId === serializedMultisigType) {
652
- // Wire format: `0x05 || rlp([account, signatures, init?])`. `init` is a
653
- // trailing optional: the bootstrap config list when present, otherwise
654
- // omitted entirely (the empty `0x80` placeholder is rejected).
655
- const [account, signatures, init] = Rlp.toHex(data) as [
656
- Hex.Hex,
657
- readonly Hex.Hex[],
658
- (Hex.Hex | MultisigConfig.Tuple)?,
659
- ]
660
- if (typeof init !== 'undefined' && !Array.isArray(init))
719
+ const depth = multisigDepth + 1
720
+ if (depth > MultisigConfig.maxNestingDepth)
661
721
  throw new InvalidSerializedError({
662
- reason:
663
- 'Invalid multisig `init`: expected the bootstrap config list or an omitted trailing element',
722
+ reason: `multisig nesting depth exceeds ${MultisigConfig.maxNestingDepth}`,
664
723
  serialized,
665
724
  })
666
- return {
725
+
726
+ // The first field distinguishes the static wire shapes: a bootstrap init
727
+ // config is an RLP list, while an initialized account is a 20-byte string.
728
+ const decoded = Rlp.toHex(data)
729
+ if (!Array.isArray(decoded) || decoded.length !== 2)
730
+ throw new InvalidSerializedError({
731
+ reason: 'invalid multisig wire shape: expected exactly two fields',
732
+ serialized,
733
+ })
734
+
735
+ const [address, signatures] = decoded
736
+ if (!Array.isArray(signatures) || signatures.some(Array.isArray))
737
+ throw new InvalidSerializedError({
738
+ reason: 'invalid multisig signatures list',
739
+ serialized,
740
+ })
741
+ if (signatures.length === 0)
742
+ throw new InvalidSerializedError({
743
+ reason: 'multisig signatures cannot be empty',
744
+ serialized,
745
+ })
746
+ if (signatures.length > MultisigConfig.maxSignatures)
747
+ throw new InvalidSerializedError({
748
+ reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
749
+ serialized,
750
+ })
751
+ for (const signature of signatures)
752
+ if (
753
+ Hex.size(signature as Hex.Hex) > MultisigConfig.maxOwnerSignatureBytes
754
+ )
755
+ throw new InvalidSerializedError({
756
+ reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
757
+ serialized,
758
+ })
759
+
760
+ if (!Array.isArray(address) && !Address.validate(address))
761
+ throw new InvalidSerializedError({
762
+ reason: 'invalid multisig account',
763
+ serialized,
764
+ })
765
+ if (Array.isArray(address)) {
766
+ const [salt, threshold, owners] = address
767
+ if (
768
+ address.length !== 3 ||
769
+ Array.isArray(salt) ||
770
+ Hex.size(salt) !== 32 ||
771
+ Array.isArray(threshold) ||
772
+ Hex.size(threshold) > 1 ||
773
+ !Array.isArray(owners) ||
774
+ owners.some(
775
+ (owner) =>
776
+ !Array.isArray(owner) ||
777
+ owner.length !== 2 ||
778
+ owner.some(Array.isArray) ||
779
+ Hex.size(owner[1] as Hex.Hex) > 1,
780
+ )
781
+ )
782
+ throw new InvalidSerializedError({
783
+ reason: 'invalid multisig init config',
784
+ serialized,
785
+ })
786
+ }
787
+
788
+ const init = Array.isArray(address)
789
+ ? MultisigConfig.fromTuple(address as unknown as MultisigConfig.Tuple)
790
+ : undefined
791
+ const account = init
792
+ ? MultisigConfig.getAddress(init)
793
+ : (address as Address.Address)
794
+ const envelope = {
667
795
  type: 'multisig',
668
796
  account,
669
- signatures: signatures.map((signature) => deserialize(signature)),
670
- ...(init
671
- ? { init: MultisigConfig.fromTuple(init as MultisigConfig.Tuple) }
672
- : {}),
797
+ signatures: signatures.map((signature) =>
798
+ deserialize_(signature as Hex.Hex, depth),
799
+ ),
800
+ ...(init ? { init } : {}),
673
801
  } satisfies Multisig
802
+ assertMultisig(envelope, depth)
803
+ return envelope
674
804
  }
675
805
 
676
806
  throw new InvalidSerializedError({
@@ -1076,19 +1206,28 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
1076
1206
  }
1077
1207
 
1078
1208
  if (
1079
- envelope.type === 'multisig' ||
1080
- ('account' in envelope && 'signatures' in envelope)
1209
+ (envelope as { type?: string | undefined }).type === 'multisig' ||
1210
+ ('signatures' in envelope && ('account' in envelope || 'init' in envelope))
1081
1211
  ) {
1082
1212
  const multisig = envelope as MultisigRpc
1083
- return {
1213
+ const hasAccount = typeof multisig.account !== 'undefined'
1214
+ const hasInit = typeof multisig.init !== 'undefined'
1215
+ if (hasAccount === hasInit)
1216
+ throw new InvalidMultisigApprovalError({
1217
+ reason: 'RPC multisig must contain exactly one of `account` or `init`',
1218
+ })
1219
+ const init = hasInit
1220
+ ? MultisigConfig.from(multisig.init as MultisigConfig.Config)
1221
+ : undefined
1222
+ const account = init ? MultisigConfig.getAddress(init) : multisig.account
1223
+ const result = {
1084
1224
  type: 'multisig',
1085
- account: multisig.account,
1086
- // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
1087
- signatures: multisig.signatures.map((signature) =>
1088
- deserialize(signature),
1089
- ),
1090
- ...(multisig.init ? { init: MultisigConfig.from(multisig.init) } : {}),
1091
- }
1225
+ account: account as Address.Address,
1226
+ signatures: multisig.signatures.map((signature) => fromRpc(signature)),
1227
+ ...(init ? { init } : {}),
1228
+ } satisfies Multisig
1229
+ assert(result)
1230
+ return result
1092
1231
  }
1093
1232
 
1094
1233
  throw new CoercionError({ envelope })
@@ -1096,8 +1235,10 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
1096
1235
 
1097
1236
  export declare namespace fromRpc {
1098
1237
  type ErrorType =
1238
+ | assert.ErrorType
1099
1239
  | CoercionError
1100
1240
  | InvalidSerializedError
1241
+ | MultisigConfig.getAddress.ErrorType
1101
1242
  | Signature.fromRpc.ErrorType
1102
1243
  | Errors.GlobalErrorType
1103
1244
  }
@@ -1173,7 +1314,9 @@ export function getType<
1173
1314
 
1174
1315
  // Detect Multisig signature
1175
1316
  if (
1176
- ('account' in envelope || 'genesisConfig' in envelope) &&
1317
+ ('account' in envelope ||
1318
+ 'genesisConfig' in envelope ||
1319
+ 'init' in envelope) &&
1177
1320
  'signatures' in envelope
1178
1321
  )
1179
1322
  return 'multisig' as never
@@ -1192,6 +1335,7 @@ export function getType<
1192
1335
  * - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
1193
1336
  * - Keychain V1: `0x03` + userAddress (20) + inner signature (recursive)
1194
1337
  * - Keychain V2: `0x04` + userAddress (20) + inner signature (recursive)
1338
+ * - Multisig: `0x05` + RLP `[account | init, signatures]`
1195
1339
  *
1196
1340
  * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
1197
1341
  *
@@ -1277,15 +1421,16 @@ export function serialize(
1277
1421
 
1278
1422
  if (type === 'multisig') {
1279
1423
  const multisig = envelope as Multisig
1280
- // Format: `0x05 || rlp([account, signatures, init?])`, where each owner
1281
- // approval is an encoded signature. `init` is a trailing optional: the
1282
- // bootstrap config list when present, otherwise omitted entirely.
1424
+ assert(multisig)
1425
+ // The first field is either the initialized account or the bootstrap init
1426
+ // config. Each owner approval is an encoded signature.
1283
1427
  return Hex.concat(
1284
1428
  serializedMultisigType,
1285
1429
  Rlp.fromHex([
1286
- multisig.account,
1430
+ multisig.init
1431
+ ? MultisigConfig.toTuple(multisig.init)
1432
+ : multisig.account,
1287
1433
  multisig.signatures.map((signature) => serialize(signature)),
1288
- ...(multisig.init ? [MultisigConfig.toTuple(multisig.init)] : []),
1289
1434
  ]),
1290
1435
  options.magic ? magicBytes : '0x',
1291
1436
  )
@@ -1302,6 +1447,16 @@ export declare namespace serialize {
1302
1447
  */
1303
1448
  magic?: boolean | undefined
1304
1449
  }
1450
+
1451
+ type ErrorType =
1452
+ | assert.ErrorType
1453
+ | CoercionError
1454
+ | Hex.concat.ErrorType
1455
+ | Hex.fromNumber.ErrorType
1456
+ | Hex.fromString.ErrorType
1457
+ | Rlp.fromHex.ErrorType
1458
+ | Signature.toHex.ErrorType
1459
+ | Errors.GlobalErrorType
1305
1460
  }
1306
1461
 
1307
1462
  /**
@@ -1435,7 +1590,9 @@ export declare namespace sortMultisigApprovals {
1435
1590
  * @param envelope - The signature envelope to convert.
1436
1591
  * @returns The RPC signature envelope with hex values.
1437
1592
  */
1438
- export function toRpc(envelope: toRpc.Input): SignatureEnvelopeRpc {
1593
+ export function toRpc<const envelope extends toRpc.Input>(
1594
+ envelope: envelope,
1595
+ ): toRpc.ReturnType<envelope> {
1439
1596
  const type = getType(envelope)
1440
1597
 
1441
1598
  if (type === 'secp256k1') {
@@ -1443,7 +1600,7 @@ export function toRpc(envelope: toRpc.Input): SignatureEnvelopeRpc {
1443
1600
  return {
1444
1601
  ...Signature.toRpc(secp256k1.signature),
1445
1602
  type: 'secp256k1',
1446
- }
1603
+ } as never
1447
1604
  }
1448
1605
 
1449
1606
  if (type === 'p256') {
@@ -1455,7 +1612,7 @@ export function toRpc(envelope: toRpc.Input): SignatureEnvelopeRpc {
1455
1612
  r: p256.signature.r,
1456
1613
  s: p256.signature.s,
1457
1614
  type: 'p256',
1458
- }
1615
+ } as never
1459
1616
  }
1460
1617
 
1461
1618
  if (type === 'webAuthn') {
@@ -1472,7 +1629,7 @@ export function toRpc(envelope: toRpc.Input): SignatureEnvelopeRpc {
1472
1629
  s: webauthn.signature.s,
1473
1630
  type: 'webAuthn',
1474
1631
  webauthnData,
1475
- }
1632
+ } as never
1476
1633
  }
1477
1634
 
1478
1635
  if (type === 'keychain') {
@@ -1483,18 +1640,32 @@ export function toRpc(envelope: toRpc.Input): SignatureEnvelopeRpc {
1483
1640
  signature: toRpc(keychain.inner),
1484
1641
  ...(keychain.keyId ? { keyId: keychain.keyId } : {}),
1485
1642
  ...(keychain.version ? { version: keychain.version } : {}),
1486
- }
1643
+ } as never
1487
1644
  }
1488
1645
 
1489
1646
  if (type === 'multisig') {
1490
1647
  const multisig = envelope as Multisig
1648
+ assert(multisig)
1649
+ const signatures = multisig.signatures.map((signature) => toRpc(signature))
1650
+ if (multisig.init) {
1651
+ const init = {
1652
+ ...multisig.init,
1653
+ salt: multisig.init.salt ?? MultisigConfig.zeroSalt,
1654
+ threshold: Number(multisig.init.threshold),
1655
+ owners: multisig.init.owners.map((owner) => ({
1656
+ ...owner,
1657
+ weight: Number(owner.weight),
1658
+ })),
1659
+ }
1660
+ return {
1661
+ init,
1662
+ signatures,
1663
+ } as never
1664
+ }
1491
1665
  return {
1492
- type: 'multisig',
1493
1666
  account: multisig.account,
1494
- // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
1495
- signatures: multisig.signatures.map((signature) => serialize(signature)),
1496
- ...(multisig.init ? { init: multisig.init } : {}),
1497
- }
1667
+ signatures,
1668
+ } as never
1498
1669
  }
1499
1670
 
1500
1671
  throw new CoercionError({ envelope })
@@ -1504,7 +1675,22 @@ export declare namespace toRpc {
1504
1675
  /** Numberish input accepted by {@link ox#SignatureEnvelope.(toRpc:function)}. */
1505
1676
  type Input = SignatureEnvelope<Hex.Hex | number>
1506
1677
 
1678
+ /** RPC signature envelope inferred from the input type. */
1679
+ type ReturnType<envelope extends Input = Input> =
1680
+ GetType<envelope> extends 'secp256k1'
1681
+ ? Secp256k1Rpc
1682
+ : GetType<envelope> extends 'p256'
1683
+ ? P256Rpc
1684
+ : GetType<envelope> extends 'webAuthn'
1685
+ ? WebAuthnRpc
1686
+ : GetType<envelope> extends 'keychain'
1687
+ ? KeychainRpc
1688
+ : GetType<envelope> extends 'multisig'
1689
+ ? MultisigRpc
1690
+ : SignatureEnvelopeRpc
1691
+
1507
1692
  type ErrorType =
1693
+ | assert.ErrorType
1508
1694
  | CoercionError
1509
1695
  | Signature.toRpc.ErrorType
1510
1696
  | Errors.GlobalErrorType