ox 0.13.1 → 0.14.0

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 (79) hide show
  1. package/Base32/package.json +6 -0
  2. package/Bech32m/package.json +6 -0
  3. package/CHANGELOG.md +25 -0
  4. package/CompactSize/package.json +6 -0
  5. package/_cjs/core/Base32.js +73 -0
  6. package/_cjs/core/Base32.js.map +1 -0
  7. package/_cjs/core/Bech32m.js +205 -0
  8. package/_cjs/core/Bech32m.js.map +1 -0
  9. package/_cjs/core/CompactSize.js +91 -0
  10. package/_cjs/core/CompactSize.js.map +1 -0
  11. package/_cjs/index.js +5 -2
  12. package/_cjs/index.js.map +1 -1
  13. package/_cjs/tempo/KeyAuthorization.js +4 -4
  14. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  15. package/_cjs/tempo/SignatureEnvelope.js +18 -3
  16. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  17. package/_cjs/tempo/TempoAddress.js +117 -0
  18. package/_cjs/tempo/TempoAddress.js.map +1 -0
  19. package/_cjs/tempo/TxEnvelopeTempo.js +5 -2
  20. package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
  21. package/_cjs/tempo/index.js +2 -1
  22. package/_cjs/tempo/index.js.map +1 -1
  23. package/_cjs/version.js +1 -1
  24. package/_esm/core/Base32.js +119 -0
  25. package/_esm/core/Base32.js.map +1 -0
  26. package/_esm/core/Bech32m.js +238 -0
  27. package/_esm/core/Bech32m.js.map +1 -0
  28. package/_esm/core/CompactSize.js +150 -0
  29. package/_esm/core/CompactSize.js.map +1 -0
  30. package/_esm/index.js +72 -0
  31. package/_esm/index.js.map +1 -1
  32. package/_esm/tempo/KeyAuthorization.js +19 -9
  33. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  34. package/_esm/tempo/SignatureEnvelope.js +22 -5
  35. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  36. package/_esm/tempo/TempoAddress.js +182 -0
  37. package/_esm/tempo/TempoAddress.js.map +1 -0
  38. package/_esm/tempo/TxEnvelopeTempo.js +42 -2
  39. package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
  40. package/_esm/tempo/index.js +21 -0
  41. package/_esm/tempo/index.js.map +1 -1
  42. package/_esm/version.js +1 -1
  43. package/_types/core/Base32.d.ts +79 -0
  44. package/_types/core/Base32.d.ts.map +1 -0
  45. package/_types/core/Bech32m.d.ts +93 -0
  46. package/_types/core/Bech32m.d.ts.map +1 -0
  47. package/_types/core/CompactSize.d.ts +107 -0
  48. package/_types/core/CompactSize.d.ts.map +1 -0
  49. package/_types/index.d.ts +72 -0
  50. package/_types/index.d.ts.map +1 -1
  51. package/_types/tempo/KeyAuthorization.d.ts +17 -7
  52. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  53. package/_types/tempo/SignatureEnvelope.d.ts +19 -5
  54. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  55. package/_types/tempo/TempoAddress.d.ts +126 -0
  56. package/_types/tempo/TempoAddress.d.ts.map +1 -0
  57. package/_types/tempo/TxEnvelopeTempo.d.ts +47 -1
  58. package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  59. package/_types/tempo/index.d.ts +21 -0
  60. package/_types/tempo/index.d.ts.map +1 -1
  61. package/_types/version.d.ts +1 -1
  62. package/core/Base32.ts +134 -0
  63. package/core/Bech32m.ts +263 -0
  64. package/core/CompactSize.ts +169 -0
  65. package/index.ts +74 -1
  66. package/package.json +21 -1
  67. package/tempo/KeyAuthorization.test.ts +70 -23
  68. package/tempo/KeyAuthorization.ts +21 -18
  69. package/tempo/SignatureEnvelope.test.ts +15 -8
  70. package/tempo/SignatureEnvelope.ts +43 -8
  71. package/tempo/TempoAddress/package.json +6 -0
  72. package/tempo/TempoAddress.test.ts +237 -0
  73. package/tempo/TempoAddress.ts +222 -0
  74. package/tempo/Transaction.test.ts +4 -2
  75. package/tempo/TxEnvelopeTempo.test.ts +7 -3
  76. package/tempo/TxEnvelopeTempo.ts +52 -1
  77. package/tempo/e2e.test.ts +45 -10
  78. package/tempo/index.ts +22 -0
  79. package/version.ts +1 -1
