ox 0.14.33 → 0.14.34

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 (35) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/tempo/KeyAuthorization.js +6 -6
  3. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  4. package/_cjs/tempo/MultisigConfig.js +4 -4
  5. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  6. package/_cjs/tempo/SignatureEnvelope.js +23 -15
  7. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  8. package/_cjs/version.js +1 -1
  9. package/_esm/tempo/KeyAuthorization.js +7 -13
  10. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  11. package/_esm/tempo/MultisigConfig.js +17 -16
  12. package/_esm/tempo/MultisigConfig.js.map +1 -1
  13. package/_esm/tempo/SignatureEnvelope.js +42 -28
  14. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  15. package/_esm/tempo/index.js +2 -2
  16. package/_esm/version.js +1 -1
  17. package/_types/tempo/KeyAuthorization.d.ts +16 -12
  18. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  19. package/_types/tempo/MultisigConfig.d.ts +20 -17
  20. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  21. package/_types/tempo/SignatureEnvelope.d.ts +26 -18
  22. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  23. package/_types/tempo/index.d.ts +2 -2
  24. package/_types/version.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/tempo/KeyAuthorization.test-d.ts +41 -6
  27. package/tempo/KeyAuthorization.test.ts +106 -60
  28. package/tempo/KeyAuthorization.ts +36 -38
  29. package/tempo/MultisigConfig.test.ts +28 -8
  30. package/tempo/MultisigConfig.ts +28 -19
  31. package/tempo/SignatureEnvelope.test.ts +64 -19
  32. package/tempo/SignatureEnvelope.ts +52 -36
  33. package/tempo/index.ts +2 -2
  34. package/tempo/multisig.e2e.test.ts +402 -145
  35. package/version.ts +1 -1
@@ -1100,7 +1100,7 @@ describe('from', () => {
1100
1100
  })
1101
1101
 
