ox 0.14.0 → 0.14.2
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/CHANGELOG.md +12 -0
- package/_cjs/core/internal/webauthn.js +2 -2
- package/_cjs/core/internal/webauthn.js.map +1 -1
- package/_cjs/tempo/AuthorizationTempo.js +7 -2
- package/_cjs/tempo/AuthorizationTempo.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +18 -4
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +27 -7
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TempoAddress.js +10 -3
- 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 +13 -3
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_cjs/webauthn/Authentication.js +9 -5
- package/_cjs/webauthn/Authentication.js.map +1 -1
- package/_cjs/webauthn/Registration.js +8 -5
- package/_cjs/webauthn/Registration.js.map +1 -1
- package/_esm/core/internal/webauthn.js +5 -2
- package/_esm/core/internal/webauthn.js.map +1 -1
- package/_esm/tempo/AuthorizationTempo.js +24 -19
- package/_esm/tempo/AuthorizationTempo.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +19 -4
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +27 -7
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TempoAddress.js +33 -3
- 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 +26 -15
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/tempo/index.js +9 -8
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_esm/webauthn/Authentication.js +12 -5
- package/_esm/webauthn/Authentication.js.map +1 -1
- package/_esm/webauthn/Registration.js +11 -5
- package/_esm/webauthn/Registration.js.map +1 -1
- package/_types/core/internal/webauthn.d.ts +4 -1
- package/_types/core/internal/webauthn.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 +4 -2
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +11 -2
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TempoAddress.d.ts +32 -4
- 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 +19 -18
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +9 -8
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/_types/webauthn/Authentication.d.ts.map +1 -1
- package/_types/webauthn/Registration.d.ts.map +1 -1
- package/core/internal/webauthn.ts +4 -1
- package/package.json +1 -1
- package/tempo/AuthorizationTempo.test.ts +13 -0
- package/tempo/AuthorizationTempo.ts +27 -21
- package/tempo/KeyAuthorization.test.ts +25 -0
- package/tempo/KeyAuthorization.ts +23 -7
- package/tempo/PoolId.test.ts +9 -0
- package/tempo/SignatureEnvelope.test.ts +108 -0
- package/tempo/SignatureEnvelope.ts +47 -9
- package/tempo/TempoAddress.test.ts +21 -0
- package/tempo/TempoAddress.ts +41 -10
- package/tempo/TokenId.test.ts +14 -0
- package/tempo/TokenId.ts +13 -10
- package/tempo/TransactionRequest.ts +3 -2
- package/tempo/TxEnvelopeTempo.test.ts +72 -0
- package/tempo/TxEnvelopeTempo.ts +37 -21
- package/tempo/e2e.test.ts +10 -21
- package/tempo/index.ts +9 -8
- package/version.ts +1 -1
- package/webauthn/Authentication.ts +14 -11
- package/webauthn/Registration.ts +17 -7
|
@@ -5,6 +5,7 @@ import * as Hex from '../core/Hex.js'
|
|
|
5
5
|
import type { Compute } from '../core/internal/types.js'
|
|
6
6
|
import * as Rlp from '../core/Rlp.js'
|
|
7
7
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
8
|
+
import * as TempoAddress from './TempoAddress.js'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Key authorization for provisioning access keys.
|
|
@@ -33,7 +34,7 @@ export type KeyAuthorization<
|
|
|
33
34
|
numberType = number,
|
|
34
35
|
> = {
|
|
35
36
|
/** Address derived from the public key of the key type. */
|
|
36
|
-
address:
|
|
37
|
+
address: TempoAddress.Address
|
|
37
38
|
/** Chain ID for replay protection. */
|
|
38
39
|
chainId: bigintType
|
|
39
40
|
/** Unix timestamp when key expires (0 = never expires). */
|
|
@@ -107,7 +108,7 @@ export type Tuple<signed extends boolean = boolean> = signed extends true
|
|
|
107
108
|
*/
|
|
108
109
|
export type TokenLimit<bigintType = bigint> = {
|
|
109
110
|
/** Address of the TIP-20 token. */
|
|
110
|
-
token:
|
|
111
|
+
token: TempoAddress.Address
|
|
111
112
|
/** Maximum spending amount for this token (enforced over the key's lifetime). */
|
|
112
113
|
limit: bigintType
|
|
113
114
|
}
|
|
@@ -255,14 +256,28 @@ export function from<
|
|
|
255
256
|
authorization: authorization | KeyAuthorization,
|
|
256
257
|
options: from.Options<signature> = {},
|
|
257
258
|
): from.ReturnType<authorization, signature> {
|
|
258
|
-
if (
|
|
259
|
-
|
|
259
|
+
if ('keyId' in authorization) return fromRpc(authorization as Rpc) as never
|
|
260
|
+
const auth = authorization as KeyAuthorization & {
|
|
261
|
+
limits?: readonly { token: TempoAddress.Address; limit: bigint }[]
|
|
262
|
+
}
|
|
263
|
+
const resolved = {
|
|
264
|
+
...auth,
|
|
265
|
+
address: TempoAddress.resolve(auth.address as TempoAddress.Address),
|
|
266
|
+
...(auth.limits
|
|
267
|
+
? {
|
|
268
|
+
limits: auth.limits.map((l) => ({
|
|
269
|
+
...l,
|
|
270
|
+
token: TempoAddress.resolve(l.token as TempoAddress.Address),
|
|
271
|
+
})),
|
|
272
|
+
}
|
|
273
|
+
: {}),
|
|
274
|
+
}
|
|
260
275
|
if (options.signature)
|
|
261
276
|
return {
|
|
262
|
-
...
|
|
277
|
+
...resolved,
|
|
263
278
|
signature: SignatureEnvelope.from(options.signature),
|
|
264
279
|
} as never
|
|
265
|
-
return
|
|
280
|
+
return resolved as never
|
|
266
281
|
}
|
|
267
282
|
|
|
268
283
|
export declare namespace from {
|
|
@@ -300,6 +315,7 @@ export declare namespace from {
|
|
|
300
315
|
* import { KeyAuthorization } from 'ox/tempo'
|
|
301
316
|
*
|
|
302
317
|
* const keyAuthorization = KeyAuthorization.fromRpc({
|
|
318
|
+
* chainId: '0x1079',
|
|
303
319
|
* expiry: '0x174876e800',
|
|
304
320
|
* keyId: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
|
|
305
321
|
* keyType: 'secp256k1',
|
|
@@ -619,7 +635,7 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
619
635
|
token,
|
|
620
636
|
limit: Hex.fromNumber(limit),
|
|
621
637
|
})),
|
|
622
|
-
keyId: address,
|
|
638
|
+
keyId: TempoAddress.resolve(address),
|
|
623
639
|
signature: SignatureEnvelope.toRpc(signature),
|
|
624
640
|
keyType: type,
|
|
625
641
|
}
|
package/tempo/PoolId.test.ts
CHANGED
|
@@ -30,4 +30,13 @@ test('from', () => {
|
|
|
30
30
|
validatorToken: 1n,
|
|
31
31
|
})
|
|
32
32
|
expect(poolId4).toBe(poolId1)
|
|
33
|
+
|
|
34
|
+
// Test with tempo address inputs
|
|
35
|
+
const tempoAddr0 = 'tempo1qqsvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv0ywuh'
|
|
36
|
+
const tempoAddr1 = 'tempo1qqsvqqqqqqqqqqqqqqqqqqqqqqqqqqqqqyr9xgnd'
|
|
37
|
+
const poolId5 = PoolId.from({
|
|
38
|
+
userToken: tempoAddr0,
|
|
39
|
+
validatorToken: tempoAddr1,
|
|
40
|
+
})
|
|
41
|
+
expect(poolId5).toBe(poolId1)
|
|
33
42
|
})
|
|
@@ -1040,6 +1040,61 @@ describe('from', () => {
|
|
|
1040
1040
|
|
|
1041
1041
|
expect(envelope.type).toBe('keychain')
|
|
1042
1042
|
})
|
|
1043
|
+
|
|
1044
|
+
test('behavior: computes keyId from p256 inner publicKey', () => {
|
|
1045
|
+
const envelope = SignatureEnvelope.from({
|
|
1046
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
1047
|
+
inner: signature_p256,
|
|
1048
|
+
})
|
|
1049
|
+
|
|
1050
|
+
expect(envelope.keyId).toBe(Address.fromPublicKey(publicKey))
|
|
1051
|
+
})
|
|
1052
|
+
|
|
1053
|
+
test('behavior: computes keyId from webAuthn inner publicKey', () => {
|
|
1054
|
+
const envelope = SignatureEnvelope.from({
|
|
1055
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
1056
|
+
inner: signature_webauthn,
|
|
1057
|
+
})
|
|
1058
|
+
|
|
1059
|
+
expect(envelope.keyId).toBe(Address.fromPublicKey(publicKey))
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
test('behavior: computes keyId from secp256k1 inner with payload', () => {
|
|
1063
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
1064
|
+
const accessKeyPublicKey = Secp256k1.getPublicKey({ privateKey })
|
|
1065
|
+
const payload = '0xdeadbeef'
|
|
1066
|
+
const sig = Secp256k1.sign({ payload, privateKey })
|
|
1067
|
+
|
|
1068
|
+
const envelope = SignatureEnvelope.from(
|
|
1069
|
+
{
|
|
1070
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
1071
|
+
inner: SignatureEnvelope.from(sig),
|
|
1072
|
+
},
|
|
1073
|
+
{ payload },
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
expect(envelope.keyId).toBe(Address.fromPublicKey(accessKeyPublicKey))
|
|
1077
|
+
})
|
|
1078
|
+
|
|
1079
|
+
test('behavior: does not compute keyId for secp256k1 without payload', () => {
|
|
1080
|
+
const envelope = SignatureEnvelope.from({
|
|
1081
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
1082
|
+
inner: SignatureEnvelope.from(signature_secp256k1),
|
|
1083
|
+
})
|
|
1084
|
+
|
|
1085
|
+
expect(envelope.keyId).toBeUndefined()
|
|
1086
|
+
})
|
|
1087
|
+
|
|
1088
|
+
test('behavior: preserves explicit keyId', () => {
|
|
1089
|
+
const explicitKeyId = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
1090
|
+
const envelope = SignatureEnvelope.from({
|
|
1091
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
1092
|
+
inner: signature_p256,
|
|
1093
|
+
keyId: explicitKeyId,
|
|
1094
|
+
})
|
|
1095
|
+
|
|
1096
|
+
expect(envelope.keyId).toBe(explicitKeyId)
|
|
1097
|
+
})
|
|
1043
1098
|
})
|
|
1044
1099
|
})
|
|
1045
1100
|
|
|
@@ -2235,6 +2290,24 @@ describe('fromRpc', () => {
|
|
|
2235
2290
|
},
|
|
2236
2291
|
})
|
|
2237
2292
|
})
|
|
2293
|
+
|
|
2294
|
+
test('behavior: preserves keyId from RPC', () => {
|
|
2295
|
+
const rpc: SignatureEnvelope.KeychainRpc = {
|
|
2296
|
+
type: 'keychain',
|
|
2297
|
+
userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
|
|
2298
|
+
keyId: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
|
|
2299
|
+
signature: {
|
|
2300
|
+
type: 'secp256k1',
|
|
2301
|
+
r: '0xa2bb71146c20ce932456c043ebb2973ed205e07cd32c35a60bdefca1285fd132',
|
|
2302
|
+
s: '0x7cba10692bccdbfba9a215418443c2903dbee6fe5cb55c91172e47efc607840e',
|
|
2303
|
+
yParity: '0x1',
|
|
2304
|
+
},
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
const envelope = SignatureEnvelope.fromRpc(rpc)
|
|
2308
|
+
|
|
2309
|
+
expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
|
|
2310
|
+
})
|
|
2238
2311
|
})
|
|
2239
2312
|
})
|
|
2240
2313
|
|
|
@@ -2352,6 +2425,41 @@ describe('toRpc', () => {
|
|
|
2352
2425
|
expect(typeof rpc.signature.pubKeyX).toBe('string')
|
|
2353
2426
|
expect(typeof rpc.signature.webauthnData).toBe('string')
|
|
2354
2427
|
})
|
|
2428
|
+
|
|
2429
|
+
test('behavior: preserves keyId in toRpc', () => {
|
|
2430
|
+
const envelope: SignatureEnvelope.Keychain = {
|
|
2431
|
+
type: 'keychain',
|
|
2432
|
+
userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
|
|
2433
|
+
keyId: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
|
|
2434
|
+
inner: {
|
|
2435
|
+
signature: signature_secp256k1,
|
|
2436
|
+
type: 'secp256k1',
|
|
2437
|
+
},
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
2441
|
+
envelope,
|
|
2442
|
+
) as SignatureEnvelope.KeychainRpc
|
|
2443
|
+
|
|
2444
|
+
expect(rpc.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
|
|
2445
|
+
})
|
|
2446
|
+
|
|
2447
|
+
test('behavior: omits keyId in toRpc when not set', () => {
|
|
2448
|
+
const envelope: SignatureEnvelope.Keychain = {
|
|
2449
|
+
type: 'keychain',
|
|
2450
|
+
userAddress: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
|
|
2451
|
+
inner: {
|
|
2452
|
+
signature: signature_secp256k1,
|
|
2453
|
+
type: 'secp256k1',
|
|
2454
|
+
},
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
const rpc = SignatureEnvelope.toRpc(
|
|
2458
|
+
envelope,
|
|
2459
|
+
) as SignatureEnvelope.KeychainRpc
|
|
2460
|
+
|
|
2461
|
+
expect(rpc.keyId).toBeUndefined()
|
|
2462
|
+
})
|
|
2355
2463
|
})
|
|
2356
2464
|
})
|
|
2357
2465
|
|
|
@@ -122,6 +122,8 @@ export type Keychain<bigintType = bigint, numberType = number> = {
|
|
|
122
122
|
userAddress: Address.Address
|
|
123
123
|
/** The actual signature from the access key (can be Secp256k1, P256, or WebAuthn) */
|
|
124
124
|
inner: SignatureEnvelope<bigintType, numberType>
|
|
125
|
+
/** The access key address (recovered address of the access key signer). */
|
|
126
|
+
keyId?: Address.Address | undefined
|
|
125
127
|
type: 'keychain'
|
|
126
128
|
/** Keychain signature version. @default 'v1' */
|
|
127
129
|
version?: KeychainVersion | undefined
|
|
@@ -130,6 +132,7 @@ export type Keychain<bigintType = bigint, numberType = number> = {
|
|
|
130
132
|
export type KeychainRpc = {
|
|
131
133
|
type: 'keychain'
|
|
132
134
|
userAddress: Address.Address
|
|
135
|
+
keyId?: Address.Address | undefined
|
|
133
136
|
signature: SignatureEnvelopeRpc
|
|
134
137
|
version?: KeychainVersion | undefined
|
|
135
138
|
}
|
|
@@ -663,6 +666,7 @@ export function deserialize(value: Serialized): SignatureEnvelope {
|
|
|
663
666
|
*/
|
|
664
667
|
export function from<const value extends from.Value>(
|
|
665
668
|
value: value | from.Value,
|
|
669
|
+
options?: from.Options | undefined,
|
|
666
670
|
): from.ReturnValue<value> {
|
|
667
671
|
if (typeof value === 'string') return deserialize(value) as never
|
|
668
672
|
|
|
@@ -679,20 +683,45 @@ export function from<const value extends from.Value>(
|
|
|
679
683
|
return {
|
|
680
684
|
...value,
|
|
681
685
|
...(type === 'p256' ? { prehash: value.prehash } : {}),
|
|
682
|
-
...(type === 'keychain'
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
686
|
+
...(type === 'keychain'
|
|
687
|
+
? {
|
|
688
|
+
...(!(
|
|
689
|
+
typeof value === 'object' &&
|
|
690
|
+
value !== null &&
|
|
691
|
+
'version' in value &&
|
|
692
|
+
value.version
|
|
693
|
+
)
|
|
694
|
+
? { version: 'v2' }
|
|
695
|
+
: {}),
|
|
696
|
+
...(!(typeof value === 'object' && 'keyId' in value && value.keyId)
|
|
697
|
+
? (() => {
|
|
698
|
+
const inner = (value as Keychain).inner
|
|
699
|
+
if (inner.type === 'p256' || inner.type === 'webAuthn')
|
|
700
|
+
return { keyId: Address.fromPublicKey(inner.publicKey) }
|
|
701
|
+
if (inner.type === 'secp256k1' && options?.payload)
|
|
702
|
+
return {
|
|
703
|
+
keyId: Address.fromPublicKey(
|
|
704
|
+
ox_Secp256k1.recoverPublicKey({
|
|
705
|
+
payload: options.payload,
|
|
706
|
+
signature: inner.signature,
|
|
707
|
+
}),
|
|
708
|
+
),
|
|
709
|
+
}
|
|
710
|
+
return {}
|
|
711
|
+
})()
|
|
712
|
+
: {}),
|
|
713
|
+
}
|
|
690
714
|
: {}),
|
|
691
715
|
type,
|
|
692
716
|
} as never
|
|
693
717
|
}
|
|
694
718
|
|
|
695
719
|
export declare namespace from {
|
|
720
|
+
type Options = {
|
|
721
|
+
/** Payload that was signed. Used to recover `keyId` for keychain envelopes with secp256k1 inner signatures. */
|
|
722
|
+
payload?: Hex.Hex | Bytes.Bytes | undefined
|
|
723
|
+
}
|
|
724
|
+
|
|
696
725
|
type Value =
|
|
697
726
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
698
727
|
| Secp256k1Flat
|
|
@@ -706,7 +735,14 @@ export declare namespace from {
|
|
|
706
735
|
? Secp256k1
|
|
707
736
|
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
708
737
|
? SignatureEnvelope
|
|
709
|
-
: Assign<
|
|
738
|
+
: Assign<
|
|
739
|
+
value,
|
|
740
|
+
{
|
|
741
|
+
readonly type: GetType<value>
|
|
742
|
+
} & (GetType<value> extends 'keychain'
|
|
743
|
+
? { keyId?: Address.Address | undefined }
|
|
744
|
+
: {})
|
|
745
|
+
>
|
|
710
746
|
>
|
|
711
747
|
>
|
|
712
748
|
}
|
|
@@ -806,6 +842,7 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
|
806
842
|
type: 'keychain',
|
|
807
843
|
userAddress: envelope.userAddress,
|
|
808
844
|
inner: fromRpc(envelope.signature),
|
|
845
|
+
...(envelope.keyId ? { keyId: envelope.keyId } : {}),
|
|
809
846
|
...(envelope.version ? { version: envelope.version } : {}),
|
|
810
847
|
}
|
|
811
848
|
|
|
@@ -1053,6 +1090,7 @@ export function toRpc(envelope: SignatureEnvelope): SignatureEnvelopeRpc {
|
|
|
1053
1090
|
type: 'keychain',
|
|
1054
1091
|
userAddress: keychain.userAddress,
|
|
1055
1092
|
signature: toRpc(keychain.inner),
|
|
1093
|
+
...(keychain.keyId ? { keyId: keychain.keyId } : {}),
|
|
1056
1094
|
...(keychain.version ? { version: keychain.version } : {}),
|
|
1057
1095
|
}
|
|
1058
1096
|
}
|
|
@@ -6,6 +6,22 @@ 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(
|
|
@@ -13,6 +29,11 @@ describe('format', () => {
|
|
|
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 }),
|
package/tempo/TempoAddress.ts
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as core_Address from '../core/Address.js'
|
|
2
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
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
|
|
7
11
|
|
|
8
12
|
/** Root type for a Tempo Address. */
|
|
9
|
-
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
|
+
}
|
|
10
43
|
|
|
11
44
|
/**
|
|
12
45
|
* Formats a raw Ethereum address (and optional zone ID) into a Tempo address string.
|
|
@@ -35,18 +68,16 @@ export type TempoAddress = `tempo1${string}` | `tempoz1${string}`
|
|
|
35
68
|
* @param options - Options.
|
|
36
69
|
* @returns The encoded Tempo address string.
|
|
37
70
|
*/
|
|
38
|
-
export function format(
|
|
39
|
-
address: Address.Address,
|
|
40
|
-
options: format.Options = {},
|
|
41
|
-
): TempoAddress {
|
|
71
|
+
export function format(address: Address, options: format.Options = {}): Tempo {
|
|
42
72
|
const { zoneId } = options
|
|
43
73
|
|
|
74
|
+
const resolved = resolve(address)
|
|
44
75
|
const hrp = zoneId != null ? 'tempoz' : 'tempo'
|
|
45
76
|
const version = new Uint8Array([0x00])
|
|
46
77
|
const zone = zoneId != null ? CompactSize.toBytes(zoneId) : new Uint8Array()
|
|
47
|
-
const data = Bytes.concat(version, zone, Bytes.fromHex(
|
|
78
|
+
const data = Bytes.concat(version, zone, Bytes.fromHex(resolved))
|
|
48
79
|
|
|
49
|
-
return Bech32m.encode(hrp, data) as
|
|
80
|
+
return Bech32m.encode(hrp, data) as Tempo
|
|
50
81
|
}
|
|
51
82
|
|
|
52
83
|
export declare namespace format {
|
|
@@ -126,7 +157,7 @@ export function parse(tempoAddress: string): parse.ReturnType {
|
|
|
126
157
|
actual: rawAddress.length,
|
|
127
158
|
})
|
|
128
159
|
|
|
129
|
-
const address =
|
|
160
|
+
const address = core_Address.checksum(Hex.fromBytes(rawAddress) as never)
|
|
130
161
|
|
|
131
162
|
return { address, zoneId }
|
|
132
163
|
}
|
|
@@ -134,7 +165,7 @@ export function parse(tempoAddress: string): parse.ReturnType {
|
|
|
134
165
|
export declare namespace parse {
|
|
135
166
|
type ReturnType = {
|
|
136
167
|
/** The raw 20-byte Ethereum address. */
|
|
137
|
-
address:
|
|
168
|
+
address: core_Address.Address
|
|
138
169
|
/** The zone ID, or `undefined` for mainnet addresses. */
|
|
139
170
|
zoneId: number | bigint | undefined
|
|
140
171
|
}
|
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
|
}
|
|
@@ -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
|
}))
|
|
@@ -692,6 +692,32 @@ describe('from', () => {
|
|
|
692
692
|
}
|
|
693
693
|
})
|
|
694
694
|
|
|
695
|
+
test('tempo address input for calls.to', () => {
|
|
696
|
+
const hexAddr = '0x70997970c51812dc3a010c7d01b50e0d17dc79c8'
|
|
697
|
+
const tempoAddr = 'tempo1qpcfj7tsc5vp9hp6qyx86qd4pcx30hreeqlrsqqr'
|
|
698
|
+
|
|
699
|
+
const envelope = TxEnvelopeTempo.from({
|
|
700
|
+
chainId: 1,
|
|
701
|
+
calls: [{ to: tempoAddr }],
|
|
702
|
+
nonce: 0n,
|
|
703
|
+
nonceKey: 0n,
|
|
704
|
+
})
|
|
705
|
+
expect(envelope.calls[0]!.to).toBe(Address.checksum(hexAddr))
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
test('tempo address input for from', () => {
|
|
709
|
+
const hexAddr = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
|
|
710
|
+
const tempoAddr = 'tempo1qreel4h9r2kc3ah5ee4t3qnj088llwfzvccugvl7'
|
|
711
|
+
|
|
712
|
+
const envelope = TxEnvelopeTempo.from({
|
|
713
|
+
chainId: 1,
|
|
714
|
+
calls: [{}],
|
|
715
|
+
nonce: 0n,
|
|
716
|
+
from: tempoAddr as any,
|
|
717
|
+
})
|
|
718
|
+
expect(envelope.from).toBe(Address.checksum(hexAddr))
|
|
719
|
+
})
|
|
720
|
+
|
|
695
721
|
test('options: signature', () => {
|
|
696
722
|
const envelope = TxEnvelopeTempo.from(
|
|
697
723
|
{
|
|
@@ -1382,6 +1408,29 @@ describe('getSignPayload', () => {
|
|
|
1382
1408
|
`"0xe1222a45806457acbe3a13940aae4c34f3180659fa16613b5a45dc183adae07c"`,
|
|
1383
1409
|
)
|
|
1384
1410
|
})
|
|
1411
|
+
|
|
1412
|
+
test('tempo address input for from', () => {
|
|
1413
|
+
const hexAddr = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
|
|
1414
|
+
const tempoAddr = 'tempo1qreel4h9r2kc3ah5ee4t3qnj088llwfzvccugvl7'
|
|
1415
|
+
|
|
1416
|
+
const transaction = TxEnvelopeTempo.from({
|
|
1417
|
+
chainId: 1,
|
|
1418
|
+
calls: [
|
|
1419
|
+
{
|
|
1420
|
+
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
|
|
1421
|
+
},
|
|
1422
|
+
],
|
|
1423
|
+
nonce: 0n,
|
|
1424
|
+
})
|
|
1425
|
+
|
|
1426
|
+
const hashHex = TxEnvelopeTempo.getSignPayload(transaction, {
|
|
1427
|
+
from: hexAddr,
|
|
1428
|
+
})
|
|
1429
|
+
const hashTempo = TxEnvelopeTempo.getSignPayload(transaction, {
|
|
1430
|
+
from: tempoAddr,
|
|
1431
|
+
})
|
|
1432
|
+
expect(hashTempo).toBe(hashHex)
|
|
1433
|
+
})
|
|
1385
1434
|
})
|
|
1386
1435
|
|
|
1387
1436
|
describe('getFeePayerSignPayload', () => {
|
|
@@ -1404,6 +1453,29 @@ describe('getFeePayerSignPayload', () => {
|
|
|
1404
1453
|
)
|
|
1405
1454
|
})
|
|
1406
1455
|
|
|
1456
|
+
test('tempo address input for sender', () => {
|
|
1457
|
+
const hexAddr = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'
|
|
1458
|
+
const tempoAddr = 'tempo1qreel4h9r2kc3ah5ee4t3qnj088llwfzvccugvl7'
|
|
1459
|
+
|
|
1460
|
+
const transaction = TxEnvelopeTempo.from({
|
|
1461
|
+
chainId: 1,
|
|
1462
|
+
calls: [
|
|
1463
|
+
{
|
|
1464
|
+
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
|
|
1465
|
+
},
|
|
1466
|
+
],
|
|
1467
|
+
nonce: 0n,
|
|
1468
|
+
})
|
|
1469
|
+
|
|
1470
|
+
const hashHex = TxEnvelopeTempo.getFeePayerSignPayload(transaction, {
|
|
1471
|
+
sender: hexAddr,
|
|
1472
|
+
})
|
|
1473
|
+
const hashTempo = TxEnvelopeTempo.getFeePayerSignPayload(transaction, {
|
|
1474
|
+
sender: tempoAddr,
|
|
1475
|
+
})
|
|
1476
|
+
expect(hashTempo).toBe(hashHex)
|
|
1477
|
+
})
|
|
1478
|
+
|
|
1407
1479
|
test('with feeToken', () => {
|
|
1408
1480
|
const transaction = TxEnvelopeTempo.from({
|
|
1409
1481
|
chainId: 1,
|