@@ -0,0 +1,222 @@
1
+ import * as Address from '../core/Address.js'
2
+ import * as Bech32m from '../core/Bech32m.js'
3
+ import * as Bytes from '../core/Bytes.js'
4
+ import * as CompactSize from '../core/CompactSize.js'
5
+ import * as Errors from '../core/Errors.js'
6
+ import * as Hex from '../core/Hex.js'
7
+
8
+ /** Root type for a Tempo Address. */
9
+ export type TempoAddress = `tempo1${string}` | `tempoz1${string}`
10
+
11
+ /**
12
+ * Formats a raw Ethereum address (and optional zone ID) into a Tempo address string.
13
+ *
14
+ * @example
15
+ * ```ts twoslash
16
+ * import { TempoAddress } from 'ox/tempo'
17
+ *
18
+ * const address = TempoAddress.format('0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28')
19
+ * // @log: 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0'
20
+ * ```
21
+ *
22
+ * @example
23
+ * ### Zone Address
24
+ * ```ts twoslash
25
+ * import { TempoAddress } from 'ox/tempo'
26
+ *
27
+ * const address = TempoAddress.format(
28
+ * '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28',
29
+ * { zoneId: 1 },
30
+ * )
31
+ * // @log: 'tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj'
32
+ * ```
33
+ *
34
+ * @param address - The raw 20-byte Ethereum address.
35
+ * @param options - Options.
36
+ * @returns The encoded Tempo address string.
37
+ */
38
+ export function format(
39
+ address: Address.Address,
40
+ options: format.Options = {},
41
+ ): TempoAddress {
42
+ const { zoneId } = options
43
+
44
+ const hrp = zoneId != null ? 'tempoz' : 'tempo'
45
+ const version = new Uint8Array([0x00])
46
+ const zone = zoneId != null ? CompactSize.toBytes(zoneId) : new Uint8Array()
47
+ const data = Bytes.concat(version, zone, Bytes.fromHex(address))
48
+
49
+ return Bech32m.encode(hrp, data) as TempoAddress
50
+ }
51
+
52
+ export declare namespace format {
53
+ type Options = {
54
+ /** Zone ID for zone addresses. */
55
+ zoneId?: number | bigint | undefined
56
+ }
57
+
58
+ type ErrorType = Errors.GlobalErrorType
59
+ }
60
+
61
+ /**
62
+ * Parses a Tempo address string into a raw Ethereum address and optional zone ID.
63
+ *
64
+ * @example
65
+ * ### Mainnet Address
66
+ * ```ts twoslash
67
+ * import { TempoAddress } from 'ox/tempo'
68
+ *
69
+ * const result = TempoAddress.parse(
70
+ * 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0',
71
+ * )
72
+ * // @log: { address: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28', zoneId: undefined }
73
+ * ```
74
+ *
75
+ * @example
76
+ * ### Zone Address
77
+ * ```ts twoslash
78
+ * import { TempoAddress } from 'ox/tempo'
79
+ *
80
+ * const result = TempoAddress.parse(
81
+ * 'tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj',
82
+ * )
83
+ * // @log: { address: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28', zoneId: 1 }
84
+ * ```
85
+ *
86
+ * @param tempoAddress - The Tempo address string to parse.
87
+ * @returns The parsed raw address and optional zone ID.
88
+ */
89
+ export function parse(tempoAddress: string): parse.ReturnType {
90
+ let hrp: string
91
+ let data: Uint8Array
92
+ try {
93
+ const decoded = Bech32m.decode(tempoAddress)
94
+ hrp = decoded.hrp
95
+ data = decoded.data
96
+ } catch {
97
+ throw new InvalidChecksumError({ address: tempoAddress })
98
+ }
99
+
100
+ if (hrp !== 'tempo' && hrp !== 'tempoz')
101
+ throw new InvalidPrefixError({ address: tempoAddress })
102
+
103
+ if (data.length < 1 || data[0] !== 0x00)
104
+ throw new InvalidVersionError({
105
+ address: tempoAddress,
106
+ version: data.length > 0 ? data[0]! : undefined,
107
+ })
108
+
109
+ const payload = data.slice(1)
110
+
111
+ let zoneId: number | bigint | undefined
112
+ let rawAddress: Uint8Array
113
+ if (hrp === 'tempoz') {
114
+ const { value, size } = CompactSize.fromBytes(payload)
115
+ zoneId = value
116
+ rawAddress = payload.slice(size)
117
+ } else {
118
+ zoneId = undefined
119
+ rawAddress = payload
120
+ }
121
+
122
+ if (rawAddress.length !== 20)
123
+ throw new InvalidLengthError({
124
+ address: tempoAddress,
125
+ expected: 20,
126
+ actual: rawAddress.length,
127
+ })
128
+
129
+ const address = Address.checksum(Hex.fromBytes(rawAddress) as Address.Address)
130
+
131
+ return { address, zoneId }
132
+ }
133
+
134
+ export declare namespace parse {
135
+ type ReturnType = {
136
+ /** The raw 20-byte Ethereum address. */
137
+ address: Address.Address
138
+ /** The zone ID, or `undefined` for mainnet addresses. */
139
+ zoneId: number | bigint | undefined
140
+ }
141
+
142
+ type ErrorType =
143
+ | InvalidPrefixError
144
+ | InvalidVersionError
145
+ | InvalidLengthError
146
+ | InvalidChecksumError
147
+ | Errors.GlobalErrorType
148
+ }
149
+
150
+ /**
151
+ * Validates a Tempo address string.
152
+ *
153
+ * @example
154
+ * ```ts twoslash
155
+ * import { TempoAddress } from 'ox/tempo'
156
+ *
157
+ * const valid = TempoAddress.validate(
158
+ * 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0',
159
+ * )
160
+ * // @log: true
161
+ * ```
162
+ *
163
+ * @param tempoAddress - The Tempo address string to validate.
164
+ * @returns Whether the address is valid.
165
+ */
166
+ export function validate(tempoAddress: string): boolean {
167
+ try {
168
+ parse(tempoAddress)
169
+ return true
170
+ } catch {
171
+ return false
172
+ }
173
+ }
174
+
175
+ /** Thrown when a Tempo address has an invalid prefix. */
176
+ export class InvalidPrefixError extends Errors.BaseError {
177
+ override readonly name = 'TempoAddress.InvalidPrefixError'
178
+
179
+ constructor({ address }: { address: string }) {
180
+ super(
181
+ `Tempo address "${address}" has an invalid prefix. Expected "tempo1" or "tempoz1".`,
182
+ )
183
+ }
184
+ }
185
+
186
+ /** Thrown when a Tempo address has an unsupported version byte. */
187
+ export class InvalidVersionError extends Errors.BaseError {
188
+ override readonly name = 'TempoAddress.InvalidVersionError'
189
+
190
+ constructor({
191
+ address,
192
+ version,
193
+ }: { address: string; version: number | undefined }) {
194
+ super(
195
+ `Tempo address "${address}" has unsupported version ${version === undefined ? '(missing)' : `0x${version.toString(16).padStart(2, '0')}`}. Only version 0x00 is supported.`,
196
+ )
197
+ }
198
+ }
199
+
200
+ /** Thrown when a Tempo address has an invalid payload length. */
201
+ export class InvalidLengthError extends Errors.BaseError {
202
+ override readonly name = 'TempoAddress.InvalidLengthError'
203
+
204
+ constructor({
205
+ address,
206
+ expected,
207
+ actual,
208
+ }: { address: string; expected: number; actual: number }) {
209
+ super(
210
+ `Tempo address "${address}" has an invalid payload length. Expected ${expected} bytes, got ${actual}.`,
211
+ )
212
+ }
213
+ }
214
+
215
+ /** Thrown when a Tempo address has an invalid checksum. */
216
+ export class InvalidChecksumError extends Errors.BaseError {
217
+ override readonly name = 'TempoAddress.InvalidChecksumError'
218
+
219
+ constructor({ address }: { address: string }) {
220
+ super(`Tempo address "${address}" has an invalid checksum.`)
221
+ }
222
+ }
@@ -164,6 +164,7 @@ describe('fromRpc', () => {
164
164
  },
165
165
  ],
