ox 1.8.5 → 1.8.7

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 (137) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/tempo/{MultisigConfig.d.ts → AccountConfig.d.ts} +116 -63
  3. package/dist/tempo/AccountConfig.d.ts.map +1 -0
  4. package/dist/tempo/{MultisigConfig.js → AccountConfig.js} +109 -55
  5. package/dist/tempo/AccountConfig.js.map +1 -0
  6. package/dist/tempo/{MultisigOperation.d.ts → AccountOperation.d.ts} +62 -62
  7. package/dist/tempo/AccountOperation.d.ts.map +1 -0
  8. package/dist/tempo/{MultisigOperation.js → AccountOperation.js} +64 -64
  9. package/dist/tempo/AccountOperation.js.map +1 -0
  10. package/dist/tempo/{MultisigSimulation.d.ts → AccountSimulation.d.ts} +21 -21
  11. package/dist/tempo/AccountSimulation.d.ts.map +1 -0
  12. package/dist/tempo/{MultisigSimulation.js → AccountSimulation.js} +20 -20
  13. package/dist/tempo/AccountSimulation.js.map +1 -0
  14. package/dist/tempo/KeyAuthorization.d.ts +37 -27
  15. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  16. package/dist/tempo/KeyAuthorization.js +17 -19
  17. package/dist/tempo/KeyAuthorization.js.map +1 -1
  18. package/dist/tempo/RpcSchemaTempo.d.ts +13 -13
  19. package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
  20. package/dist/tempo/SignatureEnvelope.d.ts +36 -33
  21. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  22. package/dist/tempo/SignatureEnvelope.js +77 -77
  23. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  24. package/dist/tempo/TokenRole.d.ts +4 -2
  25. package/dist/tempo/TokenRole.d.ts.map +1 -1
  26. package/dist/tempo/TokenRole.js +4 -1
  27. package/dist/tempo/TokenRole.js.map +1 -1
  28. package/dist/tempo/TransactionRequest.d.ts +11 -9
  29. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  30. package/dist/tempo/TransactionRequest.js +14 -10
  31. package/dist/tempo/TransactionRequest.js.map +1 -1
  32. package/dist/tempo/index.d.ts +56 -54
  33. package/dist/tempo/index.d.ts.map +1 -1
  34. package/dist/tempo/index.js +56 -54
  35. package/dist/tempo/index.js.map +1 -1
  36. package/dist/zod/tempo/{MultisigConfig.d.ts → AccountConfig.d.ts} +6 -6
  37. package/dist/zod/tempo/AccountConfig.d.ts.map +1 -0
  38. package/dist/zod/tempo/{MultisigConfig.js → AccountConfig.js} +12 -12
  39. package/dist/zod/tempo/AccountConfig.js.map +1 -0
  40. package/dist/zod/tempo/{MultisigOperation.d.ts → AccountOperation.d.ts} +5 -5
  41. package/dist/zod/tempo/AccountOperation.d.ts.map +1 -0
  42. package/dist/zod/tempo/{MultisigOperation.js → AccountOperation.js} +10 -10
  43. package/dist/zod/tempo/AccountOperation.js.map +1 -0
  44. package/dist/zod/tempo/{MultisigSimulation.d.ts → AccountSimulation.d.ts} +5 -5
  45. package/dist/zod/tempo/AccountSimulation.d.ts.map +1 -0
  46. package/dist/zod/tempo/AccountSimulation.js +44 -0
  47. package/dist/zod/tempo/AccountSimulation.js.map +1 -0
  48. package/dist/zod/tempo/AuthorizationTempo.d.ts +10 -10
  49. package/dist/zod/tempo/KeyAuthorization.d.ts +12 -12
  50. package/dist/zod/tempo/KeyAuthorization.js +6 -6
  51. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  52. package/dist/zod/tempo/RpcSchemaTempo.d.ts +59 -59
  53. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  54. package/dist/zod/tempo/RpcSchemaTempo.js +28 -28
  55. package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
  56. package/dist/zod/tempo/SignatureEnvelope.d.ts +7 -7
  57. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  58. package/dist/zod/tempo/SignatureEnvelope.js +22 -16
  59. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  60. package/dist/zod/tempo/TokenRole.d.ts +1 -1
  61. package/dist/zod/tempo/TokenRole.d.ts.map +1 -1
  62. package/dist/zod/tempo/Transaction.d.ts +24 -24
  63. package/dist/zod/tempo/TransactionRequest.d.ts +84 -84
  64. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  65. package/dist/zod/tempo/TransactionRequest.js +26 -17
  66. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  67. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +12 -12
  68. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +3 -3
  69. package/dist/zod/tempo/z.d.ts +3 -3
  70. package/dist/zod/tempo/z.d.ts.map +1 -1
  71. package/dist/zod/tempo/z.js +3 -3
  72. package/dist/zod/tempo/z.js.map +1 -1
  73. package/package.json +31 -31
  74. package/src/tempo/{MultisigConfig.test-d.ts → AccountConfig.test-d.ts} +22 -12
  75. package/src/tempo/{MultisigConfig.test.ts → AccountConfig.test.ts} +155 -37
  76. package/src/tempo/{MultisigConfig.ts → AccountConfig.ts} +138 -66
  77. package/src/tempo/AccountOperation.test-d.ts +79 -0
  78. package/src/tempo/{MultisigOperation.test.ts → AccountOperation.test.ts} +97 -97
  79. package/src/tempo/{MultisigOperation.ts → AccountOperation.ts} +95 -97
  80. package/src/tempo/{MultisigSimulation.test-d.ts → AccountSimulation.test-d.ts} +7 -9
  81. package/src/tempo/{MultisigSimulation.test.ts → AccountSimulation.test.ts} +28 -28
  82. package/src/tempo/{MultisigSimulation.ts → AccountSimulation.ts} +28 -30
  83. package/src/tempo/KeyAuthorization.test-d.ts +15 -6
  84. package/src/tempo/KeyAuthorization.test.ts +117 -37
  85. package/src/tempo/KeyAuthorization.ts +68 -59
  86. package/src/tempo/RpcSchemaTempo.test-d.ts +19 -19
  87. package/src/tempo/RpcSchemaTempo.ts +13 -13
  88. package/src/tempo/SignatureEnvelope.test-d.ts +7 -7
  89. package/src/tempo/SignatureEnvelope.test.ts +34 -34
  90. package/src/tempo/SignatureEnvelope.ts +112 -108
  91. package/src/tempo/TokenRole.ts +4 -1
  92. package/src/tempo/Transaction.test.ts +4 -4
  93. package/src/tempo/TransactionRequest.test-d.ts +17 -6
  94. package/src/tempo/TransactionRequest.test.ts +32 -17
  95. package/src/tempo/TransactionRequest.ts +27 -19
  96. package/src/tempo/{multisig.e2e.test.ts → configurableAccounts.e2e.test.ts} +16 -16
  97. package/src/tempo/e2e.test.ts +32 -20
  98. package/src/tempo/index.ts +56 -54
  99. package/src/version.ts +1 -1
  100. package/src/zod/tempo/{MultisigConfig.ts → AccountConfig.ts} +12 -12
  101. package/src/zod/tempo/{MultisigOperation.ts → AccountOperation.ts} +9 -9
  102. package/src/zod/tempo/AccountSimulation.ts +53 -0
  103. package/src/zod/tempo/KeyAuthorization.ts +8 -8
  104. package/src/zod/tempo/RpcSchemaTempo.ts +28 -28
  105. package/src/zod/tempo/SignatureEnvelope.ts +23 -17
  106. package/src/zod/tempo/TransactionRequest.ts +29 -18
  107. package/src/zod/tempo/_test/AccountConfig.test-d.ts +13 -0
  108. package/src/zod/tempo/_test/{MultisigConfig.test.ts → AccountConfig.test.ts} +7 -7
  109. package/src/zod/tempo/_test/AccountOperation.test-d.ts +27 -0
  110. package/src/zod/tempo/_test/{MultisigOperation.test.ts → AccountOperation.test.ts} +9 -9
  111. package/src/zod/tempo/_test/AccountSimulation.test-d.ts +13 -0
  112. package/src/zod/tempo/_test/{MultisigSimulation.test.ts → AccountSimulation.test.ts} +13 -13
  113. package/src/zod/tempo/_test/KeyAuthorization.test.ts +9 -9
  114. package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +27 -27
  115. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +16 -18
  116. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +5 -5
  117. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +12 -9
  118. package/src/zod/tempo/_test/TransactionRequest.test.ts +28 -8
  119. package/src/zod/tempo/z.ts +3 -3
  120. package/dist/tempo/MultisigConfig.d.ts.map +0 -1
  121. package/dist/tempo/MultisigConfig.js.map +0 -1
  122. package/dist/tempo/MultisigOperation.d.ts.map +0 -1
  123. package/dist/tempo/MultisigOperation.js.map +0 -1
  124. package/dist/tempo/MultisigSimulation.d.ts.map +0 -1
  125. package/dist/tempo/MultisigSimulation.js.map +0 -1
  126. package/dist/zod/tempo/MultisigConfig.d.ts.map +0 -1
  127. package/dist/zod/tempo/MultisigConfig.js.map +0 -1
  128. package/dist/zod/tempo/MultisigOperation.d.ts.map +0 -1
  129. package/dist/zod/tempo/MultisigOperation.js.map +0 -1
  130. package/dist/zod/tempo/MultisigSimulation.d.ts.map +0 -1
  131. package/dist/zod/tempo/MultisigSimulation.js +0 -44
  132. package/dist/zod/tempo/MultisigSimulation.js.map +0 -1
  133. package/src/tempo/MultisigOperation.test-d.ts +0 -79
  134. package/src/zod/tempo/MultisigSimulation.ts +0 -53
  135. package/src/zod/tempo/_test/MultisigConfig.test-d.ts +0 -13
  136. package/src/zod/tempo/_test/MultisigOperation.test-d.ts +0 -27
  137. package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +0 -13
