ox 0.14.44 → 0.14.46

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 (149) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/Frame/package.json +6 -0
  3. package/FrameReceipt/package.json +6 -0
  4. package/FrameSignature/package.json +6 -0
  5. package/TxEnvelopeEip8141/package.json +6 -0
  6. package/_cjs/core/Frame.js +147 -0
  7. package/_cjs/core/Frame.js.map +1 -0
  8. package/_cjs/core/FrameReceipt.js +34 -0
  9. package/_cjs/core/FrameReceipt.js.map +1 -0
  10. package/_cjs/core/FrameSignature.js +203 -0
  11. package/_cjs/core/FrameSignature.js.map +1 -0
  12. package/_cjs/core/Transaction.js +18 -2
  13. package/_cjs/core/Transaction.js.map +1 -1
  14. package/_cjs/core/TransactionReceipt.js +10 -0
  15. package/_cjs/core/TransactionReceipt.js.map +1 -1
  16. package/_cjs/core/TransactionRequest.js +11 -1
  17. package/_cjs/core/TransactionRequest.js.map +1 -1
  18. package/_cjs/core/TxEnvelopeEip8141.js +283 -0
  19. package/_cjs/core/TxEnvelopeEip8141.js.map +1 -0
  20. package/_cjs/core/internal/curves.js +6 -0
  21. package/_cjs/core/internal/curves.js.map +1 -0
  22. package/_cjs/core/internal/quantity.js +10 -0
  23. package/_cjs/core/internal/quantity.js.map +1 -0
  24. package/_cjs/erc4337/UserOperationReceipt.js.map +1 -1
  25. package/_cjs/index.js +6 -2
  26. package/_cjs/index.js.map +1 -1
  27. package/_cjs/tempo/KeyAuthorization.js +43 -10
  28. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  29. package/_cjs/tempo/MultisigConfig.js +33 -11
  30. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  31. package/_cjs/tempo/MultisigOperation.js +18 -94
  32. package/_cjs/tempo/MultisigOperation.js.map +1 -1
  33. package/_cjs/tempo/MultisigSimulation.js +18 -46
  34. package/_cjs/tempo/MultisigSimulation.js.map +1 -1
  35. package/_cjs/tempo/SignatureEnvelope.js +72 -109
  36. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  37. package/_cjs/tempo/TransactionRequest.js +5 -1
  38. package/_cjs/tempo/TransactionRequest.js.map +1 -1
  39. package/_cjs/tempo/index.js.map +1 -1
  40. package/_cjs/version.js +1 -1
  41. package/_esm/core/Frame.js +311 -0
  42. package/_esm/core/Frame.js.map +1 -0
  43. package/_esm/core/FrameReceipt.js +72 -0
  44. package/_esm/core/FrameReceipt.js.map +1 -0
  45. package/_esm/core/FrameSignature.js +422 -0
  46. package/_esm/core/FrameSignature.js.map +1 -0
  47. package/_esm/core/Transaction.js +18 -2
  48. package/_esm/core/Transaction.js.map +1 -1
  49. package/_esm/core/TransactionReceipt.js +10 -0
  50. package/_esm/core/TransactionReceipt.js.map +1 -1
  51. package/_esm/core/TransactionRequest.js +11 -1
  52. package/_esm/core/TransactionRequest.js.map +1 -1
  53. package/_esm/core/TxEnvelopeEip8141.js +490 -0
  54. package/_esm/core/TxEnvelopeEip8141.js.map +1 -0
  55. package/_esm/core/internal/curves.js +19 -0
  56. package/_esm/core/internal/curves.js.map +1 -0
  57. package/_esm/core/internal/quantity.js +14 -0
  58. package/_esm/core/internal/quantity.js.map +1 -0
  59. package/_esm/erc4337/UserOperationReceipt.js.map +1 -1
  60. package/_esm/index.js +73 -0
  61. package/_esm/index.js.map +1 -1
  62. package/_esm/tempo/KeyAuthorization.js +42 -9
  63. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  64. package/_esm/tempo/MultisigConfig.js +41 -22
  65. package/_esm/tempo/MultisigConfig.js.map +1 -1
  66. package/_esm/tempo/MultisigOperation.js +20 -100
  67. package/_esm/tempo/MultisigOperation.js.map +1 -1
  68. package/_esm/tempo/MultisigSimulation.js +18 -46
  69. package/_esm/tempo/MultisigSimulation.js.map +1 -1
  70. package/_esm/tempo/SignatureEnvelope.js +107 -169
  71. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  72. package/_esm/tempo/TransactionRequest.js +5 -1
  73. package/_esm/tempo/TransactionRequest.js.map +1 -1
  74. package/_esm/tempo/index.js +3 -1
  75. package/_esm/tempo/index.js.map +1 -1
  76. package/_esm/version.js +1 -1
  77. package/_types/core/Frame.d.ts +290 -0
  78. package/_types/core/Frame.d.ts.map +1 -0
  79. package/_types/core/FrameReceipt.d.ts +92 -0
  80. package/_types/core/FrameReceipt.d.ts.map +1 -0
  81. package/_types/core/FrameSignature.d.ts +357 -0
  82. package/_types/core/FrameSignature.d.ts.map +1 -0
  83. package/_types/core/Transaction.d.ts +29 -4
  84. package/_types/core/Transaction.d.ts.map +1 -1
  85. package/_types/core/TransactionReceipt.d.ts +15 -6
  86. package/_types/core/TransactionReceipt.d.ts.map +1 -1
  87. package/_types/core/TransactionRequest.d.ts +10 -4
  88. package/_types/core/TransactionRequest.d.ts.map +1 -1
  89. package/_types/core/TxEnvelopeEip8141.d.ts +343 -0
  90. package/_types/core/TxEnvelopeEip8141.d.ts.map +1 -0
  91. package/_types/core/internal/curves.d.ts +19 -0
  92. package/_types/core/internal/curves.d.ts.map +1 -0
  93. package/_types/core/internal/quantity.d.ts +10 -0
  94. package/_types/core/internal/quantity.d.ts.map +1 -0
  95. package/_types/erc4337/UserOperationReceipt.d.ts +1 -1
  96. package/_types/erc4337/UserOperationReceipt.d.ts.map +1 -1
  97. package/_types/index.d.ts +73 -0
  98. package/_types/index.d.ts.map +1 -1
  99. package/_types/tempo/KeyAuthorization.d.ts +48 -38
  100. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  101. package/_types/tempo/MultisigConfig.d.ts +15 -14
  102. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  103. package/_types/tempo/MultisigOperation.d.ts +3 -2
  104. package/_types/tempo/MultisigOperation.d.ts.map +1 -1
  105. package/_types/tempo/MultisigSimulation.d.ts +2 -49
  106. package/_types/tempo/MultisigSimulation.d.ts.map +1 -1
  107. package/_types/tempo/SignatureEnvelope.d.ts +69 -128
  108. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  109. package/_types/tempo/Transaction.d.ts +2 -0
  110. package/_types/tempo/Transaction.d.ts.map +1 -1
  111. package/_types/tempo/TransactionReceipt.d.ts +2 -0
  112. package/_types/tempo/TransactionReceipt.d.ts.map +1 -1
  113. package/_types/tempo/TransactionRequest.d.ts +5 -2
  114. package/_types/tempo/TransactionRequest.d.ts.map +1 -1
  115. package/_types/tempo/index.d.ts +3 -1
  116. package/_types/tempo/index.d.ts.map +1 -1
  117. package/_types/version.d.ts +1 -1
  118. package/core/Frame.ts +443 -0
  119. package/core/FrameReceipt.ts +117 -0
  120. package/core/FrameSignature.ts +606 -0
  121. package/core/Transaction.ts +73 -4
  122. package/core/TransactionReceipt.ts +41 -4
  123. package/core/TransactionRequest.ts +26 -2
  124. package/core/TxEnvelopeEip8141.ts +717 -0
  125. package/core/internal/curves.ts +22 -0
  126. package/core/internal/quantity.ts +16 -0
  127. package/erc4337/UserOperationReceipt.ts +1 -6
  128. package/index.ts +73 -0
  129. package/package.json +21 -1
  130. package/tempo/AuthorizationTempo.test.ts +8 -4
  131. package/tempo/KeyAuthorization.test-d.ts +68 -33
  132. package/tempo/KeyAuthorization.test.ts +89 -8
  133. package/tempo/KeyAuthorization.ts +124 -61
  134. package/tempo/MultisigConfig.test.ts +34 -26
  135. package/tempo/MultisigConfig.ts +54 -24
  136. package/tempo/MultisigOperation.test.ts +79 -550
  137. package/tempo/MultisigOperation.ts +31 -146
  138. package/tempo/MultisigSimulation.test-d.ts +2 -2
  139. package/tempo/MultisigSimulation.test.ts +21 -110
  140. package/tempo/MultisigSimulation.ts +25 -99
  141. package/tempo/SignatureEnvelope.test-d.ts +29 -67
  142. package/tempo/SignatureEnvelope.test.ts +301 -568
  143. package/tempo/SignatureEnvelope.ts +156 -274
  144. package/tempo/TransactionRequest.test-d.ts +6 -5
  145. package/tempo/TransactionRequest.test.ts +17 -80
  146. package/tempo/TransactionRequest.ts +23 -3
  147. package/tempo/index.ts +3 -1
  148. package/tempo/multisig.e2e.test.ts +322 -861
  149. package/version.ts +1 -1
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Curve constants, inlined so that modules needing nothing but an order do not
3
+ * pull a curve implementation into the bundle.
4
+ *
5
+ * These are fixed by the curve specifications and are asserted against
6
+ * `@noble/curves` in `src/core/_test/curves.test.ts`.
7
+ *
8
+ * @internal
9
+ */
10
+
11
+ /**
12
+ * Order of the secp256r1 (P-256) curve.
13
+ *
14
+ * @see https://www.secg.org/sec2-v2.pdf
15
+ * @internal
16
+ */
17
+ export const p256N =
18
+ 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n
19
+
20
+ /** Order of the secp256k1 curve. @internal */
21
+ export const secp256k1N =
22
+ 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n
@@ -0,0 +1,16 @@
1
+ import * as Hex from '../Hex.js'
2
+
3
+ /**
4
+ * Converts a "numberish" value (`Hex.Hex | bigint | number`) to an RPC quantity
5
+ * (`Hex.Hex`). Hex values are passed through unchanged; numbers and bigints are
6
+ * encoded via {@link ox#Hex.(fromNumber:function)}.
7
+ *
8
+ * @internal
9
+ */
10
+ export function fromNumberish(
11
+ value: Hex.Hex | bigint | number,
12
+ options?: Hex.fromNumber.Options,
13
+ ): Hex.Hex {
14
+ if (typeof value === 'string') return value
15
+ return Hex.fromNumber(value, options)
16
+ }
@@ -51,12 +51,7 @@ export type Rpc<
51
51
  entryPointVersion,
