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.
- package/Base32/package.json +6 -0
- package/Bech32m/package.json +6 -0
- package/CHANGELOG.md +25 -0
- package/CompactSize/package.json +6 -0
- package/_cjs/core/Base32.js +73 -0
- package/_cjs/core/Base32.js.map +1 -0
- package/_cjs/core/Bech32m.js +205 -0
- package/_cjs/core/Bech32m.js.map +1 -0
- package/_cjs/core/CompactSize.js +91 -0
- package/_cjs/core/CompactSize.js.map +1 -0
- package/_cjs/index.js +5 -2
- package/_cjs/index.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +4 -4
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +18 -3
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TempoAddress.js +117 -0
- package/_cjs/tempo/TempoAddress.js.map +1 -0
- package/_cjs/tempo/TxEnvelopeTempo.js +5 -2
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/tempo/index.js +2 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/Base32.js +119 -0
- package/_esm/core/Base32.js.map +1 -0
- package/_esm/core/Bech32m.js +238 -0
- package/_esm/core/Bech32m.js.map +1 -0
- package/_esm/core/CompactSize.js +150 -0
- package/_esm/core/CompactSize.js.map +1 -0
- package/_esm/index.js +72 -0
- package/_esm/index.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +19 -9
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +22 -5
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TempoAddress.js +182 -0
- package/_esm/tempo/TempoAddress.js.map +1 -0
- package/_esm/tempo/TxEnvelopeTempo.js +42 -2
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/tempo/index.js +21 -0
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/core/Base32.d.ts +79 -0
- package/_types/core/Base32.d.ts.map +1 -0
- package/_types/core/Bech32m.d.ts +93 -0
- package/_types/core/Bech32m.d.ts.map +1 -0
- package/_types/core/CompactSize.d.ts +107 -0
- package/_types/core/CompactSize.d.ts.map +1 -0
- package/_types/index.d.ts +72 -0
- package/_types/index.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +17 -7
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +19 -5
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TempoAddress.d.ts +126 -0
- package/_types/tempo/TempoAddress.d.ts.map +1 -0
- package/_types/tempo/TxEnvelopeTempo.d.ts +47 -1
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +21 -0
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/core/Base32.ts +134 -0
- package/core/Bech32m.ts +263 -0
- package/core/CompactSize.ts +169 -0
- package/index.ts +74 -1
- package/package.json +21 -1
- package/tempo/KeyAuthorization.test.ts +70 -23
- package/tempo/KeyAuthorization.ts +21 -18
- package/tempo/SignatureEnvelope.test.ts +15 -8
- package/tempo/SignatureEnvelope.ts +43 -8
- package/tempo/TempoAddress/package.json +6 -0
- package/tempo/TempoAddress.test.ts +237 -0
- package/tempo/TempoAddress.ts +222 -0
- package/tempo/Transaction.test.ts +4 -2
- package/tempo/TxEnvelopeTempo.test.ts +7 -3
- package/tempo/TxEnvelopeTempo.ts +52 -1
- package/tempo/e2e.test.ts +45 -10
- package/tempo/index.ts +22 -0
- 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":
|
|
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": "
|
|
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
|
-
`"
|
|
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
|
-
`"
|
|
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
|
-
`"
|
|
1017
|
+
`"0x76f9016501808080d8d79470997970c51812dc3a010c7d01b50e0d17dc79c88080c0808080808080c0f9013df7010294be95c3f554e9fc85ec51be69a3d807a0d55bcf2c84499602d2dad99420c000000000000000000000000000000000000183989680b901020249960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a223371322d3777222c226f726967696e223a22687474703a2f2f6c6f63616c686f7374222c2263726f73734f726967696e223a66616c73657dccbb3485d4726235f13cb15ef394fb7158179fb7b1925eccec0147671090c52e77c3c53373cc1e3b05e7c23f609deb17cea8fe097300c45411237e9fe4166b35ad8ac16e167d6992c3e120d7f17d2376bc1cbcf30c46ba6dd00ce07303e742f511edf6ce1c32de66846f56afa7be1cbd729bc35750b6d0cdcf3ec9d75461aba0"`,
|
|
1014
1018
|
)
|
|
1015
1019
|
|
|
1016
1020
|
// Verify roundtrip
|
package/tempo/TxEnvelopeTempo.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
2
|
+
export const version = '0.14.0'
|