ox 0.13.2 → 0.14.1
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/Bech32m/package.json +6 -0
- package/CHANGELOG.md +25 -0
- package/_cjs/core/Bech32m.js +205 -0
- package/_cjs/core/Bech32m.js.map +1 -0
- package/_cjs/index.js +3 -2
- package/_cjs/index.js.map +1 -1
- package/_cjs/tempo/AuthorizationTempo.js +7 -2
- package/_cjs/tempo/AuthorizationTempo.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +22 -8
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +39 -4
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TempoAddress.js +49 -41
- package/_cjs/tempo/TempoAddress.js.map +1 -1
- package/_cjs/tempo/TokenId.js +8 -5
- package/_cjs/tempo/TokenId.js.map +1 -1
- package/_cjs/tempo/TransactionRequest.js +2 -1
- package/_cjs/tempo/TransactionRequest.js.map +1 -1
- package/_cjs/tempo/TxEnvelopeTempo.js +17 -4
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/Bech32m.js +238 -0
- package/_esm/core/Bech32m.js.map +1 -0
- package/_esm/index.js +24 -0
- package/_esm/index.js.map +1 -1
- package/_esm/tempo/AuthorizationTempo.js +24 -19
- package/_esm/tempo/AuthorizationTempo.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +38 -13
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +43 -6
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TempoAddress.js +79 -48
- package/_esm/tempo/TempoAddress.js.map +1 -1
- package/_esm/tempo/TokenId.js +9 -6
- package/_esm/tempo/TokenId.js.map +1 -1
- package/_esm/tempo/TransactionRequest.js +2 -1
- package/_esm/tempo/TransactionRequest.js.map +1 -1
- package/_esm/tempo/TxEnvelopeTempo.js +66 -15
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/tempo/index.js +11 -10
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/core/Bech32m.d.ts +93 -0
- package/_types/core/Bech32m.d.ts.map +1 -0
- package/_types/index.d.ts +24 -0
- package/_types/index.d.ts.map +1 -1
- package/_types/tempo/AuthorizationTempo.d.ts +19 -19
- package/_types/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +21 -9
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +30 -7
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TempoAddress.d.ts +51 -15
- package/_types/tempo/TempoAddress.d.ts.map +1 -1
- package/_types/tempo/TokenId.d.ts +6 -5
- package/_types/tempo/TokenId.d.ts.map +1 -1
- package/_types/tempo/TransactionRequest.d.ts +2 -1
- package/_types/tempo/TransactionRequest.d.ts.map +1 -1
- package/_types/tempo/TxEnvelopeTempo.d.ts +64 -17
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +11 -10
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/core/Bech32m.ts +263 -0
- package/index.ts +24 -2
- package/package.json +6 -1
- package/tempo/AuthorizationTempo.test.ts +13 -0
- package/tempo/AuthorizationTempo.ts +27 -21
- package/tempo/KeyAuthorization.test.ts +95 -23
- package/tempo/KeyAuthorization.ts +44 -25
- package/tempo/PoolId.test.ts +9 -0
- package/tempo/SignatureEnvelope.test.ts +123 -8
- package/tempo/SignatureEnvelope.ts +82 -9
- package/tempo/TempoAddress.test.ts +70 -14
- package/tempo/TempoAddress.ts +95 -67
- package/tempo/TokenId.test.ts +14 -0
- package/tempo/TokenId.ts +13 -10
- package/tempo/Transaction.test.ts +4 -2
- package/tempo/TransactionRequest.ts +3 -2
- package/tempo/TxEnvelopeTempo.test.ts +79 -3
- package/tempo/TxEnvelopeTempo.ts +86 -19
- package/tempo/e2e.test.ts +33 -9
- package/tempo/index.ts +15 -10
- package/version.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Address } from 'ox'
|
|
1
|
+
import { Address, Bech32m } from 'ox'
|
|
2
2
|
import { TempoAddress } from 'ox/tempo'
|
|
3
3
|
import { describe, expect, test } from 'vitest'
|
|
4
4
|
|
|
@@ -6,30 +6,55 @@ const rawAddress = Address.checksum(
|
|
|
6
6
|
'0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28',
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
+
describe('resolve', () => {
|
|
10
|
+
test('hex address passthrough', () => {
|
|
11
|
+
expect(TempoAddress.resolve(rawAddress)).toBe(rawAddress)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('tempo address', () => {
|
|
15
|
+
const tempoAddr = TempoAddress.format(rawAddress)
|
|
16
|
+
expect(TempoAddress.resolve(tempoAddr)).toBe(rawAddress)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('tempo zone address', () => {
|
|
20
|
+
const tempoAddr = TempoAddress.format(rawAddress, { zoneId: 1 })
|
|
21
|
+
expect(TempoAddress.resolve(tempoAddr)).toBe(rawAddress)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
9
25
|
describe('format', () => {
|
|
10
26
|
test('mainnet address', () => {
|
|
11
27
|
expect(TempoAddress.format(rawAddress)).toMatchInlineSnapshot(
|
|
12
|
-
`"
|
|
28
|
+
`"tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0"`,
|
|
13
29
|
)
|
|
14
30
|
})
|
|
15
31
|
|
|
32
|
+
test('tempo address input', () => {
|
|
33
|
+
const tempoAddr = TempoAddress.format(rawAddress)
|
|
34
|
+
expect(TempoAddress.format(tempoAddr)).toBe(tempoAddr)
|
|
35
|
+
})
|
|
36
|
+
|
|
16
37
|
test('zone address (zone ID = 1)', () => {
|
|
17
38
|
expect(
|
|
18
39
|
TempoAddress.format(rawAddress, { zoneId: 1 }),
|
|
19
|
-
).toMatchInlineSnapshot(
|
|
40
|
+
).toMatchInlineSnapshot(
|
|
41
|
+
`"tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj"`,
|
|
42
|
+
)
|
|
20
43
|
})
|
|
21
44
|
|
|
22
45
|
test('zone address (zone ID = 252)', () => {
|
|
23
46
|
expect(
|
|
24
47
|
TempoAddress.format(rawAddress, { zoneId: 252 }),
|
|
25
|
-
).toMatchInlineSnapshot(
|
|
48
|
+
).toMatchInlineSnapshot(
|
|
49
|
+
`"tempoz1qr78gtf4e3nrfszn9yj68wzyhj08t90jh55q9k62jd"`,
|
|
50
|
+
)
|
|
26
51
|
})
|
|
27
52
|
|
|
28
53
|
test('zone address (zone ID = 253)', () => {
|
|
29
54
|
expect(
|
|
30
55
|
TempoAddress.format(rawAddress, { zoneId: 253 }),
|
|
31
56
|
).toMatchInlineSnapshot(
|
|
32
|
-
`"
|
|
57
|
+
`"tempoz1qr7l6qr5956uce35cpfjjfdrhpzte8n4jhet62q0j8hus"`,
|
|
33
58
|
)
|
|
34
59
|
})
|
|
35
60
|
|
|
@@ -37,7 +62,7 @@ describe('format', () => {
|
|
|
37
62
|
expect(
|
|
38
63
|
TempoAddress.format(rawAddress, { zoneId: 65535 }),
|
|
39
64
|
).toMatchInlineSnapshot(
|
|
40
|
-
`"
|
|
65
|
+
`"tempoz1qr7lllm5956uce35cpfjjfdrhpzte8n4jhet62q8pdj6j"`,
|
|
41
66
|
)
|
|
42
67
|
})
|
|
43
68
|
|
|
@@ -45,7 +70,7 @@ describe('format', () => {
|
|
|
45
70
|
expect(
|
|
46
71
|
TempoAddress.format(rawAddress, { zoneId: 65536 }),
|
|
47
72
|
).toMatchInlineSnapshot(
|
|
48
|
-
`"
|
|
73
|
+
`"tempoz1qrlqqqqpqp6z6dwvvc6vq5efyk3ms39une6etu4a9qdupk5c"`,
|
|
49
74
|
)
|
|
50
75
|
})
|
|
51
76
|
|
|
@@ -53,7 +78,7 @@ describe('format', () => {
|
|
|
53
78
|
expect(
|
|
54
79
|
TempoAddress.format(rawAddress, { zoneId: 4294967295 }),
|
|
55
80
|
).toMatchInlineSnapshot(
|
|
56
|
-
`"
|
|
81
|
+
`"tempoz1qrl0llllla6z6dwvvc6vq5efyk3ms39une6etu4a9qnk36qy"`,
|
|
57
82
|
)
|
|
58
83
|
})
|
|
59
84
|
|
|
@@ -61,7 +86,7 @@ describe('format', () => {
|
|
|
61
86
|
expect(
|
|
62
87
|
TempoAddress.format(rawAddress, { zoneId: BigInt('4294967296') }),
|
|
63
88
|
).toMatchInlineSnapshot(
|
|
64
|
-
`"
|
|
89
|
+
`"tempoz1qrlsqqqqqqqsqqqqwskntnrxxnq9x2f95wuyf0y7wk2l90fg4306kk"`,
|
|
65
90
|
)
|
|
66
91
|
})
|
|
67
92
|
|
|
@@ -69,6 +94,28 @@ describe('format', () => {
|
|
|
69
94
|
const result = TempoAddress.format(rawAddress)
|
|
70
95
|
expect(result).toBe(result.toLowerCase())
|
|
71
96
|
})
|
|
97
|
+
|
|
98
|
+
test('spec test vectors', () => {
|
|
99
|
+
expect(TempoAddress.format(rawAddress)).toBe(
|
|
100
|
+
'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0',
|
|
101
|
+
)
|
|
102
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 1 })).toBe(
|
|
103
|
+
'tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj',
|
|
104
|
+
)
|
|
105
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 1000 })).toBe(
|
|
106
|
+
'tempoz1qr77sqm5956uce35cpfjjfdrhpzte8n4jhet62qxx4zvx',
|
|
107
|
+
)
|
|
108
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 100000 })).toBe(
|
|
109
|
+
'tempoz1qrl2ppspqp6z6dwvvc6vq5efyk3ms39une6etu4a9qg5477g',
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('address lengths match spec', () => {
|
|
114
|
+
expect(TempoAddress.format(rawAddress).length).toBe(46)
|
|
115
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 1 }).length).toBe(49)
|
|
116
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 1000 }).length).toBe(52)
|
|
117
|
+
expect(TempoAddress.format(rawAddress, { zoneId: 100000 }).length).toBe(55)
|
|
118
|
+
})
|
|
72
119
|
})
|
|
73
120
|
|
|
74
121
|
describe('parse', () => {
|
|
@@ -144,17 +191,26 @@ describe('parse', () => {
|
|
|
144
191
|
`)
|
|
145
192
|
})
|
|
146
193
|
|
|
147
|
-
test('
|
|
194
|
+
test('all uppercase', () => {
|
|
148
195
|
const encoded = TempoAddress.format(rawAddress)
|
|
149
|
-
const upper = encoded.
|
|
196
|
+
const upper = encoded.toUpperCase()
|
|
150
197
|
expect(TempoAddress.parse(upper).address).toBe(rawAddress)
|
|
151
198
|
})
|
|
152
199
|
|
|
153
200
|
test('error: invalid prefix', () => {
|
|
201
|
+
const encoded = Bech32m.encode('bitcoin', new Uint8Array(20))
|
|
154
202
|
expect(() =>
|
|
155
|
-
TempoAddress.parse(
|
|
203
|
+
TempoAddress.parse(encoded),
|
|
156
204
|
).toThrowErrorMatchingInlineSnapshot(
|
|
157
|
-
`[TempoAddress.InvalidPrefixError: Tempo address "
|
|
205
|
+
`[TempoAddress.InvalidPrefixError: Tempo address "${encoded}" has an invalid prefix. Expected "tempo1" or "tempoz1".]`,
|
|
206
|
+
)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test('error: unsupported version', () => {
|
|
210
|
+
const data = new Uint8Array([0x01, ...new Uint8Array(20)])
|
|
211
|
+
const encoded = Bech32m.encode('tempo', data)
|
|
212
|
+
expect(() => TempoAddress.parse(encoded)).toThrow(
|
|
213
|
+
TempoAddress.InvalidVersionError,
|
|
158
214
|
)
|
|
159
215
|
})
|
|
160
216
|
|
|
@@ -174,7 +230,7 @@ describe('parse', () => {
|
|
|
174
230
|
expect(() =>
|
|
175
231
|
TempoAddress.parse(swapped),
|
|
176
232
|
).toThrowErrorMatchingInlineSnapshot(
|
|
177
|
-
`[TempoAddress.
|
|
233
|
+
`[TempoAddress.InvalidChecksumError: Tempo address "tempoz1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0" has an invalid checksum.]`,
|
|
178
234
|
)
|
|
179
235
|
})
|
|
180
236
|
})
|
package/tempo/TempoAddress.ts
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
1
|
+
import * as core_Address from '../core/Address.js'
|
|
2
|
+
import * as Bech32m from '../core/Bech32m.js'
|
|
3
3
|
import * as Bytes from '../core/Bytes.js'
|
|
4
4
|
import * as CompactSize from '../core/CompactSize.js'
|
|
5
5
|
import * as Errors from '../core/Errors.js'
|
|
6
|
-
import * as Hash from '../core/Hash.js'
|
|
7
6
|
import * as Hex from '../core/Hex.js'
|
|
7
|
+
import type { Compute } from '../core/internal/types.js'
|
|
8
|
+
|
|
9
|
+
/** An address that can be either an Ethereum hex address or a Tempo bech32m address. */
|
|
10
|
+
export type Address = core_Address.Address | Tempo
|
|
8
11
|
|
|
9
12
|
/** Root type for a Tempo Address. */
|
|
10
|
-
export type
|
|
13
|
+
export type Tempo = Compute<`tempo1${string}` | `tempoz1${string}`>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves an address input (either an Ethereum hex address or a Tempo bech32m address)
|
|
17
|
+
* to an Ethereum hex address.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts twoslash
|
|
21
|
+
* import { TempoAddress } from 'ox/tempo'
|
|
22
|
+
*
|
|
23
|
+
* const address = TempoAddress.resolve('tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0')
|
|
24
|
+
* // @log: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28'
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ### Hex Address Passthrough
|
|
29
|
+
* ```ts twoslash
|
|
30
|
+
* import { TempoAddress } from 'ox/tempo'
|
|
31
|
+
*
|
|
32
|
+
* const address = TempoAddress.resolve('0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28')
|
|
33
|
+
* // @log: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28'
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @param address - An Ethereum hex address or Tempo bech32m address.
|
|
37
|
+
* @returns The resolved Ethereum hex address.
|
|
38
|
+
*/
|
|
39
|
+
export function resolve(address: Address): core_Address.Address {
|
|
40
|
+
if (address.startsWith('tempo')) return parse(address).address
|
|
41
|
+
return address as core_Address.Address
|
|
42
|
+
}
|
|
11
43
|
|
|
12
44
|
/**
|
|
13
45
|
* Formats a raw Ethereum address (and optional zone ID) into a Tempo address string.
|
|
@@ -17,7 +49,7 @@ export type TempoAddress = `tempo1${string}` | `tempoz1${string}`
|
|
|
17
49
|
* import { TempoAddress } from 'ox/tempo'
|
|
18
50
|
*
|
|
19
51
|
* const address = TempoAddress.format('0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28')
|
|
20
|
-
* // @log: '
|
|
52
|
+
* // @log: 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0'
|
|
21
53
|
* ```
|
|
22
54
|
*
|
|
23
55
|
* @example
|
|
@@ -29,36 +61,29 @@ export type TempoAddress = `tempo1${string}` | `tempoz1${string}`
|
|
|
29
61
|
* '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28',
|
|
30
62
|
* { zoneId: 1 },
|
|
31
63
|
* )
|
|
32
|
-
* // @log: '
|
|
64
|
+
* // @log: 'tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj'
|
|
33
65
|
* ```
|
|
34
66
|
*
|
|
35
67
|
* @param address - The raw 20-byte Ethereum address.
|
|
36
68
|
* @param options - Options.
|
|
37
69
|
* @returns The encoded Tempo address string.
|
|
38
70
|
*/
|
|
39
|
-
export function format(
|
|
40
|
-
address: Address.Address,
|
|
41
|
-
options: format.Options = {},
|
|
42
|
-
): TempoAddress {
|
|
71
|
+
export function format(address: Address, options: format.Options = {}): Tempo {
|
|
43
72
|
const { zoneId } = options
|
|
44
73
|
|
|
45
|
-
const
|
|
74
|
+
const resolved = resolve(address)
|
|
75
|
+
const hrp = zoneId != null ? 'tempoz' : 'tempo'
|
|
76
|
+
const version = new Uint8Array([0x00])
|
|
46
77
|
const zone = zoneId != null ? CompactSize.toBytes(zoneId) : new Uint8Array()
|
|
47
|
-
const
|
|
78
|
+
const data = Bytes.concat(version, zone, Bytes.fromHex(resolved))
|
|
48
79
|
|
|
49
|
-
|
|
50
|
-
const checksum = Hash.sha256(Hash.sha256(input, { as: 'Bytes' }), {
|
|
51
|
-
as: 'Bytes',
|
|
52
|
-
}).slice(0, 4)
|
|
53
|
-
|
|
54
|
-
const payload = Bytes.concat(zone, address_bytes, checksum)
|
|
55
|
-
return `${prefix}${Base32.fromBytes(payload)}` as TempoAddress
|
|
80
|
+
return Bech32m.encode(hrp, data) as Tempo
|
|
56
81
|
}
|
|
57
82
|
|
|
58
83
|
export declare namespace format {
|
|
59
84
|
type Options = {
|
|
60
85
|
/** Zone ID for zone addresses. */
|
|
61
|
-
zoneId?:
|
|
86
|
+
zoneId?: number | bigint | undefined
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
type ErrorType = Errors.GlobalErrorType
|
|
@@ -73,9 +98,9 @@ export declare namespace format {
|
|
|
73
98
|
* import { TempoAddress } from 'ox/tempo'
|
|
74
99
|
*
|
|
75
100
|
* const result = TempoAddress.parse(
|
|
76
|
-
* '
|
|
101
|
+
* 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0',
|
|
77
102
|
* )
|
|
78
|
-
* // { address: '
|
|
103
|
+
* // @log: { address: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28', zoneId: undefined }
|
|
79
104
|
* ```
|
|
80
105
|
*
|
|
81
106
|
* @example
|
|
@@ -84,67 +109,55 @@ export declare namespace format {
|
|
|
84
109
|
* import { TempoAddress } from 'ox/tempo'
|
|
85
110
|
*
|
|
86
111
|
* const result = TempoAddress.parse(
|
|
87
|
-
* '
|
|
112
|
+
* 'tempoz1qqqhgtf4e3nrfszn9yj68wzyhj08t90jh55q74d9uj',
|
|
88
113
|
* )
|
|
89
|
-
* // { address: '
|
|
114
|
+
* // @log: { address: '0x742d35CC6634c0532925a3B844bc9e7595F2Bd28', zoneId: 1 }
|
|
90
115
|
* ```
|
|
91
116
|
*
|
|
92
117
|
* @param tempoAddress - The Tempo address string to parse.
|
|
93
118
|
* @returns The parsed raw address and optional zone ID.
|
|
94
119
|
*/
|
|
95
120
|
export function parse(tempoAddress: string): parse.ReturnType {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
prefix = 'tempo1'
|
|
105
|
-
hasZone = false
|
|
106
|
-
} else {
|
|
107
|
-
throw new InvalidPrefixError({ address: tempoAddress })
|
|
121
|
+
let hrp: string
|
|
122
|
+
let data: Uint8Array
|
|
123
|
+
try {
|
|
124
|
+
const decoded = Bech32m.decode(tempoAddress)
|
|
125
|
+
hrp = decoded.hrp
|
|
126
|
+
data = decoded.data
|
|
127
|
+
} catch {
|
|
128
|
+
throw new InvalidChecksumError({ address: tempoAddress })
|
|
108
129
|
}
|
|
109
130
|
|
|
110
|
-
|
|
131
|
+
if (hrp !== 'tempo' && hrp !== 'tempoz')
|
|
132
|
+
throw new InvalidPrefixError({ address: tempoAddress })
|
|
133
|
+
|
|
134
|
+
if (data.length < 1 || data[0] !== 0x00)
|
|
135
|
+
throw new InvalidVersionError({
|
|
136
|
+
address: tempoAddress,
|
|
137
|
+
version: data.length > 0 ? data[0]! : undefined,
|
|
138
|
+
})
|
|
111
139
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
140
|
+
const payload = data.slice(1)
|
|
141
|
+
|
|
142
|
+
let zoneId: number | bigint | undefined
|
|
143
|
+
let rawAddress: Uint8Array
|
|
144
|
+
if (hrp === 'tempoz') {
|
|
115
145
|
const { value, size } = CompactSize.fromBytes(payload)
|
|
116
146
|
zoneId = value
|
|
117
|
-
|
|
147
|
+
rawAddress = payload.slice(size)
|
|
118
148
|
} else {
|
|
119
149
|
zoneId = undefined
|
|
120
|
-
|
|
150
|
+
rawAddress = payload
|
|
121
151
|
}
|
|
122
152
|
|
|
123
|
-
if (
|
|
153
|
+
if (rawAddress.length !== 20)
|
|
124
154
|
throw new InvalidLengthError({
|
|
125
155
|
address: tempoAddress,
|
|
126
|
-
expected:
|
|
127
|
-
actual:
|
|
156
|
+
expected: 20,
|
|
157
|
+
actual: rawAddress.length,
|
|
128
158
|
})
|
|
129
159
|
|
|
130
|
-
const
|
|
131
|
-
const checksum = remaining.slice(20, 24)
|
|
132
|
-
|
|
133
|
-
const zoneBytes =
|
|
134
|
-
zoneId != null ? CompactSize.toBytes(zoneId) : new Uint8Array()
|
|
135
|
-
const checksumInput = Bytes.concat(
|
|
136
|
-
Bytes.fromString(prefix),
|
|
137
|
-
zoneBytes,
|
|
138
|
-
rawAddress,
|
|
139
|
-
)
|
|
140
|
-
const expected = Hash.sha256(Hash.sha256(checksumInput, { as: 'Bytes' }), {
|
|
141
|
-
as: 'Bytes',
|
|
142
|
-
}).slice(0, 4)
|
|
143
|
-
|
|
144
|
-
if (!Bytes.isEqual(checksum, expected))
|
|
145
|
-
throw new InvalidChecksumError({ address: tempoAddress })
|
|
146
|
-
|
|
147
|
-
const address = Address.checksum(Hex.fromBytes(rawAddress) as Address.Address)
|
|
160
|
+
const address = core_Address.checksum(Hex.fromBytes(rawAddress) as never)
|
|
148
161
|
|
|
149
162
|
return { address, zoneId }
|
|
150
163
|
}
|
|
@@ -152,13 +165,14 @@ export function parse(tempoAddress: string): parse.ReturnType {
|
|
|
152
165
|
export declare namespace parse {
|
|
153
166
|
type ReturnType = {
|
|
154
167
|
/** The raw 20-byte Ethereum address. */
|
|
155
|
-
address:
|
|
168
|
+
address: core_Address.Address
|
|
156
169
|
/** The zone ID, or `undefined` for mainnet addresses. */
|
|
157
|
-
zoneId: bigint | undefined
|
|
170
|
+
zoneId: number | bigint | undefined
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
type ErrorType =
|
|
161
174
|
| InvalidPrefixError
|
|
175
|
+
| InvalidVersionError
|
|
162
176
|
| InvalidLengthError
|
|
163
177
|
| InvalidChecksumError
|
|
164
178
|
| Errors.GlobalErrorType
|
|
@@ -172,9 +186,9 @@ export declare namespace parse {
|
|
|
172
186
|
* import { TempoAddress } from 'ox/tempo'
|
|
173
187
|
*
|
|
174
188
|
* const valid = TempoAddress.validate(
|
|
175
|
-
* '
|
|
189
|
+
* 'tempo1qp6z6dwvvc6vq5efyk3ms39une6etu4a9qtj2kk0',
|
|
176
190
|
* )
|
|
177
|
-
* // true
|
|
191
|
+
* // @log: true
|
|
178
192
|
* ```
|
|
179
193
|
*
|
|
180
194
|
* @param tempoAddress - The Tempo address string to validate.
|
|
@@ -200,6 +214,20 @@ export class InvalidPrefixError extends Errors.BaseError {
|
|
|
200
214
|
}
|
|
201
215
|
}
|
|
202
216
|
|
|
217
|
+
/** Thrown when a Tempo address has an unsupported version byte. */
|
|
218
|
+
export class InvalidVersionError extends Errors.BaseError {
|
|
219
|
+
override readonly name = 'TempoAddress.InvalidVersionError'
|
|
220
|
+
|
|
221
|
+
constructor({
|
|
222
|
+
address,
|
|
223
|
+
version,
|
|
224
|
+
}: { address: string; version: number | undefined }) {
|
|
225
|
+
super(
|
|
226
|
+
`Tempo address "${address}" has unsupported version ${version === undefined ? '(missing)' : `0x${version.toString(16).padStart(2, '0')}`}. Only version 0x00 is supported.`,
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
203
231
|
/** Thrown when a Tempo address has an invalid payload length. */
|
|
204
232
|
export class InvalidLengthError extends Errors.BaseError {
|
|
205
233
|
override readonly name = 'TempoAddress.InvalidLengthError'
|
package/tempo/TokenId.test.ts
CHANGED
|
@@ -23,6 +23,10 @@ test('fromAddress', () => {
|
|
|
23
23
|
expect(
|
|
24
24
|
TokenId.fromAddress('0x20c0000000000000000000000000000000000def'),
|
|
25
25
|
).toBe(0xdefn)
|
|
26
|
+
|
|
27
|
+
// tempo address input
|
|
28
|
+
const tempoAddr = 'tempo1qqsvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyr9xgnd'
|
|
29
|
+
expect(TokenId.fromAddress(tempoAddr)).toBe(1n)
|
|
26
30
|
})
|
|
27
31
|
|
|
28
32
|
test('toAddress', () => {
|
|
@@ -38,6 +42,12 @@ test('toAddress', () => {
|
|
|
38
42
|
expect(TokenId.toAddress(0xdefn)).toBe(
|
|
39
43
|
'0x20c0000000000000000000000000000000000def',
|
|
40
44
|
)
|
|
45
|
+
|
|
46
|
+
// tempo address input
|
|
47
|
+
const tempoAddr = 'tempo1qqsvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyr9xgnd'
|
|
48
|
+
expect(TokenId.toAddress(tempoAddr)).toBe(
|
|
49
|
+
'0x20C0000000000000000000000000000000000001',
|
|
50
|
+
)
|
|
41
51
|
})
|
|
42
52
|
|
|
43
53
|
test('compute', () => {
|
|
@@ -76,4 +86,8 @@ test('compute', () => {
|
|
|
76
86
|
const otherSender = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd'
|
|
77
87
|
const address3 = TokenId.compute({ sender: otherSender, salt: salt1 })
|
|
78
88
|
expect(address3).not.toBe(address1)
|
|
89
|
+
|
|
90
|
+
// tempo address input produces same result
|
|
91
|
+
const tempoSender = 'tempo1qqfrg4ncjqfrg4ncjqfrg4ncjqfrg4ncjqgmv79k'
|
|
92
|
+
expect(TokenId.compute({ sender: tempoSender, salt: salt1 })).toBe(id1)
|
|
79
93
|
})
|
package/tempo/TokenId.ts
CHANGED
|
@@ -2,11 +2,12 @@ import * as AbiParameters from '../core/AbiParameters.js'
|
|
|
2
2
|
import * as Address from '../core/Address.js'
|
|
3
3
|
import * as Hash from '../core/Hash.js'
|
|
4
4
|
import * as Hex from '../core/Hex.js'
|
|
5
|
+
import * as TempoAddress from './TempoAddress.js'
|
|
5
6
|
|
|
6
7
|
const tip20Prefix = '0x20c0'
|
|
7
8
|
|
|
8
9
|
export type TokenId = bigint
|
|
9
|
-
export type TokenIdOrAddress = TokenId |
|
|
10
|
+
export type TokenIdOrAddress = TokenId | TempoAddress.Address
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Converts a token ID or address to a token ID.
|
|
@@ -44,16 +45,17 @@ export function from(tokenIdOrAddress: TokenIdOrAddress | number): TokenId {
|
|
|
44
45
|
* ```ts twoslash
|
|
45
46
|
* import { TokenId } from 'ox/tempo'
|
|
46
47
|
*
|
|
47
|
-
* const tokenId = TokenId.fromAddress('
|
|
48
|
+
* const tokenId = TokenId.fromAddress('0x20c0000000000000000000000000000000000001')
|
|
48
49
|
* ```
|
|
49
50
|
*
|
|
50
51
|
* @param address - The token address.
|
|
51
52
|
* @returns The token ID.
|
|
52
53
|
*/
|
|
53
|
-
export function fromAddress(address:
|
|
54
|
-
|
|
54
|
+
export function fromAddress(address: TempoAddress.Address): TokenId {
|
|
55
|
+
const resolved = TempoAddress.resolve(address)
|
|
56
|
+
if (!resolved.toLowerCase().startsWith(tip20Prefix))
|
|
55
57
|
throw new Error('invalid tip20 address.')
|
|
56
|
-
return Hex.toBigInt(Hex.slice(
|
|
58
|
+
return Hex.toBigInt(Hex.slice(resolved, tip20Prefix.length))
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
/**
|
|
@@ -73,8 +75,9 @@ export function fromAddress(address: Address.Address): TokenId {
|
|
|
73
75
|
*/
|
|
74
76
|
export function toAddress(tokenId: TokenIdOrAddress): Address.Address {
|
|
75
77
|
if (typeof tokenId === 'string') {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
const resolved = TempoAddress.resolve(tokenId as TempoAddress.Address)
|
|
79
|
+
Address.assert(resolved)
|
|
80
|
+
return resolved
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
const tokenIdHex = Hex.fromNumber(tokenId, { size: 18 })
|
|
@@ -104,7 +107,7 @@ export function toAddress(tokenId: TokenIdOrAddress): Address.Address {
|
|
|
104
107
|
export function compute(value: compute.Value): bigint {
|
|
105
108
|
const hash = Hash.keccak256(
|
|
106
109
|
AbiParameters.encode(AbiParameters.from('address, bytes32'), [
|
|
107
|
-
value.sender,
|
|
110
|
+
TempoAddress.resolve(value.sender),
|
|
108
111
|
value.salt,
|
|
109
112
|
]),
|
|
110
113
|
)
|
|
@@ -115,7 +118,7 @@ export declare namespace compute {
|
|
|
115
118
|
export type Value = {
|
|
116
119
|
/** The salt (32 bytes). */
|
|
117
120
|
salt: Hex.Hex
|
|
118
|
-
/** The sender address. */
|
|
119
|
-
sender:
|
|
121
|
+
/** The sender address. Accepts both hex and Tempo bech32m addresses. */
|
|
122
|
+
sender: TempoAddress.Address
|
|
120
123
|
}
|
|
121
124
|
}
|
|
@@ -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",
|
|
@@ -4,6 +4,7 @@ import type { Compute } from '../core/internal/types.js'
|
|
|
4
4
|
import * as ox_TransactionRequest from '../core/TransactionRequest.js'
|
|
5
5
|
import * as AuthorizationTempo from './AuthorizationTempo.js'
|
|
6
6
|
import * as KeyAuthorization from './KeyAuthorization.js'
|
|
7
|
+
import * as TempoAddress from './TempoAddress.js'
|
|
7
8
|
import * as TokenId from './TokenId.js'
|
|
8
9
|
import * as Transaction from './Transaction.js'
|
|
9
10
|
import type { Call } from './TxEnvelopeTempo.js'
|
|
@@ -30,7 +31,7 @@ export type TransactionRequest<
|
|
|
30
31
|
authorizationList?:
|
|
31
32
|
| AuthorizationTempo.ListSigned<bigintType, numberType>
|
|
32
33
|
| undefined
|
|
33
|
-
calls?: readonly Call<bigintType>[] | undefined
|
|
34
|
+
calls?: readonly Call<bigintType, TempoAddress.Address>[] | undefined
|
|
34
35
|
keyAuthorization?: KeyAuthorization.KeyAuthorization<true> | undefined
|
|
35
36
|
keyData?: Hex.Hex | undefined
|
|
36
37
|
keyType?: KeyType | undefined
|
|
@@ -112,7 +113,7 @@ export function toRpc(request: TransactionRequest): Rpc {
|
|
|
112
113
|
)
|
|
113
114
|
if (request.calls)
|
|
114
115
|
request_rpc.calls = request.calls.map((call) => ({
|
|
115
|
-
to: call.to,
|
|
116
|
+
to: call.to ? TempoAddress.resolve(call.to) : call.to,
|
|
116
117
|
value: call.value ? Hex.fromNumber(call.value) : '0x',
|
|
117
118
|
data: call.data ?? '0x',
|
|
118
119
|
}))
|