52
52
  Hex.Hex,
53
53
  Hex.Hex,
54
- TransactionReceipt.TransactionReceipt<
55
- TransactionReceipt.RpcStatus,
56
- TransactionReceipt.RpcType,
57
- Hex.Hex,
58
- Hex.Hex
59
- >
54
+ TransactionReceipt.Rpc
60
55
  >
61
56
 
62
57
  /**
package/index.ts CHANGED
@@ -1547,6 +1547,60 @@ export * as Fee from './core/Fee.js'
1547
1547
  * @category Execution Spec
1548
1548
  */
1549
1549
  export * as Filter from './core/Filter.js'
1550
+ /**
1551
+ * Utilities for constructing, validating, and encoding [EIP-8141](https://eips.ethereum.org/EIPS/eip-8141) call frames.
1552
+ *
1553
+ * A {@link ox#Frame.Frame} describes a call with an execution mode, approval flags,
1554
+ * and separate execution and state gas limits.
1555
+ *
1556
+ * @example
1557
+ * ### Creating Frames
1558
+ *
1559
+ * Use {@link ox#Frame.(from:function)} to construct a frame with named mode and flags.
1560
+ *
1561
+ * ```ts twoslash
1562
+ * import { Frame } from 'ox'
1563
+ *
1564
+ * const frame = Frame.from({
1565
+ * flags: 'approveExecutionAndPayment',
1566
+ * gas: 50_000n,
1567
+ * mode: 'verify'
1568
+ * })
1569
+ * ```
1570
+ *
1571
+ * @category Transaction Envelopes
1572
+ */
1573
+ export * as Frame from './core/Frame.js'
1574
+ /**
1575
+ * Utilities for converting EIP-8141 frame receipts between RPC and decoded forms.
1576
+ *
1577
+ * Frame receipts include execution gas, state gas, logs, and execution status.
1578
+ *
1579
+ * @category Execution Spec
1580
+ */
1581
+ export * as FrameReceipt from './core/FrameReceipt.js'
1582
+ /**
1583
+ * Utilities for constructing, validating, and encoding [EIP-8141](https://eips.ethereum.org/EIPS/eip-8141) signature entries.
1584
+ *
1585
+ * Supports {@link ox#FrameSignature.Arbitrary}, {@link ox#FrameSignature.Secp256k1},
1586
+ * and {@link ox#FrameSignature.P256} entries. An empty payload selects the canonical
1587
+ * transaction signing hash.
1588
+ *
1589
+ * @example
1590
+ * ### Creating Signature Entries
1591
+ *
1592
+ * Use {@link ox#FrameSignature.(from:function)} to wrap arbitrary witness bytes.
1593
+ *
1594
+ * ```ts twoslash
1595
+ * import { FrameSignature } from 'ox'
1596
+ *
1597
+ * const entry = FrameSignature.from('0xaabb')
1598
+ * // @log: { payload: '0x', scheme: 'arbitrary', signature: '0xaabb' }
1599
+ * ```
1600
+ *
1601
+ * @category Transaction Envelopes
1602
+ */
1603
+ export * as FrameSignature from './core/FrameSignature.js'
1550
1604
  /**
1551
1605
  * Utility functions for hashing (keccak256, sha256, etc).
1552
1606
  *
@@ -3270,6 +3324,25 @@ export * as TxEnvelopeEip4844 from './core/TxEnvelopeEip4844.js'
3270
3324
  * @category Transaction Envelopes
3271
3325
  */