166
166
  keyAuthorization: {
167
+ chainId: '0x1',
167
168
  expiry: '0xffffffffffff',
168
169
  keyId: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
169
170
  keyType: 'secp256k1',
@@ -224,7 +225,7 @@ describe('fromRpc', () => {
224
225
  "hash": "0x353fdfc38a2f26115daadee9f5b8392ce62b84f410957967e2ed56b35338cdd0",
225
226
  "keyAuthorization": {
226
227
  "address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
227
- "chainId": 0n,
228
+ "chainId": 1n,
228
229
  "expiry": 281474976710655,
229
230
  "limits": [
230
231
  {
@@ -431,6 +432,7 @@ describe('toRpc', () => {
431
432
  data: undefined,
432
433
  keyAuthorization: {
433
434
  address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
435
+ chainId: 1n,
434
436
  expiry: 281474976710655,
435
437
  type: 'secp256k1',
436
438
  limits: [
@@ -487,7 +489,7 @@ describe('toRpc', () => {
487
489
  "hash": "0x353fdfc38a2f26115daadee9f5b8392ce62b84f410957967e2ed56b35338cdd0",
488
490
  "input": undefined,
489
491
  "keyAuthorization": {
490
- "chainId": "0x",
492
+ "chainId": "0x1",
491
493
  "expiry": "0xffffffffffff",
492
494
  "keyId": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
493
495
  "keyType": "secp256k1",
@@ -257,6 +257,7 @@ describe('deserialize', () => {
257
257
  const keyAuthorization = KeyAuthorization.from({
258
258
  expiry: 1234567890,
259
259
  address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
260
+ chainId: 1n,
260
261
  type: 'secp256k1',
261
262
  limits: [
262
263
  {
@@ -894,6 +895,7 @@ describe('serialize', () => {
894
895
  test('keyAuthorization (secp256k1)', () => {
895
896
  const keyAuthorization = KeyAuthorization.from({
896
897
  address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
898
+ chainId: 1n,
897
899
  expiry: 1234567890,
898
900
  type: 'secp256k1',
899
901
  limits: [
@@ -918,7 +920,7 @@ describe('serialize', () => {
918
920
 
919
921
  const serialized = TxEnvelopeTempo.serialize(transaction)
920
922
  expect(serialized).toMatchInlineSnapshot(
921
- `"0x76f8a201808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f87bf7808094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b841635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f05401b"`,
923
+ `"0x76f8a201808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f87bf7018094be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b841635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f05401b"`,
922
924
  )
923
925
 
924
926
  const deserialized = TxEnvelopeTempo.deserialize(serialized)
@@ -928,6 +930,7 @@ describe('serialize', () => {
928
930
  test('keyAuthorization (p256)', () => {
929
931
  const keyAuthorization = KeyAuthorization.from({
930
932
  address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
933
+ chainId: 1n,
931
934
  expiry: 1234567890,
932
935
  type: 'p256',
933
936
  limits: [
@@ -959,7 +962,7 @@ describe('serialize', () => {
959
962
 
960
963
  const serialized = TxEnvelopeTempo.serialize(transaction)
961
964
  expect(serialized).toMatchInlineSnapshot(
962
- `"0x76f8e301808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f8bcf7800194be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b88201ccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba001"`,
965
+ `"0x76f8e301808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f8bcf7010194be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b88201ccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba001"`,
963
966
  )
964
967
 
965
968
  const deserialized = TxEnvelopeTempo.deserialize(serialized)
@@ -979,6 +982,7 @@ describe('serialize', () => {
979
982
 
980
983
  const keyAuthorization = KeyAuthorization.from({
981
984
  address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
985
+ chainId: 1n,
982
986
  expiry: 1234567890,
983
987
  type: 'webAuthn',
984
988
  limits: [
@@ -1010,7 +1014,7 @@ describe('serialize', () => {
1010
1014
 
1011
1015
  const serialized = TxEnvelopeTempo.serialize(transaction)
1012
1016
  expect(serialized).toMatchInlineSnapshot(
1013
- `"0x76f9016501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f9013df7800294be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b901020249960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a223371322d3777222c226f726967696e223a22687474703a2f2f6c6f63616c686f7374222c2263726f73734f726967696e223a66616c73657dccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba0"`,
1017
+ `"0x76f9016501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f9013df7010294be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b901020249960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a223371322d3777222c226f726967696e223a22687474703a2f2f6c6f63616c686f7374222c2263726f73734f726967696e223a66616c73657dccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba0"`,
1014
1018
  )
1015
1019
 
1016
1020
  // Verify roundtrip
@@ -779,16 +779,67 @@ export declare namespace serialize {
779
779
  * const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
780
780
  * ```
781
781
  *
782
+ * @example
783
+ * ### Access Keys
784
+ *
785
+ * When signing as an access key on behalf of a root account, pass the
786
+ * `from` option with the root account address. This computes
787
+ * `keccak256(0x04 || sigHash || from)` which binds the signature to the
788
+ * specific user account (V2 keychain format).
789
+ *
790
+ * ```ts twoslash
791
+ * // @noErrors
792
+ * import { Secp256k1 } from 'ox'
793
+ * import { TxEnvelopeTempo, SignatureEnvelope } from 'ox/tempo'
794
+ *
795
+ * const envelope = TxEnvelopeTempo.from({
796
+ * chainId: 1,
797
+ * calls: [{
798
+ * data: '0xdeadbeef',
799
+ * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
800
+ * }],
801
+ * nonce: 0n,
802
+ * maxFeePerGas: 1000000000n,
803
+ * gas: 21000n,
804
+ * })
805
+ *
806
+ * const payload = TxEnvelopeTempo.getSignPayload(envelope, { from: '0x...' }) // [!code focus]
807
+ *
808
+ * const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
809
+ *
810
+ * const signed = TxEnvelopeTempo.serialize(envelope, {
811
+ * signature: SignatureEnvelope.from({
812
+ * userAddress: from,
813
+ * inner: SignatureEnvelope.from(signature),
814
+ * }),
815
+ * })
816
+ * ```
817
+ *
782
818
  * @param envelope - The transaction envelope to get the sign payload for.
819
+ * @param options - Options.
783
820
  * @returns The sign payload.
784
821
  */
785
822
  export function getSignPayload(
786
823
  envelope: TxEnvelopeTempo,
824
+ options: getSignPayload.Options = {},
787
825
  ): getSignPayload.ReturnValue {
788
- return hash(envelope, { presign: true })
826
+ const sigHash = hash(envelope, { presign: true })
827
+ if (options.from)
828
+ return Hash.keccak256(Hex.concat('0x04', sigHash, options.from))
829
+ return sigHash
789
830
  }
790
831
 
791
832
  export declare namespace getSignPayload {
833
+ type Options = {
834
+ /**
835
+ * The root account address for access key signing.
836
+ *
837
+ * When provided, computes `keccak256(0x04 || sigHash || from)` instead of
838
+ * the raw `sigHash`, binding the access key signature to the specific user account.
839
+ */
840
+ from?: Address.Address | undefined
841
+ }
842
+
792
843
  type ReturnValue = Hex.Hex
793
844
 
794
845
  type ErrorType = hash.ErrorType | Errors.GlobalErrorType
package/tempo/e2e.test.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from 'ox'
10
10
  import { getTransactionCount } from 'viem/actions'
11
11
  import { beforeEach, describe, expect, test } from 'vitest'
12
- import { chain, client, fundAddress } from '../../test/tempo/config.js'
12
+ import { chain, client, fundAddress, nodeEnv } from '../../test/tempo/config.js'
13
13
  import {
14
14
  AuthorizationTempo,
15
15
  KeyAuthorization,
@@ -20,6 +20,8 @@ import * as TransactionReceipt from './TransactionReceipt.js'
20
20
  import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
21
21
 
22
22
  const chainId = chain.id
23
+ // TODO: remove when v2 keychain signatures are deployed to testnet.
24
+ const keychainVersion = nodeEnv === 'localnet' ? 'v2' : undefined
23
25
 
24
26
  test('behavior: default (secp256k1)', async () => {
25
27
  const privateKey = Secp256k1.randomPrivateKey()
@@ -806,6 +808,7 @@ describe('behavior: keyAuthorization', () => {
806
808
 
807
809
  const keyAuth = KeyAuthorization.from({
808
810
  address: access.address,
811
+ chainId: BigInt(chain.id),
809
812
  type: 'secp256k1',
810
813
  })
811
814
 
@@ -839,7 +842,9 @@ describe('behavior: keyAuthorization', () => {
839
842
  })
840
843
 
841
844
  const signature = Secp256k1.sign({
842
- payload: TxEnvelopeTempo.getSignPayload(transaction),
845
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
846
+ from: keychainVersion === 'v2' ? root.address : undefined,
847
+ }),
843
848
  privateKey: access.privateKey,
844
849
  })
845
850
 
@@ -848,6 +853,7 @@ describe('behavior: keyAuthorization', () => {
848
853
  userAddress: root.address,
849
854
  inner: SignatureEnvelope.from(signature),
850
855
  type: 'keychain',
856
+ version: keychainVersion,
851
857
  }),
852
858
  })
853
859
 
@@ -978,7 +984,9 @@ describe('behavior: keyAuthorization', () => {
978
984
  })
979
985
 
980
986
  const signature = Secp256k1.sign({
981
- payload: TxEnvelopeTempo.getSignPayload(transaction),
987
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
988
+ from: keychainVersion === 'v2' ? root.address : undefined,
989
+ }),
982
990
  privateKey: access.privateKey,
983
991
  })
984
992
 
@@ -987,6 +995,7 @@ describe('behavior: keyAuthorization', () => {
987
995
  userAddress: root.address,
988
996
  inner: SignatureEnvelope.from(signature),
989
997
  type: 'keychain',
998
+ version: keychainVersion,
990
999
  }),
991
1000
  })
992
1001
 
@@ -1012,6 +1021,7 @@ describe('behavior: keyAuthorization', () => {
1012
1021
 
1013
1022
  const keyAuth = KeyAuthorization.from({
1014
1023
  address: access.address,
1024
+ chainId: BigInt(chainId),
1015
1025
  type: 'p256',
1016
1026
  })
1017
1027
 
@@ -1045,7 +1055,9 @@ describe('behavior: keyAuthorization', () => {
1045
1055
  })
1046
1056
 
1047
1057
  const signature = P256.sign({
1048
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1058
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1059
+ from: keychainVersion === 'v2' ? root.address : undefined,
1060
+ }),
1049
1061
  privateKey: access.privateKey,
1050
1062
  })
1051
1063
 
@@ -1059,6 +1071,7 @@ describe('behavior: keyAuthorization', () => {
1059
1071
  type: 'p256',
1060
1072
  }),
1061
1073
  type: 'keychain',
1074
+ version: keychainVersion,
1062
1075
  }),
1063
1076
  })
1064
1077
 
@@ -1190,7 +1203,9 @@ describe('behavior: keyAuthorization', () => {
1190
1203
  })
1191
1204
 
1192
1205
  const signature = P256.sign({
1193
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1206
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1207
+ from: keychainVersion === 'v2' ? root.address : undefined,
1208
+ }),
1194
1209
  privateKey: access.privateKey,
1195
1210
  })
1196
1211
 
@@ -1204,6 +1219,7 @@ describe('behavior: keyAuthorization', () => {
1204
1219
  type: 'p256',
1205
1220
  }),
1206
1221
  type: 'keychain',
1222
+ version: keychainVersion,
1207
1223
  }),
1208
1224
  })
1209
1225
 
@@ -1227,6 +1243,7 @@ describe('behavior: keyAuthorization', () => {
1227
1243
 
1228
1244
  const keyAuth = KeyAuthorization.from({
1229
1245
  address: access.address,
1246
+ chainId: BigInt(chainId),
1230
1247
  type: 'p256',
1231
1248
  })
1232
1249
 
@@ -1260,7 +1277,9 @@ describe('behavior: keyAuthorization', () => {
1260
1277
  })
1261
1278
 
1262
1279
  const signature = await WebCryptoP256.sign({
1263
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1280
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1281
+ from: keychainVersion === 'v2' ? root.address : undefined,
1282
+ }),
1264
1283
  privateKey: keyPair.privateKey,
1265
1284
  })
1266
1285
 
@@ -1274,6 +1293,7 @@ describe('behavior: keyAuthorization', () => {
1274
1293
  type: 'p256',
1275
1294
  }),
1276
1295
  type: 'keychain',
1296
+ version: keychainVersion,
1277
1297
  }),
1278
1298
  })
1279
1299
 
@@ -1366,7 +1386,9 @@ describe('behavior: keyAuthorization', () => {
1366
1386
  })
1367
1387
 
1368
1388
  const signature = await WebCryptoP256.sign({
1369
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1389
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1390
+ from: keychainVersion === 'v2' ? root.address : undefined,
1391
+ }),
1370
1392
  privateKey: keyPair.privateKey,
1371
1393
  })
1372
1394
 
@@ -1380,6 +1402,7 @@ describe('behavior: keyAuthorization', () => {
1380
1402
  type: 'p256',
1381
1403
  }),
1382
1404
  type: 'keychain',
1405
+ version: keychainVersion,
1383
1406
  }),
1384
1407
  })
1385
1408
 
@@ -1404,6 +1427,7 @@ describe('behavior: keyAuthorization', () => {
1404
1427
 
1405
1428
  const keyAuth = KeyAuthorization.from({
1406
1429
  address: access.address,
1430
+ chainId: BigInt(chainId),
1407
1431
  type: 'p256',
1408
1432
  expiry: Math.floor(Date.now() / 1000) + 60 * 60,
1409
1433
  limits: [
@@ -1444,7 +1468,9 @@ describe('behavior: keyAuthorization', () => {
1444
1468
  })
1445
1469
 
1446
1470
  const signature = P256.sign({
1447
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1471
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1472
+ from: keychainVersion === 'v2' ? root.address : undefined,
1473
+ }),
1448
1474
  privateKey: access.privateKey,
1449
1475
  })
1450
1476
 
@@ -1458,6 +1484,7 @@ describe('behavior: keyAuthorization', () => {
1458
1484
  type: 'p256',
1459
1485
  }),
1460
1486
  type: 'keychain',
1487
+ version: keychainVersion,
1461
1488
  }),
1462
1489
  })
1463
1490
 
@@ -1498,6 +1525,7 @@ describe('behavior: keyAuthorization', () => {
1498
1525
 
1499
1526
  const keyAuth = KeyAuthorization.from({
1500
1527
  address: access.address,
1528
+ chainId: BigInt(chainId),
1501
1529
  type: 'secp256k1',
1502
1530
  limits: [
1503
1531
  {
@@ -1537,7 +1565,9 @@ describe('behavior: keyAuthorization', () => {
1537
1565
  })
1538
1566
 
1539
1567
  const signature = Secp256k1.sign({
1540
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1568
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1569
+ from: keychainVersion === 'v2' ? root.address : undefined,
1570
+ }),
1541
1571
  privateKey: access.privateKey,
1542
1572
  })
1543
1573
 
@@ -1546,6 +1576,7 @@ describe('behavior: keyAuthorization', () => {
1546
1576
  userAddress: root.address,
1547
1577
  inner: SignatureEnvelope.from(signature),
1548
1578
  type: 'keychain',
1579
+ version: keychainVersion,
1549
1580
  }),
1550
1581
  })
1551
1582
 
@@ -1586,6 +1617,7 @@ describe('behavior: keyAuthorization', () => {
1586
1617
 
1587
1618
  const keyAuth = KeyAuthorization.from({
1588
1619
  address: access.address,
1620
+ chainId: BigInt(chainId),
1589
1621
  type: 'secp256k1',
1590
1622
  expiry: Math.floor(Date.now() / 1000) + 60 * 60,
1591
1623
  })
@@ -1620,7 +1652,9 @@ describe('behavior: keyAuthorization', () => {
1620
1652
  })
1621
1653
 
1622
1654
  const signature = Secp256k1.sign({
1623
- payload: TxEnvelopeTempo.getSignPayload(transaction),
1655
+ payload: TxEnvelopeTempo.getSignPayload(transaction, {
1656
+ from: keychainVersion === 'v2' ? root.address : undefined,
1657
+ }),
1624
1658
  privateKey: access.privateKey,
1625
1659
  })
1626
1660
 
@@ -1629,6 +1663,7 @@ describe('behavior: keyAuthorization', () => {
1629
1663
  userAddress: root.address,
1630
1664
  inner: SignatureEnvelope.from(signature),
1631
1665
  type: 'keychain',
1666
+ version: keychainVersion,
1632
1667
  }),
1633
1668
  })
1634
1669
 
package/tempo/index.ts CHANGED
@@ -121,6 +121,28 @@ export * as PoolId from './PoolId.js'
121
121
  */
122
122
  export * as SignatureEnvelope from './SignatureEnvelope.js'
123
123
 
124
+ /**
125
+ * Tempo address encoding/decoding utilities for human-readable addresses.
126
+ *
127
+ * Tempo addresses use a bech32 base32-encoded format with `tempo1` prefix for mainnet
128
+ * and `tempoz1` prefix for zone addresses. Includes CompactSize zone ID encoding
129
+ * and double-SHA256 checksumming.
130
+ *
131
+ * @example
132
+ * ```ts twoslash
133
+ * import { TempoAddress } from 'ox/tempo'
134
+ *
135
+ * const encoded = TempoAddress.format('0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28')
136
+ * // @log: 'tempo1wskntnrxxnq9x2f95wuyf0y7wk2l90fg0hlz9j'
137
+ *
138
+ * const { address, zoneId } = TempoAddress.parse(encoded)
139
+ * // @log: { address: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28', zoneId: undefined }
140
+ * ```
141
+ *
142
+ * @category Reference
143
+ */
144
+ export * as TempoAddress from './TempoAddress.js'
145
+
124
146
  /**
125
147
  * Tick-based pricing utilities for DEX price conversions.
126
148
  *
package/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '0.13.1'
2
+ export const version = '0.14.0'