@@ -8,8 +8,8 @@ import {
8
8
  WebAuthnP256,
9
9
  } from 'ox'
10
10
  import { describe, expect, test } from 'vp/test'
11
+ import * as AccountConfig from './AccountConfig.js'
11
12
  import * as KeyAuthorization from './KeyAuthorization.js'
12
- import * as MultisigConfig from './MultisigConfig.js'
13
13
  import * as Period from './Period.js'
14
14
  import * as SignatureEnvelope from './SignatureEnvelope.js'
15
15
 
@@ -17,6 +17,23 @@ const address = '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c'
17
17
  const expiry = 1234567890
18
18
  const token = '0x20c0000000000000000000000000000000000001'
19
19
 
20
+ // Unsigned RLP and hashes produced by the Tempo node's `KeyAuthorization::encode`
21
+ // (`tempo-primitives`) for `address` on chain 1337.
22
+ const limitsVectors = {
23
+ denyAll: {
24
+ hash: '0x647445af52e13830d879f11656840959bf7cffd26c5858b6e79696ef051e1115',
25
+ rlp: '0xdb8205398094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c80c0',
26
+ },
27
+ scopedDenyAll: {
28
+ hash: '0xed04735d65180756db1cac8134ee53ed77737803902572beff10d5da03eab2cf',
29
+ rlp: '0xf38205398094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c80c0d7d69420c0000000000000000000000000000000000001c0',
30
+ },
31
+ scopedUnlimited: {
32
+ hash: '0x4c30b56b178f44172feee4a80a27222101e19b72de396be4d764e2f4054f32d7',
33
+ rlp: '0xf38205398094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c8080d7d69420c0000000000000000000000000000000000001c0',
34
+ },
35
+ } as const
36
+
20
37
  const privateKey_secp256k1 =