3272
3326
  export * as TxEnvelopeEip7702 from './core/TxEnvelopeEip7702.js'
3327
+ /**
3328
+ * Utilities for constructing, signing, and encoding EIP-8141 frame transactions.
3329
+ *
3330
+ * @example
3331
+ * ### Basic Usage
3332
+ *
3333
+ * ```ts twoslash
3334
+ * import { TxEnvelopeEip8141 } from 'ox'
3335
+ *
3336
+ * const envelope = TxEnvelopeEip8141.from({
3337
+ * chainId: 1,
3338
+ * frames: [{ gas: 50_000n, mode: 'sender' }],
3339
+ * sender: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8'
3340
+ * })
3341
+ * ```
3342
+ *
3343
+ * @category Transaction Envelopes
3344
+ */
3345
+ export * as TxEnvelopeEip8141 from './core/TxEnvelopeEip8141.js'
3273
3346
  /**
3274
3347
  * Utility functions for working
3275
3348
  * with **Legacy Transaction Envelopes**.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ox",
3
3
  "description": "Ethereum Standard Library",
4
- "version": "0.14.44",
4
+ "version": "0.14.46",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -222,6 +222,21 @@
222
222
  "import": "./_esm/core/Filter.js",
223
223
  "default": "./_cjs/core/Filter.js"
224
224
  },
225
+ "./Frame": {
226
+ "types": "./_types/core/Frame.d.ts",
227
+ "import": "./_esm/core/Frame.js",
228
+ "default": "./_cjs/core/Frame.js"
229
+ },
230
+ "./FrameReceipt": {
231
+ "types": "./_types/core/FrameReceipt.d.ts",
232
+ "import": "./_esm/core/FrameReceipt.js",
233
+ "default": "./_cjs/core/FrameReceipt.js"
234
+ },
235
+ "./FrameSignature": {
236
+ "types": "./_types/core/FrameSignature.d.ts",
237
+ "import": "./_esm/core/FrameSignature.js",
238
+ "default": "./_cjs/core/FrameSignature.js"
239
+ },
225
240
  "./Hash": {
226
241
  "types": "./_types/core/Hash.d.ts",
227
242
  "import": "./_esm/core/Hash.js",
@@ -372,6 +387,11 @@
372
387
  "import": "./_esm/core/TxEnvelopeEip7702.js",
373
388
  "default": "./_cjs/core/TxEnvelopeEip7702.js"
374
389
  },
390
+ "./TxEnvelopeEip8141": {
391
+ "types": "./_types/core/TxEnvelopeEip8141.d.ts",
392
+ "import": "./_esm/core/TxEnvelopeEip8141.js",
393
+ "default": "./_cjs/core/TxEnvelopeEip8141.js"
394
+ },
375
395
  "./TxEnvelopeLegacy": {
376
396
  "types": "./_types/core/TxEnvelopeLegacy.d.ts",
377
397
  "import": "./_esm/core/TxEnvelopeLegacy.js",
@@ -326,7 +326,8 @@ describe('fromRpc', () => {
326
326
  type: 'p256',
327
327
  },
328
328
  })
329
- if (typeof result.signature === 'string') throw new Error('unreachable')
329
+ if (typeof result.signature === 'string')
330
+ throw new Error('Expected structured primitive signature')
330
331
  expect(result.signature.type).toBe('p256')
331
332
  expect(result.signature).toHaveProperty('prehash', true)
332
333
  expect(result.signature).toHaveProperty('publicKey')
@@ -349,7 +350,8 @@ describe('fromRpc', () => {
349
350
  '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22222c226f726967696e223a2268747470733a2f2f6578616d706c652e636f6d222c2263726f73734f726967696e223a66616c73657d',
350
351
  },
351
352
  })
352
- if (typeof result.signature === 'string') throw new Error('unreachable')
353
+ if (typeof result.signature === 'string')
354
+ throw new Error('Expected structured primitive signature')
353
355
  expect(result.signature.type).toBe('webAuthn')
354
356
  expect(result.signature).toHaveProperty('metadata')
355
357
  expect(result.signature).toHaveProperty('publicKey')
@@ -951,7 +953,8 @@ describe('toRpc', () => {
951
953
  type: 'p256',
952
954
  },
953
955
  })
954
- if (typeof result.signature === 'string') throw new Error('unreachable')
956
+ if (typeof result.signature === 'string')
957
+ throw new Error('Expected structured primitive signature')
955
958
  expect(result.signature.type).toBe('p256')
956
959
  })
957
960
 
@@ -973,7 +976,8 @@ describe('toRpc', () => {
973
976
  type: 'webAuthn',
974
977
  },
975
978
  })
976
- if (typeof result.signature === 'string') throw new Error('unreachable')
979
+ if (typeof result.signature === 'string')
980
+ throw new Error('Expected structured primitive signature')
977
981
  expect(result.signature.type).toBe('webAuthn')
978
982
  })
979
983
  })
@@ -1,7 +1,7 @@
1
1
  import { expectTypeOf, test } from 'vitest'
2
2
  import * as KeyAuthorization from './KeyAuthorization.js'
3
- import * as MultisigConfig from './MultisigConfig.js'
4
3
  import type * as SignatureEnvelope from './SignatureEnvelope.js'
4
+ import * as TempoAddress from './TempoAddress.js'
5
5
 
6
6
  const authorization = {
7
7
  address: '0x1111111111111111111111111111111111111111',
@@ -20,26 +20,18 @@ const signature = {
20
20
 
21
21
  const multisig = {
22
22
  account: '0x2222222222222222222222222222222222222222',
23
- config: MultisigConfig.from({
23
+ config: {
24
+ salt: `0x${'00'.repeat(32)}`,
25
+ version: 0n,
26
+ threshold: 1,
24
27
  owners: [
25
- {
26
- owner: '0x1111111111111111111111111111111111111111',
27
- weight: 1,
28
- },
28
+ { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
29
29
  ],
30
- threshold: 1,
31
- version: 1n,
32
- }),
30
+ },
33
31
  signatures: [signature],
34
32
  type: 'multisig',
35
33
  } as const satisfies SignatureEnvelope.Multisig
36
34
 
37
- const keychain = {
38
- inner: signature,
39
- type: 'keychain',
40
- userAddress: authorization.address,
41
- } as const satisfies SignatureEnvelope.Keychain
42
-
43
35
  test('accepts primitive signatures', () => {
44
36
  const signed = KeyAuthorization.from(authorization, { signature })
45
37
 
@@ -48,27 +40,56 @@ test('accepts primitive signatures', () => {
48
40
  >()
49
41
  })
50
42
 
51
- test('accepts multisig signatures', () => {
52
- const signed = KeyAuthorization.from(authorization, { signature: multisig })
53
-
54
- expectTypeOf(signed.signature).toMatchTypeOf<
55
- KeyAuthorization.Signed['signature']
56
- >()
57
-
58
- const multisigRpc = '0xf8' as const satisfies SignatureEnvelope.MultisigRpc
59
-
60
- const rpc: KeyAuthorization.Rpc = {
61
- chainId: '0x1',
62
- expiry: null,
63
- keyId: authorization.address,
64
- keyType: authorization.type,
65
- signature: multisigRpc,
66
- }
43
+ test('accepts multisig signatures and account-bound grants', () => {
44
+ const signed = KeyAuthorization.from(
45
+ { ...authorization, account: multisig.account, type: 'multisig' },
46
+ { signature: multisig },
47
+ )
48
+ expectTypeOf(signed).toMatchTypeOf<KeyAuthorization.Signed>()
49
+ expectTypeOf(
50
+ KeyAuthorization.toRpc(signed),
51
+ ).toMatchTypeOf<KeyAuthorization.Rpc>()
52
+ })
67
53
 
68
- expectTypeOf(rpc).toEqualTypeOf<KeyAuthorization.Rpc>()
54
+ test('requires an account for multisig grants', () => {
55
+ type Multisig = Extract<KeyAuthorization.Input, { type: 'multisig' }>
56
+ expectTypeOf<Multisig['account']>().toEqualTypeOf<TempoAddress.Address>()
57
+ expectTypeOf<
58
+ Extract<KeyAuthorization.Signed, { type: 'multisig' }>['account']
59
+ >().toEqualTypeOf<`0x${string}`>()
60
+ expectTypeOf<
61
+ Omit<Extract<KeyAuthorization.Rpc, { keyType: 'multisig' }>, 'account'>
62
+ >().not.toExtend<KeyAuthorization.Rpc>()
63
+ expectTypeOf<{
64
+ address: `0x${string}`
65
+ chainId: bigint
66
+ type: 'multisig'
67
+ }>().not.toExtend<KeyAuthorization.Input>()
68
+ expectTypeOf<{
69
+ account: undefined
70
+ address: `0x${string}`
71
+ chainId: bigint
72
+ type: 'multisig'
73
+ }>().not.toExtend<KeyAuthorization.Input>()
74
+ expectTypeOf<{
75
+ account: null
76
+ chainId: '0x1'
77
+ expiry: null
78
+ keyId: `0x${string}`
79
+ keyType: 'multisig'
80
+ signature: KeyAuthorization.SignatureRpc
81
+ }>().not.toExtend<KeyAuthorization.Rpc>()
82
+ expectTypeOf<
83
+ Extract<KeyAuthorization.Rpc, { keyType: 'multisig' }>['account']
84
+ >().toEqualTypeOf<`0x${string}`>()
69
85
  })
70
86
 
71
87
  test('rejects keychain signatures', () => {
88
+ const keychain = {
89
+ inner: signature,
90
+ type: 'keychain',
91
+ userAddress: authorization.address,
92
+ } as const
72
93
  KeyAuthorization.from(authorization, {
73
94
  // @ts-expect-error Key authorizations do not accept keychain signatures.
74
95
  signature: keychain,
@@ -94,5 +115,19 @@ test('rejects keychain signatures', () => {
94
115
  signature: keychainRpc,
95
116
  }
96
117
 
97
- expectTypeOf(rpc).toEqualTypeOf<KeyAuthorization.Rpc>()
118
+ expectTypeOf(rpc).toMatchTypeOf<KeyAuthorization.Rpc>()
119
+ })
120
+
121
+ test('resolves Tempo address bindings for multisig grants', () => {
122
+ const signed = KeyAuthorization.from(
123
+ {
124
+ account: TempoAddress.format(authorization.address),
125
+ address: TempoAddress.format(authorization.address),
126
+ chainId: 1n,
127
+ type: 'multisig',
128
+ },
129
+ { signature },
130
+ )
131
+ expectTypeOf(signed.account).toMatchTypeOf<`0x${string}`>()
132
+ expectTypeOf(signed.address).toMatchTypeOf<`0x${string}`>()
98
133
  })
@@ -78,7 +78,89 @@ const signature_keychain = {
78
78
  userAddress: address,
79
79
  } as const satisfies SignatureEnvelope.Keychain
80
80
 
81
+ describe('multisig account binding', () => {
82
+ test('resolves Tempo account bindings before encoding', () => {
83
+ const signed = KeyAuthorization.from(
84
+ { account: `tempox${address}`, address, chainId: 1n, type: 'multisig' },
85
+ { signature: signature_secp256k1 },
86
+ )
87
+ expect(signed.account).toMatchInlineSnapshot(
88
+ `"0xBE95c3f554e9Fc85ec51bE69a3D807A0D55BCF2C"`,
89
+ )
90
+ expect(KeyAuthorization.toRpc(signed).account).toMatchInlineSnapshot(
91
+ `"0xBE95c3f554e9Fc85ec51bE69a3D807A0D55BCF2C"`,
92
+ )
93
+ expect(
94
+ KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)).account,
95
+ ).toMatchInlineSnapshot(`"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c"`)
96
+ })
97
+
98
+ test('rejects unbound grants before signing or encoding', () => {
99
+ const authorization = { address, chainId: 1n, type: 'multisig' } as const
100
+ const signed = {
101
+ ...authorization,
102
+ signature: SignatureEnvelope.from(signature_secp256k1),
103
+ }
104
+ for (const encode of [
105
+ // @ts-expect-error Deliberately unbound multisig grant.
106
+ () => KeyAuthorization.from(authorization),
107
+ // @ts-expect-error Deliberately unbound multisig grant.
108
+ () => KeyAuthorization.toTuple(authorization),
109
+ // @ts-expect-error Deliberately unbound multisig grant.
110
+ () => KeyAuthorization.getSignPayload(authorization),
111
+ // @ts-expect-error Deliberately unbound multisig grant.
112
+ () => KeyAuthorization.serialize(authorization),
113
+ // @ts-expect-error Deliberately unbound multisig grant.
114
+ () => KeyAuthorization.toRpc(signed),
115
+ ])
116
+ expect(encode).toThrowErrorMatchingInlineSnapshot(
117
+ `[KeyAuthorization.MissingAccountError: Multisig key grants require a parent account binding.]`,
118
+ )
119
+ })
120
+
121
+ test('rejects unbound RPC and RLP grants', () => {
122
+ const signed = KeyAuthorization.from(
123
+ { account: address, address, chainId: 1n, type: 'multisig' },
124
+ { signature: signature_secp256k1 },
125
+ )
126
+ const rpc = KeyAuthorization.toRpc(signed)
127
+ for (const account of [undefined, null]) {
128
+ // @ts-expect-error Deliberately unbound multisig grant.
129
+ expect(() => KeyAuthorization.fromRpc({ ...rpc, account })).toThrowError(
130
+ KeyAuthorization.MissingAccountError,
131
+ )
132
+ }
133
+ expect(() =>
134
+ KeyAuthorization.fromTuple([['0x01', '0x03', address]]),
135
+ ).toThrowError(KeyAuthorization.MissingAccountError)
136
+ expect(KeyAuthorization.fromRpc(rpc).account).toBe(address)
137
+ expect(
138
+ KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)).account,
139
+ ).toBe(address)
140
+ })
141
+ })
142
+
81
143
  describe('from', () => {
144
+ test('accepts multisig grants and delegates', () => {
145
+ const signed = KeyAuthorization.from(
146
+ {
147
+ address,
148
+ chainId: 1n,
149
+ type: 'multisig',
150
+ account: signature_multisig.account,
151
+ },
152
+ { signature: signature_multisig },
153
+ )
154
+ expect(
155
+ KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)),
156
+ ).toEqual(signed)
157
+ expect(KeyAuthorization.fromRpc(KeyAuthorization.toRpc(signed))).toEqual(
158
+ signed,
159
+ )
160
+ })
161
+ })
162
+
163
+ describe('fromRpc', () => {
82
164
  test('default', () => {
83
165
  const authorization = KeyAuthorization.from({
84
166
  address,
@@ -1154,7 +1236,6 @@ describe('deserialize', () => {
1154
1236
  "account": "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
1155
1237
  "address": "0x1eff47bc3a10a45d4b230b5d10e37751fe6aa718",
1156
1238
  "chainId": 4217n,
1157
- "isAdmin": false,
1158
1239
  "signature": {
1159
1240
  "account": "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
1160
1241
  "config": {
@@ -2589,11 +2670,11 @@ describe('admin keys (TIP-1049)', () => {
2589
2670
  expect((authTuple as unknown as unknown[]).length).toBe(3)
2590
2671
  })
2591
2672
 
2592
- test('fromTuple: drops orphan isAdmin without account', () => {
2673
+ test('fromTuple: preserves isAdmin without account', () => {
2593
2674
  const restored = KeyAuthorization.fromTuple([
2594
2675
  ['0x01', '0x', address, '0x', [], [], '0x', '0x01'],
2595
2676
  ])
2596
- expect(restored.isAdmin).toBeUndefined()
2677
+ expect(restored.isAdmin).toBe(true)
2597
2678
  expect(restored.account).toBeUndefined()
2598
2679
  })
2599
2680
 
@@ -2601,7 +2682,7 @@ describe('admin keys (TIP-1049)', () => {
2601
2682
  const restored = KeyAuthorization.fromTuple([
2602
2683
  ['0x01', '0x', address, '0x', [], [], '0x', '0x', account],
2603
2684
  ])
2604
- expect(restored.isAdmin).toBe(false)
2685
+ expect(restored.isAdmin).toBeUndefined()
2605
2686
  expect(restored.account).toBe(account)
2606
2687
  })
2607
2688
 
@@ -2647,7 +2728,7 @@ describe('admin keys (TIP-1049)', () => {
2647
2728
  })
2648
2729
  const serialized = KeyAuthorization.serialize(authorization)
2649
2730
  const restored = KeyAuthorization.deserialize(serialized)
2650
- expect(restored.isAdmin).toBe(false)
2731
+ expect(restored.isAdmin).toBeUndefined()
2651
2732
  expect(restored.account).toBe(account)
2652
2733
  })
2653
2734
 
@@ -2670,14 +2751,14 @@ describe('admin keys (TIP-1049)', () => {
2670
2751
  expect(restored.account).toBe(account)
2671
2752
  })
2672
2753
 
2673
- test('fromRpc: drops orphan isAdmin without account', () => {
2754
+ test('fromRpc: preserves isAdmin without account', () => {
2674
2755
  const authorization = KeyAuthorization.from(
2675
2756
  { address, chainId: 1n, type: 'secp256k1' },
2676
2757
  { signature: SignatureEnvelope.from(signature_secp256k1) },
2677
2758
  )
2678
2759
  const rpc = KeyAuthorization.toRpc(authorization)
2679
2760
  const restored = KeyAuthorization.fromRpc({ ...rpc, isAdmin: true })
2680
- expect(restored.isAdmin).toBeUndefined()
2761
+ expect(restored.isAdmin).toBe(true)
2681
2762
  expect(restored.account).toBeUndefined()
2682
2763
  })
2683
2764
 
@@ -2688,7 +2769,7 @@ describe('admin keys (TIP-1049)', () => {
2688
2769
  )
2689
2770
  const rpc = KeyAuthorization.toRpc(authorization)
2690
2771
  const restored = KeyAuthorization.fromRpc({ ...rpc, account })
2691
- expect(restored.isAdmin).toBe(false)
2772
+ expect(restored.isAdmin).toBeUndefined()
2692
2773
  expect(restored.account).toBe(account)
2693
2774
  })
2694
2775