ens-budget 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Namepass LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,299 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # ens-budget
4
+
5
+ **Renew ENS names by budget.**
6
+
7
+ Give it a label and a stablecoin amount. It returns the longest renewal that amount buys under ENS v2 pricing, checked against ENS's own quote, and can send the renewal for you.
8
+
9
+ [![npm](https://img.shields.io/npm/v/ens-budget?style=flat-square&color=0080BC)](https://www.npmjs.com/package/ens-budget)
10
+ [![CI](https://img.shields.io/github/actions/workflow/status/wildcardlabs/ens-budget/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/wildcardlabs/ens-budget/actions/workflows/ci.yml)
11
+ [![size](https://deno.bundlejs.com/badge?q=ens-budget&config=%7B%22esbuild%22%3A%7B%22external%22%3A%5B%22viem%22%5D%7D%7D&badge-style=flat-square)](https://bundlejs.com/?q=ens-budget&config=%7B%22esbuild%22%3A%7B%22external%22%3A%5B%22viem%22%5D%7D%7D)
12
+ [![types](https://img.shields.io/npm/types/ens-budget?style=flat-square)](https://www.npmjs.com/package/ens-budget)
13
+ [![viem](https://img.shields.io/npm/dependency-version/ens-budget/peer/viem?style=flat-square)](https://viem.sh)
14
+ [![license](https://img.shields.io/github/license/wildcardlabs/ens-budget?style=flat-square)](./LICENSE)
15
+
16
+ [Install](#install) · [Quick start](#quick-start) · [How it works](#how-it-works) · [Recipes](#recipes) · [API](#api) · [Networks](#networks)
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ - **Exact.** Prices match ENS's `getRenewPrice` to the smallest token unit, including multi-year discounts.
25
+ - **Optimal.** Returns the longest affordable term even where a longer term is cheaper.
26
+ - **Complete.** Picks the right renewer, handles the token approval, and sends the renewal.
27
+ - **Composable.** Returns plain calls for EIP-5792 batching, smart accounts and relayers.
28
+ - **Small.** About 3 kB min+gzip. viem is the only dependency, as a peer.
29
+
30
+ ## Install
31
+
32
+ ```sh
33
+ npm install ens-budget viem
34
+ ```
35
+
36
+ ## Quick start
37
+
38
+ ```ts
39
+ import { createPublicClient, http } from 'viem'
40
+ import { mainnet } from 'viem/chains'
41
+ import { addresses, quoteRenewal } from 'ens-budget'
42
+
43
+ const client = createPublicClient({ chain: mainnet, transport: http() })
44
+
45
+ const quote = await quoteRenewal(client, {
46
+ label: 'namepass', // namepass.eth
47
+ budget: 25_000000n, // $25 in USDC (6 decimals)
48
+ token: addresses[1].usdc,
49
+ })
50
+
51
+ quote.duration // 143345079n seconds, about 4 years 6 months
52
+ quote.amount // 25000000n, the exact cost
53
+ quote.tier // { duration: 94608000n, discountBps: 3125 } 3-year rate, 31.25% off
54
+ quote.nextTier // { duration: 189216000n, extraAmount: 2000071n, … } 6 years for $2.01 more
55
+ ```
56
+
57
+ To renew, pass a viem wallet client with an `account` (using wagmi? see [Renew with wagmi](#recipes)):
58
+
59
+ ```ts
60
+ import { renewWithBudget } from 'ens-budget'
61
+
62
+ const { hash, quote } = await renewWithBudget(walletClient, {
63
+ label: 'namepass',
64
+ budget: 25_000000n,
65
+ token: addresses[1].usdc,
66
+ })
67
+ ```
68
+
69
+ ## How it works
70
+
71
+ ENS v2 prices renewals in USD per second, by label length, and discounts the whole term once it reaches 2, 3 or 6 years. Because the discount applies to every second of the term, a term just short of a tier can cost more than the tier itself:
72
+
73
+ | `namepass.eth` | Price |
74
+ | --- | --- |
75
+ | 1 year | $8.01 |
76
+ | 1 year 364 days | $15.98 |
77
+ | **2 years** | **$14.01** |
78
+ | 5 years 364 days | $32.99 |
79
+ | **6 years** | **$27.01** |
80
+
81
+ Prices are rounded up to the cent. ENS charges exact token units, a fraction of a cent above the round figure (6 years is 27.000071 USDC), so $27.00 buys about 4 years 11 months and $27.01 buys 6 years.
82
+
83
+ Dividing a budget by an annual rate gets this wrong. `quoteRenewal` instead:
84
+
85
+ 1. Asks both ENS renewers `isRenewable(label)` and uses the one that says yes.
86
+ 2. Reads the live rates, discount tiers and token exchange rate from the renewer's price oracle.
87
+ 3. Solves for the longest duration whose exact price fits the budget, trying discount tiers from best to worst.
88
+ 4. Confirms with `getRenewPrice` that ENS charges exactly the solved amount, and more than the budget for one second longer.
89
+
90
+ ### Approvals
91
+
92
+ Renewals are paid in an ERC-20 token, so the renewer needs an allowance. `renewWithBudget` checks the current allowance and, only if it is short, approves **exactly** `quote.amount` and waits for confirmation before renewing. It never approves an unlimited amount or the full budget, so a price change between quote and send makes the renewal revert instead of overspending.
93
+
94
+ ## Recipes
95
+
96
+ <details>
97
+ <summary><b>Warn when a chosen date costs more than a longer term</b></summary>
98
+
99
+ Apps with a "renew until" date picker can catch terms that fall just short of a tier:
100
+
101
+ ```ts
102
+ import { getPricing, priceOf, solve } from 'ens-budget'
103
+
104
+ const pricing = await getPricing(client, { token }) // once
105
+
106
+ const cost = priceOf(pricing, { label, duration }) // the term the user picked
107
+ const best = solve(pricing, { label, budget: cost })
108
+
109
+ if (best.tier && best.tier.duration > duration) {
110
+ // e.g. "6 years costs $27.01, less than the $32.99 you selected"
111
+ const tierCost = priceOf(pricing, { label, duration: best.tier.duration })
112
+ }
113
+ ```
114
+
115
+ `priceOf` and `solve` run locally, so this can run on every date change.
116
+
117
+ </details>
118
+
119
+ <details>
120
+ <summary><b>Offer a top-up to the next tier</b></summary>
121
+
122
+ When a budget lands just short of a tier, `quote.nextTier` says how much more reaches it:
123
+
124
+ ```ts
125
+ const { nextTier } = await quoteRenewal(client, { label, budget: 26_000000n, token })
126
+ // nextTier.extraAmount 1000071n, about $1.01
127
+ // nextTier.extraDuration about 15 more months
128
+ ```
129
+
130
+ </details>
131
+
132
+ <details>
133
+ <summary><b>Renew with wagmi</b></summary>
134
+
135
+ `renewWithBudget` needs a viem wallet client with an `account`. With wagmi it is simpler to quote with the public client, build the calls with `prepareRenewal`, and send them through wagmi:
136
+
137
+ ```ts
138
+ import { useAccount, usePublicClient, useSendCalls } from 'wagmi'
139
+ import { prepareRenewal, quoteRenewal } from 'ens-budget'
140
+
141
+ const client = usePublicClient()
142
+ const { address } = useAccount()
143
+ const { sendCallsAsync } = useSendCalls()
144
+
145
+ const quote = await quoteRenewal(client, { label, budget, token })
146
+ const calls = await prepareRenewal(client, quote, { account: address })
147
+ await sendCallsAsync({ calls })
148
+ ```
149
+
150
+ For wallets without EIP-5792, send the calls in order with `useSendTransaction`, and wait for the `approve` receipt before sending `renew`.
151
+
152
+ </details>
153
+
154
+ <details>
155
+ <summary><b>Batch approve and renew into one prompt (EIP-5792)</b></summary>
156
+
157
+ ```ts
158
+ import { prepareRenewal, quoteRenewal } from 'ens-budget'
159
+
160
+ const quote = await quoteRenewal(client, { label, budget, token })
161
+ const calls = await prepareRenewal(client, quote, { account: wallet.account.address })
162
+ // [approve, renew], or [renew] when the allowance already covers it
163
+
164
+ await wallet.sendCalls({ calls })
165
+ ```
166
+
167
+ The same `{ to, data }` calls work with Safe transactions, bundlers and relayers.
168
+
169
+ </details>
170
+
171
+ <details>
172
+ <summary><b>Run on a server</b></summary>
173
+
174
+ ```ts
175
+ import { createWalletClient, http, nonceManager, publicActions } from 'viem'
176
+ import { privateKeyToAccount } from 'viem/accounts'
177
+
178
+ const account = privateKeyToAccount(process.env.PRIVATE_KEY, { nonceManager })
179
+ const wallet = createWalletClient({ account, chain: mainnet, transport: http(RPC_URL) }).extend(publicActions)
180
+
181
+ await renewWithBudget(wallet, { label, budget, token })
182
+ ```
183
+
184
+ Exact approvals mean one `approve` transaction per renewal. For frequent renewals, set a standing allowance on the renewer yourself; `prepareRenewal` skips the approval whenever the allowance covers the price.
185
+
186
+ </details>
187
+
188
+ <details>
189
+ <summary><b>Price locally for live UIs</b></summary>
190
+
191
+ Read pricing once, then solve without network calls, for example on every slider move:
192
+
193
+ ```ts
194
+ import { getPricing, priceOf, solve } from 'ens-budget'
195
+
196
+ const pricing = await getPricing(client, { token })
197
+
198
+ solve(pricing, { label: 'namepass', budget: 25_000000n }) // { duration, amount, tier, nextTier }
199
+ priceOf(pricing, { label: 'namepass', duration: 31536000n }) // 8000021n
200
+ ```
201
+
202
+ </details>
203
+
204
+ <details>
205
+ <summary><b>Normalize user input</b></summary>
206
+
207
+ Labels are used exactly as given, as in ensjs, so non-normalized legacy names stay renewable. Normalize anything a user types:
208
+
209
+ ```ts
210
+ import { normalize } from 'viem/ens'
211
+
212
+ const label = normalize(input).replace(/\.eth$/, '')
213
+ ```
214
+
215
+ Length is counted in Unicode code points of the label you pass, as ENS counts it. `❤️❤️❤️` with variation selectors is 6 code points and priced as a 5+ character name; `❤❤❤` is 3.
216
+
217
+ </details>
218
+
219
+ ## API
220
+
221
+ ### Network
222
+
223
+ | Function | Returns |
224
+ | --- | --- |
225
+ | `quoteRenewal(client, { label, budget, token, renewers? })` | `Quote`: the longest renewal for the budget, confirmed with ENS. |
226
+ | `renewWithBudget(walletClient, { label, budget, token, referrer?, renewers? })` | `{ hash, quote }` after approving if needed and renewing. Needs a wallet client with an `account`. |
227
+ | `prepareRenewal(client, quote, { account, referrer? })` | `Call[]`: `[approve?, renew]` as `{ to, data }`. |
228
+ | `getPricing(client, { token, renewer?, renewers? })` | `Pricing`: oracle rates, discount tiers and token ratio. |
229
+ | `getRenewer(client, { label, renewers? })` | The renewer address for a label, or `null`. |
230
+
231
+ ### Pure
232
+
233
+ | Function | Returns |
234
+ | --- | --- |
235
+ | `solve(pricing, { label, budget })` | `{ duration, amount, tier, nextTier }` |
236
+ | `priceOf(pricing, { label, duration })` | Exact cost in token units. |
237
+
238
+ All amounts are `bigint` in the token's smallest unit. All durations are `bigint` seconds.
239
+
240
+ <details>
241
+ <summary><b>Types</b></summary>
242
+
243
+ ```ts
244
+ type Quote = {
245
+ label: string
246
+ renewer: Address // ETHRegistrar or ETHRenewerV1
247
+ token: Address
248
+ oracle: Address
249
+ budget: bigint
250
+ duration: bigint
251
+ amount: bigint // <= budget
252
+ tier: Tier | null // null at full price
253
+ nextTier: NextTier | null
254
+ }
255
+
256
+ type Tier = { duration: bigint; discountBps: number }
257
+
258
+ type NextTier = Tier & {
259
+ amount: bigint // cost of nextTier.duration
260
+ extraAmount: bigint // amount - budget
261
+ extraDuration: bigint // extra time gained by paying amount
262
+ }
263
+ ```
264
+
265
+ </details>
266
+
267
+ ### Errors
268
+
269
+ All errors extend `EnsBudgetError`.
270
+
271
+ | Error | Thrown when |
272
+ | --- | --- |
273
+ | `InvalidLabelError` | The label is empty, over 255 bytes, or a length ENS does not sell. |
274
+ | `NotRenewableError` | Neither renewer will renew the label (unregistered or past grace). |
275
+ | `UnsupportedTokenError` | The price oracle does not accept the token. |
276
+ | `UnsupportedChainError` | No built-in addresses for the chain and no `renewers` given. |
277
+ | `BudgetTooLowError` | The budget buys less than the minimum renewal. |
278
+ | `QuoteMismatchError` | The local price disagrees with ENS's quote. The renewal is not sent. |
279
+
280
+ ## Networks
281
+
282
+ | Network | ETHRegistrar (v2 names) | ETHRenewerV1 (unmigrated v1 names) | Tokens |
283
+ | --- | --- | --- | --- |
284
+ | Mainnet | `0xD6D8369e55c94Ae8AFA714E6cF7AB5bE54D5CC81` | `0x56c447F308F463A01121C41E72b0E7e7557fCb68` | USDC, DAI |
285
+ | Sepolia | `0xf633e7FC17e2bbE0D0965D18ec1821dcB754a3d3` | `0xf2ece44980778966b8a0FccB3A9E339440f6e045` | USDC, DAI |
286
+
287
+ Token addresses are exported as `addresses[chainId].usdc` and `.dai`. For forks or other deployments, pass `renewers: { ethRegistrar, ethRenewerV1 }`. The price oracle is always read from the renewer, never hardcoded.
288
+
289
+ ## Development
290
+
291
+ ```sh
292
+ npm test # unit tests, including a brute-force optimality check
293
+ npm run test:live # read-only checks against mainnet (MAINNET_RPC_URL optional)
294
+ npm run test:fork # real renewals on an anvil mainnet fork; requires Foundry
295
+ ```
296
+
297
+ ## License
298
+
299
+ [MIT](./LICENSE) © Namepass LLC
package/dist/abi.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * ABI fragments, inlined so the package carries only what it calls.
3
+ *
4
+ * `ETHRegistrar` (v2 names) and `ETHRenewerV1` (unmigrated v1 names) both
5
+ * implement `IETHRenewer`, so one fragment drives either.
6
+ */
7
+ export declare const renewerAbi: readonly [{
8
+ readonly type: "function";
9
+ readonly name: "renew";
10
+ readonly stateMutability: "nonpayable";
11
+ readonly inputs: readonly [{
12
+ readonly name: "rd";
13
+ readonly type: "tuple";
14
+ readonly components: readonly [{
15
+ readonly name: "label";
16
+ readonly type: "string";
17
+ }, {
18
+ readonly name: "duration";
19
+ readonly type: "uint64";
20
+ }, {
21
+ readonly name: "referrer";
22
+ readonly type: "bytes32";
23
+ }];
24
+ }, {
25
+ readonly name: "paymentToken";
26
+ readonly type: "address";
27
+ }];
28
+ readonly outputs: readonly [];
29
+ }, {
30
+ readonly type: "function";
31
+ readonly name: "isRenewable";
32
+ readonly stateMutability: "view";
33
+ readonly inputs: readonly [{
34
+ readonly name: "label";
35
+ readonly type: "string";
36
+ }];
37
+ readonly outputs: readonly [{
38
+ readonly name: "";
39
+ readonly type: "bool";
40
+ }];
41
+ }, {
42
+ readonly type: "function";
43
+ readonly name: "getRenewPrice";
44
+ readonly stateMutability: "view";
45
+ readonly inputs: readonly [{
46
+ readonly name: "label";
47
+ readonly type: "string";
48
+ }, {
49
+ readonly name: "duration";
50
+ readonly type: "uint64";
51
+ }, {
52
+ readonly name: "paymentToken";
53
+ readonly type: "address";
54
+ }];
55
+ readonly outputs: readonly [{
56
+ readonly name: "";
57
+ readonly type: "uint256";
58
+ }];
59
+ }, {
60
+ readonly type: "function";
61
+ readonly name: "rentPriceOracle";
62
+ readonly stateMutability: "view";
63
+ readonly inputs: readonly [];
64
+ readonly outputs: readonly [{
65
+ readonly name: "";
66
+ readonly type: "address";
67
+ }];
68
+ }, {
69
+ readonly type: "function";
70
+ readonly name: "MIN_RENEW_DURATION";
71
+ readonly stateMutability: "view";
72
+ readonly inputs: readonly [];
73
+ readonly outputs: readonly [{
74
+ readonly name: "";
75
+ readonly type: "uint64";
76
+ }];
77
+ }];
78
+ export declare const oracleAbi: readonly [{
79
+ readonly type: "function";
80
+ readonly name: "DISCOUNT_DENOMINATOR";
81
+ readonly stateMutability: "view";
82
+ readonly inputs: readonly [];
83
+ readonly outputs: readonly [{
84
+ readonly name: "";
85
+ readonly type: "uint128";
86
+ }];
87
+ }, {
88
+ readonly type: "function";
89
+ readonly name: "getBaseRates";
90
+ readonly stateMutability: "view";
91
+ readonly inputs: readonly [];
92
+ readonly outputs: readonly [{
93
+ readonly name: "";
94
+ readonly type: "uint256[]";
95
+ }];
96
+ }, {
97
+ readonly type: "function";
98
+ readonly name: "getDiscountPoints";
99
+ readonly stateMutability: "view";
100
+ readonly inputs: readonly [];
101
+ readonly outputs: readonly [{
102
+ readonly name: "";
103
+ readonly type: "tuple[]";
104
+ readonly components: readonly [{
105
+ readonly name: "duration";
106
+ readonly type: "uint64";
107
+ }, {
108
+ readonly name: "numer";
109
+ readonly type: "uint128";
110
+ }];
111
+ }];
112
+ }, {
113
+ readonly type: "function";
114
+ readonly name: "getPaymentTokenRatio";
115
+ readonly stateMutability: "view";
116
+ readonly inputs: readonly [{
117
+ readonly name: "paymentToken";
118
+ readonly type: "address";
119
+ }];
120
+ readonly outputs: readonly [{
121
+ readonly name: "numer";
122
+ readonly type: "uint128";
123
+ }, {
124
+ readonly name: "denom";
125
+ readonly type: "uint128";
126
+ }];
127
+ }];
128
+ export declare const erc20Abi: readonly [{
129
+ readonly type: "function";
130
+ readonly name: "allowance";
131
+ readonly stateMutability: "view";
132
+ readonly inputs: readonly [{
133
+ readonly name: "owner";
134
+ readonly type: "address";
135
+ }, {
136
+ readonly name: "spender";
137
+ readonly type: "address";
138
+ }];
139
+ readonly outputs: readonly [{
140
+ readonly name: "";
141
+ readonly type: "uint256";
142
+ }];
143
+ }, {
144
+ readonly type: "function";
145
+ readonly name: "approve";
146
+ readonly stateMutability: "nonpayable";
147
+ readonly inputs: readonly [{
148
+ readonly name: "spender";
149
+ readonly type: "address";
150
+ }, {
151
+ readonly name: "amount";
152
+ readonly type: "uint256";
153
+ }];
154
+ readonly outputs: readonly [{
155
+ readonly name: "";
156
+ readonly type: "bool";
157
+ }];
158
+ }];
package/dist/abi.js ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * ABI fragments, inlined so the package carries only what it calls.
3
+ *
4
+ * `ETHRegistrar` (v2 names) and `ETHRenewerV1` (unmigrated v1 names) both
5
+ * implement `IETHRenewer`, so one fragment drives either.
6
+ */
7
+ export const renewerAbi = [
8
+ {
9
+ type: 'function',
10
+ name: 'renew',
11
+ stateMutability: 'nonpayable',
12
+ inputs: [
13
+ {
14
+ name: 'rd',
15
+ type: 'tuple',
16
+ components: [
17
+ { name: 'label', type: 'string' },
18
+ { name: 'duration', type: 'uint64' },
19
+ { name: 'referrer', type: 'bytes32' },
20
+ ],
21
+ },
22
+ { name: 'paymentToken', type: 'address' },
23
+ ],
24
+ outputs: [],
25
+ },
26
+ {
27
+ type: 'function',
28
+ name: 'isRenewable',
29
+ stateMutability: 'view',
30
+ inputs: [{ name: 'label', type: 'string' }],
31
+ outputs: [{ name: '', type: 'bool' }],
32
+ },
33
+ {
34
+ type: 'function',
35
+ name: 'getRenewPrice',
36
+ stateMutability: 'view',
37
+ inputs: [
38
+ { name: 'label', type: 'string' },
39
+ { name: 'duration', type: 'uint64' },
40
+ { name: 'paymentToken', type: 'address' },
41
+ ],
42
+ outputs: [{ name: '', type: 'uint256' }],
43
+ },
44
+ {
45
+ type: 'function',
46
+ name: 'rentPriceOracle',
47
+ stateMutability: 'view',
48
+ inputs: [],
49
+ outputs: [{ name: '', type: 'address' }],
50
+ },
51
+ {
52
+ type: 'function',
53
+ name: 'MIN_RENEW_DURATION',
54
+ stateMutability: 'view',
55
+ inputs: [],
56
+ outputs: [{ name: '', type: 'uint64' }],
57
+ },
58
+ ];
59
+ export const oracleAbi = [
60
+ {
61
+ type: 'function',
62
+ name: 'DISCOUNT_DENOMINATOR',
63
+ stateMutability: 'view',
64
+ inputs: [],
65
+ outputs: [{ name: '', type: 'uint128' }],
66
+ },
67
+ {
68
+ type: 'function',
69
+ name: 'getBaseRates',
70
+ stateMutability: 'view',
71
+ inputs: [],
72
+ outputs: [{ name: '', type: 'uint256[]' }],
73
+ },
74
+ {
75
+ type: 'function',
76
+ name: 'getDiscountPoints',
77
+ stateMutability: 'view',
78
+ inputs: [],
79
+ outputs: [
80
+ {
81
+ name: '',
82
+ type: 'tuple[]',
83
+ components: [
84
+ { name: 'duration', type: 'uint64' },
85
+ { name: 'numer', type: 'uint128' },
86
+ ],
87
+ },
88
+ ],
89
+ },
90
+ {
91
+ type: 'function',
92
+ name: 'getPaymentTokenRatio',
93
+ stateMutability: 'view',
94
+ inputs: [{ name: 'paymentToken', type: 'address' }],
95
+ outputs: [
96
+ { name: 'numer', type: 'uint128' },
97
+ { name: 'denom', type: 'uint128' },
98
+ ],
99
+ },
100
+ ];
101
+ export const erc20Abi = [
102
+ {
103
+ type: 'function',
104
+ name: 'allowance',
105
+ stateMutability: 'view',
106
+ inputs: [
107
+ { name: 'owner', type: 'address' },
108
+ { name: 'spender', type: 'address' },
109
+ ],
110
+ outputs: [{ name: '', type: 'uint256' }],
111
+ },
112
+ {
113
+ type: 'function',
114
+ name: 'approve',
115
+ stateMutability: 'nonpayable',
116
+ inputs: [
117
+ { name: 'spender', type: 'address' },
118
+ { name: 'amount', type: 'uint256' },
119
+ ],
120
+ outputs: [{ name: '', type: 'bool' }],
121
+ },
122
+ ];
123
+ //# sourceMappingURL=abi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abi.js","sourceRoot":"","sources":["../src/abi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Address } from 'viem';
2
+ export type Renewers = {
3
+ /** v2 `ETHRegistrar`: renews names registered on, or migrated to, v2. */
4
+ ethRegistrar: Address;
5
+ /** `ETHRenewerV1`: renews unmigrated legacy (v1) names. */
6
+ ethRenewerV1: Address;
7
+ };
8
+ /**
9
+ * Deployed ENS v2 renewers and the stablecoins ENS prices, per chain.
10
+ *
11
+ * Sourced from `@ensdomains/ensjs` and checked on-chain: both renewers expose
12
+ * `renew((string,uint64,bytes32),address)` and neither is an upgradeable
13
+ * proxy. The rent price oracle is *not* listed here; renewers can point at a
14
+ * new one, so it is read from `rentPriceOracle()` every time.
15
+ */
16
+ export declare const addresses: {
17
+ readonly 1: {
18
+ readonly ethRegistrar: "0xD6D8369e55c94Ae8AFA714E6cF7AB5bE54D5CC81";
19
+ readonly ethRenewerV1: "0x56c447F308F463A01121C41E72b0E7e7557fCb68";
20
+ readonly usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
21
+ readonly dai: "0x6B175474E89094C44Da98b954EedeAC495271d0F";
22
+ };
23
+ readonly 11155111: {
24
+ readonly ethRegistrar: "0xf633e7FC17e2bbE0D0965D18ec1821dcB754a3d3";
25
+ readonly ethRenewerV1: "0xf2ece44980778966b8a0FccB3A9E339440f6e045";
26
+ readonly usdc: "0x240b0316Df57887DBBE58b586508b19e633a14aa";
27
+ readonly dai: "0xF6faC8A58a0BE13b9197f27C41B73162Fe32572b";
28
+ };
29
+ };
30
+ export declare function renewersFor(chainId: number | undefined, override?: Renewers): Renewers;
@@ -0,0 +1,32 @@
1
+ import { UnsupportedChainError } from './errors.js';
2
+ /**
3
+ * Deployed ENS v2 renewers and the stablecoins ENS prices, per chain.
4
+ *
5
+ * Sourced from `@ensdomains/ensjs` and checked on-chain: both renewers expose
6
+ * `renew((string,uint64,bytes32),address)` and neither is an upgradeable
7
+ * proxy. The rent price oracle is *not* listed here; renewers can point at a
8
+ * new one, so it is read from `rentPriceOracle()` every time.
9
+ */
10
+ export const addresses = {
11
+ 1: {
12
+ ethRegistrar: '0xD6D8369e55c94Ae8AFA714E6cF7AB5bE54D5CC81',
13
+ ethRenewerV1: '0x56c447F308F463A01121C41E72b0E7e7557fCb68',
14
+ usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
15
+ dai: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
16
+ },
17
+ 11155111: {
18
+ ethRegistrar: '0xf633e7FC17e2bbE0D0965D18ec1821dcB754a3d3',
19
+ ethRenewerV1: '0xf2ece44980778966b8a0FccB3A9E339440f6e045',
20
+ usdc: '0x240b0316Df57887DBBE58b586508b19e633a14aa',
21
+ dai: '0xF6faC8A58a0BE13b9197f27C41B73162Fe32572b',
22
+ },
23
+ };
24
+ export function renewersFor(chainId, override) {
25
+ if (override)
26
+ return override;
27
+ const known = chainId === undefined ? undefined : addresses[chainId];
28
+ if (!known)
29
+ throw new UnsupportedChainError(chainId);
30
+ return known;
31
+ }
32
+ //# sourceMappingURL=addresses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addresses.js","sourceRoot":"","sources":["../src/addresses.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AASnD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,CAAC,EAAE;QACD,YAAY,EAAE,4CAA4C;QAC1D,YAAY,EAAE,4CAA4C;QAC1D,IAAI,EAAE,4CAA4C;QAClD,GAAG,EAAE,4CAA4C;KAClD;IACD,QAAQ,EAAE;QACR,YAAY,EAAE,4CAA4C;QAC1D,YAAY,EAAE,4CAA4C;QAC1D,IAAI,EAAE,4CAA4C;QAClD,GAAG,EAAE,4CAA4C;KAClD;CAC4E,CAAA;AAE/E,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE,QAAmB;IAC1E,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,KAAK,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,OAAiC,CAAC,CAAA;IAC9F,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAAA;IACpD,OAAO,KAAK,CAAA;AACd,CAAC"}