21
38
  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
22
39
  const signature_secp256k1 = Secp256k1.sign({
@@ -54,9 +71,9 @@ const signature_webauthn = SignatureEnvelope.from({
54
71
  },
55
72
  })
56
73
 
57
- const signature_multisig = {
74
+ const signature_configurable = {
58
75
  account: address,
59
- config: MultisigConfig.from({
76
+ config: AccountConfig.from({
60
77
  owners: [
61
78
  {
62
79
  owner: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
@@ -67,9 +84,9 @@ const signature_multisig = {
67
84
  version: 1n,
68
85
  }),
69
86
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
70
- type: 'multisig',
71
- } as const satisfies SignatureEnvelope.Multisig
72
- const signature_multisig_rpc =
87
+ type: 'configurable',
88
+ } as const satisfies SignatureEnvelope.Configurable
89
+ const signature_configurable_rpc =
73
90
  '0xf89794be95c3f554e9fc85ec51be69a3d807a0d55bcf2cf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d694f39fd6e51aad88f6f4ce6ab8827279cfffb9226601f843b841fa78c5905fb0b9d6066ef531f962a62bc6ef0d5eb59ecb134056d206f75aaed7780926ff2601a935c2c79707d9e1799948c9f19dcdde1e090e903b19a07923d01c' as const
74
91
 
75
92
  const signature_keychain = {
@@ -78,9 +95,13 @@ const signature_keychain = {
78
95
  userAddress: address,
79
96
  } as const satisfies SignatureEnvelope.Keychain
80
97
 
81
- describe('multisig account binding', () => {
98
+ describe('configurable account binding', () => {
82
99
  test('rejects unbound grants before signing or encoding', () => {
83
- const authorization = { address, chainId: 1n, type: 'multisig' } as const
100
+ const authorization = {
101
+ address,
102
+ chainId: 1n,
103
+ type: 'configurable',
104
+ } as const
84
105
  const signed = {
85
106
  ...authorization,
86
107
  signature: SignatureEnvelope.from(signature_secp256k1),
@@ -93,13 +114,13 @@ describe('multisig account binding', () => {
93
114
  () => KeyAuthorization.toRpc(signed),
94
115
  ])
95
116
  expect(encode).toThrowErrorMatchingInlineSnapshot(
96
- `[KeyAuthorization.MissingAccountError: Multisig key grants require a parent account binding.]`,
117
+ `[KeyAuthorization.MissingAccountError: Configurable account key grants require a parent account binding.]`,
97
118
  )
98
119
  })
99
120
 
100
121
  test('rejects unbound RPC and RLP grants', () => {
101
122
  const signed = KeyAuthorization.from(
102
- { account: address, address, chainId: 1n, type: 'multisig' },
123
+ { account: address, address, chainId: 1n, type: 'configurable' },
103
124
  { signature: signature_secp256k1 },
104
125
  )
105
126
  const rpc = KeyAuthorization.toRpc(signed)
@@ -118,15 +139,36 @@ describe('multisig account binding', () => {
118
139
  })
119
140
 
120
141
  describe('from', () => {
121
- test('accepts multisig grants and delegates', () => {
142
+ test('behavior: limits encode like the Tempo node', () => {
143
+ const encode = (authorization: KeyAuthorization.KeyAuthorization) => ({
144
+ hash: KeyAuthorization.hash(authorization),
145
+ rlp: Rlp.fromHex(KeyAuthorization.toTuple(authorization)[0]),
146
+ })
147
+ const base = { address, chainId: 1337n, type: 'secp256k1' } as const
148
+ expect({
149
+ denyAll: encode(KeyAuthorization.from({ ...base, limits: [] })),
150
+ scopedDenyAll: encode(
151
+ KeyAuthorization.from({
152
+ ...base,
153
+ limits: [],
154
+ scopes: [{ address: token }],
155
+ }),
156
+ ),
157
+ scopedUnlimited: encode(
158
+ KeyAuthorization.from({ ...base, scopes: [{ address: token }] }),
159
+ ),
160
+ }).toEqual(limitsVectors)
161
+ })
162
+
163
+ test('accepts configurable grants and delegates', () => {
122
164
  const signed = KeyAuthorization.from(
123
165
  {
124
166
  address,
125
167
  chainId: 1n,
126
- type: 'multisig',
127
- account: signature_multisig.account,
168
+ type: 'configurable',
169
+ account: signature_configurable.account,
128
170
  },
129
- { signature: signature_multisig },
171
+ { signature: signature_configurable },
130
172
  )
131
173
  expect(
132
174
  KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)),
@@ -501,7 +543,7 @@ describe('fromRpc', () => {
501
543
  `)
502
544
  })
503
545
 
504
- test('with signature (multisig)', () => {
546
+ test('with signature (configurable)', () => {
505
547
  const authorization = KeyAuthorization.from({
506
548
  address,
507
549
  chainId: 1n,
@@ -509,20 +551,20 @@ describe('fromRpc', () => {
509
551
  })
510
552
 
511
553
  const signed = KeyAuthorization.from(authorization, {
512
- signature: signature_multisig,
554
+ signature: signature_configurable,
513
555
  })
514
- expect(signed.signature).toEqual(signature_multisig)
556
+ expect(signed.signature).toEqual(signature_configurable)
515
557
  expect(
516
558
  KeyAuthorization.from(authorization, {
517
- signature: SignatureEnvelope.serialize(signature_multisig),
559
+ signature: SignatureEnvelope.serialize(signature_configurable),
518
560
  }).signature,
519
- ).toEqual(signature_multisig)
561
+ ).toEqual(signature_configurable)
520
562
  expect(
521
563
  KeyAuthorization.from({
522
564
  ...authorization,
523
- signature: signature_multisig,
565
+ signature: signature_configurable,
524
566
  }).signature,
525
- ).toEqual(signature_multisig)
567
+ ).toEqual(signature_configurable)
526
568
  expect(
527
569
  KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)),
528
570
  ).toEqual(signed)
@@ -540,7 +582,7 @@ describe('fromRpc', () => {
540
582
  signature: signature_keychain as never,
541
583
  }),
542
584
  ).toThrowErrorMatchingInlineSnapshot(
543
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
585
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`configurable\`.]`,
544
586
  )
545
587
 
546
588
  expect(() =>
@@ -548,7 +590,7 @@ describe('fromRpc', () => {
548
590
  signature: SignatureEnvelope.serialize(signature_keychain),
549
591
  }),
550
592
  ).toThrowErrorMatchingInlineSnapshot(
551
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
593
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`configurable\`.]`,
552
594
  )
553
595
 
554
596
  expect(() =>
@@ -557,19 +599,19 @@ describe('fromRpc', () => {
557
599
  signature: signature_keychain,
558
600
  } as never),
559
601
  ).toThrowErrorMatchingInlineSnapshot(
560
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
602
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`configurable\`.]`,
561
603
  )
562
604
  })
563
605
  })
564
606
 
565
607
  describe('fromRpc', () => {
566
- test('behavior: multisig', () => {
608
+ test('behavior: configurable', () => {
567
609
  const authorization = KeyAuthorization.fromRpc({
568
610
  chainId: '0x1',
569
611
  expiry: null,
570
612
  keyId: address,
571
613
  keyType: 'secp256k1',
572
- signature: signature_multisig_rpc,
614
+ signature: signature_configurable_rpc,
573
615
  })
574
616
 
575
617
  expect(authorization.signature).toMatchInlineSnapshot(`
@@ -596,7 +638,7 @@ describe('fromRpc', () => {
596
638
  "type": "secp256k1",
597
639
  },
598
640
  ],
599
- "type": "multisig",
641
+ "type": "configurable",
600
642
  }
601
643
  `)
602
644
  })
@@ -611,7 +653,7 @@ describe('fromRpc', () => {
611
653
  signature: SignatureEnvelope.toRpc(signature_keychain),
612
654
  } as never),
613
655
  ).toThrowErrorMatchingInlineSnapshot(
614
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
656
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`configurable\`.]`,
615
657
  )
616
658
  })
617
659
 
@@ -832,6 +874,22 @@ describe('fromRpc', () => {
832
874
  })
833
875
 
834
876
  describe('fromTuple', () => {
877
+ test('behavior: keeps absent and empty limits distinct', () => {
878
+ const decoded = Object.fromEntries(
879
+ Object.entries(limitsVectors).map(([name, { rlp }]) => [
880
+ name,
881
+ KeyAuthorization.fromTuple([Rlp.toHex(rlp) as never]).limits,
882
+ ]),
883
+ )
884
+ expect(decoded).toMatchInlineSnapshot(`
885
+ {
886
+ "denyAll": [],
887
+ "scopedDenyAll": [],
888
+ "scopedUnlimited": undefined,
889
+ }
890
+ `)
891
+ })
892
+
835
893
  test('default', () => {
836
894
  const authorization = KeyAuthorization.fromTuple([
837
895
  [
@@ -1169,7 +1227,7 @@ describe('getSignPayload', () => {
1169
1227
  })
1170
1228
 
1171
1229
  describe('deserialize', () => {
1172
- test('example: matches the frozen multisig witness vector', () => {
1230
+ test('example: matches the frozen configurable witness vector', () => {
1173
1231
  const serialized =
1174
1232
  '0xf8f1f85382107980941eff47bc3a10a45d4b230b5d10e37751fe6aa718808080a053535353535353535353535353535353535353535353535353535353535353538094c4a590afa7337e5cd5eb3aa60cacf91c5400044bb89a05f89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b8412cfed9350faf80e4115e3c4c967356b80a8e6ee42e4a49ab953c8c8a3537d8d8359bf035a9bea18f32b0da7129cdce3ce701abc09ad8919b93da21a0d57e21451b' as const
1175
1233
  const authorization = KeyAuthorization.deserialize(serialized)
@@ -1202,7 +1260,7 @@ describe('deserialize', () => {
1202
1260
  "type": "secp256k1",
1203
1261
  },
1204
1262
  ],
1205
- "type": "multisig",
1263
+ "type": "configurable",
1206
1264
  },
1207
1265
  "type": "secp256k1",
1208
1266
  "witness": "0x5353535353535353535353535353535353535353535353535353535353535353",
@@ -1211,14 +1269,14 @@ describe('deserialize', () => {
1211
1269
  expect(KeyAuthorization.serialize(authorization)).toBe(serialized)
1212
1270
  })
1213
1271
 
1214
- test('multisig', () => {
1272
+ test('configurable', () => {
1215
1273
  const serialized = Rlp.fromHex([
1216
1274
  ['0x1', '0x', address],
1217
- SignatureEnvelope.serialize(signature_multisig),
1275
+ SignatureEnvelope.serialize(signature_configurable),
1218
1276
  ])
1219
1277
 
1220
1278
  expect(KeyAuthorization.deserialize(serialized).signature).toEqual(
1221
- signature_multisig,
1279
+ signature_configurable,
1222
1280
  )
1223
1281
  })
1224
1282
 
@@ -1392,16 +1450,28 @@ describe('serialize', () => {
1392
1450
  })
1393
1451
 
1394
1452
  describe('toRpc', () => {
1395
- test('behavior: multisig', () => {
1453
+ test('behavior: configurable key type uses the RPC multisig key type', () => {
1454
+ const authorization = KeyAuthorization.from(
1455
+ { account: address, address, chainId: 1n, type: 'configurable' },
1456
+ { signature: signature_secp256k1 },
1457
+ )
1458
+ const rpc = KeyAuthorization.toRpc(authorization)
1459
+ expect(rpc.keyType).toMatchInlineSnapshot(`"multisig"`)
1460
+ expect(KeyAuthorization.fromRpc(rpc).type).toMatchInlineSnapshot(
1461
+ `"configurable"`,
1462
+ )
1463
+ })
1464
+
1465
+ test('behavior: configurable', () => {
1396
1466
  const authorization = KeyAuthorization.toRpc({
1397
1467
  address,
1398
1468
  chainId: 1n,
1399
- signature: signature_multisig,
1469
+ signature: signature_configurable,
1400
1470
  type: 'secp256k1',
1401
1471
  })
1402
1472
 
1403
1473
  expect(authorization.signature).toMatchInlineSnapshot(
1404
- `"${signature_multisig_rpc}"`,
1474
+ `"${signature_configurable_rpc}"`,
1405
1475
  )
1406
1476
  })
1407
1477
 
@@ -1662,6 +1732,16 @@ describe('toRpc', () => {
1662
1732
  })
1663
1733
 
1664
1734
  describe('toTuple', () => {
1735
+ test('behavior: round-trips Tempo node limits encoding', () => {
1736
+ for (const { hash, rlp } of Object.values(limitsVectors)) {
1737
+ const authorization = KeyAuthorization.fromTuple([
1738
+ Rlp.toHex(rlp) as never,
1739
+ ])
1740
+ expect(Rlp.fromHex(KeyAuthorization.toTuple(authorization)[0])).toBe(rlp)
1741
+ expect(KeyAuthorization.hash(authorization)).toBe(hash)
1742
+ }
1743
+ })
1744
+
1665
1745
  test('default', () => {
1666
1746
  const authorization = KeyAuthorization.from({
1667
1747
  address,
@@ -2074,7 +2154,7 @@ describe('toTuple', () => {
2074
2154
  "0x",
2075
2155
  "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
2076
2156
  "0x",
2077
- [],
2157
+ "0x",
2078
2158
  [
2079
2159
  [
2080
2160
  "0x1234567890123456789012345678901234567890",
@@ -29,7 +29,7 @@ import * as SignatureEnvelope from './SignatureEnvelope.js'
29
29
  * - `chainId`: Chain ID for replay protection (0 = valid on any chain)
30
30
  * - `expiry`: Unix timestamp when the key expires (undefined = never expires)
31
31
  * - `limits`: Per-TIP-20 token spending limits (only applies to `transfer()` and `approve()` calls)
32
- * - `type`: Key type (`secp256k1`, `p256`, `webAuthn`, or `multisig`)
32
+ * - `type`: Key type (`secp256k1`, `p256`, `webAuthn`, or `configurable`)
33
33
  *
34
34
  * [Access Keys Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#access-keys)
35
35
  */
@@ -38,7 +38,7 @@ export type KeyAuthorization<
38
38
  bigintType = bigint,
39
39
  numberType = number,
40
40
  > = {
41
- /** Account address this authorization is bound to. Required for multisig grants. */
41
+ /** Account address this authorization is bound to. Required for configurable account grants. */
42
42
  account?: Address.Address | undefined
43
43
  /** Address derived from the public key of the key type. */
44
44
  address: Address.Address
@@ -48,7 +48,13 @@ export type KeyAuthorization<
48
48
  expiry?: numberType | null | undefined
49
49
  /** Whether this authorization provisions an admin access key. */
50
50
  isAdmin?: boolean | undefined
51
- /** TIP20 spending limits for this key. */
51
+ /**
52
+ * TIP20 spending limits for this key.
53
+ *
54
+ * - `undefined` = unlimited spending
55
+ * - `[]` = no spending allowed
56
+ * - `[...]` = only listed token limits allowed
57
+ */
52
58
  limits?: readonly TokenLimit<bigintType, numberType>[] | undefined
53
59
  /**
54
60
  * Call scopes restricting which contracts/selectors this key can call.
@@ -58,8 +64,8 @@ export type KeyAuthorization<
58
64
  * - `[...]` = only listed contract+selector combinations allowed
59
65
  */
60
66
  scopes?: readonly Scope[] | undefined
61
- /** Key type (secp256k1, P256, WebAuthn, or multisig). */
62
- type: SignatureEnvelope.Type | 'multisig'
67
+ /** Key type (secp256k1, P256, WebAuthn, or configurable account). */
68
+ type: SignatureEnvelope.Type | 'configurable'
63
69
  /**
64
70
  * Optional 32-byte witness bound into the signing hash.
65
71
  *
@@ -74,23 +80,23 @@ export type KeyAuthorization<
74
80
  ? {
75
81
  signature:
76
82
  | SignatureEnvelope.Primitive<numberType>
77
- | SignatureEnvelope.Multisig<numberType>
83
+ | SignatureEnvelope.Configurable<numberType>
78
84
  }
79
85
  : {
80
86
  signature?:
81
87
  | SignatureEnvelope.Primitive<numberType>
82
- | SignatureEnvelope.Multisig<numberType>
88
+ | SignatureEnvelope.Configurable<numberType>
83
89
  | undefined
84
90
  })
85
91
 
86
92
  /** Signature that can authorize an access key. */
87
93
  export type Signature<numberType = number> =
88
94
  | SignatureEnvelope.Primitive<numberType>
89
- | SignatureEnvelope.Multisig<numberType>
95
+ | SignatureEnvelope.Configurable<numberType>
90
96
 
91
97
  /** RPC-formatted signature that can authorize an access key. */
92
98
  export type SignatureRpc =
93
- | SignatureEnvelope.MultisigRpc
99
+ | SignatureEnvelope.ConfigurableRpc
94
100
  | SignatureEnvelope.PrimitiveRpc
95
101
 
96
102
  /** Input type for a Key Authorization. */
@@ -110,12 +116,12 @@ export type Rpc = {
110
116
  isAdmin?: boolean | null | undefined
111
117
  /** Key identifier. */
112
118
  keyId: Address.Address
113
- /** Key type. */
119
+ /** Key type. Configurable accounts use the node's `multisig` key type. */
114
120
  keyType: SignatureEnvelope.Type | 'multisig'
115
- /** Token spending limits. */
121
+ /** Token spending limits (`null`/`undefined` = unlimited, `[]` = no spending allowed). */
116
122
  limits?: readonly RpcTokenLimit[] | null | undefined
117
- /** Primitive or multisig signature authorizing this key. */
118
- signature: SignatureEnvelope.PrimitiveRpc | SignatureEnvelope.MultisigRpc
123
+ /** Primitive or configurable account signature authorizing this key. */
124
+ signature: SignatureEnvelope.PrimitiveRpc | SignatureEnvelope.ConfigurableRpc
119
125
  /** Optional 32-byte witness (hex). */
120
126
  witness?: Hex.Hex | null | undefined
121
127
  }
@@ -147,9 +153,9 @@ export type Signed<bigintType = bigint, numberType = number> = KeyAuthorization<
147
153
  >
148
154
 
149
155
  type SignatureValue =
150
- | SignatureEnvelope.from.MultisigFromConfig
156
+ | SignatureEnvelope.from.ConfigurableFromConfig
151
157
  | UnionPartialBy<SignatureEnvelope.Primitive, 'prehash' | 'type'>
152
- | PartialBy<SignatureEnvelope.Multisig, 'type'>
158
+ | PartialBy<SignatureEnvelope.Configurable, 'type'>
153
159
  | SignatureEnvelope.Secp256k1Flat
154
160
  | SignatureEnvelope.Serialized
155
161
 
@@ -173,36 +179,42 @@ type CallScopeTuple = readonly [
173
179
  selectorRules: readonly SelectorRuleTuple[],
174
180
  ]
175
181
 
182
+ /** Spending limits slot; `'0x'` (RLP null) when skipped before a later field. */
183
+ type LimitsTuple = readonly TokenLimitTuple[] | '0x'
184
+
185
+ /** Call scopes slot; `'0x'` (RLP null) when skipped before a later field. */
186
+ type CallsTuple = readonly CallScopeTuple[] | '0x'
187
+
176
188
  type AuthorizationTuple =
177
189
  | BaseTuple
178
190
  | readonly [...BaseTuple, expiry: Hex.Hex]
179
- | readonly [...BaseTuple, expiry: Hex.Hex, limits: readonly TokenLimitTuple[]]
191
+ | readonly [...BaseTuple, expiry: Hex.Hex, limits: LimitsTuple]
180
192
  | readonly [
181
193
  ...BaseTuple,
182
194
  expiry: Hex.Hex,
183
- limits: readonly TokenLimitTuple[],
184
- calls: readonly CallScopeTuple[],
195
+ limits: LimitsTuple,
196
+ calls: CallsTuple,
185
197
  ]
186
198
  | readonly [
187
199
  ...BaseTuple,
188
200
  expiry: Hex.Hex,
189
- limits: readonly TokenLimitTuple[],
190
- calls: readonly CallScopeTuple[],
201
+ limits: LimitsTuple,
202
+ calls: CallsTuple,
191
203
  witness: Hex.Hex,
192
204
  ]
193
205
  | readonly [
194
206
  ...BaseTuple,
195
207
  expiry: Hex.Hex,
196
- limits: readonly TokenLimitTuple[],
197
- calls: readonly CallScopeTuple[],
208
+ limits: LimitsTuple,
209
+ calls: CallsTuple,
198
210
  witness: Hex.Hex,
199
211
  isAdmin: Hex.Hex,
200
212
  ]
201
213
  | readonly [
202
214
  ...BaseTuple,
203
215
  expiry: Hex.Hex,
204
- limits: readonly TokenLimitTuple[],
205
- calls: readonly CallScopeTuple[],
216
+ limits: LimitsTuple,
217
+ calls: CallsTuple,
206
218
  witness: Hex.Hex,
207
219
  isAdmin: Hex.Hex,
208
220
  account: Address.Address,
@@ -472,11 +484,11 @@ export declare namespace from {
472
484
  type Options<
473
485
  signature extends SignatureValue | undefined = SignatureValue | undefined,
474
486
  > = {
475
- /** The primitive or multisig signature to attach to the Key Authorization. */
487
+ /** The primitive or configurable account signature to attach to the Key Authorization. */
476
488
  signature?:
477
489
  | signature
478
490
  | SignatureEnvelope.Primitive
479
- | SignatureEnvelope.Multisig
491
+ | SignatureEnvelope.Configurable
480
492
  | undefined
481
493
  }
482
494
 
@@ -491,7 +503,7 @@ export declare namespace from {
491
503
  ? {
492
504
  signature: Extract<
493
505
  SignatureEnvelope.from.ReturnValue<signature>,
494
- SignatureEnvelope.Primitive | SignatureEnvelope.Multisig
506
+ SignatureEnvelope.Primitive | SignatureEnvelope.Configurable
495
507
  >
496
508
  }
497
509
  : {})
@@ -539,7 +551,8 @@ export function fromRpc(authorization: Rpc): Signed {
539
551
  const witness = authorization.witness ?? undefined
540
552
  const isAdmin = authorization.isAdmin ?? undefined
541
553
  const account = authorization.account ?? undefined
542
- assertAccountBinding(keyType, account)
554
+ const type = keyType === 'multisig' ? 'configurable' : keyType
555
+ assertAccountBinding(type, account)
543
556
  const signature = SignatureEnvelope.fromRpc(authorization.signature)
544
557
  assertSignature(signature)
545
558
  if (witness !== undefined) assertWitness(witness)
@@ -574,7 +587,7 @@ export function fromRpc(authorization: Rpc): Signed {
574
587
  })),
575
588
  ...(scopes ? { scopes } : {}),
576
589
  signature,
577
- type: keyType,
590
+ type,
578
591
  ...(witness !== undefined ? { witness } : {}),
579
592
  ...(account !== undefined ? { account } : {}),
580
593
  ...(isAdmin ? { isAdmin: true as const } : {}),
@@ -658,7 +671,7 @@ export function fromTuple<const tuple extends Tuple>(
658
671
  case '0x02':
659
672
  return 'webAuthn'
660
673
  case '0x03':
661
- return 'multisig'
674
+ return 'configurable'
662
675
  default:
663
676
  throw new Error(`Invalid key type: ${keyType_hex}`)
664
677
  }
@@ -671,17 +684,18 @@ export function fromTuple<const tuple extends Tuple>(
671
684
  const expiry = isAbsent(rawExpiry)
672
685
  ? undefined
673
686
  : hexToNumber(rawExpiry as Hex.Hex) || undefined
674
- const limits =
675
- Array.isArray(rawLimits) && rawLimits.length > 0
676
- ? rawLimits.map((limitTuple: any) => {
677
- const [token, limit, period] = limitTuple
678
- return {
679
- token,
680
- limit: hexToBigint(limit),
681
- ...(period !== undefined ? { period: hexToNumber(period) } : {}),
682
- }
683
- })
684
- : undefined
687
+ // An empty list (`0xc0`) is an explicit deny-all, distinct from an absent
688
+ // field (`0x80` or omitted), which leaves spending unlimited.
689
+ const limits = Array.isArray(rawLimits)
690
+ ? rawLimits.map((limitTuple: any) => {
691
+ const [token, limit, period] = limitTuple
692
+ return {
693
+ token,
694
+ limit: hexToBigint(limit),
695
+ ...(period !== undefined ? { period: hexToNumber(period) } : {}),
696
+ }
697
+ })
698
+ : undefined
685
699
  const scopes = Array.isArray(rawScopes)
686
700
  ? rawScopes.flatMap((scopeTuple: any) => {
687
701
  const [address, selectorRules] = scopeTuple
@@ -994,7 +1008,7 @@ export function toRpc(authorization: toRpc.Input): Rpc {
994
1008
  chainId: chainIdHex === '0x0' ? '0x' : chainIdHex,
995
1009
  expiry: expiry != null ? Quantity.fromNumberish(expiry) : null,
996
1010
  keyId: address,
997
- keyType: type,
1011
+ keyType: type === 'configurable' ? 'multisig' : type,
998
1012
  limits: limits?.map(({ token, limit, period }) => ({
999
1013
  token,
1000
1014
  limit: Quantity.fromNumberish(limit),
@@ -1002,7 +1016,7 @@ export function toRpc(authorization: toRpc.Input): Rpc {
1002
1016
  })),
1003
1017
  signature: SignatureEnvelope.toRpc(signature) as
1004
1018
  | SignatureEnvelope.PrimitiveRpc
1005
- | SignatureEnvelope.MultisigRpc,
1019
+ | SignatureEnvelope.ConfigurableRpc,
1006
1020
  ...(allowedCalls ? { allowedCalls } : {}),
1007
1021
  ...(witness !== undefined ? { witness } : {}),
1008
1022
  ...(isAdmin ? { isAdmin: true } : {}),
@@ -1081,7 +1095,7 @@ export function toTuple<const authorization extends KeyAuthorization>(
1081
1095
  return '0x01'
1082
1096
  case 'webAuthn':
1083
1097
  return '0x02'
1084
- case 'multisig':
1098
+ case 'configurable':
1085
1099
  return '0x03'
1086
1100
  default:
1087
1101
  throw new Error(`Invalid key type: ${authorization.type}`)
@@ -1121,18 +1135,12 @@ export function toTuple<const authorization extends KeyAuthorization>(
1121
1135
  // Optional trailing fields in wire order. Each entry's `placeholder` is
1122
1136
  // emitted when this field is skipped but a later field is present.
1123
1137
  //
1124
- // Placeholder convention:
1125
- // - `'0x'` (RLP null) is the canonical placeholder for fields added at or
1126
- // after TIP-1053. The node decodes it as `None` (unrestricted).
1127
- // - `limits` keeps `[]` as its skipped placeholder for pre-TIP-1053 wire
1128
- // shapes — preserving byte-for-byte equivalence with signed payloads
1129
- // produced before TIP-1053 was added. When any TIP-1053+ field is
1130
- // present, the canonical `'0x'` placeholder is used instead.
1138
+ // `'0x'` (RLP null) is the canonical placeholder: the node decodes it as
1139
+ // `None` (unrestricted). An empty list would decode as `Some([])`, so a
1140
+ // skipped `limits` must never use `[]` (that reads as deny-all spending).
1131
1141
  //
1132
1142
  // To add a new optional trailing field (e.g. from a future TIP): append a
1133
1143
  // single entry to this list with `placeholder: '0x'`.
1134
- const hasTip1053Plus =
1135
- witness !== undefined || isAdmin || account !== undefined
1136
1144
  const optionals: readonly { placeholder: unknown; value: unknown }[] = [
1137
1145
  {
1138
1146
  value:
@@ -1143,7 +1151,7 @@ export function toTuple<const authorization extends KeyAuthorization>(
1143
1151
  },
1144
1152
  {
1145
1153
  value: limitsValue,
1146
- placeholder: hasTip1053Plus ? '0x' : [],
1154
+ placeholder: '0x',
1147
1155
  },
1148
1156
  { value: callsValue, placeholder: '0x' },
1149
1157
  { value: witness, placeholder: '0x' },
@@ -1211,7 +1219,8 @@ function assertAccountBinding(
1211
1219
  type: KeyAuthorization['type'],
1212
1220
  account: Address.Address | null | undefined,
1213
1221
  ): void {
1214
- if (type === 'multisig' && account == null) throw new MissingAccountError()
1222
+ if (type === 'configurable' && account == null)
1223
+ throw new MissingAccountError()
1215
1224
  }
1216
1225
 
1217
1226
  function assertWitness(witness: Hex.Hex): void {
@@ -1222,7 +1231,7 @@ function assertSignature<numberType>(
1222
1231
  signature: SignatureEnvelope.SignatureEnvelope<numberType>,
1223
1232
  ): asserts signature is
1224
1233
  | SignatureEnvelope.Primitive<numberType>
1225
- | SignatureEnvelope.Multisig<numberType> {
1234
+ | SignatureEnvelope.Configurable<numberType> {
1226
1235
  if (signature.type === 'keychain')
1227
1236
  throw new InvalidSignatureTypeError(signature.type)
1228
1237
  }
@@ -1256,15 +1265,15 @@ export class InvalidSignatureTypeError extends Error {
1256
1265
  override readonly name = 'KeyAuthorization.InvalidSignatureTypeError'
1257
1266
  constructor(type: SignatureEnvelope.SignatureEnvelope['type']) {
1258
1267
  super(
1259
- `Signature type \`${type}\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.`,
1268
+ `Signature type \`${type}\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`configurable\`.`,
1260
1269
  )
1261
1270
  }
1262
1271
  }
1263
1272
 
1264
- /** Thrown when a multisig key grant omits its parent account binding. */
1273
+ /** Thrown when a configurable account key grant omits its parent account binding. */
1265
1274
  export class MissingAccountError extends Error {
1266
1275
  override readonly name = 'KeyAuthorization.MissingAccountError'
1267
1276
  constructor() {
1268
- super('Multisig key grants require a parent account binding.')
1277
+ super('Configurable account key grants require a parent account binding.')
1269
1278
  }
1270
1279
  }