ox 0.14.11 → 0.14.13
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 +14 -0
- package/_cjs/erc8021/Attribution.js +7 -1
- package/_cjs/erc8021/Attribution.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +150 -21
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/Period.js +31 -0
- package/_cjs/tempo/Period.js.map +1 -0
- package/_cjs/tempo/index.js +2 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/erc8021/Attribution.js +7 -1
- package/_esm/erc8021/Attribution.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +159 -22
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/Period.js +92 -0
- package/_esm/tempo/Period.js.map +1 -0
- package/_esm/tempo/index.js +29 -0
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/erc8021/Attribution.d.ts +2 -0
- package/_types/erc8021/Attribution.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +95 -19
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/Period.d.ts +78 -0
- package/_types/tempo/Period.d.ts.map +1 -0
- package/_types/tempo/index.d.ts +29 -0
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/erc8021/Attribution.ts +12 -1
- package/package.json +6 -1
- package/tempo/KeyAuthorization.test.ts +407 -3
- package/tempo/KeyAuthorization.ts +291 -51
- package/tempo/Period/package.json +6 -0
- package/tempo/Period.test.ts +44 -0
- package/tempo/Period.ts +97 -0
- package/tempo/e2e.test.ts +969 -1
- package/tempo/index.ts +30 -0
- package/version.ts +1 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the number of seconds in `n` days.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts twoslash
|
|
6
|
+
* import { Period } from 'ox/tempo'
|
|
7
|
+
*
|
|
8
|
+
* const seconds = Period.days(1) // 86400
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare function days(n: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the number of seconds in `n` hours.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts twoslash
|
|
17
|
+
* import { Period } from 'ox/tempo'
|
|
18
|
+
*
|
|
19
|
+
* const seconds = Period.hours(2) // 7200
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function hours(n: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the number of seconds in `n` minutes.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts twoslash
|
|
28
|
+
* import { Period } from 'ox/tempo'
|
|
29
|
+
*
|
|
30
|
+
* const seconds = Period.minutes(5) // 300
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function minutes(n: number): number;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the number of seconds in `n` months (30 days).
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts twoslash
|
|
39
|
+
* import { Period } from 'ox/tempo'
|
|
40
|
+
*
|
|
41
|
+
* const seconds = Period.months(1) // 2592000
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function months(n: number): number;
|
|
45
|
+
/**
|
|
46
|
+
* Returns the number of seconds in `n` seconds.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts twoslash
|
|
50
|
+
* import { Period } from 'ox/tempo'
|
|
51
|
+
*
|
|
52
|
+
* const seconds = Period.seconds(30) // 30
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function seconds(n: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Returns the number of seconds in `n` weeks.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts twoslash
|
|
61
|
+
* import { Period } from 'ox/tempo'
|
|
62
|
+
*
|
|
63
|
+
* const seconds = Period.weeks(1) // 604800
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function weeks(n: number): number;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the number of seconds in `n` years (365 days).
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts twoslash
|
|
72
|
+
* import { Period } from 'ox/tempo'
|
|
73
|
+
*
|
|
74
|
+
* const seconds = Period.years(1) // 31536000
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function years(n: number): number;
|
|
78
|
+
//# sourceMappingURL=Period.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Period.d.ts","sourceRoot":"","sources":["../../tempo/Period.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,UAE7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,UAE9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,UAEhC;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,UAE/B;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,UAEhC;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,UAE9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,UAE9B"}
|
package/_types/tempo/index.d.ts
CHANGED
|
@@ -73,6 +73,35 @@ export * as AuthorizationTempo from './AuthorizationTempo.js';
|
|
|
73
73
|
* @category Reference
|
|
74
74
|
*/
|
|
75
75
|
export * as KeyAuthorization from './KeyAuthorization.js';
|
|
76
|
+
/**
|
|
77
|
+
* Utilities for constructing period durations (in seconds) for recurring spending limits.
|
|
78
|
+
*
|
|
79
|
+
* Periods define the reset interval for access key spending limits. A spending limit with a
|
|
80
|
+
* period will reset every `period` seconds. For example, a daily spending limit uses
|
|
81
|
+
* `Period.days(1)` (86400 seconds).
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts twoslash
|
|
85
|
+
* import { Value } from 'ox'
|
|
86
|
+
* import { KeyAuthorization, Period } from 'ox/tempo'
|
|
87
|
+
*
|
|
88
|
+
* const authorization = KeyAuthorization.from({
|
|
89
|
+
* address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
|
|
90
|
+
* chainId: 4217n,
|
|
91
|
+
* type: 'secp256k1',
|
|
92
|
+
* limits: [{
|
|
93
|
+
* token: '0x20c0000000000000000000000000000000000001',
|
|
94
|
+
* limit: Value.from('100', 6),
|
|
95
|
+
* period: Period.days(1), // resets daily
|
|
96
|
+
* }],
|
|
97
|
+
* })
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* [Access Keys Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#access-keys)
|
|
101
|
+
*
|
|
102
|
+
* @category Reference
|
|
103
|
+
*/
|
|
104
|
+
export * as Period from './Period.js';
|
|
76
105
|
/**
|
|
77
106
|
* Pool ID utilities for computing pool identifiers from token pairs.
|
|
78
107
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../tempo/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,YAAY,EAAE,CAAA;AAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../tempo/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,YAAY,EAAE,CAAA;AAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA"}
|
package/_types/version.d.ts
CHANGED
package/erc8021/Attribution.ts
CHANGED
|
@@ -58,6 +58,8 @@ export type AttributionSchemaId2 = {
|
|
|
58
58
|
appCode?: string | undefined
|
|
59
59
|
/** Wallet attribution code. */
|
|
60
60
|
walletCode?: string | undefined
|
|
61
|
+
/** Service codes identifying additional service providers (e.g., block builders, relayers, solvers). */
|
|
62
|
+
serviceCodes?: readonly string[] | undefined
|
|
61
63
|
/** Custom code registries keyed by entity type. */
|
|
62
64
|
registries?: AttributionSchemaId2Registries | undefined
|
|
63
65
|
/** Arbitrary metadata key-value pairs. */
|
|
@@ -134,7 +136,12 @@ export const ercSuffixSize = /*#__PURE__*/ Hex.size(ercSuffix)
|
|
|
134
136
|
* @returns The schema ID (0 for canonical registry, 1 for custom registry, 2 for CBOR-encoded).
|
|
135
137
|
*/
|
|
136
138
|
export function getSchemaId(attribution: Attribution): SchemaId {
|
|
137
|
-
if (
|
|
139
|
+
if (
|
|
140
|
+
'appCode' in attribution ||
|
|
141
|
+
'walletCode' in attribution ||
|
|
142
|
+
'serviceCodes' in attribution
|
|
143
|
+
)
|
|
144
|
+
return 2
|
|
138
145
|
if ('codeRegistry' in attribution) return 1
|
|
139
146
|
return 0
|
|
140
147
|
}
|
|
@@ -416,6 +423,7 @@ export declare namespace fromData {
|
|
|
416
423
|
type Schema2CborMap = {
|
|
417
424
|
a?: string
|
|
418
425
|
w?: string
|
|
426
|
+
s?: string[]
|
|
419
427
|
r?: {
|
|
420
428
|
a?: { c: string; a: string }
|
|
421
429
|
w?: { c: string; a: string }
|
|
@@ -429,6 +437,8 @@ function schema2ToDataSuffix(attribution: AttributionSchemaId2): Hex.Hex {
|
|
|
429
437
|
|
|
430
438
|
if (attribution.appCode) cborMap.a = attribution.appCode
|
|
431
439
|
if (attribution.walletCode) cborMap.w = attribution.walletCode
|
|
440
|
+
if (attribution.serviceCodes && attribution.serviceCodes.length > 0)
|
|
441
|
+
cborMap.s = [...attribution.serviceCodes]
|
|
432
442
|
|
|
433
443
|
if (attribution.registries) {
|
|
434
444
|
const r: Schema2CborMap['r'] = {}
|
|
@@ -484,6 +494,7 @@ function schema2FromData(data: Hex.Hex): AttributionSchemaId2 | undefined {
|
|
|
484
494
|
|
|
485
495
|
if (typeof decoded.a === 'string') result.appCode = decoded.a
|
|
486
496
|
if (typeof decoded.w === 'string') result.walletCode = decoded.w
|
|
497
|
+
if (Array.isArray(decoded.s)) result.serviceCodes = decoded.s
|
|
487
498
|
|
|
488
499
|
if (decoded.r) {
|
|
489
500
|
const registries: AttributionSchemaId2Registries = {}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ox",
|
|
3
3
|
"description": "Ethereum Standard Library",
|
|
4
|
-
"version": "0.14.
|
|
4
|
+
"version": "0.14.13",
|
|
5
5
|
"main": "./_cjs/index.js",
|
|
6
6
|
"module": "./_esm/index.js",
|
|
7
7
|
"types": "./_types/index.d.ts",
|
|
@@ -509,6 +509,11 @@
|
|
|
509
509
|
"import": "./_esm/tempo/KeyAuthorization.js",
|
|
510
510
|
"default": "./_cjs/tempo/KeyAuthorization.js"
|
|
511
511
|
},
|
|
512
|
+
"./tempo/Period": {
|
|
513
|
+
"types": "./_types/tempo/Period.d.ts",
|
|
514
|
+
"import": "./_esm/tempo/Period.js",
|
|
515
|
+
"default": "./_cjs/tempo/Period.js"
|
|
516
|
+
},
|
|
512
517
|
"./tempo/PoolId": {
|
|
513
518
|
"types": "./_types/tempo/PoolId.d.ts",
|
|
514
519
|
"import": "./_esm/tempo/PoolId.js",
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from 'ox'
|
|
10
10
|
import { describe, expect, test } from 'vitest'
|
|
11
11
|
import * as KeyAuthorization from './KeyAuthorization.js'
|
|
12
|
+
import * as Period from './Period.js'
|
|
12
13
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
13
14
|
|
|
14
15
|
const address = '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c'
|
|
@@ -1670,11 +1671,10 @@ describe('toTuple', () => {
|
|
|
1670
1671
|
expect(restored.limits?.[0]?.limit).toBe(0n)
|
|
1671
1672
|
})
|
|
1672
1673
|
|
|
1673
|
-
test('
|
|
1674
|
+
test('undefined expiry roundtrips through RLP', () => {
|
|
1674
1675
|
const authorization = KeyAuthorization.from({
|
|
1675
1676
|
address,
|
|
1676
1677
|
chainId: 0n,
|
|
1677
|
-
expiry: 0,
|
|
1678
1678
|
type: 'secp256k1',
|
|
1679
1679
|
limits: [
|
|
1680
1680
|
{
|
|
@@ -1709,7 +1709,7 @@ describe('toTuple', () => {
|
|
|
1709
1709
|
expect(rlpDecoded).toEqual(authorizationTuple)
|
|
1710
1710
|
|
|
1711
1711
|
const restored = KeyAuthorization.fromTuple(tuple)
|
|
1712
|
-
expect(restored.expiry).
|
|
1712
|
+
expect(restored.expiry).toBeUndefined()
|
|
1713
1713
|
})
|
|
1714
1714
|
|
|
1715
1715
|
test('hash works with zero spending limit', () => {
|
|
@@ -1728,4 +1728,408 @@ describe('toTuple', () => {
|
|
|
1728
1728
|
|
|
1729
1729
|
expect(() => KeyAuthorization.hash(authorization)).not.toThrow()
|
|
1730
1730
|
})
|
|
1731
|
+
|
|
1732
|
+
test('periodic spending limit (period > 0)', () => {
|
|
1733
|
+
const authorization = KeyAuthorization.from({
|
|
1734
|
+
address,
|
|
1735
|
+
chainId: 1n,
|
|
1736
|
+
expiry,
|
|
1737
|
+
type: 'secp256k1',
|
|
1738
|
+
limits: [
|
|
1739
|
+
{
|
|
1740
|
+
token,
|
|
1741
|
+
limit: Value.from('10', 6),
|
|
1742
|
+
period: Period.months(1),
|
|
1743
|
+
},
|
|
1744
|
+
],
|
|
1745
|
+
})
|
|
1746
|
+
|
|
1747
|
+
const tuple = KeyAuthorization.toTuple(authorization)
|
|
1748
|
+
|
|
1749
|
+
expect(tuple).toMatchInlineSnapshot(`
|
|
1750
|
+
[
|
|
1751
|
+
[
|
|
1752
|
+
"0x1",
|
|
1753
|
+
"0x",
|
|
1754
|
+
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
|
|
1755
|
+
"0x499602d2",
|
|
1756
|
+
[
|
|
1757
|
+
[
|
|
1758
|
+
"0x20c0000000000000000000000000000000000001",
|
|
1759
|
+
"0x989680",
|
|
1760
|
+
"0x278d00",
|
|
1761
|
+
],
|
|
1762
|
+
],
|
|
1763
|
+
],
|
|
1764
|
+
]
|
|
1765
|
+
`)
|
|
1766
|
+
|
|
1767
|
+
// Roundtrip
|
|
1768
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1769
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1770
|
+
expect(restored.limits?.[0]?.period).toBe(2592000)
|
|
1771
|
+
expect(restored.limits?.[0]?.limit).toBe(Value.from('10', 6))
|
|
1772
|
+
})
|
|
1773
|
+
|
|
1774
|
+
test('one-time limit omits period from tuple', () => {
|
|
1775
|
+
const authorization = KeyAuthorization.from({
|
|
1776
|
+
address,
|
|
1777
|
+
chainId: 1n,
|
|
1778
|
+
expiry,
|
|
1779
|
+
type: 'secp256k1',
|
|
1780
|
+
limits: [
|
|
1781
|
+
{
|
|
1782
|
+
token,
|
|
1783
|
+
limit: Value.from('10', 6),
|
|
1784
|
+
},
|
|
1785
|
+
],
|
|
1786
|
+
})
|
|
1787
|
+
|
|
1788
|
+
const tuple = KeyAuthorization.toTuple(authorization)
|
|
1789
|
+
// Canonical 2-field form — no period element
|
|
1790
|
+
const [authTuple] = tuple
|
|
1791
|
+
const limitTuple = (authTuple as any)[4][0]
|
|
1792
|
+
expect(limitTuple).toHaveLength(2)
|
|
1793
|
+
})
|
|
1794
|
+
|
|
1795
|
+
test('mixed one-time and periodic limits', () => {
|
|
1796
|
+
const authorization = KeyAuthorization.from({
|
|
1797
|
+
address,
|
|
1798
|
+
chainId: 1n,
|
|
1799
|
+
expiry,
|
|
1800
|
+
type: 'secp256k1',
|
|
1801
|
+
limits: [
|
|
1802
|
+
{
|
|
1803
|
+
token: '0x20c0000000000000000000000000000000000001',
|
|
1804
|
+
limit: Value.from('10', 6),
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
token: '0x20c0000000000000000000000000000000000002',
|
|
1808
|
+
limit: Value.from('100', 6),
|
|
1809
|
+
period: Period.days(1),
|
|
1810
|
+
},
|
|
1811
|
+
],
|
|
1812
|
+
})
|
|
1813
|
+
|
|
1814
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1815
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1816
|
+
expect(restored.limits?.[0]?.period).toBeUndefined()
|
|
1817
|
+
expect(restored.limits?.[1]?.period).toBe(86400)
|
|
1818
|
+
})
|
|
1819
|
+
|
|
1820
|
+
test('call scopes: address-only (any selector)', () => {
|
|
1821
|
+
const authorization = KeyAuthorization.from({
|
|
1822
|
+
address,
|
|
1823
|
+
chainId: 1n,
|
|
1824
|
+
type: 'secp256k1',
|
|
1825
|
+
scopes: [
|
|
1826
|
+
{
|
|
1827
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1828
|
+
},
|
|
1829
|
+
],
|
|
1830
|
+
})
|
|
1831
|
+
|
|
1832
|
+
const tuple = KeyAuthorization.toTuple(authorization)
|
|
1833
|
+
expect(tuple).toMatchInlineSnapshot(`
|
|
1834
|
+
[
|
|
1835
|
+
[
|
|
1836
|
+
"0x1",
|
|
1837
|
+
"0x",
|
|
1838
|
+
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
|
|
1839
|
+
"0x",
|
|
1840
|
+
[],
|
|
1841
|
+
[
|
|
1842
|
+
[
|
|
1843
|
+
"0x1234567890123456789012345678901234567890",
|
|
1844
|
+
[],
|
|
1845
|
+
],
|
|
1846
|
+
],
|
|
1847
|
+
],
|
|
1848
|
+
]
|
|
1849
|
+
`)
|
|
1850
|
+
|
|
1851
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1852
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1853
|
+
expect(restored.scopes).toHaveLength(1)
|
|
1854
|
+
expect(restored.scopes?.[0]?.address).toBe(
|
|
1855
|
+
'0x1234567890123456789012345678901234567890',
|
|
1856
|
+
)
|
|
1857
|
+
expect(restored.scopes?.[0]?.selector).toBeUndefined()
|
|
1858
|
+
})
|
|
1859
|
+
|
|
1860
|
+
test('call scopes: explicit selectors', () => {
|
|
1861
|
+
const authorization = KeyAuthorization.from({
|
|
1862
|
+
address,
|
|
1863
|
+
chainId: 1n,
|
|
1864
|
+
type: 'secp256k1',
|
|
1865
|
+
scopes: [
|
|
1866
|
+
{
|
|
1867
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1868
|
+
selector: '0xa9059cbb', // transfer
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1872
|
+
selector: '0x095ea7b3', // approve
|
|
1873
|
+
},
|
|
1874
|
+
],
|
|
1875
|
+
})
|
|
1876
|
+
|
|
1877
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1878
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1879
|
+
expect(restored.scopes).toHaveLength(2)
|
|
1880
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
1881
|
+
expect(restored.scopes?.[1]?.selector).toBe('0x095ea7b3')
|
|
1882
|
+
expect(restored.scopes?.[0]?.recipients).toBeUndefined()
|
|
1883
|
+
})
|
|
1884
|
+
|
|
1885
|
+
test('call scopes: selectors with recipients', () => {
|
|
1886
|
+
const authorization = KeyAuthorization.from({
|
|
1887
|
+
address,
|
|
1888
|
+
chainId: 1n,
|
|
1889
|
+
type: 'secp256k1',
|
|
1890
|
+
scopes: [
|
|
1891
|
+
{
|
|
1892
|
+
address: token,
|
|
1893
|
+
selector: '0xa9059cbb',
|
|
1894
|
+
recipients: [
|
|
1895
|
+
'0x1111111111111111111111111111111111111111',
|
|
1896
|
+
'0x2222222222222222222222222222222222222222',
|
|
1897
|
+
],
|
|
1898
|
+
},
|
|
1899
|
+
],
|
|
1900
|
+
})
|
|
1901
|
+
|
|
1902
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1903
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1904
|
+
expect(restored.scopes?.[0]?.recipients).toEqual([
|
|
1905
|
+
'0x1111111111111111111111111111111111111111',
|
|
1906
|
+
'0x2222222222222222222222222222222222222222',
|
|
1907
|
+
])
|
|
1908
|
+
})
|
|
1909
|
+
|
|
1910
|
+
test('scopes = undefined (unrestricted, omitted from wire)', () => {
|
|
1911
|
+
const authorization = KeyAuthorization.from({
|
|
1912
|
+
address,
|
|
1913
|
+
chainId: 1n,
|
|
1914
|
+
type: 'secp256k1',
|
|
1915
|
+
})
|
|
1916
|
+
|
|
1917
|
+
const tuple = KeyAuthorization.toTuple(authorization)
|
|
1918
|
+
// No scopes field in tuple
|
|
1919
|
+
const [authTuple] = tuple
|
|
1920
|
+
expect((authTuple as unknown as any[]).length).toBe(3) // chainId, type, address
|
|
1921
|
+
|
|
1922
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1923
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1924
|
+
expect(restored.scopes).toBeUndefined()
|
|
1925
|
+
})
|
|
1926
|
+
|
|
1927
|
+
test('scopes = [] (scoped, no calls allowed)', () => {
|
|
1928
|
+
const authorization = KeyAuthorization.from({
|
|
1929
|
+
address,
|
|
1930
|
+
chainId: 1n,
|
|
1931
|
+
type: 'secp256k1',
|
|
1932
|
+
scopes: [],
|
|
1933
|
+
})
|
|
1934
|
+
|
|
1935
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1936
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1937
|
+
expect(restored.scopes).toEqual([])
|
|
1938
|
+
})
|
|
1939
|
+
|
|
1940
|
+
test('scopes + limits + expiry combined', () => {
|
|
1941
|
+
const authorization = KeyAuthorization.from({
|
|
1942
|
+
address,
|
|
1943
|
+
chainId: 1n,
|
|
1944
|
+
expiry,
|
|
1945
|
+
type: 'secp256k1',
|
|
1946
|
+
limits: [
|
|
1947
|
+
{
|
|
1948
|
+
token,
|
|
1949
|
+
limit: Value.from('10', 6),
|
|
1950
|
+
period: Period.days(1),
|
|
1951
|
+
},
|
|
1952
|
+
],
|
|
1953
|
+
scopes: [
|
|
1954
|
+
{
|
|
1955
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1956
|
+
selector: '0xa9059cbb',
|
|
1957
|
+
recipients: ['0x1111111111111111111111111111111111111111'],
|
|
1958
|
+
},
|
|
1959
|
+
],
|
|
1960
|
+
})
|
|
1961
|
+
|
|
1962
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
1963
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
1964
|
+
expect(restored.expiry).toBe(expiry)
|
|
1965
|
+
expect(restored.limits?.[0]?.period).toBe(86400)
|
|
1966
|
+
expect(restored.scopes?.[0]?.recipients).toEqual([
|
|
1967
|
+
'0x1111111111111111111111111111111111111111',
|
|
1968
|
+
])
|
|
1969
|
+
})
|
|
1970
|
+
|
|
1971
|
+
test('hash consistency with scopes', () => {
|
|
1972
|
+
const authorization = KeyAuthorization.from({
|
|
1973
|
+
address,
|
|
1974
|
+
chainId: 1n,
|
|
1975
|
+
type: 'secp256k1',
|
|
1976
|
+
scopes: [
|
|
1977
|
+
{
|
|
1978
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1979
|
+
selector: '0xa9059cbb',
|
|
1980
|
+
},
|
|
1981
|
+
],
|
|
1982
|
+
})
|
|
1983
|
+
|
|
1984
|
+
const hash1 = KeyAuthorization.hash(authorization)
|
|
1985
|
+
const hash2 = KeyAuthorization.hash(authorization)
|
|
1986
|
+
expect(hash1).toBe(hash2)
|
|
1987
|
+
|
|
1988
|
+
// Different scopes should produce different hash
|
|
1989
|
+
const authorization2 = KeyAuthorization.from({
|
|
1990
|
+
address,
|
|
1991
|
+
chainId: 1n,
|
|
1992
|
+
type: 'secp256k1',
|
|
1993
|
+
scopes: [
|
|
1994
|
+
{
|
|
1995
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
1996
|
+
selector: '0x095ea7b3',
|
|
1997
|
+
},
|
|
1998
|
+
],
|
|
1999
|
+
})
|
|
2000
|
+
expect(KeyAuthorization.hash(authorization2)).not.toBe(hash1)
|
|
2001
|
+
})
|
|
2002
|
+
|
|
2003
|
+
test('call scopes: selector from function signature string', () => {
|
|
2004
|
+
const authorization = KeyAuthorization.from({
|
|
2005
|
+
address,
|
|
2006
|
+
chainId: 1n,
|
|
2007
|
+
type: 'secp256k1',
|
|
2008
|
+
scopes: [
|
|
2009
|
+
{
|
|
2010
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2011
|
+
selector: 'function transfer(address,uint256)',
|
|
2012
|
+
},
|
|
2013
|
+
{
|
|
2014
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2015
|
+
selector: 'function approve(address,uint256)',
|
|
2016
|
+
},
|
|
2017
|
+
],
|
|
2018
|
+
})
|
|
2019
|
+
|
|
2020
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
2021
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
2022
|
+
expect(restored.scopes).toHaveLength(2)
|
|
2023
|
+
// transfer(address,uint256) => 0xa9059cbb
|
|
2024
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
2025
|
+
// approve(address,uint256) => 0x095ea7b3
|
|
2026
|
+
expect(restored.scopes?.[1]?.selector).toBe('0x095ea7b3')
|
|
2027
|
+
})
|
|
2028
|
+
|
|
2029
|
+
test('call scopes: selector from signature with recipients', () => {
|
|
2030
|
+
const authorization = KeyAuthorization.from({
|
|
2031
|
+
address,
|
|
2032
|
+
chainId: 1n,
|
|
2033
|
+
type: 'secp256k1',
|
|
2034
|
+
scopes: [
|
|
2035
|
+
{
|
|
2036
|
+
address: token,
|
|
2037
|
+
selector: 'function transfer(address,uint256)',
|
|
2038
|
+
recipients: ['0x1111111111111111111111111111111111111111'],
|
|
2039
|
+
},
|
|
2040
|
+
],
|
|
2041
|
+
})
|
|
2042
|
+
|
|
2043
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
2044
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
2045
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
2046
|
+
expect(restored.scopes?.[0]?.recipients).toEqual([
|
|
2047
|
+
'0x1111111111111111111111111111111111111111',
|
|
2048
|
+
])
|
|
2049
|
+
})
|
|
2050
|
+
|
|
2051
|
+
test('call scopes: selector from bare signature (no function prefix)', () => {
|
|
2052
|
+
const authorization = KeyAuthorization.from({
|
|
2053
|
+
address,
|
|
2054
|
+
chainId: 1n,
|
|
2055
|
+
type: 'secp256k1',
|
|
2056
|
+
scopes: [
|
|
2057
|
+
{
|
|
2058
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2059
|
+
selector: 'transfer(address,uint256)',
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2063
|
+
selector: 'approve(address,uint256)',
|
|
2064
|
+
},
|
|
2065
|
+
],
|
|
2066
|
+
})
|
|
2067
|
+
|
|
2068
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
2069
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
2070
|
+
expect(restored.scopes).toHaveLength(2)
|
|
2071
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
2072
|
+
expect(restored.scopes?.[1]?.selector).toBe('0x095ea7b3')
|
|
2073
|
+
})
|
|
2074
|
+
|
|
2075
|
+
test('call scopes: mixed hex and signature selectors', () => {
|
|
2076
|
+
const authorization = KeyAuthorization.from({
|
|
2077
|
+
address,
|
|
2078
|
+
chainId: 1n,
|
|
2079
|
+
type: 'secp256k1',
|
|
2080
|
+
scopes: [
|
|
2081
|
+
{
|
|
2082
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2083
|
+
selector: '0xa9059cbb',
|
|
2084
|
+
},
|
|
2085
|
+
{
|
|
2086
|
+
address: '0x1234567890123456789012345678901234567890',
|
|
2087
|
+
selector: 'function approve(address,uint256)',
|
|
2088
|
+
},
|
|
2089
|
+
],
|
|
2090
|
+
})
|
|
2091
|
+
|
|
2092
|
+
const serialized = KeyAuthorization.serialize(authorization)
|
|
2093
|
+
const restored = KeyAuthorization.deserialize(serialized)
|
|
2094
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
2095
|
+
expect(restored.scopes?.[1]?.selector).toBe('0x095ea7b3')
|
|
2096
|
+
})
|
|
2097
|
+
|
|
2098
|
+
test('toRpc/fromRpc roundtrip with period and scopes', () => {
|
|
2099
|
+
const authorization = KeyAuthorization.from(
|
|
2100
|
+
{
|
|
2101
|
+
address,
|
|
2102
|
+
chainId: 1n,
|
|
2103
|
+
expiry,
|
|
2104
|
+
type: 'secp256k1',
|
|
2105
|
+
limits: [
|
|
2106
|
+
{
|
|
2107
|
+
token,
|
|
2108
|
+
limit: Value.from('10', 6),
|
|
2109
|
+
period: Period.months(1),
|
|
2110
|
+
},
|
|
2111
|
+
],
|
|
2112
|
+
scopes: [
|
|
2113
|
+
{
|
|
2114
|
+
address: token,
|
|
2115
|
+
selector: '0xa9059cbb',
|
|
2116
|
+
recipients: ['0x1111111111111111111111111111111111111111'],
|
|
2117
|
+
},
|
|
2118
|
+
],
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
signature: SignatureEnvelope.from(signature_secp256k1),
|
|
2122
|
+
},
|
|
2123
|
+
)
|
|
2124
|
+
|
|
2125
|
+
const rpc = KeyAuthorization.toRpc(authorization)
|
|
2126
|
+
const restored = KeyAuthorization.fromRpc(rpc)
|
|
2127
|
+
|
|
2128
|
+
expect(restored.limits?.[0]?.period).toBe(2592000)
|
|
2129
|
+
expect(restored.scopes?.[0]?.address).toBe(token)
|
|
2130
|
+
expect(restored.scopes?.[0]?.selector).toBe('0xa9059cbb')
|
|
2131
|
+
expect(restored.scopes?.[0]?.recipients).toEqual([
|
|
2132
|
+
'0x1111111111111111111111111111111111111111',
|
|
2133
|
+
])
|
|
2134
|
+
})
|
|
1731
2135
|
})
|