ox 0.14.11 → 0.14.12
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 +8 -0
- package/_cjs/erc8021/Attribution.js +7 -1
- package/_cjs/erc8021/Attribution.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +141 -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 +150 -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 +91 -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 +312 -3
- package/tempo/KeyAuthorization.ts +277 -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 +890 -1
- package/tempo/index.ts +30 -0
- package/version.ts +1 -1
|
@@ -41,7 +41,17 @@ export type KeyAuthorization<
|
|
|
41
41
|
/** Unix timestamp when key expires (undefined = never expires). */
|
|
42
42
|
expiry?: numberType | null | undefined
|
|
43
43
|
/** TIP20 spending limits for this key. */
|
|
44
|
-
limits?:
|
|
44
|
+
limits?:
|
|
45
|
+
| readonly TokenLimit<bigintType, numberType, addressType>[]
|
|
46
|
+
| undefined
|
|
47
|
+
/**
|
|
48
|
+
* Call scopes restricting which contracts/selectors this key can call.
|
|
49
|
+
*
|
|
50
|
+
* - `undefined` = unrestricted key (any call allowed)
|
|
51
|
+
* - `[]` = scoped mode with no calls allowed
|
|
52
|
+
* - `[...]` = only listed contract+selector combinations allowed
|
|
53
|
+
*/
|
|
54
|
+
scopes?: readonly Scope<addressType>[] | undefined
|
|
45
55
|
/** Key type. (secp256k1, P256, WebAuthn). */
|
|
46
56
|
type: SignatureEnvelope.Type
|
|
47
57
|
} & (signed extends true
|
|
@@ -60,16 +70,43 @@ export type Input = KeyAuthorization<
|
|
|
60
70
|
TempoAddress.Address
|
|
61
71
|
>
|
|
62
72
|
|
|
63
|
-
/** RPC representation
|
|
64
|
-
export type Rpc =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
/** RPC representation matching the node's wire format. */
|
|
74
|
+
export type Rpc = {
|
|
75
|
+
/** Allowed call scopes (node field: `allowedCalls`). */
|
|
76
|
+
allowedCalls?: readonly RpcCallScope[] | undefined
|
|
77
|
+
/** Chain ID (hex quantity). */
|
|
78
|
+
chainId: Hex.Hex
|
|
79
|
+
/** Expiry timestamp (hex quantity or null). */
|
|
80
|
+
expiry: Hex.Hex | null | undefined
|
|
81
|
+
/** Key identifier. */
|
|
68
82
|
keyId: Address.Address
|
|
83
|
+
/** Key type. */
|
|
69
84
|
keyType: SignatureEnvelope.Type
|
|
85
|
+
/** Token spending limits. */
|
|
86
|
+
limits?: readonly RpcTokenLimit[] | undefined
|
|
87
|
+
/** Signature envelope. */
|
|
70
88
|
signature: SignatureEnvelope.SignatureEnvelopeRpc
|
|
71
89
|
}
|
|
72
90
|
|
|
91
|
+
/** RPC representation of a token limit (matches node's `TokenLimit` serde). */
|
|
92
|
+
export type RpcTokenLimit = {
|
|
93
|
+
token: Address.Address
|
|
94
|
+
limit: Hex.Hex
|
|
95
|
+
period?: Hex.Hex | undefined
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** RPC representation of a call scope (matches node's `CallScope` serde). */
|
|
99
|
+
export type RpcCallScope = {
|
|
100
|
+
target: Address.Address
|
|
101
|
+
selectorRules?: readonly RpcSelectorRule[]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** RPC representation of a selector rule (matches node's `SelectorRule` serde). */
|
|
105
|
+
export type RpcSelectorRule = {
|
|
106
|
+
selector: Hex.Hex
|
|
107
|
+
recipients?: readonly Address.Address[]
|
|
108
|
+
}
|
|
109
|
+
|
|
73
110
|
/** Signed representation of a Key Authorization. */
|
|
74
111
|
export type Signed<
|
|
75
112
|
bigintType = bigint,
|
|
@@ -83,29 +120,64 @@ type BaseTuple = readonly [
|
|
|
83
120
|
keyId: Address.Address,
|
|
84
121
|
]
|
|
85
122
|
|
|
123
|
+
type TokenLimitTuple =
|
|
124
|
+
| readonly [token: Address.Address, limit: Hex.Hex]
|
|
125
|
+
| readonly [token: Address.Address, limit: Hex.Hex, period: Hex.Hex]
|
|
126
|
+
|
|
127
|
+
type SelectorRuleTuple = readonly [
|
|
128
|
+
selector: Hex.Hex,
|
|
129
|
+
recipients: readonly Address.Address[],
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
type CallScopeTuple = readonly [
|
|
133
|
+
target: Address.Address,
|
|
134
|
+
selectorRules: readonly SelectorRuleTuple[],
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
type AuthorizationTuple =
|
|
138
|
+
| BaseTuple
|
|
139
|
+
| readonly [...BaseTuple, expiry: Hex.Hex]
|
|
140
|
+
| readonly [...BaseTuple, expiry: Hex.Hex, limits: readonly TokenLimitTuple[]]
|
|
141
|
+
| readonly [
|
|
142
|
+
...BaseTuple,
|
|
143
|
+
expiry: Hex.Hex,
|
|
144
|
+
limits: readonly TokenLimitTuple[],
|
|
145
|
+
calls: readonly CallScopeTuple[],
|
|
146
|
+
]
|
|
147
|
+
|
|
86
148
|
/** Tuple representation of a Key Authorization. */
|
|
87
149
|
export type Tuple<signed extends boolean = boolean> = signed extends true
|
|
88
|
-
? readonly [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
150
|
+
? readonly [authorization: AuthorizationTuple, signature: Hex.Hex]
|
|
151
|
+
: readonly [authorization: AuthorizationTuple]
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Call scope entry restricting which contract, selector, and recipients an access key can use.
|
|
155
|
+
*
|
|
156
|
+
* Multiple entries with the same `contractAddress` are grouped by target on the wire.
|
|
157
|
+
*
|
|
158
|
+
* - `{ contractAddress }` = any selector on this contract
|
|
159
|
+
* - `{ contractAddress, selector }` = specific selector
|
|
160
|
+
* - `{ contractAddress, selector, recipients }` = selector + recipient constraint
|
|
161
|
+
*
|
|
162
|
+
* [TIP-1011 Specification](https://docs.tempo.xyz/protocol/transactions/tip-1011)
|
|
163
|
+
*/
|
|
164
|
+
export type Scope<addressType = Address.Address> = {
|
|
165
|
+
/** Target contract address. */
|
|
166
|
+
contractAddress: addressType
|
|
167
|
+
/**
|
|
168
|
+
* 4-byte function selector. Omit to allow any selector on this contract.
|
|
169
|
+
*/
|
|
170
|
+
selector?: Hex.Hex | undefined
|
|
171
|
+
/**
|
|
172
|
+
* Recipient allowlist for this selector (first ABI `address` argument).
|
|
173
|
+
*
|
|
174
|
+
* - `undefined` or `[]` = any recipient allowed
|
|
175
|
+
* - `[...]` = only listed recipients allowed
|
|
176
|
+
*
|
|
177
|
+
* Only valid for constrained selectors: `transfer`, `approve`, `transferWithMemo`.
|
|
178
|
+
*/
|
|
179
|
+
recipients?: readonly addressType[] | undefined
|
|
180
|
+
}
|
|
109
181
|
|
|
110
182
|
/**
|
|
111
183
|
* Token spending limit for access keys.
|
|
@@ -115,11 +187,22 @@ export type Tuple<signed extends boolean = boolean> = signed extends true
|
|
|
115
187
|
*
|
|
116
188
|
* [Access Keys Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#access-keys)
|
|
117
189
|
*/
|
|
118
|
-
export type TokenLimit<
|
|
190
|
+
export type TokenLimit<
|
|
191
|
+
bigintType = bigint,
|
|
192
|
+
numberType = number,
|
|
193
|
+
addressType = Address.Address,
|
|
194
|
+
> = {
|
|
119
195
|
/** Address of the TIP-20 token. */
|
|
120
196
|
token: addressType
|
|
121
|
-
/** Maximum spending amount for this token (enforced over the key's lifetime). */
|
|
197
|
+
/** Maximum spending amount for this token (enforced over the key's lifetime, or per period if `period` \> 0). */
|
|
122
198
|
limit: bigintType
|
|
199
|
+
/**
|
|
200
|
+
* Period duration in seconds for recurring spending limits.
|
|
201
|
+
*
|
|
202
|
+
* - `0` or `undefined` = one-time limit
|
|
203
|
+
* - `\> 0` = periodic limit that resets every `period` seconds
|
|
204
|
+
*/
|
|
205
|
+
period?: numberType | undefined
|
|
123
206
|
}
|
|
124
207
|
|
|
125
208
|
/**
|
|
@@ -268,6 +351,11 @@ export function from<
|
|
|
268
351
|
if ('keyId' in authorization) return fromRpc(authorization as Rpc) as never
|
|
269
352
|
const auth = authorization as KeyAuthorization & {
|
|
270
353
|
limits?: readonly { token: TempoAddress.Address; limit: bigint }[]
|
|
354
|
+
scopes?: readonly {
|
|
355
|
+
contractAddress: TempoAddress.Address
|
|
356
|
+
selector?: Hex.Hex
|
|
357
|
+
recipients?: readonly TempoAddress.Address[]
|
|
358
|
+
}[]
|
|
271
359
|
}
|
|
272
360
|
const resolved = {
|
|
273
361
|
...auth,
|
|
@@ -280,6 +368,21 @@ export function from<
|
|
|
280
368
|
})),
|
|
281
369
|
}
|
|
282
370
|
: {}),
|
|
371
|
+
...(auth.scopes
|
|
372
|
+
? {
|
|
373
|
+
scopes: auth.scopes.map((scope) => ({
|
|
374
|
+
...scope,
|
|
375
|
+
contractAddress: TempoAddress.resolve(scope.contractAddress),
|
|
376
|
+
...(scope.recipients
|
|
377
|
+
? {
|
|
378
|
+
recipients: scope.recipients.map((r) =>
|
|
379
|
+
TempoAddress.resolve(r),
|
|
380
|
+
),
|
|
381
|
+
}
|
|
382
|
+
: {}),
|
|
383
|
+
})),
|
|
384
|
+
}
|
|
385
|
+
: {}),
|
|
283
386
|
}
|
|
284
387
|
if (options.signature)
|
|
285
388
|
return {
|
|
@@ -344,8 +447,27 @@ export declare namespace from {
|
|
|
344
447
|
* @returns A signed {@link ox#AuthorizationTempo.AuthorizationTempo}.
|
|
345
448
|
*/
|
|
346
449
|
export function fromRpc(authorization: Rpc): Signed {
|
|
347
|
-
const { chainId, keyId, expiry, limits, keyType } =
|
|
450
|
+
const { allowedCalls, chainId, keyId, expiry, limits, keyType } =
|
|
451
|
+
authorization
|
|
348
452
|
const signature = SignatureEnvelope.fromRpc(authorization.signature)
|
|
453
|
+
|
|
454
|
+
// Unflatten nested allowedCalls into flat scopes
|
|
455
|
+
const scopes = allowedCalls
|
|
456
|
+
? allowedCalls.flatMap((callScope) => {
|
|
457
|
+
if (!callScope.selectorRules || callScope.selectorRules.length === 0)
|
|
458
|
+
return [{ contractAddress: callScope.target }] as Scope[]
|
|
459
|
+
return callScope.selectorRules.map(
|
|
460
|
+
(rule): Scope => ({
|
|
461
|
+
contractAddress: callScope.target,
|
|
462
|
+
selector: normalizeSelector(rule.selector),
|
|
463
|
+
...(rule.recipients && rule.recipients.length > 0
|
|
464
|
+
? { recipients: rule.recipients }
|
|
465
|
+
: {}),
|
|
466
|
+
}),
|
|
467
|
+
)
|
|
468
|
+
})
|
|
469
|
+
: undefined
|
|
470
|
+
|
|
349
471
|
return {
|
|
350
472
|
address: keyId,
|
|
351
473
|
chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
|
|
@@ -353,7 +475,11 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
353
475
|
limits: limits?.map((limit) => ({
|
|
354
476
|
token: limit.token,
|
|
355
477
|
limit: BigInt(limit.limit),
|
|
478
|
+
...(limit.period && hexToNumber(limit.period) > 0
|
|
479
|
+
? { period: hexToNumber(limit.period) }
|
|
480
|
+
: {}),
|
|
356
481
|
})),
|
|
482
|
+
...(scopes ? { scopes } : {}),
|
|
357
483
|
signature,
|
|
358
484
|
type: keyType,
|
|
359
485
|
}
|
|
@@ -406,7 +532,7 @@ export function fromTuple<const tuple extends Tuple>(
|
|
|
406
532
|
tuple: tuple,
|
|
407
533
|
): fromTuple.ReturnType<tuple> {
|
|
408
534
|
const [authorization, signatureSerialized] = tuple
|
|
409
|
-
const [chainId, keyType_hex, keyId, expiry, limits] = authorization
|
|
535
|
+
const [chainId, keyType_hex, keyId, expiry, limits, scopes] = authorization
|
|
410
536
|
const keyType = (() => {
|
|
411
537
|
switch (keyType_hex) {
|
|
412
538
|
case '0x':
|
|
@@ -422,16 +548,50 @@ export function fromTuple<const tuple extends Tuple>(
|
|
|
422
548
|
})()
|
|
423
549
|
const args: KeyAuthorization = {
|
|
424
550
|
address: keyId,
|
|
425
|
-
expiry:
|
|
551
|
+
expiry:
|
|
552
|
+
typeof expiry !== 'undefined'
|
|
553
|
+
? hexToNumber(expiry) || undefined
|
|
554
|
+
: undefined,
|
|
426
555
|
type: keyType,
|
|
427
556
|
chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
|
|
428
|
-
...(typeof expiry !== 'undefined'
|
|
429
|
-
|
|
557
|
+
...(typeof expiry !== 'undefined'
|
|
558
|
+
? { expiry: hexToNumber(expiry) || undefined }
|
|
559
|
+
: {}),
|
|
560
|
+
...(typeof limits !== 'undefined' &&
|
|
561
|
+
Array.isArray(limits) &&
|
|
562
|
+
limits.length > 0
|
|
430
563
|
? {
|
|
431
|
-
limits: limits.map((
|
|
432
|
-
token,
|
|
433
|
-
|
|
434
|
-
|
|
564
|
+
limits: limits.map((limitTuple: any) => {
|
|
565
|
+
const [token, limit, period] = limitTuple
|
|
566
|
+
return {
|
|
567
|
+
token,
|
|
568
|
+
limit: hexToBigint(limit),
|
|
569
|
+
...(typeof period !== 'undefined'
|
|
570
|
+
? { period: hexToNumber(period) }
|
|
571
|
+
: {}),
|
|
572
|
+
}
|
|
573
|
+
}),
|
|
574
|
+
}
|
|
575
|
+
: {}),
|
|
576
|
+
...(typeof scopes !== 'undefined' && Array.isArray(scopes)
|
|
577
|
+
? {
|
|
578
|
+
scopes: scopes.flatMap((scopeTuple: any) => {
|
|
579
|
+
const [contractAddress, selectorRules] = scopeTuple
|
|
580
|
+
// If no selector rules, this is an address-only scope
|
|
581
|
+
if (!Array.isArray(selectorRules) || selectorRules.length === 0)
|
|
582
|
+
return [{ contractAddress }]
|
|
583
|
+
// Flatten each selector rule into a separate scope entry
|
|
584
|
+
return selectorRules.map((ruleTuple: any) => {
|
|
585
|
+
const [selector, recipients] = ruleTuple
|
|
586
|
+
return {
|
|
587
|
+
contractAddress,
|
|
588
|
+
selector,
|
|
589
|
+
...(Array.isArray(recipients) && recipients.length > 0
|
|
590
|
+
? { recipients }
|
|
591
|
+
: {}),
|
|
592
|
+
}
|
|
593
|
+
})
|
|
594
|
+
}),
|
|
435
595
|
}
|
|
436
596
|
: {}),
|
|
437
597
|
}
|
|
@@ -637,18 +797,45 @@ export declare namespace serialize {
|
|
|
637
797
|
* @returns An RPC-formatted Key Authorization.
|
|
638
798
|
*/
|
|
639
799
|
export function toRpc(authorization: Signed): Rpc {
|
|
640
|
-
const { address, chainId, expiry, limits, type, signature } =
|
|
800
|
+
const { address, scopes, chainId, expiry, limits, type, signature } =
|
|
801
|
+
authorization
|
|
802
|
+
|
|
803
|
+
// Group flat scopes by contractAddress into nested allowedCalls wire format
|
|
804
|
+
const allowedCalls = (() => {
|
|
805
|
+
if (!scopes) return undefined
|
|
806
|
+
const grouped = new Map<string, RpcSelectorRule[]>()
|
|
807
|
+
for (const scope of scopes) {
|
|
808
|
+
const key = scope.contractAddress as string
|
|
809
|
+
if (!grouped.has(key)) grouped.set(key, [])
|
|
810
|
+
if (scope.selector) {
|
|
811
|
+
grouped.get(key)!.push({
|
|
812
|
+
selector: scope.selector,
|
|
813
|
+
...(scope.recipients && scope.recipients.length > 0
|
|
814
|
+
? { recipients: scope.recipients }
|
|
815
|
+
: {}),
|
|
816
|
+
})
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return [...grouped.entries()].map(
|
|
820
|
+
([target, selectorRules]): RpcCallScope => ({
|
|
821
|
+
target: target as Address.Address,
|
|
822
|
+
...(selectorRules.length > 0 ? { selectorRules } : {}),
|
|
823
|
+
}),
|
|
824
|
+
)
|
|
825
|
+
})()
|
|
641
826
|
|
|
642
827
|
return {
|
|
643
828
|
chainId: chainId === 0n ? '0x' : Hex.fromNumber(chainId),
|
|
644
829
|
expiry: typeof expiry === 'number' ? Hex.fromNumber(expiry) : null,
|
|
645
|
-
|
|
830
|
+
keyId: TempoAddress.resolve(address),
|
|
831
|
+
keyType: type,
|
|
832
|
+
limits: limits?.map(({ token, limit, period }) => ({
|
|
646
833
|
token,
|
|
647
834
|
limit: Hex.fromNumber(limit),
|
|
835
|
+
...(period ? { period: numberToHex(period) } : {}),
|
|
648
836
|
})),
|
|
649
|
-
keyId: TempoAddress.resolve(address),
|
|
650
837
|
signature: SignatureEnvelope.toRpc(signature),
|
|
651
|
-
|
|
838
|
+
...(allowedCalls ? { allowedCalls } : {}),
|
|
652
839
|
}
|
|
653
840
|
}
|
|
654
841
|
|
|
@@ -690,7 +877,7 @@ export declare namespace toRpc {
|
|
|
690
877
|
export function toTuple<const authorization extends KeyAuthorization>(
|
|
691
878
|
authorization: authorization,
|
|
692
879
|
): toTuple.ReturnType<authorization> {
|
|
693
|
-
const { address, chainId, expiry, limits } = authorization
|
|
880
|
+
const { address, chainId, scopes, expiry, limits } = authorization
|
|
694
881
|
const signature = authorization.signature
|
|
695
882
|
? SignatureEnvelope.serialize(authorization.signature)
|
|
696
883
|
: undefined
|
|
@@ -706,20 +893,52 @@ export function toTuple<const authorization extends KeyAuthorization>(
|
|
|
706
893
|
throw new Error(`Invalid key type: ${authorization.type}`)
|
|
707
894
|
}
|
|
708
895
|
})()
|
|
709
|
-
const limitsValue = limits?.map((limit) =>
|
|
710
|
-
limit.token,
|
|
711
|
-
|
|
712
|
-
|
|
896
|
+
const limitsValue = limits?.map((limit) => {
|
|
897
|
+
const tuple: any[] = [limit.token, bigintToHex(limit.limit)]
|
|
898
|
+
// Canonical: omit period when 0 (one-time limit)
|
|
899
|
+
if (limit.period && limit.period > 0) tuple.push(numberToHex(limit.period))
|
|
900
|
+
return tuple
|
|
901
|
+
})
|
|
902
|
+
// Group flat scopes by contractAddress for wire format
|
|
903
|
+
const callsValue = (() => {
|
|
904
|
+
if (!scopes) return undefined
|
|
905
|
+
const grouped = new Map<
|
|
906
|
+
string,
|
|
907
|
+
[Hex.Hex, (readonly Address.Address[])[]][]
|
|
908
|
+
>()
|
|
909
|
+
for (const scope of scopes) {
|
|
910
|
+
const key = scope.contractAddress as string
|
|
911
|
+
if (!grouped.has(key)) grouped.set(key, [])
|
|
912
|
+
if (scope.selector) {
|
|
913
|
+
grouped
|
|
914
|
+
.get(key)!
|
|
915
|
+
.push([
|
|
916
|
+
scope.selector,
|
|
917
|
+
(scope.recipients ??
|
|
918
|
+
[]) as unknown as (readonly Address.Address[])[],
|
|
919
|
+
])
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return [...grouped.entries()].map(([contractAddress, selectorRules]) => [
|
|
923
|
+
contractAddress,
|
|
924
|
+
selectorRules.map(([selector, recipients]) => [selector, recipients]),
|
|
925
|
+
])
|
|
926
|
+
})()
|
|
713
927
|
const authorizationTuple = [
|
|
714
928
|
bigintToHex(chainId),
|
|
715
929
|
type,
|
|
716
930
|
address,
|
|
717
|
-
// expiry is required in the tuple when limits are present
|
|
718
|
-
|
|
931
|
+
// expiry is required in the tuple when limits or scopes are present
|
|
932
|
+
// expiry=0 is treated the same as undefined (never expires)
|
|
933
|
+
(expiry !== null && expiry !== undefined && expiry !== 0) ||
|
|
934
|
+
limitsValue ||
|
|
935
|
+
callsValue
|
|
719
936
|
? numberToHex(expiry ?? 0)
|
|
720
937
|
: undefined,
|
|
721
|
-
|
|
722
|
-
|
|
938
|
+
// limits is required in the tuple when scopes are present
|
|
939
|
+
limitsValue || callsValue ? (limitsValue ?? []) : undefined,
|
|
940
|
+
callsValue,
|
|
941
|
+
].filter((x) => typeof x !== 'undefined')
|
|
723
942
|
return [authorizationTuple, ...(signature ? [signature] : [])] as never
|
|
724
943
|
}
|
|
725
944
|
|
|
@@ -745,3 +964,10 @@ function hexToBigint(hex: Hex.Hex): bigint {
|
|
|
745
964
|
function hexToNumber(hex: Hex.Hex): number {
|
|
746
965
|
return hex === '0x' ? 0 : Hex.toNumber(hex)
|
|
747
966
|
}
|
|
967
|
+
|
|
968
|
+
function normalizeSelector(selector: Hex.Hex | number[]): Hex.Hex {
|
|
969
|
+
if (typeof selector === 'string') return selector
|
|
970
|
+
if (Array.isArray(selector))
|
|
971
|
+
return Hex.fromBytes(new Uint8Array(selector)) as Hex.Hex
|
|
972
|
+
return selector
|
|
973
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import * as Period from './Period.js'
|
|
3
|
+
|
|
4
|
+
describe('seconds', () => {
|
|
5
|
+
test('default', () => {
|
|
6
|
+
expect(Period.seconds(30)).toBe(30)
|
|
7
|
+
})
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
describe('minutes', () => {
|
|
11
|
+
test('default', () => {
|
|
12
|
+
expect(Period.minutes(5)).toBe(300)
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('hours', () => {
|
|
17
|
+
test('default', () => {
|
|
18
|
+
expect(Period.hours(2)).toBe(7200)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('days', () => {
|
|
23
|
+
test('default', () => {
|
|
24
|
+
expect(Period.days(1)).toBe(86400)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('weeks', () => {
|
|
29
|
+
test('default', () => {
|
|
30
|
+
expect(Period.weeks(1)).toBe(604800)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('months', () => {
|
|
35
|
+
test('default', () => {
|
|
36
|
+
expect(Period.months(1)).toBe(2592000)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('years', () => {
|
|
41
|
+
test('default', () => {
|
|
42
|
+
expect(Period.years(1)).toBe(31536000)
|
|
43
|
+
})
|
|
44
|
+
})
|
package/tempo/Period.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
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 function days(n: number) {
|
|
12
|
+
return n * 24 * 60 * 60
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the number of seconds in `n` hours.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts twoslash
|
|
20
|
+
* import { Period } from 'ox/tempo'
|
|
21
|
+
*
|
|
22
|
+
* const seconds = Period.hours(2) // 7200
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function hours(n: number) {
|
|
26
|
+
return n * 60 * 60
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the number of seconds in `n` minutes.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts twoslash
|
|
34
|
+
* import { Period } from 'ox/tempo'
|
|
35
|
+
*
|
|
36
|
+
* const seconds = Period.minutes(5) // 300
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function minutes(n: number) {
|
|
40
|
+
return n * 60
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the number of seconds in `n` months (30 days).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts twoslash
|
|
48
|
+
* import { Period } from 'ox/tempo'
|
|
49
|
+
*
|
|
50
|
+
* const seconds = Period.months(1) // 2592000
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function months(n: number) {
|
|
54
|
+
return n * 30 * 24 * 60 * 60
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns the number of seconds in `n` seconds.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts twoslash
|
|
62
|
+
* import { Period } from 'ox/tempo'
|
|
63
|
+
*
|
|
64
|
+
* const seconds = Period.seconds(30) // 30
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function seconds(n: number) {
|
|
68
|
+
return n
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns the number of seconds in `n` weeks.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts twoslash
|
|
76
|
+
* import { Period } from 'ox/tempo'
|
|
77
|
+
*
|
|
78
|
+
* const seconds = Period.weeks(1) // 604800
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function weeks(n: number) {
|
|
82
|
+
return n * 7 * 24 * 60 * 60
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns the number of seconds in `n` years (365 days).
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts twoslash
|
|
90
|
+
* import { Period } from 'ox/tempo'
|
|
91
|
+
*
|
|
92
|
+
* const seconds = Period.years(1) // 31536000
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function years(n: number) {
|
|
96
|
+
return n * 365 * 24 * 60 * 60
|
|
97
|
+
}
|