1102
1102
  describe('multisig', () => {
1103
- const genesisConfig = MultisigConfig.from({
1103
+ const initialConfig = MultisigConfig.from({
1104
1104
  threshold: 1,
1105
1105
  owners: [
1106
1106
  {
@@ -1110,29 +1110,29 @@ describe('from', () => {
1110
1110
  ],
1111
1111
  })
1112
1112
 
1113
- test('behavior: derives `account` from `genesisConfig`', () => {
1113
+ test('behavior: derives `account` from `initialConfig`', () => {
1114
1114
  const envelope = SignatureEnvelope.from({
1115
- genesisConfig,
1115
+ initialConfig,
1116
1116
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
1117
1117
  })
1118
1118
 
1119
1119
  expect(envelope).toMatchObject({
1120
1120
  type: 'multisig',
1121
- account: MultisigConfig.getAddress(genesisConfig),
1121
+ account: MultisigConfig.getAddress(initialConfig),
1122
1122
  })
1123
- expect('genesisConfig' in envelope).toBe(false)
1123
+ expect('initialConfig' in envelope).toBe(false)
1124
1124
  expect((envelope as SignatureEnvelope.Multisig).init).toBeUndefined()
1125
1125
  })
1126
1126
 
1127
- test('behavior: `init: true` opts into bootstrap (uses `genesisConfig` as `init`)', () => {
1127
+ test('behavior: `init: true` opts into bootstrap (uses `initialConfig` as `init`)', () => {
1128
1128
  const envelope = SignatureEnvelope.from({
1129
- genesisConfig,
1129
+ initialConfig,
1130
1130
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
1131
1131
  init: true,
1132
1132
  })
1133
1133
 
1134
1134
  expect((envelope as SignatureEnvelope.Multisig).init).toEqual(
1135
- genesisConfig,
1135
+ initialConfig,
1136
1136
  )
1137
1137
  })
1138
1138
 
@@ -1147,7 +1147,7 @@ describe('from', () => {
1147
1147
  ],
1148
1148
  })
1149
1149
  const envelope = SignatureEnvelope.from({
1150
- genesisConfig,
1150
+ initialConfig,
1151
1151
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
1152
1152
  init: otherConfig,
1153
1153
  })
@@ -1156,7 +1156,7 @@ describe('from', () => {
1156
1156
  })
1157
1157
 
1158
1158
  test('behavior: `{ account }` form still works', () => {
1159
- const account = MultisigConfig.getAddress(genesisConfig)
1159
+ const account = MultisigConfig.getAddress(initialConfig)
1160
1160
  const envelope = SignatureEnvelope.from({
1161
1161
  account,
1162
1162
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
@@ -1769,7 +1769,7 @@ describe('serialize', () => {
1769
1769
  })
1770
1770
 
1771
1771
  describe('sortMultisigApprovals', () => {
1772
- // Build owner key pairs first so we can construct a real genesis config
1772
+ // Build owner key pairs first so we can construct a real initial config
1773
1773
  // whose owner set matches the keys that produce the approvals below.
1774
1774
  const ownerKeys = Array.from({ length: 3 }, () => {
1775
1775
  const privateKey = Secp256k1.randomPrivateKey()
@@ -1782,12 +1782,15 @@ describe('sortMultisigApprovals', () => {
1782
1782
  Hex.toBigInt(a.address) < Hex.toBigInt(b.address) ? -1 : 1,
1783
1783
  )
1784
1784
 
1785
- const genesisConfig = MultisigConfig.from({
1785
+ const initialConfig = MultisigConfig.from({
1786
1786
  threshold: 2,
1787
1787
  owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
1788
1788
  })
1789
1789
  const payload = `0x${'42'.repeat(32)}` as const
1790
- const digest = MultisigConfig.getSignPayload({ payload, genesisConfig })
1790
+ const digest = MultisigConfig.getSignPayload({
1791
+ payload,
1792
+ initialConfig,
1793
+ })
1791
1794
 
1792
1795
  const owners = ownerKeys.map((owner) => ({
1793
1796
  address: owner.address,
@@ -1802,7 +1805,7 @@ describe('sortMultisigApprovals', () => {
1802
1805
 
1803
1806
  test('behavior: orders approvals ascending by recovered owner address', () => {
1804
1807
  const ordered = SignatureEnvelope.sortMultisigApprovals({
1805
- genesisConfig,
1808
+ initialConfig,
1806
1809
  payload,
1807
1810
  // Provide approvals in reverse of the canonical order.
1808
1811
  signatures: [...ascending].reverse().map((owner) => owner.signature),
@@ -1814,7 +1817,7 @@ describe('sortMultisigApprovals', () => {
1814
1817
  const signatures = ascending.map((owner) => owner.signature)
1815
1818
  expect(
1816
1819
  SignatureEnvelope.sortMultisigApprovals({
1817
- genesisConfig,
1820
+ initialConfig,
1818
1821
  payload,
1819
1822
  signatures,
1820
1823
  }),
@@ -1823,7 +1826,7 @@ describe('sortMultisigApprovals', () => {
1823
1826
 
1824
1827
  test('behavior: recovered order matches the config owner order', () => {
1825
1828
  const ordered = SignatureEnvelope.sortMultisigApprovals({
1826
- genesisConfig,
1829
+ initialConfig,
1827
1830
  payload,
1828
1831
  signatures: owners.map((owner) => owner.signature),
1829
1832
  })
@@ -1833,12 +1836,12 @@ describe('sortMultisigApprovals', () => {
1833
1836
  expect(recovered).toEqual(ascending.map((owner) => owner.address))
1834
1837
  })
1835
1838
 
1836
- test('behavior: `genesisConfig` and `{ account }` produce identical ordering', () => {
1837
- const account = MultisigConfig.getAddress(genesisConfig)
1839
+ test('behavior: `initialConfig` and `{ account }` produce identical ordering', () => {
1840
+ const account = MultisigConfig.getAddress(initialConfig)
1838
1841
  const signatures = owners.map((owner) => owner.signature)
1839
1842
 
1840
1843
  const fromConfig = SignatureEnvelope.sortMultisigApprovals({
1841
- genesisConfig,
1844
+ initialConfig,
1842
1845
  payload,
1843
1846
  signatures,
1844
1847
  })
@@ -3013,6 +3016,14 @@ describe('multisig', () => {
3013
3016
  ).toThrowErrorMatchingInlineSnapshot(
3014
3017
  `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
3015
3018
  )
3019
+
3020
+ const serialized = Hex.concat(
3021
+ '0x05',
3022
+ Rlp.fromHex([account, [SignatureEnvelope.serialize(oversized)]]),
3023
+ )
3024
+ expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
3025
+ SignatureEnvelope.InvalidSerializedError,
3026
+ )
3016
3027
  })
3017
3028
 
3018
3029
  test('assert: rejects keychain owner approvals', () => {
@@ -3054,6 +3065,40 @@ describe('multisig', () => {
3054
3065
  expect(() => SignatureEnvelope.assert(depth2)).not.toThrowError()
3055
3066
  })
3056
3067
 
3068
+ test('accepts a nested approval above the primitive byte limit', () => {
3069
+ const primitive = SignatureEnvelope.from({
3070
+ ...signature_webauthn,
3071
+ metadata: {
3072
+ ...signature_webauthn.metadata,
3073
+ clientDataJSON: JSON.stringify({ value: 'x'.repeat(1_050) }),
3074
+ },
3075
+ signature: {
3076
+ r: signature_webauthn.signature.r,
3077
+ s: signature_webauthn.signature.s,
3078
+ },
3079
+ })
3080
+ const largeNested = SignatureEnvelope.from({
3081
+ type: 'multisig',
3082
+ account: nestedAccount,
3083
+ signatures: [primitive, primitive],
3084
+ })
3085
+ expect(Hex.size(SignatureEnvelope.serialize(primitive))).toBeLessThan(
3086
+ MultisigConfig.maxOwnerSignatureBytes,
3087
+ )
3088
+ expect(
3089
+ Hex.size(SignatureEnvelope.serialize(largeNested)),
3090
+ ).toBeGreaterThan(MultisigConfig.maxOwnerSignatureBytes)
3091
+
3092
+ const parent = SignatureEnvelope.from({
3093
+ type: 'multisig',
3094
+ account,
3095
+ signatures: [largeNested],
3096
+ })
3097
+ expect(
3098
+ SignatureEnvelope.deserialize(SignatureEnvelope.serialize(parent)),
3099
+ ).toEqual(parent)
3100
+ })
3101
+
3057
3102
  test('assert: rejects nesting deeper than the maximum', () => {
3058
3103
  const depth3 = SignatureEnvelope.from({
3059
3104
  type: 'multisig',
@@ -424,12 +424,13 @@ function assertMultisig(envelope: Multisig, depth: number): void {
424
424
  reason: 'nested multisig owner approvals cannot carry `init`',
425
425
  })
426
426
  assertMultisig(multisig, depth + 1)
427
- } else assert(inner)
428
-
429
- if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
430
- throw new InvalidMultisigApprovalError({
431
- reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
432
- })
427
+ } else {
428
+ assert(inner)
429
+ if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
430
+ throw new InvalidMultisigApprovalError({
431
+ reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
432
+ })
433
+ }
433
434
  }
434
435
  }
435
436
 
@@ -740,7 +741,10 @@ function deserialize_(
740
741
  })
741
742
  for (const signature of signatures)
742
743
  if (
743
- Hex.size(signature as Hex.Hex) > MultisigConfig.maxOwnerSignatureBytes
744
+ Hex.size(signature as Hex.Hex) >
745
+ MultisigConfig.maxOwnerSignatureBytes &&
746
+ Hex.slice(signature as Hex.Hex, 0, 1) !==
747
+ MultisigConfig.signatureTypeByte
744
748
  )
745
749
  throw new InvalidSerializedError({
746
750
  reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
@@ -912,17 +916,17 @@ function deserialize_(
912
916
  * ```
913
917
  *
914
918
  * @example
915
- * ### Multisig (from genesis config)
919
+ * ### Multisig (from initial config)
916
920
  *
917
- * Pass `genesisConfig` to derive `account` automatically. Set `init: true` to
918
- * opt into bootstrap (uses `genesisConfig` as the bootstrap `init`); omit
921
+ * Pass `initialConfig` to derive `account` automatically. Set `init: true` to
922
+ * opt into bootstrap (uses `initialConfig` as the bootstrap `init`); omit
919
923
  * `init` for subsequent (non-bootstrap) transactions.
920
924
  *
921
925
  * ```ts twoslash
922
926
  * import { Secp256k1 } from 'ox'
923
927
  * import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
924
928
  *
925
- * const genesisConfig = MultisigConfig.from({
929
+ * const initialConfig = MultisigConfig.from({
926
930
  * threshold: 1,
927
931
  * owners: [
928
932
  * { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
@@ -936,14 +940,14 @@ function deserialize_(
936
940
  *
937
941
  * // Bootstrap transaction
938
942
  * const bootstrap = SignatureEnvelope.from({
939
- * genesisConfig,
943
+ * initialConfig,
940
944
  * signatures: [signature],
941
945
  * init: true,
942
946
  * })
943
947
  *
944
948
  * // Subsequent (non-bootstrap) transactions
945
949
  * const subsequent = SignatureEnvelope.from({
946
- * genesisConfig,
950
+ * initialConfig,
947
951
  * signatures: [signature],
948
952
  * })
949
953
  * ```
@@ -970,20 +974,20 @@ export function from<const value extends from.Value>(
970
974
 
971
975
  if (type === 'multisig') {
972
976
  const multisig = value as Multisig & {
973
- genesisConfig?: MultisigConfig.Config | undefined
977
+ initialConfig?: MultisigConfig.Config | undefined
974
978
  init?: MultisigConfig.Config | boolean | undefined
975
979
  }
976
- const { genesisConfig, init, ...rest } = multisig
977
- // Derive `account` from `genesisConfig` when not provided explicitly.
980
+ const { initialConfig, init, ...rest } = multisig
981
+ // Derive `account` from `initialConfig` when not provided explicitly.
978
982
  const account = (() => {
979
983
  if (rest.account) return rest.account
980
- if (genesisConfig) return MultisigConfig.getAddress(genesisConfig)
984
+ if (initialConfig) return MultisigConfig.getAddress(initialConfig)
981
985
  return rest.account
982
986
  })()
983
- // `init: true` opts into bootstrap using the supplied `genesisConfig`.
987
+ // `init: true` opts into bootstrap using the supplied `initialConfig`.
984
988
  // Otherwise, `init` is treated as the explicit bootstrap config (or
985
989
  // omitted).
986
- const initSource = init === true ? genesisConfig : init || undefined
990
+ const initSource = init === true ? initialConfig : init || undefined
987
991
  return {
988
992
  ...rest,
989
993
  account,
@@ -1039,13 +1043,13 @@ export declare namespace from {
1039
1043
 
1040
1044
  /**
1041
1045
  * Multisig envelope input variant where `account` is derived from the
1042
- * supplied `genesisConfig`. Pass `init: true` to opt into bootstrap (uses
1043
- * `genesisConfig` as the bootstrap `init`); omit `init` for subsequent
1046
+ * supplied `initialConfig`. Pass `init: true` to opt into bootstrap (uses
1047
+ * `initialConfig` as the bootstrap `init`); omit `init` for subsequent
1044
1048
  * (non-bootstrap) transactions.
1045
1049
  */
1046
- type MultisigFromGenesisConfig = {
1050
+ type MultisigFromInitialConfig = {
1047
1051
  type?: 'multisig' | undefined
1048
- genesisConfig: MultisigConfig.Config
1052
+ initialConfig: MultisigConfig.Config
1049
1053
  signatures: readonly SignatureEnvelope[]
1050
1054
  init?: MultisigConfig.Config | boolean | undefined
1051
1055
  }
@@ -1054,7 +1058,7 @@ export declare namespace from {
1054
1058
  | UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
1055
1059
  | Secp256k1Flat
1056
1060
  | Serialized
1057
- | MultisigFromGenesisConfig
1061
+ | MultisigFromInitialConfig
1058
1062
 
1059
1063
  type ReturnValue<value extends Value> = Compute<
1060
1064
  OneOf<
@@ -1062,7 +1066,7 @@ export declare namespace from {
1062
1066
  ? SignatureEnvelope
1063
1067
  : value extends Secp256k1Flat
1064
1068
  ? Secp256k1
1065
- : value extends MultisigFromGenesisConfig
1069
+ : value extends MultisigFromInitialConfig
1066
1070
  ? Multisig
1067
1071
  : IsNarrowable<value, SignatureEnvelope> extends true
1068
1072
  ? SignatureEnvelope
@@ -1285,7 +1289,7 @@ export function getType<
1285
1289
  // Detect Multisig signature
1286
1290
  if (
1287
1291
  ('account' in envelope ||
1288
- 'genesisConfig' in envelope ||
1292
+ 'initialConfig' in envelope ||
1289
1293
  'init' in envelope) &&
1290
1294
  'signatures' in envelope
1291
1295
  )
@@ -1436,7 +1440,7 @@ export declare namespace serialize {
1436
1440
  * recovered owner address. Works for any owner key type (secp256k1, p256,
1437
1441
  * webAuthn).
1438
1442
  *
1439
- * Config updates never change `account`, so the genesis config is the correct
1443
+ * Config updates never change `account`, so the initial config is the correct
1440
1444
  * input even for post-update transactions.
1441
1445
  *
1442
1446
  * @example
@@ -1444,7 +1448,7 @@ export declare namespace serialize {
1444
1448
  * import { Secp256k1 } from 'ox'
1445
1449
  * import { MultisigConfig, SignatureEnvelope, TxEnvelopeTempo } from 'ox/tempo'
1446
1450
  *
1447
- * const genesisConfig = MultisigConfig.from({
1451
+ * const initialConfig = MultisigConfig.from({
1448
1452
  * threshold: 2,
1449
1453
  * owners: [
1450
1454
  * { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
@@ -1455,14 +1459,20 @@ export declare namespace serialize {
1455
1459
  * const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
1456
1460
  * const payload = TxEnvelopeTempo.getSignPayload(tx)
1457
1461
  *
1458
- * const privateKeys = [Secp256k1.randomPrivateKey(), Secp256k1.randomPrivateKey()]
1459
- * const digest = MultisigConfig.getSignPayload({ payload, genesisConfig })
1462
+ * const privateKeys = [
1463
+ * Secp256k1.randomPrivateKey(),
1464
+ * Secp256k1.randomPrivateKey(),
1465
+ * ]
1466
+ * const digest = MultisigConfig.getSignPayload({
1467
+ * payload,
1468
+ * initialConfig,
1469
+ * })
1460
1470
  * const signatures = privateKeys.map((privateKey) =>
1461
1471
  * SignatureEnvelope.from(Secp256k1.sign({ payload: digest, privateKey })),
1462
1472
  * )
1463
1473
  *
1464
1474
  * const ordered = SignatureEnvelope.sortMultisigApprovals({ // [!code focus]
1465
- * genesisConfig, // [!code focus]
1475
+ * initialConfig, // [!code focus]
1466
1476
  * payload, // [!code focus]
1467
1477
  * signatures, // [!code focus]
1468
1478
  * }) // [!code focus]
@@ -1474,11 +1484,15 @@ export declare namespace serialize {
1474
1484
  export function sortMultisigApprovals(
1475
1485
  value: sortMultisigApprovals.Value,
1476
1486
  ): readonly SignatureEnvelope[] {
1477
- const { payload, signatures } = value
1487
+ const { payload, signatures, version = 0n } = value
1478
1488
  const digest = MultisigConfig.getSignPayload(
1479
- 'genesisConfig' in value && value.genesisConfig
1480
- ? { payload, genesisConfig: value.genesisConfig }
1481
- : { payload, account: (value as { account: Address.Address }).account },
1489
+ 'initialConfig' in value && value.initialConfig
1490
+ ? { payload, initialConfig: value.initialConfig, version }
1491
+ : {
1492
+ payload,
1493
+ account: (value as { account: Address.Address }).account,
1494
+ version,
1495
+ },
1482
1496
  )
1483
1497
  // Recover each signer once (decorate–sort–undecorate) rather than inside the
1484
1498
  // comparator.
@@ -1497,6 +1511,8 @@ export declare namespace sortMultisigApprovals {
1497
1511
  payload: Hex.Hex | Bytes.Bytes
1498
1512
  /** The owner approvals to order. */
1499
1513
  signatures: readonly SignatureEnvelope[]
1514
+ /** Current multisig config version. Defaults to `0n`. */
1515
+ version?: bigint | undefined
1500
1516
  } & OneOf<
1501
1517
  | {
1502
1518
  /** The native multisig account address. */
@@ -1507,7 +1523,7 @@ export declare namespace sortMultisigApprovals {
1507
1523
  * The initial multisig config (the bootstrap config that derived the
1508
1524
  * permanent `account`). Used to derive the account automatically.
1509
1525
  */
1510
- genesisConfig: MultisigConfig.Config
1526
+ initialConfig: MultisigConfig.Config
1511
1527
  }
1512
1528
  >
1513
1529
 
package/tempo/index.ts CHANGED
@@ -136,7 +136,7 @@ export * as KeyAuthorization from './KeyAuthorization.js'
136
136
  * ```ts twoslash
137
137
  * import { MultisigConfig } from 'ox/tempo'
138
138
  *
139
- * const genesisConfig = MultisigConfig.from({
139
+ * const initialConfig = MultisigConfig.from({
140
140
  * threshold: 2,
141
141
  * owners: [
142
142
  * { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
@@ -144,7 +144,7 @@ export * as KeyAuthorization from './KeyAuthorization.js'
144
144
  * ],
145
145
  * })
146
146
  *
147
- * const account = MultisigConfig.getAddress(genesisConfig)
147
+ * const account = MultisigConfig.getAddress(initialConfig)
148
148
  * ```
149
149
  *
150
150
  * @category Reference