diff --git a/.github/workflows/continuous-releases.yml b/.github/workflows/continuous-releases.yml new file mode 100644 index 000000000..5bee513f2 --- /dev/null +++ b/.github/workflows/continuous-releases.yml @@ -0,0 +1,25 @@ +name: Continuous Release +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Build + run: pnpm -r --filter @ensdomains/ensjs-abi --filter @ensdomains/ensjs build + + - name: Publish to pkg.pr.new + run: pnpm dlx pkg-pr-new publish "./packages/ensjs-abi" "./packages/ensjs" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa643dbd5..f271558a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,11 +22,20 @@ jobs: - run: pnpm install --frozen-lockfile - - name: Build ensjs - run: pnpm -F @ensdomains/ensjs build + - name: Build ensjs-abi and ensjs + run: pnpm -F @ensdomains/ensjs-abi -F @ensdomains/ensjs build + + - name: Start test environment + run: docker compose up -d devnet + working-directory: packages/ensjs - name: Run tests - run: pnpm -F @ensdomains/ensjs tenv start --extra-time 11368000 + run: pnpm -F @ensdomains/ensjs test --run + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} lint: name: Lint runs-on: ubuntu-latest @@ -43,8 +52,11 @@ jobs: - run: pnpm install --frozen-lockfile + - name: Build ensjs-abi + run: pnpm -F @ensdomains/ensjs-abi build + - name: Lint run: pnpm lint - name: Type check - run: pnpm -F @ensdomains/ensjs build + run: pnpm -F @ensdomains/ensjs check:types diff --git a/.npmrc b/.npmrc index 1e31ea269..6ac091e3c 100644 --- a/.npmrc +++ b/.npmrc @@ -1,4 +1,4 @@ strict-peer-dependencies=false link-workspace-packages=deep provenance=true -auto-install-peers=false \ No newline at end of file +auto-install-peers=false diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..e1832f992 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,121 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +ENSjs is a TypeScript library for interacting with the Ethereum Name Service (ENS). It's built on top of Viem and provides comprehensive ENS contract interaction with tree-shaking support. + +## Build & Development Commands + +```bash +# Install dependencies +pnpm install + +# Build all packages +pnpm -r build + +# Build specific package +pnpm -F @ensdomains/ensjs build + +# Lint all packages +pnpm lint + +# Run tests (ensjs package) +pnpm -F @ensdomains/ensjs test + +# Watch mode testing +pnpm -F @ensdomains/ensjs test:watch + +# Run a single test file +pnpm -F @ensdomains/ensjs test src/actions/public/getRecords.test.ts + +# Start test environment (with ENS contracts) +pnpm -F @ensdomains/ensjs denv + +# Start local anvil only (no ENSnode, no scripts) +pnpm -F @ensdomains/ensjs anvil + +# Generate documentation +pnpm -F @ensdomains/ensjs generateDocs + +# Versioning with changesets +pnpm chgset:run # Create changeset +pnpm chgset:version # Update versions +``` + +## Monorepo Structure + +``` +packages/ +├── ensjs/ # Main ENS library (@ensdomains/ensjs) +├── react/ # React hooks (@ensdomains/ensjs-react) +└── query-core/ # @wagmi/core integration (@ensdomains/ensjs-query-core) +``` + +## Architecture + +### Package: @ensdomains/ensjs + +**Actions** (`src/actions/`): Composable functions that extend viem clients +- `public/v1/` - ENS v1 read operations (getExpiry, getOwner, getWrapperData) +- `public/v2/` - ENS v2 read operations (getExpiry, getNameRegistries, getNameRolesForAccount) +- `wallet/v2/` - ENS v2 write operations (deploySubregistry, grantRoles, setSubregistry) +- `dns/` - DNS-related functions +- `subgraph/` - Subgraph query functions + +**Clients** (`src/clients/`): Factory functions for L1/L2 chain support +- `l1.ts` - Mainnet client +- `l2.ts` - L2 chain clients +- Supported chains: mainnet (1), sepolia (11155111) + +**Contracts** (`src/contracts/`): ABIs and chain-specific addresses for 20+ ENS contracts + +**Utils** (`src/utils/`): +- `coders/` - ABI encoding/decoding (getAbi, getAddress, getContentHash, getText) +- `name/` - Name normalization and validation +- `v2/registry/` - V2 registry utilities (labelToCanonicalId) +- `v2/roles/` - Role encoding/decoding + +**Exports** (`src/exports/`): Public API surface +- `public.ts` - Public client actions +- `wallet.ts` - Wallet client actions +- `chain.ts` - Chain configurations + +### Action Pattern + +Actions follow this structure: +```typescript +export const actionName = async ( + client: SupportedClient, + params: ActionParams +): Promise => { + // Uses viem's getAction() and multicall for batching +} +``` + +### Client Types + +- **Public Client**: Read-only operations +- **Wallet Client**: Write operations (registration, renewals, records) + +## Testing + +- Framework: Vitest +- Tests collocated with source: `*.test.ts` +- Test setup: `packages/ensjs/src/test/setup.ts` +- Uses `ens-test-env` for integration tests with deployed contracts + +## Code Quality + +- Formatter/Linter: Biome +- TypeScript: Strict mode enabled +- Node version: >=22 (main packages), >=18 (query-core) + +## Key Dependencies + +- `viem` (peer dependency ^2.30.6) - Ethereum interactions +- `@adraffy/ens-normalize` - Name normalization +- `@ensdomains/address-encoder` - Address encoding +- `@ensdomains/content-hash` - Content hash encoding +- `graphql-request` - Subgraph queries diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..8c2418079 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,375 @@ +# Contributing to ENSjs + +Thanks for taking the time to contribute. This guide covers how the codebase is laid out +and the conventions you should follow when adding new actions, ABI snippets, or contract +addresses. + +## Repo basics + +- Monorepo managed with **pnpm** workspaces (`pnpm-workspace.yaml`). +- **Biome** for formatting and linting (`pnpm lint`). +- **Vitest** for tests, run against a local Anvil node with deployed ENS contracts + (`pnpm -F @ensdomains/ensjs denv` to start the test environment). +- **Changesets** for versioning — every user-visible change to a published package needs a + changeset (`pnpm chgset:run`). + +```sh +pnpm install +pnpm -r build +pnpm lint +pnpm -F @ensdomains/ensjs test +``` + +## Packages + +- `packages/ensjs` — the main library +- `packages/ensjs-abi` — ABI snippets, no runtime logic +- `packages/react` — React hooks +- `packages/query-core` — `@wagmi/core` integration + +ABI snippets always live in `@ensdomains/ensjs-abi`. The main `ensjs` package re-exports +them from `src/contracts/index.ts` and consumes them inside actions. + +--- + +## Adding a new ABI snippet + +ABIs live in `packages/ensjs-abi/src`, split by version: + +``` +packages/ensjs-abi/src/ +├── registry.ts # version-neutral +├── universalResolver.ts +├── ... +├── v1/ +│ ├── ethRegistrarController.ts +│ ├── nameWrapper.ts +│ └── ... +└── v2/ + ├── permissionedRegistry.ts + ├── permissionedResolver.ts + └── ... +``` + +### Steps + +1. **Pick the right file.** Group snippets by contract (e.g. all PermissionedRegistry + functions go into `v2/permissionedRegistry.ts`). Create a new file only when adding a + new contract; in that case mirror the existing layout. + +2. **Export each function as its own snippet.** One named export per function or error + group, typed `as const` so viem can infer it. Naming convention: + + - Function: `{contract}{FunctionName}Snippet`, e.g. + `permissionedRegistryGetStateSnippet`, `ethRegistrarControllerRegisterSnippet`. + - Errors group: `{contract}Errors`, e.g. `nameWrapperErrors`. + + Keep snippets minimal — only the function fragments an action actually needs. Don't + paste full ABIs. + + ```ts + export const permissionedRegistryGetStateSnippet = [ + { + type: 'function', + name: 'getState', + inputs: [{ name: 'anyId', type: 'uint256' }], + outputs: [ + /* ... */ + ], + stateMutability: 'view', + }, + ] as const + ``` + +3. **Wire up the subpath export** in `packages/ensjs-abi/package.json` if you created a new + file. Each contract has its own export path (e.g. `./v2/permissionedRegistry`). If you + added a brand-new file you must also add it here so consumers can import it directly. + +4. **Re-export from the umbrella index.** Add a line to + `packages/ensjs-abi/src/v1/index.ts` or `v2/index.ts` (or the top-level `index.ts` for + version-neutral contracts) so the snippet is reachable via `@ensdomains/ensjs-abi`. + +5. **Re-export from `ensjs/contracts`.** In + `packages/ensjs/src/contracts/index.ts` add the snippet to the appropriate + `export { … } from '@ensdomains/ensjs-abi/...'` block, keeping the alphabetical order + already used. This is what action files import from. + +6. **Build the ABI package** before consuming the snippet from `ensjs`: + `pnpm -F @ensdomains/ensjs-abi build`. (CI runs the full `pnpm -r build`.) + +--- + +## Adding a new action + +Actions live in `packages/ensjs/src/actions/`: + +``` +src/actions/ +├── public/ # version-neutral / cross-cutting reads +│ ├── v1/ # legacy Registry / NameWrapper reads +│ └── v2/ # PermissionedRegistry / PermissionedResolver reads +├── wallet/ # writes +│ └── v2/ # v2 writes +├── dns/ +└── subgraph/ +``` + +### Where does it go? + +- **Write** = `wallet/`, **read** = `public/`. +- v1-only contract? `…/v1/`. v2-only? `…/v2/`. Works against the universal resolver or + shared infrastructure? Top-level `public/` or `wallet/`. +- Subgraph queries → `subgraph/`. DNS-related → `dns/`. + +### Action shape + +Each action ships **two** layers: + +1. **`get{ActionName}ReadParameters` / `{actionName}WriteParameters`** — a pure helper + that builds the full call payload (`ReadContractParameters` for reads, + `WriteContractParameters` for writes: address + abi + functionName + args, plus + `chain` / `account` for writes) without actually hitting the chain. +2. **`{actionName}` action** — a thin convenience wrapper that calls the parameters + helper and forwards the result to viem's `readContract` / `multicall` / + `writeContract`. + +**Consumers should be steered toward the parameters helpers.** Pair them with viem's +`readContract` / `writeContract` (or with `multicall`, `simulateContract`, +`estimateContractGas`, `resolverMulticallParameters`, etc.) so they keep full control +over batching, simulation, gas estimation, and transaction inspection. The action is +just there for the simple end-to-end case. + +JSDoc `@example`s on **new** actions should demonstrate the parameters helper — the +action itself is the obvious one-liner. + +#### Read action skeleton + +```ts +// 1. ReadParameters helper — pure, builds the read payload. +export type Get{ActionName}ReadParametersParameters = { /* on-chain inputs */ } +export type Get{ActionName}ReadParametersReturnType = + ReturnType +export type Get{ActionName}ReadParametersErrorType = /* coder errors */ + +export const get{ActionName}ReadParameters = ( + params: Get{ActionName}ReadParametersParameters, +) => { + return { + address, + ...{coder}Parameters(params), // delegate abi/functionName/args to a coder + } as const satisfies ReadContractParameters +} + +// 2. Action — wraps ReadParameters + readContract (or multicall). +export type {ActionName}Parameters = Get{ActionName}ReadParametersParameters +export type {ActionName}ReturnType = /* decoded result */ +export type {ActionName}ErrorType = + | Get{ActionName}ReadParametersErrorType + | ReadContractErrorType + +export async function {actionName}( + client: Client, + params: {ActionName}Parameters, +): Promise<{ActionName}ReturnType> { + ASSERT_NO_TYPE_ERROR(client) + const readContractAction = getAction(client, readContract, 'readContract') + const result = await readContractAction(get{ActionName}ReadParameters(params)) + return decode{X}Result(result) +} +``` + +A handful of older actions (e.g. `src/actions/public/v2/getOwner.ts`) call `readContract` +inline without exporting a `ReadParameters` helper. **Don't follow that pattern for new +code** — extract a `get{ActionName}ReadParameters` helper, even if the action is small. + +#### Write action skeleton + +```ts +// 1. WriteParameters helper — pure, builds the tx payload. +export type {ActionName}WriteParametersParameters = { /* on-chain inputs */ } +export type {ActionName}WriteParametersReturnType = + ReturnType +export type {ActionName}WriteParametersErrorType = /* coder errors */ + +export const {actionName}WriteParameters = ( + client: Client, + params: {ActionName}WriteParametersParameters, +) => { + return { + address, + chain: client.chain, + account: client.account, + ...{coder}Parameters(params), // delegate abi/functionName/args to a coder + } as const satisfies WriteContractParameters +} + +// 2. Action — wraps WriteParameters + writeContract. +export type {ActionName}Parameters = + Prettify<{ActionName}WriteParametersParameters & WriteTransactionParameters<…>> +export type {ActionName}ReturnType = WriteContractReturnType +export type {ActionName}ErrorType = + | {ActionName}WriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +export async function {actionName}(client, { …params, ...txArgs }) { + const writeParameters = {actionName}WriteParameters( + clientWithOverrides(client, txArgs), + params, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ ...writeParameters, ...txArgs } as WriteContractParameters) +} +``` + +`src/actions/wallet/setTextRecord.ts` is the canonical write example. For reads, build the +helper the same way using `ReadContractParameters` (no `chain` / `account`) — the action +just calls `readContract` (or `multicall`) on the result. + +Conventions: + +- Always go through `getAction(client, viemAction, 'viemAction')` so user-supplied action + overrides are respected. +- Read actions that hit multiple calls should use `multicall` for batching. +- Import ABIs from `@ensdomains/ensjs-abi/...`, **not** by hand-rolling them inside the + action file. +- If the action requires specific contracts on the chain, type the `client` parameter with + `RequireClientContracts<…>` (see `clients/shared.ts`) so misconfigured chains fail at + compile time instead of runtime. +- Keep parameter and return types `Prettify`-wrapped where it helps DX. +- Every public type, parameter, and return value should be exported so downstream apps can + build wrappers. +- Add a JSDoc block on the function with a one-line description and a usage `@example`. The + `generateDocs` script depends on these. + +### Coders: preparing read/write parameters for actions + +Most actions don't talk to viem directly — they delegate the work of *preparing the +read or write call* to a coder in `packages/ensjs/src/utils/coders/`. A coder is a small +module that produces the `{ abi, functionName, args }` payload an action (or a user +hand-rolling a batched call) feeds into `readContract`, `writeContract`, `multicall`, or +`encodeFunctionData`. This is the layer that lets users compose calls themselves via +`resolveNameData`, `resolverMulticallParameters`, etc., without re-implementing the action. + +The convention for each coder file (e.g. `getText.ts`, `setAddr.ts`, `getAbi.ts`): + +- **`get{X}Parameters({ … })`** — prepares a **read** call. Returns + `{ abi, functionName, args } as const`, ready to hand to `readContract`, `multicall`, or + `encodeFunctionData`. Export the matching `Get{X}ParametersParameters`, + `Get{X}ParametersReturnType`, and `Get{X}ParametersErrorType` types. +- **`set{X}Parameters({ … })`** — prepares a **write** call. Same shape, intended for + `writeContract` / `encodeFunctionData`. +- **`decode{X}Result(data, { strict })`** — decodes the raw `Hex` returned by a read into + the user-facing shape. Should respect `strict` (throw on decode error vs. return + `null`). +- **`decode{X}ResultFromPrimitiveTypes({ decodedData })`** — pure transform from the ABI's + decoded primitive into the final shape. Split out so callers that already decoded the + result themselves (e.g. inside a multicall handler) can reuse the post-processing + without re-running ABI decoding. + +An action then composes the coder rather than hand-building ABI args: + +```ts +// Read action: prepare params with the coder, send via resolver, decode the result. +const result = await resolveNameDataAction({ + name, + data: encodeFunctionData(getAddressParameters({ name, coin })), +}) +return decodeAddressResult(result.resolvedData, { strict, coin }) +``` + +```ts +// Write action: prepare params with the coder, hand off to writeContract. +return writeContractAction({ + ...setTextParameters({ name, key, value }), + address: resolverAddress, +}) +``` + +When you add a new record type or on-chain call, **write the coder first**, then build the +action on top of it. Coders are exported from `@ensdomains/ensjs/utils` (or `/utils/v2`) — +add new ones to the relevant `src/utils/index.ts` so consumers can import them and build +their own batched calls. + +### Wire it into the public API + +Each action subpath has an entry-point file under `src/exports/` that re-exports +everything in that group. Add a line to the right one: + +| Action location | Export file | +| --- | --- | +| `actions/public/*.ts` | `src/exports/public.ts` | +| `actions/public/v1/*.ts` | `src/exports/public/v1.ts` | +| `actions/public/v2/*.ts` | `src/exports/public/v2.ts` | +| `actions/wallet/*.ts` | `src/exports/wallet.ts` | +| `actions/wallet/v2/*.ts` | `src/exports/wallet/v2.ts` (also re-exported from `wallet.ts` if shared) | +| `actions/subgraph/*.ts` | `src/subgraph.ts` | +| `actions/dns/*.ts` | `src/dns.ts` | + +Keep these export lists alphabetical. + +### Tests + +Co-locate the test next to the action: `getFoo.ts` → `getFoo.test.ts`. Use the helpers in +`src/test/`: + +```ts +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getOwner } from './getOwner.js' + +describe('getOwner', () => { + it('returns the owner for a V2 name', async () => { + const owner = await getOwner(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'example', + }) + expect(owner).not.toEqual(zeroAddress) + }) +}) +``` + +Run with `pnpm -F @ensdomains/ensjs test`. The local environment must be running +(`pnpm -F @ensdomains/ensjs denv`) for tests that hit deployed contracts. + +--- + +## Adding or updating a contract address + +Chain-pinned ENS addresses live in `packages/ensjs/src/clients/l1.ts`: + +1. Add the contract name to the `supportedL1Contracts` tuple. +2. Add the address under each chain entry in `ensL1Contracts` (use `zeroAddress` if it + isn't deployed on that chain yet — strict typing requires every entry to be present). +3. If the contract is part of a new ENS version or category, also extend `ChainWithEns` / + `extendChainWithEns` typing as needed. + +Subgraph URLs are next to the contract addresses in the same file (`ensL1Subgraphs`). + +--- + +## Errors + +Custom errors live in `packages/ensjs/src/errors/` and are re-exported from +`packages/ensjs/src/index.ts`. New error classes should: + +- Extend `BaseError`. +- Have a stable `name` for `instanceof`-style discrimination. +- Be added to the appropriate file (`general.ts`, `public.ts`, `subgraph.ts`, `utils.ts`, + `dns.ts`, `contracts.ts`) and re-exported from `index.ts`. + +--- + +## Pull request checklist + +- [ ] Code formatted and linted (`pnpm lint`). +- [ ] Tests added or updated, `pnpm -F @ensdomains/ensjs test` passes. +- [ ] Type-check passes (`pnpm -F @ensdomains/ensjs check:types`). +- [ ] New actions are re-exported from the relevant entry point. +- [ ] New ABI snippets are exported from `@ensdomains/ensjs-abi` and re-exported from + `ensjs/contracts`. +- [ ] Changeset added (`pnpm chgset:run`) describing the user-visible change. +- [ ] JSDoc `@example` on any new public function. diff --git a/README.md b/README.md index 35f793f1a..19739c6b0 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,153 @@ # ![ENSjs](https://user-images.githubusercontent.com/11844316/161689061-98ea01ee-b119-40ac-a512-5370eb8b4107.svg) -The ultimate ENS javascript library, with [viem](https://github.com/wagmi-dev/viem) under the hood. +The ultimate ENS JavaScript library, with [viem](https://github.com/wevm/viem) under the hood. + +ENSjs is a TypeScript library for interacting with the Ethereum Name Service. It provides +tree-shakeable, composable actions on top of viem, with first-class support for both +ENS v1 and ENS v2. ## Features -- Super fast response times -- Easy call batchability -- Written in TypeScript -- Supports the most cutting edge ENS features -- Full tree-shaking support +- Composable actions that extend any viem `Client` +- Full tree-shaking — pay only for the actions you import +- Multicall batching for read actions +- TypeScript-first, with strict types for chain contracts +- ENS v1 + ENS v2 support side by side +- Subgraph and DNS helpers included +- Standalone `@ensdomains/ensjs-abi` package for ABI snippets -## Installation +## Monorepo layout + +| Package | Description | +| --- | --- | +| [`@ensdomains/ensjs`](./packages/ensjs) | Main library — actions, clients, utils | +| [`@ensdomains/ensjs-abi`](./packages/ensjs-abi) | ABI snippets for every ENS contract (v1 + v2) | +| [`@ensdomains/ensjs-react`](./packages/react) | React hooks built on `@wagmi/core` | +| [`@ensdomains/ensjs-query-core`](./packages/query-core) | `@wagmi/core` query integration | -Install @ensdomains/ensjs, alongside [viem](https://github.com/wagmi-dev/viem). +Supported chains: `mainnet` (1) and `sepolia` (11155111). + +## Installation ```sh -npm install @ensdomains/ensjs viem +pnpm add @ensdomains/ensjs viem ``` -## Getting Started +You'll also need `viem` ≥ 2.9.2 as a peer dependency. -The most simple way to get started is to create a public ENS client, with a supported -chain and transport imported from viem. The public client has all the read functions available on it, -as well as all subgraph functions. +## Getting started + +`addEnsL1Contracts` extends a viem chain with all ENS contract addresses and subgraph URLs, +so you can use it with any viem `createPublicClient` / `createWalletClient`. ```ts -// Import viem transport, viem chain, and ENSjs -import { http } from 'viem' +import { http, createPublicClient } from 'viem' import { mainnet } from 'viem/chains' -import { createEnsPublicClient } from '@ensdomains/ensjs' +import { addEnsL1Contracts } from '@ensdomains/ensjs' +import { getAddressRecord, getRecords } from '@ensdomains/ensjs/public' -// Create the client -const client = createEnsPublicClient({ - chain: mainnet, +const client = createPublicClient({ + chain: addEnsL1Contracts(mainnet), transport: http(), }) -// Use the client -const ethAddress = client.getAddressRecord({ name: 'ens.eth' }) +const eth = await getAddressRecord(client, { name: 'ens.eth' }) +const records = await getRecords(client, { + name: 'ens.eth', + texts: ['com.twitter', 'avatar'], + contentHash: true, +}) +``` + +## Entry points + +The `@ensdomains/ensjs` package is split into subpath exports so bundlers can drop +everything you don't import. + +| Import | Contents | +| --- | --- | +| `@ensdomains/ensjs` | `addEnsL1Contracts`, error classes | +| `@ensdomains/ensjs/public` | Shared read actions (resolution, records, reverse, price, availability) | +| `@ensdomains/ensjs/public/v1` | v1-specific reads | +| `@ensdomains/ensjs/public/v2` | v2-specific reads | +| `@ensdomains/ensjs/wallet` | Shared write actions (register, renew, set records, wrap, transfer, …) | +| `@ensdomains/ensjs/wallet/v2` | v2-specific writes | +| `@ensdomains/ensjs/subgraph` | Subgraph client + queries (`getSubnames`, `getNamesForAddress`, history, …) | +| `@ensdomains/ensjs/dns` | DNS helpers (`getDnsOwner`, `importDnsName`, `getDnsImportData`, …) | +| `@ensdomains/ensjs/utils` | Coders (`getAddress`, `getText`, `getAbi`, `getContentHash`), name utils | +| `@ensdomains/ensjs/utils/v2` | v2 utils (role encoding, resolver resources, canonical IDs) | +| `@ensdomains/ensjs/contracts` | Re-exports of ABI snippets and `getChainContractAddress` | +| `@ensdomains/ensjs/chain` | Chain types and helpers (`ChainWithEns`, `extendChainWithEns`) | + +ABIs themselves are published as a standalone package and can be imported directly: + +```ts +import { permissionedRegistryGetStateSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { ethRegistrarControllerRegisterSnippet } from '@ensdomains/ensjs-abi/v1/ethRegistrarController' +``` + +## Action pattern + +Every action is a plain function that takes the viem `Client` first and parameters second. + +```ts +const result = await actionName(client, { + /* params */ +}) +``` + +Read actions use `getAction` + `readContract` / `multicall` so they work with whatever +batching configuration is on the client. Write actions return a transaction hash and accept +the standard viem write parameters (`account`, `chain`, gas, etc.). + +## Development + +```sh +# Install +pnpm install + +# Build everything +pnpm -r build + +# Build a single package +pnpm -F @ensdomains/ensjs build +pnpm -F @ensdomains/ensjs-abi build + +# Lint (Biome) +pnpm lint + +# Test the main package +pnpm -F @ensdomains/ensjs test +pnpm -F @ensdomains/ensjs test:watch +pnpm -F @ensdomains/ensjs test src/actions/public/getRecords.test.ts + +# Local test environment with deployed ENS contracts +pnpm -F @ensdomains/ensjs denv + +# Just the local anvil node (no contract deployment scripts) +pnpm -F @ensdomains/ensjs anvil + +# Generate the markdown docs site +pnpm -F @ensdomains/ensjs generateDocs + +# Versioning (changesets) +pnpm chgset:run +pnpm chgset:version ``` +Node ≥ 22 is required for the main packages (≥ 18 for `query-core`). Tooling: pnpm 10, +TypeScript strict mode, Biome for formatting and linting, Vitest for tests. + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidance on adding new actions, wiring up new +ABI snippets, registering new contract addresses, and the test conventions used across the +repo. + ## Docs -Docs can be found [here](https://github.com/ensdomains/ensjs-v3/tree/main/docs). Full docs site coming soon. +Per-action markdown docs live under [`docs/`](./docs). A hosted docs site is in progress. + +## License + +MIT diff --git a/biome.json b/biome.json index c29ff8f03..39e3df977 100644 --- a/biome.json +++ b/biome.json @@ -1,58 +1,56 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", - "files": { - "ignore": [ - "node_modules", - "package.json", - "dist", - "coverage", - "data", - "cache", - "tsconfig.*.json", - "packages/ensjs/contracts" - ] - }, - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "complexity": { - "noForEach": "off", - "noBannedTypes": "warn" - }, - "style": { - "noNonNullAssertion": "off" - }, - "correctness": { - "noUnusedImports": "error", - "noUnusedVariables": "error", - "noUnusedPrivateClassMembers": "error" - }, - "performance": { - "noAccumulatingSpread": "off" - }, - "suspicious": { - "noAssignInExpressions": "off", - "noExplicitAny": "warn" - } - } - }, - "formatter": { - "enabled": true - }, - "javascript": { - "formatter": { - "enabled": true, - "semicolons": "asNeeded", - "quoteStyle": "single", - "indentWidth": 2, - "indentStyle": "space" - }, - "linter": { - "enabled": true - } - } + "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "packages/**/*.{cts,ts,mts}", + "packages/*/*.js", + "examples/**/*.{ts}", + "!**/*.gen.ts", + "!**/node_modules/**", + "!**/dist/**" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "noParameterAssign": "error", + "useAsConstAssertion": "error", + "useDefaultParameterLast": "error", + "useEnumInitializers": "error", + "useSelfClosingElements": "error", + "useSingleVarDeclarator": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "asNeeded" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } } diff --git a/examples/basic-esm/src/index.js b/examples/basic-esm/src/index.js deleted file mode 100644 index aa1d89877..000000000 --- a/examples/basic-esm/src/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import { createEnsPublicClient } from '@ensdomains/ensjs' -import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public' -import { http } from 'viem' -import { mainnet } from 'viem/chains' - -const client = createEnsPublicClient({ - chain: mainnet, - transport: http('https://web3.euc.li/v1/mainnet'), -}) - -const main = async () => { - const records = await client.getSubgraphRecords({ name: 'ens.eth' }) - const recordData = await client.getRecords({ - name: 'ens.eth', - coins: [...(records?.coins || []), 'BTC', 'ETH', 'ETC', 'SOL'], - texts: [...(records?.texts || []), 'avatar', 'email', 'description'], - contentHash: true, - abi: true, - }) - console.log(recordData) - - const batchData = await client.ensBatch( - getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), - getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), - ) - console.log(batchData) -} - -main() diff --git a/examples/basic-tsnode-esm/package.json b/examples/basic-tsnode-esm/package.json deleted file mode 100644 index b97f2c981..000000000 --- a/examples/basic-tsnode-esm/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "basic-tsnode-esm", - "private": true, - "type": "module", - "version": "0.0.1", - "scripts": { - "start": "ts-node-esm src/index.ts" - }, - "dependencies": { - "@ensdomains/ensjs": "workspace:*", - "ts-node": "^10.9.2", - "typescript": "^5.6.2", - "viem": "^2.30.6" - } -} diff --git a/examples/basic-tsnode-esm/src/index.ts b/examples/basic-tsnode-esm/src/index.ts deleted file mode 100644 index 6b3fb4263..000000000 --- a/examples/basic-tsnode-esm/src/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createEnsPublicClient } from '@ensdomains/ensjs' -import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public' -import { http } from 'viem' -import { mainnet } from 'viem/chains' - -const client = createEnsPublicClient({ - chain: mainnet, - transport: http('https://web3.euc.li/v1/mainnet'), -}) - -const main = async () => { - const records = await client.getSubgraphRecords({ name: 'ens.eth' }) - const recordData = await client.getRecords({ - name: 'ens.eth', - abi: true, - contentHash: true, - ...records, - }) - console.log(recordData) - - const batchData = await client.ensBatch( - getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), - getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), - ) - console.log(batchData) -} - -main() diff --git a/examples/basic-tsnode-esm/tsconfig.json b/examples/basic-tsnode-esm/tsconfig.json deleted file mode 100644 index 292a1522c..000000000 --- a/examples/basic-tsnode-esm/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "ts-node": { - "compilerOptions": { - "target": "es2020", - "esModuleInterop": true - } - }, - "include": ["src/**/*.ts"] -} diff --git a/examples/basic-esm/package.json b/examples/basic/package.json similarity index 67% rename from examples/basic-esm/package.json rename to examples/basic/package.json index 9ee083d4e..089331d0f 100644 --- a/examples/basic-esm/package.json +++ b/examples/basic/package.json @@ -1,13 +1,14 @@ { - "name": "basic-esm", + "name": "basic", "private": true, "type": "module", "version": "0.0.1", "scripts": { - "start": "node src/index.js" + "start": "node src/index.ts" }, "dependencies": { "@ensdomains/ensjs": "workspace:*", + "typescript": "^5.6.2", "viem": "^2.30.6" } -} +} \ No newline at end of file diff --git a/examples/basic/src/index.ts b/examples/basic/src/index.ts new file mode 100644 index 000000000..c676b0bc8 --- /dev/null +++ b/examples/basic/src/index.ts @@ -0,0 +1,49 @@ +import { addEnsL1Contracts } from '@ensdomains/ensjs' +import { getRecords, resolveNameData } from '@ensdomains/ensjs/public' +import { + decodeAddressResult, + decodeTextResult, + getAddressParameters, + getTextParameters, +} from '@ensdomains/ensjs/utils' +import { createPublicClient, encodeFunctionData, http } from 'viem' +import { mainnet } from 'viem/chains' + +const client = createPublicClient({ + chain: addEnsL1Contracts(mainnet), + transport: http('https://ethereum-rpc.publicnode.com'), +}) + +const main = async () => { + const recordData = await getRecords(client, { + name: 'ens.eth', + abi: true, + contentHash: true, + }) + + console.log(recordData) + + const batchData = await resolveNameData(client, { + name: 'ens.eth', + data: [ + encodeFunctionData( + getTextParameters({ name: 'ens.eth', key: 'com.twitter' }), + ), + encodeFunctionData( + getAddressParameters({ name: 'ens.eth', coin: 'ETH' }), + ), + ], + }) + if (!batchData) throw new Error('No data returned') + + console.log( + 'Text:', + decodeTextResult(batchData.resolvedData[0].returnData, { strict: true }), + ) + console.log( + 'Address:', + decodeAddressResult(batchData.resolvedData[1].returnData, { strict: true }), + ) +} + +main() diff --git a/examples/basic/tsconfig.json b/examples/basic/tsconfig.json new file mode 100644 index 000000000..fd8b5e417 --- /dev/null +++ b/examples/basic/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*.ts"] +} diff --git a/package.json b/package.json index 842279ad2..aa59d5a87 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,14 @@ "chgset:run": "changeset", "release": "pnpm publish -r --access public && changeset tag", "chgset": "pnpm chgset:run && pnpm chgset:version", - "lint": "biome check" + "lint": "pnpm -r lint" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "2.0.6", "@changesets/changelog-github": "^0.5.0", "@changesets/cli": "^2.27.8", - "ts-node": "^10.7.0", - "typescript": "^5.8.3" + "typescript": "^5.9.3", + "viem": "^2.38.3" }, "resolutions": { "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@0.3.0-beta.13" @@ -29,5 +29,5 @@ "unplugged": true } }, - "packageManager": "pnpm@10.10.0" + "packageManager": "pnpm@10.16.0" } diff --git a/packages/ensjs-abi/package.json b/packages/ensjs-abi/package.json new file mode 100644 index 000000000..0a5d2f5ce --- /dev/null +++ b/packages/ensjs-abi/package.json @@ -0,0 +1,145 @@ +{ + "name": "@ensdomains/ensjs-abi", + "version": "5.0.0-alpha.1", + "description": "ENS ABI snippets for contract interaction", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./erc165": { + "types": "./dist/erc165.d.ts", + "default": "./dist/erc165.js" + }, + "./registry": { + "types": "./dist/registry.d.ts", + "default": "./dist/registry.js" + }, + "./reverseRegistrar": { + "types": "./dist/reverseRegistrar.d.ts", + "default": "./dist/reverseRegistrar.js" + }, + "./defaultReverseRegistrar": { + "types": "./dist/defaultReverseRegistrar.d.ts", + "default": "./dist/defaultReverseRegistrar.js" + }, + "./defaultReverseResolver": { + "types": "./dist/defaultReverseResolver.d.ts", + "default": "./dist/defaultReverseResolver.js" + }, + "./multicall": { + "types": "./dist/multicall.d.ts", + "default": "./dist/multicall.js" + }, + "./universalResolver": { + "types": "./dist/universalResolver.d.ts", + "default": "./dist/universalResolver.js" + }, + "./compositeResolver": { + "types": "./dist/compositeResolver.d.ts", + "default": "./dist/compositeResolver.js" + }, + "./dnsRegistrar": { + "types": "./dist/dnsRegistrar.d.ts", + "default": "./dist/dnsRegistrar.js" + }, + "./dnssecImpl": { + "types": "./dist/dnssecImpl.d.ts", + "default": "./dist/dnssecImpl.js" + }, + "./v1": { + "types": "./dist/v1/index.d.ts", + "default": "./dist/v1/index.js" + }, + "./v1/ethRegistrarController": { + "types": "./dist/v1/ethRegistrarController.d.ts", + "default": "./dist/v1/ethRegistrarController.js" + }, + "./v1/publicResolver": { + "types": "./dist/v1/publicResolver.d.ts", + "default": "./dist/v1/publicResolver.js" + }, + "./v1/nameWrapper": { + "types": "./dist/v1/nameWrapper.d.ts", + "default": "./dist/v1/nameWrapper.js" + }, + "./v1/baseRegistrar": { + "types": "./dist/v1/baseRegistrar.d.ts", + "default": "./dist/v1/baseRegistrar.js" + }, + "./v1/bulkRenewal": { + "types": "./dist/v1/bulkRenewal.d.ts", + "default": "./dist/v1/bulkRenewal.js" + }, + "./v1/ethRenewer": { + "types": "./dist/v1/ethRenewer.d.ts", + "default": "./dist/v1/ethRenewer.js" + }, + "./v2": { + "types": "./dist/v2/index.d.ts", + "default": "./dist/v2/index.js" + }, + "./v2/ethRegistrar": { + "types": "./dist/v2/ethRegistrar.d.ts", + "default": "./dist/v2/ethRegistrar.js" + }, + "./v2/userRegistry": { + "types": "./dist/v2/userRegistry.d.ts", + "default": "./dist/v2/userRegistry.js" + }, + "./v2/permissionedRegistry": { + "types": "./dist/v2/permissionedRegistry.d.ts", + "default": "./dist/v2/permissionedRegistry.js" + }, + "./v2/permissionedResolver": { + "types": "./dist/v2/permissionedResolver.d.ts", + "default": "./dist/v2/permissionedResolver.js" + }, + "./v2/standardRentPriceOracle": { + "types": "./dist/v2/standardRentPriceOracle.d.ts", + "default": "./dist/v2/standardRentPriceOracle.js" + }, + "./v2/enhancedAccessControl": { + "types": "./dist/v2/enhancedAccessControl.d.ts", + "default": "./dist/v2/enhancedAccessControl.js" + }, + "./v2/verifiableFactory": { + "types": "./dist/v2/verifiableFactory.d.ts", + "default": "./dist/v2/verifiableFactory.js" + }, + "./v2/migrationHelper": { + "types": "./dist/v2/migrationHelper.d.ts", + "default": "./dist/v2/migrationHelper.js" + }, + "./v2/lockedMigrationController": { + "types": "./dist/v2/lockedMigrationController.d.ts", + "default": "./dist/v2/lockedMigrationController.js" + }, + "./v2/unlockedMigrationController": { + "types": "./dist/v2/unlockedMigrationController.d.ts", + "default": "./dist/v2/unlockedMigrationController.js" + }, + "./package.json": "./package.json" + }, + "files": [ + "dist/", + "src/" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/ensdomains/ensjs.git", + "directory": "packages/ensjs-abi" + }, + "scripts": { + "build": "tsc --project tsconfig.build.json" + }, + "dependencies": {}, + "peerDependencies": {}, + "engines": { + "node": ">=22" + } +} \ No newline at end of file diff --git a/packages/ensjs-abi/src/compositeResolver.ts b/packages/ensjs-abi/src/compositeResolver.ts new file mode 100644 index 000000000..2b0bfa726 --- /dev/null +++ b/packages/ensjs-abi/src/compositeResolver.ts @@ -0,0 +1,23 @@ +export const compositeResolverGetResolver = [ + { + inputs: [ + { + name: 'name', + type: 'bytes', + }, + ], + name: 'getResolver', + outputs: [ + { + name: 'resolver', + type: 'address', + }, + { + name: 'offchain', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/src/defaultReverseRegistrar.ts b/packages/ensjs-abi/src/defaultReverseRegistrar.ts new file mode 100644 index 000000000..ba7e026fb --- /dev/null +++ b/packages/ensjs-abi/src/defaultReverseRegistrar.ts @@ -0,0 +1,45 @@ +export const defaultReverseRegistrarSetNameSnippet = [ + { + inputs: [ + { + name: 'name', + type: 'string', + }, + ], + name: 'setName', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const defaultReverseRegistrarSetNameForAddrWithSignatureSnippet = [ + { + inputs: [ + { + name: 'addr', + type: 'address', + }, + { + name: 'signatureExpiry', + type: 'uint256', + }, + { + name: 'name', + type: 'string', + }, + { + name: 'coinTypes', + type: 'uint256[]', + }, + { + name: 'signature', + type: 'bytes', + }, + ], + name: 'setNameForAddrWithSignature', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/src/defaultReverseResolver.ts b/packages/ensjs-abi/src/defaultReverseResolver.ts new file mode 100644 index 000000000..d195e29d3 --- /dev/null +++ b/packages/ensjs-abi/src/defaultReverseResolver.ts @@ -0,0 +1,19 @@ +export const defaultReverseResolverResolveNamesSnippet = [ + { + inputs: [ + { + name: 'addrs', + type: 'address[]', + }, + ], + name: 'resolveNames', + outputs: [ + { + name: 'names', + type: 'string[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/contracts/dnsRegistrar.ts b/packages/ensjs-abi/src/dnsRegistrar.ts similarity index 89% rename from packages/ensjs/src/contracts/dnsRegistrar.ts rename to packages/ensjs-abi/src/dnsRegistrar.ts index 64b28a362..81dc6cf95 100644 --- a/packages/ensjs/src/contracts/dnsRegistrar.ts +++ b/packages/ensjs-abi/src/dnsRegistrar.ts @@ -2,7 +2,6 @@ export const dnsRegistrarErrors = [ { inputs: [ { - internalType: 'bytes', name: 'name', type: 'bytes', }, @@ -18,12 +17,10 @@ export const dnsRegistrarErrors = [ { inputs: [ { - internalType: 'uint256', name: 'offset', type: 'uint256', }, { - internalType: 'uint256', name: 'length', type: 'uint256', }, @@ -34,12 +31,10 @@ export const dnsRegistrarErrors = [ { inputs: [ { - internalType: 'address', name: 'caller', type: 'address', }, { - internalType: 'address', name: 'owner', type: 'address', }, @@ -100,12 +95,10 @@ export const dnsRegistrarProveAndClaimWithResolverSnippet = [ { components: [ { - internalType: 'bytes', name: 'rrset', type: 'bytes', }, { - internalType: 'bytes', name: 'sig', type: 'bytes', }, diff --git a/packages/ensjs/src/contracts/dnssecImpl.ts b/packages/ensjs-abi/src/dnssecImpl.ts similarity index 77% rename from packages/ensjs/src/contracts/dnssecImpl.ts rename to packages/ensjs-abi/src/dnssecImpl.ts index 92e49b9e2..1dcb59923 100644 --- a/packages/ensjs/src/contracts/dnssecImpl.ts +++ b/packages/ensjs-abi/src/dnssecImpl.ts @@ -1,4 +1,16 @@ +export const dnssecImplErrors = [ + { + inputs: [ + { name: 'inception', type: 'uint32' }, + { name: 'now', type: 'uint32' }, + ], + name: 'SignatureExpired', + type: 'error', + }, +] as const + export const dnssecImplVerifyRrSetSnippet = [ + ...dnssecImplErrors, { inputs: [ { diff --git a/packages/ensjs/src/contracts/erc165.ts b/packages/ensjs-abi/src/erc165.ts similarity index 100% rename from packages/ensjs/src/contracts/erc165.ts rename to packages/ensjs-abi/src/erc165.ts diff --git a/packages/ensjs-abi/src/index.ts b/packages/ensjs-abi/src/index.ts new file mode 100644 index 000000000..9684161f4 --- /dev/null +++ b/packages/ensjs-abi/src/index.ts @@ -0,0 +1,13 @@ +export * from './compositeResolver.js' +export * from './defaultReverseRegistrar.js' +export * from './defaultReverseResolver.js' +export * from './dnsRegistrar.js' +export * from './dnssecImpl.js' +export * from './erc165.js' +export * from './multicall.js' +export * from './registry.js' +export * from './reverseRegistrar.js' +export * from './universalResolver.js' + +export * as v1 from './v1/index.js' +export * as v2 from './v2/index.js' diff --git a/packages/ensjs/src/contracts/multicall.ts b/packages/ensjs-abi/src/multicall.ts similarity index 77% rename from packages/ensjs/src/contracts/multicall.ts rename to packages/ensjs-abi/src/multicall.ts index c48049ba4..de91199b3 100644 --- a/packages/ensjs/src/contracts/multicall.ts +++ b/packages/ensjs-abi/src/multicall.ts @@ -56,3 +56,23 @@ export const multicallGetCurrentBlockTimestampSnippet = [ type: 'function', }, ] as const + +export const multicallSnippet = [ + { + inputs: [ + { + name: 'data', + type: 'bytes[]', + }, + ], + name: 'multicall', + outputs: [ + { + name: 'results', + type: 'bytes[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/contracts/registry.ts b/packages/ensjs-abi/src/registry.ts similarity index 86% rename from packages/ensjs/src/contracts/registry.ts rename to packages/ensjs-abi/src/registry.ts index 583979ca9..6d75db22a 100644 --- a/packages/ensjs/src/contracts/registry.ts +++ b/packages/ensjs-abi/src/registry.ts @@ -180,3 +180,23 @@ export const registrySetRecordSnippet = [ type: 'function', }, ] as const + +export const registryGetSubregistrySnippet = [ + { + name: 'getSubregistry', + inputs: [{ name: 'label', type: 'string' }], + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const registryGetResolverSnippet = [ + { + name: 'getResolver', + inputs: [{ name: 'label', type: 'string' }], + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/contracts/reverseRegistrar.ts b/packages/ensjs-abi/src/reverseRegistrar.ts similarity index 100% rename from packages/ensjs/src/contracts/reverseRegistrar.ts rename to packages/ensjs-abi/src/reverseRegistrar.ts diff --git a/packages/ensjs/src/contracts/universalResolver.ts b/packages/ensjs-abi/src/universalResolver.ts similarity index 50% rename from packages/ensjs/src/contracts/universalResolver.ts rename to packages/ensjs-abi/src/universalResolver.ts index d8a3fbd3b..bdccfe7db 100644 --- a/packages/ensjs/src/contracts/universalResolver.ts +++ b/packages/ensjs-abi/src/universalResolver.ts @@ -1,23 +1,52 @@ export const universalResolverErrors = [ { - inputs: [], - name: 'ResolverNotFound', + inputs: [ + { + name: 'dns', + type: 'bytes', + }, + ], + name: 'DNSDecodingFailed', + type: 'error', + }, + { + inputs: [ + { + name: 'ens', + type: 'string', + }, + ], + name: 'DNSEncodingFailed', type: 'error', }, { inputs: [], - name: 'ResolverWildcardNotSupported', + name: 'EmptyAddress', + type: 'error', + }, + { + inputs: [ + { + name: 'status', + type: 'uint16', + }, + { + name: 'message', + type: 'string', + }, + ], + name: 'HttpError', type: 'error', }, { inputs: [], - name: 'ResolverNotContract', + name: 'InvalidBatchGatewayResponse', type: 'error', }, { inputs: [ { - name: 'returnData', + name: 'errorData', type: 'bytes', }, ], @@ -27,21 +56,49 @@ export const universalResolverErrors = [ { inputs: [ { - components: [ - { - name: 'status', - type: 'uint16', - }, - { - name: 'message', - type: 'string', - }, - ], - name: 'errors', - type: 'tuple[]', + name: 'name', + type: 'bytes', + }, + { + name: 'resolver', + type: 'address', }, ], - name: 'HttpError', + name: 'ResolverNotContract', + type: 'error', + }, + { + inputs: [ + { + name: 'name', + type: 'bytes', + }, + ], + name: 'ResolverNotFound', + type: 'error', + }, + { + inputs: [ + { + name: 'primary', + type: 'string', + }, + { + name: 'primaryAddress', + type: 'bytes', + }, + ], + name: 'ReverseAddressMismatch', + type: 'error', + }, + { + inputs: [ + { + name: 'selector', + type: 'bytes4', + }, + ], + name: 'UnsupportedResolverProfile', type: 'error', }, ] as const @@ -49,16 +106,19 @@ export const universalResolverErrors = [ const universalResolverReverse = { inputs: [ { - name: 'reverseName', + name: 'encodedAddress', type: 'bytes', }, + { + name: 'coinType', + type: 'uint256', + }, ], name: 'reverse', outputs: [ - { type: 'string', name: 'resolvedName' }, - { type: 'address', name: 'resolvedAddress' }, - { type: 'address', name: 'reverseResolver' }, + { type: 'string', name: 'name' }, { type: 'address', name: 'resolver' }, + { type: 'address', name: 'reverseResolver' }, ], stateMutability: 'view', type: 'function', @@ -73,6 +133,7 @@ export const universalResolverReverseWithGatewaysSnippet = [ ...universalResolverErrors, { ...universalResolverReverse, + name: 'reverseWithGateways', inputs: [ ...universalResolverReverse.inputs, { @@ -118,6 +179,7 @@ export const universalResolverResolveWithGatewaysSnippet = [ ...universalResolverErrors, { ...universalResolverResolve, + name: 'resolveWithGateways', inputs: [ ...universalResolverResolve.inputs, { @@ -128,63 +190,56 @@ export const universalResolverResolveWithGatewaysSnippet = [ }, ] as const -const universalResolverResolveArray = { - inputs: [ - { - name: 'name', - type: 'bytes', - }, - { - name: 'data', - type: 'bytes[]', - }, - ], - name: 'resolve', - outputs: [ - { - components: [ - { - name: 'success', - type: 'bool', - }, - { - name: 'returnData', - type: 'bytes', - }, - ], - name: '', - type: 'tuple[]', - }, - { - name: '', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', -} as const - -export const universalResolverResolveArraySnippet = [ +export const universalResolverFindResolverSnippet = [ ...universalResolverErrors, - universalResolverResolveArray, + { + inputs: [ + { + name: 'name', + type: 'bytes', + }, + ], + name: 'findResolver', + outputs: [ + { + name: 'address', + type: 'address', + }, + { + name: 'node', + type: 'bytes32', + }, + { + name: 'offset', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, ] as const -export const universalResolverResolveArrayWithGatewaysSnippet = [ - ...universalResolverErrors, +export const universalResolverV2FindOwnerSnippet = [ { - ...universalResolverResolveArray, inputs: [ - ...universalResolverResolveArray.inputs, { - name: 'gateways', - type: 'string[]', + name: 'name', + type: 'bytes', }, ], + name: 'findOwner', + outputs: [ + { + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', }, ] as const -export const universalResolverFindResolverSnippet = [ - ...universalResolverErrors, +export const universalResolverV2FindParentRegistrySnippet = [ { inputs: [ { @@ -192,15 +247,33 @@ export const universalResolverFindResolverSnippet = [ type: 'bytes', }, ], - name: 'findResolver', + name: 'findParentRegistry', outputs: [ { name: '', type: 'address', }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const universalResolverFindRegistriesSnippet = [ + ...universalResolverErrors, + { + inputs: [ + { + name: 'name', + type: 'bytes', + }, + ], + name: 'findRegistries', + outputs: [ { + internalType: 'contract IRegistry[]', name: '', - type: 'bytes32', + type: 'address[]', }, ], stateMutability: 'view', diff --git a/packages/ensjs-abi/src/v1/baseRegistrar.ts b/packages/ensjs-abi/src/v1/baseRegistrar.ts new file mode 100644 index 000000000..9ed3a423d --- /dev/null +++ b/packages/ensjs-abi/src/v1/baseRegistrar.ts @@ -0,0 +1,231 @@ +const erc721OwnerOfSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + ], + name: 'ownerOf', + outputs: [ + { + name: 'owner', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +const erc721SafeTransferFromSnippet = [ + { + inputs: [ + { + name: 'from', + type: 'address', + }, + { + name: 'to', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +const erc721SafeTransferFromWithDataSnippet = [ + { + inputs: [ + { + name: 'from', + type: 'address', + }, + { + name: 'to', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + { + name: '_data', + type: 'bytes', + }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const baseRegistrarAvailableSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + ], + name: 'available', + outputs: [ + { + name: 'available', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const baseRegistrarNameExpiresSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + ], + name: 'nameExpires', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const baseRegistrarGracePeriodSnippet = [ + { + inputs: [], + name: 'GRACE_PERIOD', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const baseRegistrarReclaimSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + { + name: 'owner', + type: 'address', + }, + ], + name: 'reclaim', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const baseRegistrarRegisterSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + { + name: 'owner', + type: 'address', + }, + { + name: 'duration', + type: 'uint256', + }, + ], + name: 'register', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const baseRegistrarAddControllerSnippet = [ + { + inputs: [ + { + name: 'controller', + type: 'address', + }, + ], + name: 'addController', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const baseRegistrarOwnerSnippet = [ + { + inputs: [], + name: 'owner', + outputs: [ + { + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const baseRegistrarControllersSnippet = [ + { + inputs: [ + { + name: '', + type: 'address', + }, + ], + name: 'controllers', + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const baseRegistrarSafeTransferFromSnippet = [ + ...erc721SafeTransferFromSnippet, +] as const + +export const baseRegistrarSafeTransferFromWithDataSnippet = [ + ...erc721SafeTransferFromWithDataSnippet, +] as const + +export const baseRegistrarOwnerOfSnippet = [...erc721OwnerOfSnippet] as const diff --git a/packages/ensjs/src/contracts/bulkRenewal.ts b/packages/ensjs-abi/src/v1/bulkRenewal.ts similarity index 100% rename from packages/ensjs/src/contracts/bulkRenewal.ts rename to packages/ensjs-abi/src/v1/bulkRenewal.ts diff --git a/packages/ensjs/src/contracts/ethRegistrarController.ts b/packages/ensjs-abi/src/v1/ethRegistrarController.ts similarity index 100% rename from packages/ensjs/src/contracts/ethRegistrarController.ts rename to packages/ensjs-abi/src/v1/ethRegistrarController.ts diff --git a/packages/ensjs-abi/src/v1/index.ts b/packages/ensjs-abi/src/v1/index.ts new file mode 100644 index 000000000..513314252 --- /dev/null +++ b/packages/ensjs-abi/src/v1/index.ts @@ -0,0 +1,5 @@ +export * from './baseRegistrar.js' +export * from './bulkRenewal.js' +export * from './ethRegistrarController.js' +export * from './nameWrapper.js' +export * from './publicResolver.js' diff --git a/packages/ensjs/src/contracts/nameWrapper.ts b/packages/ensjs-abi/src/v1/nameWrapper.ts similarity index 79% rename from packages/ensjs/src/contracts/nameWrapper.ts rename to packages/ensjs-abi/src/v1/nameWrapper.ts index db8db9752..285873c89 100644 --- a/packages/ensjs/src/contracts/nameWrapper.ts +++ b/packages/ensjs-abi/src/v1/nameWrapper.ts @@ -1,6 +1,72 @@ -import { erc721OwnerOfSnippet } from './erc721.js' -import { erc1155SafeTransferFromSnippet } from './erc1155.js' -import { registrySetResolverSnippet } from './registry.js' +const erc721OwnerOfSnippet = [ + { + inputs: [ + { + name: 'id', + type: 'uint256', + }, + ], + name: 'ownerOf', + outputs: [ + { + name: 'owner', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +const erc1155SafeTransferFromSnippet = [ + { + inputs: [ + { + name: 'from', + type: 'address', + }, + { + name: 'to', + type: 'address', + }, + { + name: 'id', + type: 'uint256', + }, + { + name: 'amount', + type: 'uint256', + }, + { + name: 'data', + type: 'bytes', + }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +const registrySetResolverSnippet = [ + { + inputs: [ + { + name: 'node', + type: 'bytes32', + }, + { + name: 'resolver', + type: 'address', + }, + ], + name: 'setResolver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const export const nameWrapperErrors = [ { @@ -304,6 +370,39 @@ export const nameWrapperWrapSnippet = [ }, ] as const +export const nameWrapperWrapEth2ldSnippet = [ + ...nameWrapperErrors, + { + inputs: [ + { + name: 'label', + type: 'string', + }, + { + name: 'wrappedOwner', + type: 'address', + }, + { + name: 'ownerControlledFuses', + type: 'uint16', + }, + { + name: 'resolver', + type: 'address', + }, + ], + name: 'wrapETH2LD', + outputs: [ + { + name: 'tokenId', + type: 'uint64', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + export const nameWrapperUnwrapSnippet = [ ...nameWrapperErrors, { diff --git a/packages/ensjs/src/contracts/publicResolver.ts b/packages/ensjs-abi/src/v1/publicResolver.ts similarity index 95% rename from packages/ensjs/src/contracts/publicResolver.ts rename to packages/ensjs-abi/src/v1/publicResolver.ts index 1764f8aa1..36e0b0f09 100644 --- a/packages/ensjs/src/contracts/publicResolver.ts +++ b/packages/ensjs-abi/src/v1/publicResolver.ts @@ -70,7 +70,6 @@ export const publicResolverContenthashSnippet = [ { inputs: [ { - internalType: 'bytes32', name: 'node', type: 'bytes32', }, @@ -78,7 +77,6 @@ export const publicResolverContenthashSnippet = [ name: 'contenthash', outputs: [ { - internalType: 'bytes', name: '', type: 'bytes', }, @@ -208,7 +206,6 @@ export const publicResolverClearRecordsSnippet = [ { inputs: [ { - internalType: 'bytes32', name: 'node', type: 'bytes32', }, @@ -220,6 +217,16 @@ export const publicResolverClearRecordsSnippet = [ }, ] as const +export const publicResolverClearRecordsV2Snippet = [ + { + inputs: [], + name: 'clearRecords', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + export const publicResolverMulticallSnippet = [ { inputs: [ diff --git a/packages/ensjs-abi/src/v2/enhancedAccessControl.ts b/packages/ensjs-abi/src/v2/enhancedAccessControl.ts new file mode 100644 index 000000000..040b40ba4 --- /dev/null +++ b/packages/ensjs-abi/src/v2/enhancedAccessControl.ts @@ -0,0 +1,226 @@ +export const eacRolesChangedEventSnippet = [ + { + inputs: [ + { + indexed: true, + name: 'resource', + type: 'uint256', + }, + { + indexed: true, + name: 'account', + type: 'address', + }, + { + indexed: false, + name: 'oldRoleBitmap', + type: 'uint256', + }, + { + indexed: false, + name: 'newRoleBitmap', + type: 'uint256', + }, + ], + name: 'EACRolesChanged', + type: 'event', + anonymous: false, + }, +] as const + +export const eacErrors = [ + { + type: 'error', + name: 'EACCannotGrantRoles', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + }, + { + type: 'error', + name: 'EACCannotRevokeRoles', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + }, + { + type: 'error', + name: 'EACInvalidAccount', + inputs: [], + }, + { + type: 'error', + name: 'EACInvalidRoleBitmap', + inputs: [{ name: 'roleBitmap', type: 'uint256' }], + }, + { + type: 'error', + name: 'EACMaxAssignees', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + ], + }, + { + type: 'error', + name: 'EACMinAssignees', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + ], + }, + { + type: 'error', + name: 'EACRootResourceNotAllowed', + inputs: [], + }, + { + type: 'error', + name: 'EACUnauthorizedAccountRoles', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + }, +] as const + +export const eacGrantRolesSnippet = [ + ...eacErrors, + { + type: 'function', + name: 'grantRoles', + inputs: [ + { + name: 'resource', + type: 'uint256', + }, + { + name: 'roleBitmap', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, +] as const + +export const eacGrantRootRolesSnippet = [ + ...eacErrors, + { + type: 'function', + name: 'grantRootRoles', + inputs: [ + { + name: 'roleBitmap', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, +] as const + +export const eacHasRolesSnippet = [ + { + type: 'function', + name: 'hasRoles', + inputs: [ + { + name: 'resource', + type: 'uint256', + }, + { + name: 'rolesBitmap', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + }, +] as const + +export const eacRevokeRolesSnippet = [ + ...eacErrors, + { + type: 'function', + name: 'revokeRoles', + inputs: [ + { + name: 'resource', + type: 'uint256', + }, + { + name: 'roleBitmap', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, +] as const + +export const eacRevokeRootRolesSnippet = [ + ...eacErrors, + { + type: 'function', + name: 'revokeRootRoles', + inputs: [ + { + name: 'roleBitmap', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/ethRegistrar.ts b/packages/ensjs-abi/src/v2/ethRegistrar.ts new file mode 100644 index 000000000..60c0792ee --- /dev/null +++ b/packages/ensjs-abi/src/v2/ethRegistrar.ts @@ -0,0 +1,305 @@ +export const ethRegistrarNameRegisteredEventSnippet = { + type: 'event', + name: 'NameRegistered', + inputs: [ + { name: 'tokenId', type: 'uint256', indexed: true }, + { name: 'label', type: 'string', indexed: false }, + { name: 'owner', type: 'address', indexed: false }, + { name: 'subregistry', type: 'address', indexed: false }, + { name: 'resolver', type: 'address', indexed: false }, + { name: 'duration', type: 'uint64', indexed: false }, + { name: 'paymentToken', type: 'address', indexed: false }, + { name: 'referrer', type: 'bytes32', indexed: true }, + { name: 'base', type: 'uint256', indexed: false }, + { name: 'premium', type: 'uint256', indexed: false }, + ], + anonymous: false, +} as const + +export const ethRegistrarNameRenewedEventSnippet = { + type: 'event', + name: 'NameRenewed', + inputs: [ + { name: 'tokenId', type: 'uint256', indexed: true }, + { name: 'label', type: 'string', indexed: false }, + { name: 'duration', type: 'uint64', indexed: false }, + { name: 'newExpiry', type: 'uint64', indexed: false }, + { name: 'paymentToken', type: 'address', indexed: false }, + { name: 'referrer', type: 'bytes32', indexed: true }, + { name: 'amount', type: 'uint256', indexed: false }, + ], + anonymous: false, +} as const + +export const ethRegistrarCommitmentMadeEventSnippet = { + type: 'event', + name: 'CommitmentMade', + inputs: [{ name: 'commitment', type: 'bytes32', indexed: false }], + anonymous: false, +} as const + +export const ethRegistrarRentPriceOracleUpdatedEventSnippet = { + type: 'event', + name: 'RentPriceOracleUpdated', + inputs: [{ name: 'oracle', type: 'address', indexed: false }], + anonymous: false, +} as const + +export const ethRegistrarErrors = [ + { + inputs: [ + { name: 'commitment', type: 'bytes32' }, + { name: 'validFrom', type: 'uint64' }, + { name: 'blockTimestamp', type: 'uint64' }, + ], + name: 'CommitmentTooNew', + type: 'error', + }, + { + inputs: [ + { name: 'commitment', type: 'bytes32' }, + { name: 'validTo', type: 'uint64' }, + { name: 'blockTimestamp', type: 'uint64' }, + ], + name: 'CommitmentTooOld', + type: 'error', + }, + { + inputs: [ + { name: 'duration', type: 'uint64' }, + { name: 'minDuration', type: 'uint64' }, + ], + name: 'DurationTooShort', + type: 'error', + }, + { + inputs: [], + name: 'InvalidOwner', + type: 'error', + }, + { + inputs: [], + name: 'MaxCommitmentAgeTooLow', + type: 'error', + }, + { + inputs: [{ name: 'label', type: 'string' }], + name: 'NameNotAvailable', + type: 'error', + }, + { + inputs: [{ name: 'label', type: 'string' }], + name: 'NameNotRenewable', + type: 'error', + }, + // bubbled from `IRentPriceOracle` via `getRegisterPrice` / `getRenewPrice` + { + inputs: [{ name: 'label', type: 'string' }], + name: 'NotValid', + type: 'error', + }, + // bubbled from `IRentPriceOracle` via `getRegisterPrice` / `getRenewPrice` + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'PaymentTokenNotSupported', + type: 'error', + }, + { + inputs: [{ name: 'token', type: 'address' }], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { + inputs: [{ name: 'commitment', type: 'bytes32' }], + name: 'UnexpiredCommitmentExists', + type: 'error', + }, +] as const + +export const ethRegistrarGetRegisterPriceSnippet = [ + ...ethRegistrarErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + ], + name: 'getRegisterPrice', + outputs: [ + { name: 'base', type: 'uint256' }, + { name: 'premium', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarGetRenewPriceSnippet = [ + ...ethRegistrarErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + ], + name: 'getRenewPrice', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarRentPriceOracleSnippet = [ + { + inputs: [], + name: 'rentPriceOracle', + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarIsRenewableSnippet = [ + { + inputs: [{ name: 'label', type: 'string' }], + name: 'isRenewable', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarGetRemainingGracePeriodSnippet = [ + { + inputs: [{ name: 'label', type: 'string' }], + name: 'getRemainingGracePeriod', + outputs: [{ name: '', type: 'uint64' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarCommitSnippet = [ + ...ethRegistrarErrors, + { + inputs: [{ name: 'commitment', type: 'bytes32' }], + name: 'commit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const ethRegistrarMakeCommitmentSnippet = [ + ...ethRegistrarErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'secret', type: 'bytes32' }, + { name: 'subregistry', type: 'address' }, + { name: 'resolver', type: 'address' }, + { name: 'duration', type: 'uint64' }, + { name: 'referrer', type: 'bytes32' }, + ], + name: 'makeCommitment', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'pure', + type: 'function', + }, +] as const + +export const ethRegistrarCommitmentsSnippet = [ + ...ethRegistrarErrors, + { + inputs: [{ name: 'commitment', type: 'bytes32' }], + name: 'commitmentAt', + outputs: [{ name: 'commitTime', type: 'uint64' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarAvailableSnippet = [ + ...ethRegistrarErrors, + { + inputs: [{ name: 'label', type: 'string' }], + name: 'isAvailable', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarIsAvailableSnippet = [ + { + name: 'isAvailable', + inputs: [{ name: 'label', type: 'string' }], + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const ethRegistrarRegisterSnippet = [ + ...ethRegistrarErrors, + { + type: 'function', + name: 'register', + inputs: [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'secret', type: 'bytes32' }, + { name: 'subregistry', type: 'address' }, + { name: 'resolver', type: 'address' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + { name: 'referrer', type: 'bytes32' }, + ], + outputs: [{ name: 'tokenId', type: 'uint256' }], + stateMutability: 'nonpayable', + }, +] as const + +export const ethRegistrarRenewErrors = [ + { + inputs: [ + { name: 'duration', type: 'uint64' }, + { name: 'minDuration', type: 'uint64' }, + ], + name: 'DurationTooShort', + type: 'error', + }, + { + inputs: [{ name: 'label', type: 'string' }], + name: 'NameNotRenewable', + type: 'error', + }, + // bubbled from `IRentPriceOracle` via `getRenewPrice` + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'PaymentTokenNotSupported', + type: 'error', + }, + { + inputs: [{ name: 'token', type: 'address' }], + name: 'SafeERC20FailedOperation', + type: 'error', + }, +] as const + +export const ethRegistrarRenewSnippet = [ + ...ethRegistrarRenewErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + { name: 'referrer', type: 'bytes32' }, + ], + name: 'renew', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/index.ts b/packages/ensjs-abi/src/v2/index.ts new file mode 100644 index 000000000..ce016e48b --- /dev/null +++ b/packages/ensjs-abi/src/v2/index.ts @@ -0,0 +1,10 @@ +export * from './enhancedAccessControl.js' +export * from './ethRegistrar.js' +export * from './lockedMigrationController.js' +export * from './migrationHelper.js' +export * from './permissionedRegistry.js' +export * from './permissionedResolver.js' +export * from './standardRentPriceOracle.js' +export * from './unlockedMigrationController.js' +export * from './userRegistry.js' +export * from './verifiableFactory.js' diff --git a/packages/ensjs-abi/src/v2/lockedMigrationController.ts b/packages/ensjs-abi/src/v2/lockedMigrationController.ts new file mode 100644 index 000000000..735c86177 --- /dev/null +++ b/packages/ensjs-abi/src/v2/lockedMigrationController.ts @@ -0,0 +1,25 @@ +/** + * ABI snippets for `LockedMigrationController`. + * + * Receives ERC1155 transfers of locked v1 NameWrapper tokens during migration. + * The manager does not call this contract directly; these snippets exist so + * its custom errors can be decoded when raised through `MigrationHelper`. + */ + +export const lockedMigrationControllerErrors = [ + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'NameNotLocked', + type: 'error', + }, + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'NameDataMismatch', + type: 'error', + }, + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'FrozenTokenApproval', + type: 'error', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/migrationHelper.ts b/packages/ensjs-abi/src/v2/migrationHelper.ts new file mode 100644 index 000000000..b31e55f16 --- /dev/null +++ b/packages/ensjs-abi/src/v2/migrationHelper.ts @@ -0,0 +1,74 @@ +/** + * ABI snippets for `MigrationHelper`. + * + * Helper contract that batches v1 -> v2 ENS name migrations. Callers send the + * unwrapped/unlocked/locked groups in a single `migrate` call; the helper + * forwards each token to the appropriate `*MigrationController`. + */ + +export const migrationHelperErrors = [ + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'WrappedOwnerMismatch', + type: 'error', + }, + { + inputs: [{ name: 'name', type: 'bytes' }], + name: 'ParentNotMigrated', + type: 'error', + }, + { + inputs: [ + { name: 'nft', type: 'address' }, + { name: 'owner', type: 'address' }, + ], + name: 'NotApprovedOperator', + type: 'error', + }, +] as const + +const migrationDataComponents = [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'subregistry', type: 'address' }, + { name: 'resolver', type: 'address' }, +] as const + +export const migrationHelperMigrateSnippet = [ + ...migrationHelperErrors, + { + type: 'function', + name: 'migrate', + inputs: [ + { + name: 'unwrapped', + type: 'tuple[]', + components: migrationDataComponents, + }, + { + name: 'unlockedGroups', + type: 'tuple[][]', + components: migrationDataComponents, + }, + { + name: 'lockedGroups', + type: 'tuple[][]', + components: migrationDataComponents, + }, + { + name: 'lockedChildrenGroups', + type: 'tuple[]', + components: [ + { name: 'parentName', type: 'bytes' }, + { + name: 'groups', + type: 'tuple[][]', + components: migrationDataComponents, + }, + ], + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/permissionedRegistry.ts b/packages/ensjs-abi/src/v2/permissionedRegistry.ts new file mode 100644 index 000000000..3c533cc52 --- /dev/null +++ b/packages/ensjs-abi/src/v2/permissionedRegistry.ts @@ -0,0 +1,252 @@ +export const permissionedRegistryGetStateSnippet = [ + { + type: 'function', + name: 'getState', + inputs: [{ name: 'anyId', type: 'uint256' }], + outputs: [ + { + components: [ + { name: 'status', type: 'uint8' }, + { name: 'expiry', type: 'uint64' }, + { name: 'latestOwner', type: 'address' }, + { name: 'tokenId', type: 'uint256' }, + { name: 'resource', type: 'uint256' }, + ], + name: 'state', + type: 'tuple', + }, + ], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryGetStatusSnippet = [ + { + type: 'function', + name: 'getStatus', + inputs: [{ name: 'anyId', type: 'uint256' }], + outputs: [{ name: '', type: 'uint8' }], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryGetResolverSnippet = [ + { + type: 'function', + name: 'getResolver', + inputs: [{ name: 'label', type: 'string' }], + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryGetSubregistrySnippet = [ + { + type: 'function', + name: 'getSubregistry', + inputs: [{ name: 'label', type: 'string' }], + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryRoleCountSnippet = [ + { + type: 'function', + name: 'roleCount', + inputs: [ + { + name: 'resource', + type: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryGetExpirySnippet = [ + { + type: 'function', + name: 'getExpiry', + inputs: [ + { + name: 'anyId', + type: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + }, +] as const + +export const eacRolesGrantedEventSnippet = { + type: 'event', + name: 'EACRolesChanged', + inputs: [ + { + name: 'resource', + type: 'uint256', + indexed: true, + }, + { + name: 'account', + type: 'address', + indexed: true, + }, + { + name: 'oldRoleBitmap', + type: 'uint256', + indexed: false, + }, + { + name: 'newRoleBitmap', + type: 'uint256', + indexed: false, + }, + ], + anonymous: false, +} as const + +export const eacRolesEvents = [eacRolesGrantedEventSnippet] as const + +export const permissionedRegistryLabelRegisteredEventSnippet = { + type: 'event', + name: 'LabelRegistered', + inputs: [ + { + name: 'tokenId', + type: 'uint256', + indexed: true, + }, + { + name: 'labelHash', + type: 'bytes32', + indexed: true, + }, + { + name: 'label', + type: 'string', + indexed: false, + }, + { + name: 'owner', + type: 'address', + indexed: false, + }, + { + name: 'expiry', + type: 'uint64', + indexed: false, + }, + { + name: 'sender', + type: 'address', + indexed: true, + }, + ], + anonymous: false, +} as const + +export const permissionedRegistryGetTokenIdSnippet = [ + { + type: 'function', + name: 'getTokenId', + inputs: [ + { + name: 'anyId', + type: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryGetResourceSnippet = [ + { + type: 'function', + name: 'getResource', + inputs: [ + { + name: 'anyId', + type: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistryRolesSnippet = [ + { + type: 'function', + name: 'roles', + inputs: [ + { + name: 'anyId', + type: 'uint256', + }, + { + name: 'account', + type: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + }, +] as const + +export const permissionedRegistrySetResolverSnippet = [ + { + type: 'function', + name: 'setResolver', + inputs: [ + { + name: 'anyId', + type: 'uint256', + }, + { + name: 'resolver', + type: 'address', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const + +export const permissionedRegistryUnregisterSnippet = [ + { + type: 'function', + name: 'unregister', + inputs: [{ name: 'anyId', type: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/permissionedResolver.ts b/packages/ensjs-abi/src/v2/permissionedResolver.ts new file mode 100644 index 000000000..dfcd832ba --- /dev/null +++ b/packages/ensjs-abi/src/v2/permissionedResolver.ts @@ -0,0 +1,243 @@ +export const permissionedResolverAliasSnippet = [ + { + name: 'setAlias', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'fromName', type: 'bytes' }, + { name: 'toName', type: 'bytes' }, + ], + outputs: [], + }, + { + name: 'getAlias', + type: 'function', + stateMutability: 'view', + inputs: [{ name: 'fromName', type: 'bytes' }], + outputs: [{ name: 'toName', type: 'bytes' }], + }, +] as const + +export const permissionedResolverHasRootRolesSnippet = [ + { + name: 'hasRootRoles', + type: 'function', + stateMutability: 'view', + inputs: [ + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverHasRolesSnippet = [ + { + name: 'hasRoles', + type: 'function', + stateMutability: 'view', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverRolesSnippet = [ + { + name: 'roles', + type: 'function', + stateMutability: 'view', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'uint256' }], + }, +] as const + +export const permissionedResolverGrantRootRolesSnippet = [ + { + name: 'grantRootRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverRevokeRootRolesSnippet = [ + { + name: 'revokeRootRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +/** + * @deprecated `revokeRoles` on `PermissionedResolver` is `pure` and always reverts with + * `EACCannotRevokeRoles`. Use {@link permissionedResolverRevokeRootRolesSnippet} for root + * roles, or {@link permissionedResolverAuthorizeNameRolesSnippet} / + * {@link permissionedResolverAuthorizeTextRolesSnippet} / + * {@link permissionedResolverAuthorizeAddrRolesSnippet} / + * {@link permissionedResolverAuthorizeDataRolesSnippet} for name- or part-scoped roles. + */ +export const permissionedResolverRevokeRolesSnippet = [ + { + name: 'revokeRoles', + type: 'function', + stateMutability: 'pure', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +/** + * @deprecated `grantRoles` on `PermissionedResolver` is `pure` and always reverts with + * `EACCannotGrantRoles`. Use {@link permissionedResolverGrantRootRolesSnippet} for root + * roles, or {@link permissionedResolverAuthorizeNameRolesSnippet} / + * {@link permissionedResolverAuthorizeTextRolesSnippet} / + * {@link permissionedResolverAuthorizeAddrRolesSnippet} / + * {@link permissionedResolverAuthorizeDataRolesSnippet} for name- or part-scoped roles. + */ +export const permissionedResolverGrantRolesSnippet = [ + { + name: 'grantRoles', + type: 'function', + stateMutability: 'pure', + inputs: [ + { name: 'resource', type: 'uint256' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverAuthorizeNameRolesSnippet = [ + { + name: 'authorizeNameRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'toName', type: 'bytes' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'account', type: 'address' }, + { name: 'grant', type: 'bool' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverAuthorizeTextRolesSnippet = [ + { + name: 'authorizeTextRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'toName', type: 'bytes' }, + { name: 'key', type: 'string' }, + { name: 'account', type: 'address' }, + { name: 'grant', type: 'bool' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverAuthorizeAddrRolesSnippet = [ + { + name: 'authorizeAddrRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'toName', type: 'bytes' }, + { name: 'coinType', type: 'uint256' }, + { name: 'account', type: 'address' }, + { name: 'grant', type: 'bool' }, + ], + outputs: [{ name: 'updated', type: 'bool' }], + }, +] as const + +export const permissionedResolverAuthorizeDataRolesSnippet = [ + { + name: 'authorizeDataRoles', + type: 'function', + stateMutability: 'nonpayable', + inputs: [ + { name: 'toName', type: 'bytes' }, + { name: 'key', type: 'string' }, + { name: 'account', type: 'address' }, + { name: 'grant', type: 'bool' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const + +export const permissionedResolverNameSnippet = [ + { + inputs: [ + { + name: 'node', + type: 'bytes32', + }, + ], + name: 'name', + outputs: [ + { + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const permissionedResolverMulticallWithNodeCheckSnippet = [ + { + inputs: [ + { + name: '', + type: 'bytes32', + }, + { + name: 'calls', + type: 'bytes[]', + }, + ], + name: 'multicallWithNodeCheck', + outputs: [ + { + name: '', + type: 'bytes[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const permissionedResolverClearRecordsSnippet = [ + { + inputs: [{ name: 'node', type: 'bytes32' }], + name: 'clearRecords', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/standardRentPriceOracle.ts b/packages/ensjs-abi/src/v2/standardRentPriceOracle.ts new file mode 100644 index 000000000..1fa772c35 --- /dev/null +++ b/packages/ensjs-abi/src/v2/standardRentPriceOracle.ts @@ -0,0 +1,269 @@ +/** + * ABI snippets for `StandardRentPriceOracle`. + * + * Pricing/oracle contract used by `ETHRegistrar` and `ETHRenewerV1`. End users typically + * call the registrar's 3-arg `getRegisterPrice` / `getRenewPrice`; these snippets expose + * the oracle's lower-level functions plus its admin surface. + */ + +export const standardRentPriceOracleErrors = [ + // from `IRentPriceOracle` + { + inputs: [{ name: 'label', type: 'string' }], + name: 'NotValid', + type: 'error', + }, + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'PaymentTokenNotSupported', + type: 'error', + }, + // local to `StandardRentPriceOracle` + { + inputs: [], + name: 'InvalidBaseRates', + type: 'error', + }, + { + inputs: [], + name: 'InvalidRatio', + type: 'error', + }, + { + inputs: [], + name: 'InvalidDiscount', + type: 'error', + }, +] as const + +export const standardRentPriceOraclePaymentTokenUpdatedEventSnippet = { + type: 'event', + name: 'PaymentTokenUpdated', + inputs: [ + { name: 'paymentToken', type: 'address', indexed: true }, + { name: 'numer', type: 'uint128', indexed: false }, + { name: 'denom', type: 'uint128', indexed: false }, + ], + anonymous: false, +} as const + +/** `IRentPriceOracle.getRegisterPrice(label, available, duration, paymentToken)`. */ +export const standardRentPriceOracleGetRegisterPriceSnippet = [ + ...standardRentPriceOracleErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'available', type: 'uint64' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + ], + name: 'getRegisterPrice', + outputs: [ + { name: 'base', type: 'uint256' }, + { name: 'premium', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +/** `IRentPriceOracle.getRenewPrice(label, expiry, duration, paymentToken)`. */ +export const standardRentPriceOracleGetRenewPriceSnippet = [ + ...standardRentPriceOracleErrors, + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'expiry', type: 'uint64' }, + { name: 'duration', type: 'uint64' }, + { name: 'paymentToken', type: 'address' }, + ], + name: 'getRenewPrice', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetBasePriceSnippet = [ + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'duration', type: 'uint64' }, + ], + name: 'getBasePrice', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetPremiumPriceAfterSnippet = [ + { + inputs: [{ name: 'duration', type: 'uint64' }], + name: 'getPremiumPriceAfter', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleApplyDiscountSnippet = [ + { + inputs: [ + { name: 'value', type: 'uint256' }, + { name: 'duration', type: 'uint64' }, + ], + name: 'applyDiscount', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleConvertUnitsSnippet = [ + { + inputs: [ + { name: 'value', type: 'uint256' }, + { name: 'paymentToken', type: 'address' }, + ], + name: 'convertUnits', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetBaseRatesSnippet = [ + { + inputs: [], + name: 'getBaseRates', + outputs: [{ name: '', type: 'uint256[]' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetDiscountPointsSnippet = [ + { + inputs: [], + name: 'getDiscountPoints', + outputs: [ + { + name: '', + type: 'tuple[]', + components: [ + { name: 'duration', type: 'uint64' }, + { name: 'numer', type: 'uint128' }, + ], + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetLengthSnippet = [ + { + inputs: [{ name: 'label', type: 'string' }], + name: 'getLength', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'pure', + type: 'function', + }, +] as const + +export const standardRentPriceOracleIsValidSnippet = [ + { + inputs: [{ name: 'label', type: 'string' }], + name: 'isValid', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleIsPaymentTokenSnippet = [ + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'isPaymentToken', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleGetPaymentTokenRatioSnippet = [ + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'getPaymentTokenRatio', + outputs: [ + { name: 'numer', type: 'uint128' }, + { name: 'denom', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const standardRentPriceOracleUpdatePaymentTokenSnippet = [ + ...standardRentPriceOracleErrors, + { + inputs: [ + { name: 'paymentToken', type: 'address' }, + { name: 'numer', type: 'uint128' }, + { name: 'denom', type: 'uint128' }, + ], + name: 'updatePaymentToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const standardRentPriceOracleDisablePaymentTokenSnippet = [ + { + inputs: [{ name: 'paymentToken', type: 'address' }], + name: 'disablePaymentToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const standardRentPriceOracleConstantsSnippet = [ + { + inputs: [], + name: 'DISCOUNT_DENOMINATOR', + outputs: [{ name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PREMIUM_PRICE_INITIAL', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PREMIUM_HALVING_PERIOD', + outputs: [{ name: '', type: 'uint64' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PREMIUM_PERIOD', + outputs: [{ name: '', type: 'uint64' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'PREMIUM_PRICE_OFFSET', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/unlockedMigrationController.ts b/packages/ensjs-abi/src/v2/unlockedMigrationController.ts new file mode 100644 index 000000000..ab4d8936d --- /dev/null +++ b/packages/ensjs-abi/src/v2/unlockedMigrationController.ts @@ -0,0 +1,26 @@ +/** + * ABI snippets for `UnlockedMigrationController`. + * + * Receives ERC721/ERC1155 transfers of unwrapped or unlocked v1 names during + * migration. The manager does not call this contract directly; these snippets + * exist so its custom errors can be decoded when raised through + * `MigrationHelper`. + */ + +export const unlockedMigrationControllerErrors = [ + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'NameIsLocked', + type: 'error', + }, + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'NameDataMismatch', + type: 'error', + }, + { + inputs: [], + name: 'InvalidData', + type: 'error', + }, +] as const diff --git a/packages/ensjs-abi/src/v2/userRegistry.ts b/packages/ensjs-abi/src/v2/userRegistry.ts new file mode 100644 index 000000000..f0a45fce6 --- /dev/null +++ b/packages/ensjs-abi/src/v2/userRegistry.ts @@ -0,0 +1,271 @@ +// ─── ERC1155 events ──────────────────────────────────────────────── + +export const approvalForAllEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'account', type: 'address' }, + { indexed: true, name: 'operator', type: 'address' }, + { indexed: false, name: 'approved', type: 'bool' }, + ], + name: 'ApprovalForAll', + type: 'event', + anonymous: false, + }, +] as const + +export const transferSingleEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'operator', type: 'address' }, + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'id', type: 'uint256' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + name: 'TransferSingle', + type: 'event', + anonymous: false, + }, +] as const + +export const transferBatchEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'operator', type: 'address' }, + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'ids', type: 'uint256[]' }, + { indexed: false, name: 'values', type: 'uint256[]' }, + ], + name: 'TransferBatch', + type: 'event', + anonymous: false, + }, +] as const + +export const uriEventSnippet = [ + { + inputs: [ + { indexed: false, name: 'value', type: 'string' }, + { indexed: true, name: 'id', type: 'uint256' }, + ], + name: 'URI', + type: 'event', + anonymous: false, + }, +] as const + +// ─── Registry lifecycle events ───────────────────────────────────── + +export const labelRegisteredEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'labelHash', type: 'bytes32' }, + { indexed: false, name: 'label', type: 'string' }, + { indexed: false, name: 'owner', type: 'address' }, + { indexed: false, name: 'expiry', type: 'uint64' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'LabelRegistered', + type: 'event', + anonymous: false, + }, +] as const + +export const labelReservedEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'labelHash', type: 'bytes32' }, + { indexed: false, name: 'label', type: 'string' }, + { indexed: false, name: 'expiry', type: 'uint64' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'LabelReserved', + type: 'event', + anonymous: false, + }, +] as const + +export const labelUnregisteredEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'LabelUnregistered', + type: 'event', + anonymous: false, + }, +] as const + +export const expiryUpdatedEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'newExpiry', type: 'uint64' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'ExpiryUpdated', + type: 'event', + anonymous: false, + }, +] as const + +export const parentUpdatedEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'parent', type: 'address' }, + { indexed: false, name: 'label', type: 'string' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'ParentUpdated', + type: 'event', + anonymous: false, + }, +] as const + +export const resolverUpdatedEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'resolver', type: 'address' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'ResolverUpdated', + type: 'event', + anonymous: false, + }, +] as const + +export const subregistryUpdatedEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'subregistry', type: 'address' }, + { indexed: true, name: 'sender', type: 'address' }, + ], + name: 'SubregistryUpdated', + type: 'event', + anonymous: false, + }, +] as const + +export const tokenResourceEventSnippet = [ + { + inputs: [ + { indexed: true, name: 'tokenId', type: 'uint256' }, + { indexed: true, name: 'resource', type: 'uint256' }, + ], + name: 'TokenResource', + type: 'event', + anonymous: false, + }, +] as const + +export const upgradedEventSnippet = [ + { + inputs: [{ indexed: true, name: 'implementation', type: 'address' }], + name: 'Upgraded', + type: 'event', + anonymous: false, + }, +] as const + +// ─── Functions ───────────────────────────────────────────────────── + +export const userRegistrySetSubregistrySnippet = [ + { + inputs: [ + { name: 'anyId', type: 'uint256' }, + { name: 'registry', type: 'address' }, + ], + name: 'setSubregistry', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistryRegisterSnippet = [ + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'registry', type: 'address' }, + { name: 'resolver', type: 'address' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'expiry', type: 'uint64' }, + ], + name: 'register', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistryRenewSnippet = [ + { + inputs: [ + { name: 'tokenId', type: 'uint256' }, + { name: 'expiry', type: 'uint64' }, + ], + name: 'renew', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistryUnregisterSnippet = [ + { + inputs: [{ name: 'tokenId', type: 'uint256' }], + name: 'unregister', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistrySetResolverSnippet = [ + { + inputs: [ + { name: 'tokenId', type: 'uint256' }, + { name: 'resolver', type: 'address' }, + ], + name: 'setResolver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistryInitializeSnippet = [ + { + inputs: [ + { name: 'admin', type: 'address' }, + { name: 'roleBitmap', type: 'uint256' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const userRegistryAllEventsSnippet = [ + ...approvalForAllEventSnippet, + ...expiryUpdatedEventSnippet, + ...labelRegisteredEventSnippet, + ...labelReservedEventSnippet, + ...labelUnregisteredEventSnippet, + ...parentUpdatedEventSnippet, + ...resolverUpdatedEventSnippet, + ...subregistryUpdatedEventSnippet, + ...tokenResourceEventSnippet, + ...transferBatchEventSnippet, + ...transferSingleEventSnippet, + ...upgradedEventSnippet, + ...uriEventSnippet, +] as const diff --git a/packages/ensjs-abi/src/v2/verifiableFactory.ts b/packages/ensjs-abi/src/v2/verifiableFactory.ts new file mode 100644 index 000000000..3733d87a5 --- /dev/null +++ b/packages/ensjs-abi/src/v2/verifiableFactory.ts @@ -0,0 +1,142 @@ +export const verifiableFactoryErrors = [] as const + +export const verifiableFactoryDeployProxySnippet = [ + ...verifiableFactoryErrors, + { + inputs: [ + { + name: 'implementation', + type: 'address', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'data', + type: 'bytes', + }, + ], + name: 'deployProxy', + outputs: [ + { + name: '', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + indexed: true, + name: 'sender', + type: 'address', + }, + { + indexed: true, + name: 'proxyAddress', + type: 'address', + }, + { + indexed: false, + name: 'salt', + type: 'uint256', + }, + { + indexed: false, + name: 'implementation', + type: 'address', + }, + ], + name: 'ProxyDeployed', + anonymous: false, + type: 'event', + }, +] as const + +export const proxyDeployedEventSnippet = [ + { + inputs: [ + { + indexed: true, + name: 'sender', + type: 'address', + }, + { + indexed: true, + name: 'proxyAddress', + type: 'address', + }, + { + indexed: false, + name: 'salt', + type: 'uint256', + }, + { + indexed: false, + name: 'implementation', + type: 'address', + }, + ], + name: 'ProxyDeployed', + type: 'event', + anonymous: false, + }, +] as const + +export const subregistryInitializeSnippet = [ + { + inputs: [ + { + name: 'admin', + type: 'address', + }, + { + name: 'roleBitmap', + type: 'uint256', + }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const proxyInitializeSnippet = [ + { + inputs: [ + { name: 'admin', type: 'address' }, + { name: 'roleBitmap', type: 'uint256' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const + +export const verifiableFactoryProxyLogicSnippet = [ + { + inputs: [], + name: 'proxyLogic', + outputs: [{ name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const verifiableFactoryVerifyContractSnippet = [ + { + inputs: [ + { name: 'proxy', type: 'address' }, + { name: 'implementation', type: 'address' }, + ], + name: 'verifyContract', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs-abi/tsconfig.build.json b/packages/ensjs-abi/tsconfig.build.json new file mode 100644 index 000000000..2a31ec8b3 --- /dev/null +++ b/packages/ensjs-abi/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "sourceMap": true, + "outDir": "./dist", + "declaration": true, + "declarationMap": true + } +} \ No newline at end of file diff --git a/packages/ensjs-abi/tsconfig.json b/packages/ensjs-abi/tsconfig.json new file mode 100644 index 000000000..bf6440f01 --- /dev/null +++ b/packages/ensjs-abi/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts", "*.ts"] +} \ No newline at end of file diff --git a/packages/ensjs/compose.yml b/packages/ensjs/compose.yml new file mode 100644 index 000000000..74585721a --- /dev/null +++ b/packages/ensjs/compose.yml @@ -0,0 +1,18 @@ +services: + devnet: + image: ghcr.io/ensdomains/contracts-v2:fix-docker-runtime-ci-arm-1d9b670 + command: ./script/runDevnet.ts --testNames + pull_policy: always + ports: + - "8545:8545" + - "8000:8000" + environment: + ANVIL_IP_ADDR: "0.0.0.0" + ANVIL_CHAIN_ID: "1" + healthcheck: + test: ["CMD", "curl", "--fail", "-s", "http://localhost:8000/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + start_interval: 1s diff --git a/packages/ensjs/deploy/00_deploy_bulk_renewal.ts b/packages/ensjs/deploy/00_deploy_bulk_renewal.ts deleted file mode 100644 index b6f703a89..000000000 --- a/packages/ensjs/deploy/00_deploy_bulk_renewal.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import type { Abi, AbiFunction } from 'viem' -import { labelhash, namehash } from 'viem/ens' -import { bytesToHex, hexToBytes, toFunctionHash } from 'viem/utils' - -export const createInterfaceId = (iface: iface) => { - const bytesId = iface - .filter((item): item is AbiFunction => item.type === 'function') - .map((f) => toFunctionHash(f)) - .map((h) => hexToBytes(h).slice(0, 4)) - .reduce((memo, bytes) => { - for (let i = 0; i < 4; i++) { - memo[i] = memo[i] ^ bytes[i] // xor - } - return memo - }, new Uint8Array(4)) - - return bytesToHex(bytesId) -} - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments, network, viem } = hre - const { deploy } = deployments - const { deployer, owner } = await getNamedAccounts() - const clients = await viem.getNamedClients() - - if (!network.tags.use_root) { - return true - } - - const root = await viem.getContract('Root', clients.owner) - const registry = await viem.getContract('ENSRegistry', clients.owner) - const resolver = await viem.getContract('PublicResolver', clients.owner) - const registrar = await viem.getContract('BaseRegistrarImplementation') - const controller = await viem.getContract('ETHRegistrarController') - const wrapper = await viem.getContract('NameWrapper') - const controllerArtifact = await deployments.getArtifact( - 'IETHRegistrarController', - ) - - const bulkRenewal = await deploy('BulkRenewal', { - from: deployer, - args: [registry.address], - log: true, - }) - - console.log('Temporarily setting owner of eth tld to owner ') - const tx = await root.write.setSubnodeOwner([labelhash('eth')], owner) - await tx.wait() - - console.log('Set default resolver for eth tld to public resolver') - const tx111 = await registry.write.setResolver( - [namehash('eth')], - resolver.address, - ) - await tx111.wait() - - console.log('Set interface implementor of eth tld for bulk renewal') - const tx2 = await resolver.write.setInterface( - [namehash('eth'), createInterfaceId(bulkRenewal.abi)], - bulkRenewal.address, - ) - await tx2.wait() - - console.log('Set interface implementor of eth tld for registrar controller') - const tx3 = await resolver.setInterface( - namehash('eth'), - createInterfaceId(controllerArtifact.abi), - controller.address, - ) - await tx3.wait() - - console.log('Set interface implementor of eth tld for name wrapper') - const tx4 = await resolver.setInterface( - namehash('eth'), - createInterfaceId(wrapper.interface), - wrapper.address, - ) - await tx4.wait() - - console.log('Set owner of eth tld back to registrar') - const tx11 = await root.setSubnodeOwner([labelhash('eth'), registrar.address]) - await tx11.wait() - - return true -} - -func.id = 'bulk-renewal' -func.tags = ['ethregistrar', 'BulkRenewal'] -func.dependencies = [ - 'root', - 'registry', - 'BaseRegistrarImplementation', - 'PublicResolver', - 'ETHRegistrarController', -] - -export default func diff --git a/packages/ensjs/deploy/00_deploy_multicall.ts b/packages/ensjs/deploy/00_deploy_multicall.ts deleted file mode 100644 index ae4ec5e1c..000000000 --- a/packages/ensjs/deploy/00_deploy_multicall.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { existsSync, mkdirSync } from 'node:fs' -import { readFile, writeFile } from 'node:fs/promises' -import { resolve } from 'node:path' -import type { ArtifactData, DeployFunction } from 'hardhat-deploy/dist/types.js' - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts } = hre - const { deployer } = await getNamedAccounts() - - let contractJson: ArtifactData - - const jsonPath = resolve(import.meta.dirname, '../cache/multicall.json') - - if (!existsSync(resolve(jsonPath, '../'))) mkdirSync(resolve(jsonPath, '../')) - - if (existsSync(jsonPath)) { - console.log('Multicall JSON file found, using it...') - contractJson = JSON.parse(await readFile(jsonPath, { encoding: 'utf8' })) - } else { - console.log('Downloading Multicall JSON file...') - contractJson = await fetch( - 'https://github.com/mds1/multicall/releases/latest/download/Multicall3.json', - ).then((res) => res.json()) - await writeFile(jsonPath, JSON.stringify(contractJson)) - console.log('Wrote Multicall JSON file to', jsonPath) - } - - await hre.deployments.deploy('Multicall', { - from: deployer, - contract: contractJson, - }) -} - -func.id = 'multicall' - -export default func diff --git a/packages/ensjs/deploy/00_legacy_registry.ts b/packages/ensjs/deploy/00_legacy_registry.ts deleted file mode 100644 index e531c0210..000000000 --- a/packages/ensjs/deploy/00_legacy_registry.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { labelhash, namehash } from 'viem/ens' - -const ZERO_HASH = - '0x0000000000000000000000000000000000000000000000000000000000000000' - -const names = ['legacy'] - -const func: DeployFunction = async (hre) => { - const { viem } = hre - const { owner } = await viem.getNamedClients() - - const registry = await viem.getContract('LegacyENSRegistry', owner) - - const tldTx = await registry.write.setSubnodeOwner( - [ZERO_HASH, labelhash('test'), owner.address], - owner, - ) - console.log(`Creating .test TLD (tx: ${tldTx.hash})...`) - - await viem.waitForTransactionSuccess(tldTx) - - await Promise.all( - names.map(async (name) => { - const nameTx = await registry.write.setSubnodeOwner( - [namehash('test'), labelhash(name), owner.address], - owner, - ) - console.log(`Creating ${name}.test (tx: ${nameTx.hash})...`) - await viem.waitForTransactionSuccess(nameTx) - }), - ) - - return true -} - -func.id = 'legacy-registry-names' -func.tags = ['legacy-registry-names'] -func.dependencies = ['ENSRegistry'] -func.skip = async (hre) => { - const { getNamedAccounts, viem } = hre - const { owner } = await getNamedAccounts() - - const registry = await viem.getContract('LegacyENSRegistry') - - const ownerOfTestTld = await registry.read.owner([namehash('test')]) - if (ownerOfTestTld !== owner) { - return false - } - return true -} -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/00_register_concurrently.ts b/packages/ensjs/deploy/00_register_concurrently.ts deleted file mode 100644 index 0af4bf9d1..000000000 --- a/packages/ensjs/deploy/00_register_concurrently.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { MAX_DATE_INT } from '../dist/utils/consts.js' -import { encodeFuses } from '../dist/utils/fuses.js' - -import type { Hash } from 'viem' -import { makeNameGenerator as makeLegacyNameGenerator } from '../utils/legacyNameGenerator.js' -import { makeNonceManager } from '../utils/nonceManager.js' -import { makeNameGenerator as makeWrappedNameGenerator } from '../utils/wrappedNameGenerator.js' - -const DURATION = 31556000 - -const names: { - label: string - namedOwner: string - namedAddr?: string - type: 'wrapped' | 'legacy' - data?: any[] - reverseRecord?: boolean - fuses?: number - subnames?: { - label: string - namedOwner: string - fuses?: number - expiry?: number - }[] - duration?: number | bigint -}[] = [ - ...Array.from({ length: 2 }, (_, index) => ({ - label: `concurrent-legacy-name-${index}`, - type: 'legacy' as const, - namedOwner: 'owner4', - reverseRecord: true, - duration: DURATION, - })), - ...Array.from({ length: 2 }, (_, index) => ({ - label: `concurrent-wrapped-name-${index}`, - type: 'wrapped' as const, - namedOwner: 'owner4', - fuses: encodeFuses({ - input: { - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), - duration: DURATION, - subnames: [ - { - label: 'xyz', - namedOwner: 'owner4', - type: 'wrapped' as const, - expiry: MAX_DATE_INT, - fuses: encodeFuses({ - input: { - parent: { - named: ['PARENT_CANNOT_CONTROL'], - }, - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), - }, - ], - })), -] - -const func: DeployFunction = async (hre) => { - const { network, viem } = hre - - const nonceManager = await makeNonceManager(hre) - const wrappedNameGenerator = await makeWrappedNameGenerator(hre, nonceManager) - const legacyNameGenerator = await makeLegacyNameGenerator(hre, nonceManager) - - await network.provider.send('evm_setAutomine', [false]) - - // Commit - const commitTxs = await Promise.all( - names.map( - ({ - label, - type, - namedOwner, - namedAddr = namedOwner, - data = [], - reverseRecord = false, - fuses = 0, - duration = 31536000, - }) => { - console.log(`Committing commitment for ${label}.eth...`) - if (type === 'legacy') - return legacyNameGenerator.commit({ - label, - namedOwner, - namedAddr, - }) - return wrappedNameGenerator.commit({ - label, - namedOwner, - data, - reverseRecord, - fuses, - duration, - }) - }, - ), - ) - - network.provider.send('evm_mine') - await Promise.all( - commitTxs.map(async (tx) => { - return viem.waitForTransactionSuccess(tx) - }), - ) - - const oldTimestamp = ( - await (await viem.getPublicClient()).getBlock({ blockTag: 'latest' }) - ).timestamp - await network.provider.send('evm_setNextBlockTimestamp', [ - Number(oldTimestamp + 60n), - ]) - await network.provider.send('evm_increaseTime', [300]) - await network.provider.send('evm_mine') - - // Register - const registerTxs = await Promise.all( - names.map( - ({ - label, - type, - namedOwner, - namedAddr = namedOwner, - data = [], - reverseRecord = false, - fuses = 0, - duration = 31536000, - }) => { - if (type === 'legacy') - return legacyNameGenerator.register({ - label, - namedOwner, - namedAddr, - duration, - }) - return wrappedNameGenerator.register({ - label, - namedOwner, - data, - reverseRecord, - fuses, - duration, - }) - }, - ), - ) - - await network.provider.send('evm_mine') - await Promise.all( - registerTxs.map(async (tx: Hash) => { - return viem.waitForTransactionSuccess(tx) - }), - ) - - await network.provider.send('evm_setAutomine', [true]) - - // Create subnames - for (const { label, namedOwner, type, subnames } of names) { - if (!subnames) continue - console.log(`Setting subnames for ${label}.eth...`) - for (const { - label: subnameLabel, - namedOwner: namedSubnameOwner, - fuses: subnameFuses = 0, - expiry: subnameExpiry = 2n ** 64n - 1n, - } of subnames) { - let setSubnameTx: Hash - if (type === 'legacy') - setSubnameTx = await legacyNameGenerator.subname({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - }) - else - setSubnameTx = await wrappedNameGenerator.subname({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - subnameFuses, - subnameExpiry, - }) - console.log(` - ${subnameLabel} (tx: ${setSubnameTx})...`) - await viem.waitForTransactionSuccess(setSubnameTx) - } - } - - await network.provider.send('evm_mine') - return true -} - -func.id = 'register-concurrent-names' -func.tags = ['register-concurrent-names'] -func.dependencies = ['ETHRegistrarController', 'register-wrapped-names'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/00_register_legacy.ts b/packages/ensjs/deploy/00_register_legacy.ts deleted file mode 100644 index 07b8be0e7..000000000 --- a/packages/ensjs/deploy/00_register_legacy.ts +++ /dev/null @@ -1,510 +0,0 @@ -import cbor from 'cbor' -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import pako from 'pako' -import { type Address, bytesToHex, namehash, stringToBytes } from 'viem' -import { makeNameGenerator } from '../utils/legacyNameGenerator.js' - -const dummyABI = [ - { - type: 'event', - anonymous: false, - name: 'ABIChanged', - inputs: [ - { - type: 'bytes32', - indexed: true, - }, - { - type: 'uint256', - indexed: true, - }, - ], - }, - { - type: 'event', - anonymous: false, - name: 'VersionChanged', - inputs: [ - { - type: 'bytes32', - indexed: true, - }, - { - type: 'uint64', - }, - ], - }, - { - type: 'function', - name: 'ABI', - constant: true, - stateMutability: 'view', - payable: false, - inputs: [ - { - type: 'bytes32', - }, - { - type: 'uint256', - }, - ], - outputs: [ - { - type: 'uint256', - }, - { - type: 'bytes', - }, - ], - }, - { - type: 'function', - name: 'clearRecords', - constant: false, - payable: false, - inputs: [ - { - type: 'bytes32', - }, - ], - outputs: [], - }, - { - type: 'function', - name: 'recordVersions', - constant: true, - stateMutability: 'view', - payable: false, - inputs: [ - { - type: 'bytes32', - }, - ], - outputs: [ - { - type: 'uint64', - }, - ], - }, - { - type: 'function', - name: 'setABI', - constant: false, - payable: false, - inputs: [ - { - type: 'bytes32', - }, - { - type: 'uint256', - }, - { - type: 'bytes', - }, - ], - outputs: [], - }, - { - type: 'function', - name: 'supportsInterface', - constant: true, - stateMutability: 'view', - payable: false, - inputs: [ - { - type: 'bytes4', - }, - ], - outputs: [ - { - type: 'bool', - }, - ], - }, -] - -const names: { - label: string - namedOwner: string - namedAddr: string - records?: { - text?: { - key: string - value: string - }[] - addr?: { - key: number - value: string - }[] - contenthash?: string - abi?: - | { - contentType: 1 | 2 | 4 | 8 | 256 - data: object | string - } - | { - contentType: 1 | 2 | 4 | 8 | 256 - data: string - }[] - } - duration?: number - subnames?: Subname[] -}[] = [ - { - label: 'test123', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'to-be-wrapped', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'resume-and-wrap', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'other-registrant', - namedOwner: 'deployer', - namedAddr: 'deployer', - }, - { - label: 'other-eth-record', - namedOwner: 'owner', - namedAddr: 'deployer', - }, - { - label: 'from-settings', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'with-legacy-resolver', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'with-oldest-resolver', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'with-profile', - namedOwner: 'owner2', - namedAddr: 'owner2', - records: { - text: [ - { key: 'description', value: 'Hello2' }, - { key: 'url', value: 'twitter.com' }, - { key: 'blankrecord', value: '' }, - { key: 'email', value: 'fakeemail@fake.com' }, - ], - addr: [ - { key: 61, value: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' }, - { key: 0, value: '0x00149010587f8364b964fcaa70687216b53bd2cbd798' }, - { key: 2, value: '0x0000000000000000000000000000000000000000' }, - ], - }, - }, - { - label: 'with-empty-addr', - namedOwner: 'owner2', - namedAddr: 'owner2', - records: { - addr: [{ key: 61, value: '0x' }], - }, - }, - { - label: 'with-contenthash', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - contenthash: - '0xe301017012204edd2984eeaf3ddf50bac238ec95c5713fb40b5e428b508fdbe55d3b9f155ffe', - }, - }, - { - label: 'to-be-renewed', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'with-subnames', - namedOwner: 'owner', - namedAddr: 'owner', - subnames: [ - { label: 'test', namedOwner: 'owner2' }, - { label: 'legacy', namedOwner: 'owner2' }, - { label: 'xyz', namedOwner: 'owner2' }, - { label: 'addr', namedOwner: 'owner2' }, - ], - }, - { - label: 'with-unknown-subnames', - namedOwner: 'owner', - namedAddr: 'owner', - subnames: [ - { label: 'abc123', namedOwner: 'owner2' }, - { label: 'not-known', namedOwner: 'owner2' }, - ], - }, - { - label: 'aaa123', - namedOwner: 'owner', - namedAddr: 'owner', - }, - { - label: 'with-type-1-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: { - contentType: 1, - data: dummyABI, - }, - }, - }, - { - label: 'with-type-2-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: { - contentType: 2, - data: dummyABI, - }, - }, - }, - { - label: 'with-type-4-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: { - contentType: 4, - data: dummyABI, - }, - }, - }, - { - label: 'with-type-8-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: { - contentType: 8, - data: 'https://example.com', - }, - }, - }, - { - label: 'with-type-256-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: { - contentType: 256, - data: dummyABI, - }, - }, - }, - { - label: 'with-type-all-abi', - namedOwner: 'owner', - namedAddr: 'owner', - records: { - abi: [ - { - contentType: 1, - data: dummyABI, - }, - { - contentType: 2, - data: dummyABI, - }, - { - contentType: 4, - data: dummyABI, - }, - { - contentType: 8, - data: 'https://example.com', - }, - ], - }, - }, - { - label: 'expired', - namedOwner: 'owner', - namedAddr: 'owner', - duration: 2419200, - }, - { - label: 'deletable', - namedOwner: 'owner', - namedAddr: 'owner', - subnames: [ - { label: 'xyz', namedOwner: 'owner2' }, - { label: 'test', namedOwner: 'owner' }, - { label: 'unwrapped-deleted', namedOwner: 'owner' }, - { label: 'wrapped-deleted', namedOwner: 'owner' }, - ], - }, - ...Array.from({ length: 34 }, (_, i) => ({ - label: `${i}-dummy`, - namedOwner: 'owner2', - namedAddr: 'owner2', - })), - ...Array.from({ length: 2 }, (_, i) => ({ - label: `nonconcurrent-legacy-name-${i}`, - namedOwner: 'owner4', - namedAddr: 'owner4', - duration: 31536000 / 2, - subnames: [ - { - label: 'test', - namedOwner: 'owner4', - }, - { - label: 'xyz', - namedOwner: 'owner4', - }, - ], - })), -] - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts, network, viem } = hre - const allNamedAccts = await getNamedAccounts() - - const publicResolver = await viem.getContract('LegacyPublicResolver') - - await network.provider.send('anvil_setBlockTimestampInterval', [60]) - - const nameGenerator = await makeNameGenerator(hre) - - for (const { - label, - namedOwner, - namedAddr, - records, - subnames, - duration = 31536000, - } of names) { - const registrant = allNamedAccts[namedOwner] as Address - const commitTx = await nameGenerator.commit({ - label, - namedOwner, - namedAddr, - }) - - console.log(`Committing commitment for ${label}.eth (tx: ${commitTx})...`) - await viem.waitForTransactionSuccess(commitTx) - - await network.provider.send('evm_mine') - - const registerTx = await nameGenerator.register({ - label, - namedOwner, - namedAddr, - duration, - }) - console.log(`Registering name ${label}.eth (tx: ${registerTx})...`) - - await viem.waitForTransactionSuccess(registerTx) - - if (records) { - // const _publicResolver = publicResolver.connect( - // await viem.getWalletClient(registrant), - // ) - - const hash = namehash(`${label}.eth`) - console.log(`Setting records for ${label}.eth...`) - if (records.text) { - console.log('TEXT') - for (const { key, value } of records.text) { - const setTextTx = await publicResolver.write.setText( - [hash, key, value], - { account: registrant }, - ) - console.log(` - ${key} ${value} (tx: ${setTextTx})...`) - await viem.waitForTransactionSuccess(setTextTx) - } - } - if (records.addr) { - console.log('ADDR') - for (const { key, value } of records.addr) { - const setAddrTx = await publicResolver.write.setAddr( - [hash, key, value], - { account: registrant }, - ) - console.log(` - ${key} ${value} (tx: ${setAddrTx})...`) - await viem.waitForTransactionSuccess(setAddrTx) - } - } - if (records.contenthash) { - console.log('CONTENTHASH') - const setContenthashTx = await publicResolver.write.setContenthash( - [hash, records.contenthash], - { account: registrant }, - ) - console.log(` - ${records.contenthash} (tx: ${setContenthashTx})...`) - await viem.waitForTransactionSuccess(setContenthashTx) - } - if (records.abi) { - const abis = Array.isArray(records.abi) ? records.abi : [records.abi] - for (const abi of abis) { - /** - * @type {string | Buffer | Uint8Array} - */ - let data: string | Buffer | Uint8Array - if (abi.contentType === 1 || abi.contentType === 256) { - data = stringToBytes(JSON.stringify(abi.data)) - } else if (abi.contentType === 2) { - data = pako.deflate(JSON.stringify(abi.data)) - } else if (abi.contentType === 4) { - data = cbor.encode(abi.data) - } else { - data = stringToBytes(abi.data) - } - const setABITx = await publicResolver.write.setABI( - [hash, abi.contentType, bytesToHex(data)], - { account: registrant }, - ) - console.log(` - ${abi.contentType} (tx: ${setABITx})...`) - await viem.waitForTransactionSuccess(setABITx) - } - } - } - - if (subnames) { - console.log(`Setting subnames for ${label}.eth...`) - for (const { - label: subnameLabel, - namedOwner: namedSubnameOwner, - } of subnames) { - const setSubnameTx = await nameGenerator.subname({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - }) - console.log(` - ${subnameLabel} (tx: ${setSubnameTx})...`) - await viem.waitForTransactionSuccess(setSubnameTx) - } - } - } - - await network.provider.send('anvil_setBlockTimestampInterval', [1]) - - return true -} - -func.id = 'register-unwrapped-names' -func.tags = ['register-unwrapped-names'] -func.dependencies = ['LegacyETHRegistrarController'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/00_register_wrapped.ts b/packages/ensjs/deploy/00_register_wrapped.ts deleted file mode 100644 index 95ce35478..000000000 --- a/packages/ensjs/deploy/00_register_wrapped.ts +++ /dev/null @@ -1,198 +0,0 @@ -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { MAX_DATE_INT } from '../dist/utils/consts.js' -import { encodeFuses } from '../dist/utils/fuses.js' -import { makeNameGenerator } from '../utils/wrappedNameGenerator.js' - -const names: { - label: string - namedOwner: string - data?: any[] - reverseRecord?: boolean - fuses?: number - subnames?: { - label: string - namedOwner: string - fuses?: number - expiry?: number - }[] - duration?: number | bigint -}[] = [ - { - label: 'wrapped', - namedOwner: 'owner', - reverseRecord: true, - }, - { - label: 'wrapped-with-subnames', - namedOwner: 'owner', - subnames: [ - { label: 'test', namedOwner: 'owner2' }, - { label: 'legacy', namedOwner: 'owner2' }, - { label: 'xyz', namedOwner: 'owner2' }, - { label: 'addr', namedOwner: 'owner2' }, - ], - }, - { - label: 'expired-wrapped', - namedOwner: 'owner', - subnames: [{ label: 'test', namedOwner: 'owner2' }], - duration: 2419200, - }, - { - label: 'wrapped-big-duration', - namedOwner: 'owner3', - duration: Math.floor((MAX_DATE_INT - Date.now()) / 1000), - }, - { - label: 'wrapped-max-duration', - namedOwner: 'owner3', - duration: BigInt('18446744073709'), - }, - { - label: 'wrapped-with-expiring-subnames', - namedOwner: 'owner', - fuses: encodeFuses({ - input: { - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), - subnames: [ - { - label: 'test', - namedOwner: 'owner2', - expiry: Math.floor(Date.now() / 1000), - }, - { - label: 'test1', - namedOwner: 'owner2', - expiry: 0, - }, - { - label: 'recent-pcc', - namedOwner: 'owner2', - expiry: Math.floor(Date.now() / 1000), - fuses: encodeFuses({ - input: { - parent: { named: ['PARENT_CANNOT_CONTROL'] }, - }, - }), - }, - { - label: 'pcc', - namedOwner: 'owner2', - expiry: MAX_DATE_INT, - fuses: encodeFuses({ - input: { - parent: { named: ['PARENT_CANNOT_CONTROL'] }, - }, - }), - }, - ], - }, - ...Array.from({ length: 2 }, (_, index) => ({ - label: `nonconcurrent-wrapped-name-${index}`, - namedOwner: 'owner4', - fuses: encodeFuses({ - input: { - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), - duration: 31556000 * 2, - subnames: [ - { - label: 'xyz', - namedOwner: 'owner4', - expiry: MAX_DATE_INT, - fuses: encodeFuses({ - input: { - parent: { - named: ['PARENT_CANNOT_CONTROL'], - }, - child: { - named: ['CANNOT_UNWRAP'], - }, - }, - }), - }, - ], - })), -] - -const func: DeployFunction = async (hre) => { - const { network, viem } = hre - const nameGenerator = await makeNameGenerator(hre) - - await network.provider.send('anvil_setBlockTimestampInterval', [60]) - - for (const { - label, - namedOwner, - data = [], - reverseRecord = false, - fuses = 0, - subnames, - duration = 31536000, - } of names) { - const commitTx = await nameGenerator.commit({ - label, - namedOwner, - data, - reverseRecord, - fuses, - duration, - }) - - console.log(`Committing commitment for ${label}.eth (tx: ${commitTx})...`) - await viem.waitForTransactionSuccess(commitTx) - - await network.provider.send('evm_mine') - - const registerTx = await nameGenerator.register({ - label, - namedOwner, - data, - reverseRecord, - fuses, - duration, - }) - - console.log(`Registering name ${label}.eth (tx: ${registerTx})...`) - await viem.waitForTransactionSuccess(registerTx) - - if (subnames) { - console.log(`Setting subnames for ${label}.eth...`) - for (const { - label: subnameLabel, - namedOwner: namedSubnameOwner, - fuses: subnameFuses = 0, - expiry: subnameExpiry = 2n ** 64n - 1n, - } of subnames) { - const setSubnameTx = await nameGenerator.subname({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - subnameFuses, - subnameExpiry, - }) - console.log(` - ${subnameLabel} (tx: ${setSubnameTx})...`) - await viem.waitForTransactionSuccess(setSubnameTx) - } - } - } - - await network.provider.send('anvil_setBlockTimestampInterval', [1]) - - return true -} - -func.id = 'register-wrapped-names' -func.tags = ['register-wrapped-names'] -func.dependencies = ['ETHRegistrarController'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/01_delete_names.ts b/packages/ensjs/deploy/01_delete_names.ts deleted file mode 100644 index 97ce6c0ed..000000000 --- a/packages/ensjs/deploy/01_delete_names.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { labelhash, namehash, packetToBytes } from 'viem/ens' -import { toHex } from 'viem/utils' -import { EMPTY_ADDRESS } from '../dist/utils/consts.js' - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts, viem } = hre - const allNamedAccts = await getNamedAccounts() - const clients = await viem.getNamedClients() - - const nameWrapper = await viem.getContract('NameWrapper', clients.owner) - const registry = await viem.getContract('ENSRegistry', clients.owner) - - const deleteName = async (name: string) => { - const labels = name.split('.') - const label = labelhash(labels.shift()!) - const node = namehash(labels.join('.')) - - const tx = await registry.write.setSubnodeRecord([ - node, - label, - EMPTY_ADDRESS, - EMPTY_ADDRESS, - 0, - ]) - await viem.waitForTransactionSuccess(tx) - } - - const name1 = 'wrapped-deleted.deletable.eth' - const name2 = 'unwrapped-deleted.deletable.eth' - - // wrap wrapped-deleted.deletable.eth - const approveTx = await registry.write.setApprovalForAll([ - nameWrapper.address, - true, - ]) - await viem.waitForTransactionSuccess(approveTx) - const wrapTx = await nameWrapper.write.wrap([ - toHex(packetToBytes(name1)), - allNamedAccts.owner, - EMPTY_ADDRESS, - ]) - await viem.waitForTransactionSuccess(wrapTx) - - await deleteName(name1) - await deleteName(name2) - - for (const name of [name1, name2]) { - const owner = await registry.read.owner([namehash(name)]) - if (owner !== EMPTY_ADDRESS) { - throw new Error(`Failed to delete name ${name}`) - } - } - - return true -} - -func.id = 'delete-names' -func.tags = ['delete-names'] -func.dependencies = ['register-unwrapped-names'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/01_set_legacy_resolver.ts b/packages/ensjs/deploy/01_set_legacy_resolver.ts deleted file mode 100644 index cc1b2540c..000000000 --- a/packages/ensjs/deploy/01_set_legacy_resolver.ts +++ /dev/null @@ -1,66 +0,0 @@ -import fs from 'node:fs/promises' -import { resolve } from 'node:path' -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { namehash } from 'viem/ens' - -const names = [ - { - namedOwner: 'owner', - name: 'with-legacy-resolver.eth', - addr: [{ key: 60, value: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' }], - }, -] - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments, viem } = hre - const allNamedAccts = await getNamedAccounts() - - const registry = await viem.getContract('ENSRegistry') - - console.log('deploying NoMulticallResolver') - await deployments.deploy('NoMulticallResolver', { - from: allNamedAccts.deployer, - contract: JSON.parse( - await fs.readFile( - resolve(import.meta.dirname, '../contracts/NoMulticallResolver.json'), - { - encoding: 'utf8', - }, - ), - ), - args: [registry.address], - }) - - const resolver = await viem.getContract('NoMulticallResolver') - for (const { namedOwner, name, addr } of names) { - const owner = (await viem.getNamedClients())[namedOwner] - - const tx = await registry.write.setResolver( - [namehash(name), resolver.address], - { - account: owner.address, - }, - ) - console.log( - `Setting resolver for ${name} to ${resolver.address} (tx: ${tx})...`, - ) - await viem.waitForTransactionSuccess(tx) - - for (const { key, value } of addr) { - const tx2 = await resolver.write.setAddr([namehash(name), value], { - account: owner.address, - gasLimit: 100000, - }) - console.log(`Setting address for ${key} to ${value} (tx: ${tx})...`) - await viem.waitForTransactionSuccess(tx2) - } - } - - return true -} - -func.id = 'set-legacy-resolver' -func.tags = ['set-legacy-resolver'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/01_set_oldest_resolver.ts b/packages/ensjs/deploy/01_set_oldest_resolver.ts deleted file mode 100644 index 8581541ee..000000000 --- a/packages/ensjs/deploy/01_set_oldest_resolver.ts +++ /dev/null @@ -1,64 +0,0 @@ -import fs from 'node:fs/promises' -import { resolve } from 'node:path' -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { namehash } from 'viem' - -const names = [ - { - namedOwner: 'owner', - name: 'with-oldest-resolver.eth', - addr: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', - }, -] - -const func: DeployFunction = async (hre) => { - const { getNamedAccounts, deployments, viem } = hre - const allNamedAccts = await getNamedAccounts() - - const registry = await viem.getContract('ENSRegistry') - - await deployments.deploy('OldestResolver', { - from: allNamedAccts.deployer, - contract: JSON.parse( - await fs.readFile( - resolve(import.meta.dirname, '../contracts/OldResolver.json'), - { - encoding: 'utf8', - }, - ), - ), - args: [registry.address], - }) - - const resolver = await viem.getContract('OldestResolver') - - for (const { namedOwner, name, addr } of names) { - const owner = allNamedAccts[namedOwner] - - const tx = await registry.write.setResolver( - [namehash(name), resolver.address], - { - account: owner, - }, - ) - console.log( - `Setting resolver for ${name} to ${resolver.address} (tx: ${tx})...`, - ) - await viem.waitForTransactionSuccess(tx) - - const tx2 = await resolver.write.setAddr([namehash(name), addr], { - account: owner, - gasLimit: 100000, - }) - console.log(`Setting address for 60 to ${addr} (tx: ${tx})...`) - await viem.waitForTransactionSuccess(tx2) - } - - return true -} - -func.id = 'set-oldest-resolver' -func.tags = ['set-oldest-resolver'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/01_set_primary.ts b/packages/ensjs/deploy/01_set_primary.ts deleted file mode 100644 index 80880f64b..000000000 --- a/packages/ensjs/deploy/01_set_primary.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ - -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' - -const names = [ - { - namedOwner: 'owner2', - primaryName: 'with-profile.eth', - }, -] - -const func: DeployFunction = async (hre) => { - const { viem } = hre - - for (const { namedOwner, primaryName } of names) { - const owner = (await viem.getNamedClients())[namedOwner] - const reverseRegistrar = await viem.getContract('ReverseRegistrar', owner) - const setPrimaryTx = await reverseRegistrar.write.setName([primaryName]) - console.log( - `Setting primary name for ${owner.address} to ${primaryName} (tx: ${setPrimaryTx})...`, - ) - await viem.waitForTransactionSuccess(setPrimaryTx) - } - - return true -} - -func.id = 'set-primary' -func.tags = ['set-primary'] -func.runAtTheEnd = true - -export default func diff --git a/packages/ensjs/deploy/02_get_contract_addresses.ts b/packages/ensjs/deploy/02_get_contract_addresses.ts deleted file mode 100644 index 4a11b5741..000000000 --- a/packages/ensjs/deploy/02_get_contract_addresses.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { writeFile } from 'node:fs/promises' -import { resolve } from 'node:path' -import type { DeployFunction } from 'hardhat-deploy/dist/types.js' -import { getAddress } from 'viem' - -const func: DeployFunction = async (hre) => { - const allDeployments = await hre.deployments.all() - const deploymentAddressMap = Object.fromEntries( - Object.keys(allDeployments).map((dkey) => [ - dkey, - getAddress(allDeployments[dkey].address), - ]), - ) - - await writeFile( - resolve(import.meta.dirname, '../.env.local'), - `DEPLOYMENT_ADDRESSES='${JSON.stringify(deploymentAddressMap)}'`, - ) - console.log('Wrote contract addresses to .env.local') - - await (await hre.viem.getTestClient()).request({ method: 'evm_snapshot' }) -} - -func.runAtTheEnd = true -func.dependencies = ['set-legacy-resolver'] - -export default func diff --git a/packages/ensjs/ens-test-env.config.js b/packages/ensjs/ens-test-env.config.js deleted file mode 100644 index cce8aff40..000000000 --- a/packages/ensjs/ens-test-env.config.js +++ /dev/null @@ -1,29 +0,0 @@ -import { config } from 'dotenv' - -config({ path: `${process.env.INIT_CWD}/.env.local` }) -config({ - path: `${process.env.INIT_CWD}/.env`, - override: true, -}) - -process.env.ADDRESS_ETH_REGISTRAR = '0xc5a5C42992dECbae36851359345FE25997F5C42d' -process.env.ADDRESS_NAME_WRAPPER = '0x9E545E3C0baAB3E08CdfD552C960A1050f373042' -process.env.BATCH_GATEWAY_URLS = JSON.stringify([ - 'https://universal-offchain-unwrapper.ens-cf.workers.dev/', -]) - -/** - * @type {import('@ensdomains/ens-test-env').ENSTestEnvConfig} - * */ -export default { - deployCommand: 'pnpm hh deploy --no-compile', - scripts: [ - { - command: - process.env.STATIC_ENS === 'true' ? 'pnpm test:static' : 'pnpm test', - name: 'vitest', - prefixColor: 'yellow.bold', - finishOnExit: true, - }, - ], -} diff --git a/packages/ensjs/hardhat.config.cts b/packages/ensjs/hardhat.config.cts deleted file mode 100644 index 3be714793..000000000 --- a/packages/ensjs/hardhat.config.cts +++ /dev/null @@ -1,96 +0,0 @@ -import '@ensdomains/hardhat-toolbox-viem-extended' -import '@nomicfoundation/hardhat-viem' -import 'dotenv/config' -import 'hardhat-deploy' - -import { resolve } from 'node:path' - -import type { HardhatUserConfig } from 'hardhat/config.js' - -const ensContractsPath = './node_modules/@ensdomains/ens-contracts' - -process.env.BATCH_GATEWAY_URLS = JSON.stringify([ - 'https://universal-offchain-unwrapper.ens-cf.workers.dev/', -]) - -const config: HardhatUserConfig = { - solidity: { - compilers: [ - { - version: '0.8.13', - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - ], - }, - defaultNetwork: 'localhost', - networks: { - hardhat: { - saveDeployments: false, - chainId: 1337, - accounts: { - mnemonic: 'test test test test test test test test test test test junk', - }, - live: false, - tags: ['test', 'legacy', 'use_root'], - }, - localhost: { - saveDeployments: false, - url: 'http://localhost:8545', - chainId: 1337, - accounts: { - mnemonic: 'test test test test test test test test test test test junk', - }, - live: false, - tags: ['test', 'legacy', 'use_root'], - }, - }, - // namedAccounts: { - // deployer: { - // default: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - // }, - // }, - namedAccounts: { - deployer: { - default: 0, - }, - owner: { - default: 1, - }, - owner2: { - default: 2, - }, - owner3: { - default: 3, - }, - owner4: { - default: 4, - }, - }, - external: { - contracts: [ - { - artifacts: [ - resolve(ensContractsPath, 'artifacts'), - resolve(ensContractsPath, './deployments/archive'), - ], - deploy: resolve(ensContractsPath, './build/deploy'), - }, - ], - }, - paths: { - artifacts: resolve(ensContractsPath, 'artifacts'), - }, -} - -declare module '@nomicfoundation/hardhat-viem/types.js' { - interface Register { - config: typeof config - } -} - -export default config diff --git a/packages/ensjs/package.json b/packages/ensjs/package.json index d8fd10bd0..abb135b79 100644 --- a/packages/ensjs/package.json +++ b/packages/ensjs/package.json @@ -1,8 +1,9 @@ { "name": "@ensdomains/ensjs", - "version": "4.0.2", + "version": "5.0.0-alpha.1", "description": "ENS javascript library for contract interaction", "type": "module", + "module": "./dist/index.js", "types": "./dist/index.d.ts", "typings": "./dist/index.d.ts", "sideEffects": false, @@ -20,8 +21,16 @@ "default": "./dist/dns.js" }, "./public": { - "types": "./dist/public.d.ts", - "default": "./dist/public.js" + "types": "./dist/exports/public.d.ts", + "default": "./dist/exports/public.js" + }, + "./public/v1": { + "types": "./dist/exports/public/v1.d.ts", + "default": "./dist/exports/public/v1.js" + }, + "./public/v2": { + "types": "./dist/exports/public/v2.d.ts", + "default": "./dist/exports/public/v2.js" }, "./subgraph": { "types": "./dist/subgraph.d.ts", @@ -31,9 +40,25 @@ "types": "./dist/utils/index.d.ts", "default": "./dist/utils/index.js" }, + "./utils/v2": { + "types": "./dist/utils/v2/index.d.ts", + "default": "./dist/utils/v2/index.js" + }, "./wallet": { - "types": "./dist/wallet.d.ts", - "default": "./dist/wallet.js" + "types": "./dist/exports/wallet.d.ts", + "default": "./dist/exports/wallet.js" + }, + "./wallet/v2": { + "types": "./dist/exports/wallet/v2.d.ts", + "default": "./dist/exports/wallet/v2.js" + }, + "./chain": { + "types": "./dist/exports/chain.d.ts", + "default": "./dist/exports/chain.js" + }, + "./internal": { + "types": "./dist/exports/internal.d.ts", + "default": "./dist/exports/internal.js" }, "./package.json": "./package.json" }, @@ -46,7 +71,7 @@ "./dist/dns.d.ts" ], "public": [ - "./dist/public.d.ts" + "./dist/exports/public.d.ts" ], "subgraph": [ "./dist/subgraph.d.ts" @@ -55,7 +80,13 @@ "./dist/utils/index.d.ts" ], "wallet": [ - "./dist/wallet.d.ts" + "./dist/exports/wallet.d.ts" + ], + "chain": [ + "./dist/exports/chain.d.ts" + ], + "internal": [ + "./dist/exports/internal.d.ts" ] } }, @@ -69,63 +100,41 @@ "author": "TateB ", "license": "MIT", "scripts": { - "tenv": "ens-test-env -c ./ens-test-env.config.js -a", - "denv": "pnpm tenv start --no-scripts --no-build --extra-time 11368000 --verbosity 1", - "anvil": "pnpm denv --no-ensnode --no-scripts", - "start": "ts-node --files src/index.test.ts", - "test": "vitest --no-file-parallelism", - "test:watch": "vitest --watch --no-file-parallelism", + "test": "vitest", + "test:watch": "vitest --watch", "clean": "rm -rf ./dist ./README.md ./LICENSE", - "lint": "eslint ./src/* --no-error-on-unmatched-pattern", + "lint": "biome check", + "check:types": "tsc --noEmit", "build": "tsc --project tsconfig.build.json", - "tsn": "TS_NODE_PROJECT=tsconfig.node.json node --loader ts-node/esm", "prepublish": "pnpm build && cp ../../README.md ../../LICENSE ./", - "prepack": "pnpm tsn ./scripts/prepack.ts", - "ver": "pnpm tsn ./scripts/updateVersion.ts", - "generateDocs": "pnpm tsn ./scripts/generateDocs.ts", - "rcBranchVersion": "pnpm tsn ./scripts/rcBranchVersion.ts", - "hh": "NODE_OPTIONS='--experimental-loader ts-node/esm/transpile-only' hardhat" + "ver": "node ./scripts/updateVersion.ts", + "generateDocs": "node ./scripts/generateDocs.ts", + "rcBranchVersion": "node ./scripts/rcBranchVersion.ts" }, "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@ensdomains/address-encoder": "1.1.1", "@ensdomains/content-hash": "3.1.0-rc.1", - "@ensdomains/dnsprovejs": "^0.5.1", - "abitype": "^1.0.0", + "@ensdomains/ensjs-abi": "workspace:*", "dns-packet": "^5.3.1", "graphql": "^16.11.0", - "graphql-request": "7.1.2", - "pako": "^2.1.0", + "graphql-request": "7.2.0", "ts-pattern": "^5.4.0" }, "devDependencies": { - "@ensdomains/buffer": "^0.1.1", - "@ensdomains/ens-contracts": "1.4.0-beta.2", + "@ensdomains/buffer": "^0.1.3", + "@ensdomains/dnsprovejs": "^0.5.2", "@ensdomains/ens-test-env": "^1.0.1", - "@ensdomains/hardhat-toolbox-viem-extended": "^0.0.5", - "@nomicfoundation/hardhat-toolbox-viem": "^3.0.0", - "@nomicfoundation/hardhat-viem": "2.0.6", "@openzeppelin/contracts": "^4.5.0", - "@openzeppelin/test-helpers": "^0.5.16", - "@types/jsonfile": "^6.1.4", + "@types/dns-packet": "^5.6.5", "@types/node": "^22.15.14", - "@types/pako": "^2.0.0", - "@vitest/coverage-v8": "^1.3.1", - "cbor": "^8.1.0", - "dotenv": "^16.0.0", - "ethers": "^5.7.2", - "happy-dom": "^13.3.8", - "hardhat": "2.24.1", - "hardhat-abi-exporter": "^2.8.0", - "hardhat-deploy": "^1.0.2", - "jsonfile": "^6.1.0", - "multiformats": "^12.0.1", - "solc": "^0.8.13", - "ts-node": "^10.9.2", + "@types/pako": "^2.0.3", + "@vitest/coverage-v8": "^3.2.6", + "pako": "^2.1.0", "typedoc": "^0.24.8", "typedoc-plugin-markdown": "^4.0.0-next.16", - "viem": "^2.30.6", - "vitest": "^1.3.1" + "viem": "^2.47.10", + "vitest": "^3.2.6" }, "peerDependencies": { "viem": "^2.9.2" @@ -133,4 +142,4 @@ "engines": { "node": ">=22" } -} \ No newline at end of file +} diff --git a/packages/ensjs/scripts/prepack.ts b/packages/ensjs/scripts/prepack.ts deleted file mode 100644 index 42249cbc1..000000000 --- a/packages/ensjs/scripts/prepack.ts +++ /dev/null @@ -1,97 +0,0 @@ -// from https://github.com/wagmi-dev/viem/blob/main/scripts/prepublishOnly.ts - -import fs from 'node:fs' -import path from 'node:path' -/* eslint-disable no-continue */ -import jsonFs from 'jsonfile' - -type Exports = { - [key: string]: string | { types?: string; import: string; default: string } -} - -// Generates a package.json to be published to NPM with only the necessary fields. -function generatePackageJson() { - const packageJsonPath = path.join(import.meta.dirname, '../package.json') - const tmpPackageJson = jsonFs.readFileSync(packageJsonPath) - - jsonFs.writeFileSync(`${packageJsonPath}.tmp`, tmpPackageJson, { spaces: 2 }) - - const { - name, - description, - dependencies, - peerDependencies, - peerDependenciesMeta, - version, - files, - exports: exports_, - // NOTE: We explicitly don't want to publish the type field. We create a separate package.json for `dist/cjs` and `dist/esm` that has the type field. - // type, - main, - module, - types, - typings, - typesVersions, - sideEffects, - license, - repository, - authors, - keywords, - } = tmpPackageJson - - // Generate proxy packages for each export. - const files_ = [...files] - for (const [key, value] of Object.entries(exports_ as Exports)) { - if (typeof value === 'string') continue - if (key === '.') continue - if (!value.default || !value.import) - throw new Error('`default` and `import` are required.') - if (!fs.existsSync(key)) fs.mkdirSync(key) - if (!fs.existsSync(`${key}/package.json`)) - fs.writeFileSync( - `${key}/package.json`, - `{ - ${Object.entries(value) - .map(([k, v]) => { - const key_ = (() => { - if (k === 'import') return 'module' - if (k === 'default') return 'main' - if (k === 'types') return 'types' - throw new Error('Invalid key') - })() - return `"${key_}": "${v.replace('./', '../')}"` - }) - .join(',\n ')} -}`, - ) - files_.push(key.replace('./', '')) - } - - jsonFs.writeFileSync( - packageJsonPath, - { - name, - description, - dependencies, - peerDependencies, - peerDependenciesMeta, - version, - files: files_, - exports: exports_, - // type, - main, - module, - types, - typings, - typesVersions, - sideEffects, - license, - repository, - authors, - keywords, - }, - { spaces: 2 }, - ) -} - -generatePackageJson() diff --git a/packages/ensjs/src/@types/dns-packet.d.ts b/packages/ensjs/src/@types/dns-packet.d.ts deleted file mode 100644 index a4519dca2..000000000 --- a/packages/ensjs/src/@types/dns-packet.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'dns-packet' diff --git a/packages/ensjs/src/@types/dns-packet/index.d.ts b/packages/ensjs/src/@types/dns-packet/index.d.ts deleted file mode 100644 index e121d00a3..000000000 --- a/packages/ensjs/src/@types/dns-packet/index.d.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -declare module 'dns-packet' { - const AUTHORITATIVE_ANSWER: number - const TRUNCATED_RESPONSE: number - const RECURSION_DESIRED: number - const RECURSION_AVAILABLE: number - const AUTHENTIC_DATA: number - const CHECKING_DISABLED: number - const DNSSEC_OK: number - - interface decoder { - (buf: Buffer, offset?: number): T - bytes: number - } - - const decode: decoder - - interface encoder { - (packet: T, buf?: Buffer, offset?: number): Buffer - bytes: number - } - - const encode: encoder - - interface Packet { - id?: number - type: 'query' | 'response' - flags?: number - rcode?: string - questions: Question[] - answers?: Answer[] - authorities?: Answer[] - additionals?: Answer[] - } - - interface Question { - type: string - class: string - name: string - } - - interface AnswerBase { - type: string - class: string - name: string - ttl?: number - } - - interface A extends AnswerBase { - type: 'A' - data: string - } - - interface Dnskey extends AnswerBase { - type: 'DNSKEY' - data: { - flags: number - algorithm: number - key: Buffer - } - } - - interface Ds extends AnswerBase { - type: 'DS' - data: { - keyTag: number - algorithm: number - digestType: number - digest: Buffer - } - } - - interface Opt extends AnswerBase { - type: 'OPT' - udpPayloadSize?: number - extendedRcode?: number - ednsVersion?: number - flags?: number - data?: any - } - - interface Rrsig extends AnswerBase { - type: 'RRSIG' - data: { - typeCovered: string - algorithm: number - labels: number - originalTTL: number - expiration: number - inception: number - keyTag: number - signersName: string - signature: Buffer - } - } - - interface Rtxt extends AnswerBase { - type: 'TXT' - data: Buffer[] - } - - type Answer = A | Dnskey | Ds | Opt | Rrsig | Rtxt - - interface Encodable { - decode: decoder - encode: encoder - } - - const record: (type: string) => Encodable - const answer: Encodable - const dnskey: Encodable - const name: Encodable - const rrsig: Encodable -} diff --git a/packages/ensjs/src/@types/dns-packet/types.d.ts b/packages/ensjs/src/@types/dns-packet/types.d.ts deleted file mode 100644 index df08299c9..000000000 --- a/packages/ensjs/src/@types/dns-packet/types.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'dns-packet/types.js' { - function toType(type: string): number - // biome-ignore lint/suspicious/noShadowRestrictedNames: type defs - function toString(type: number): string -} diff --git a/packages/ensjs/src/@types/pako.d.ts b/packages/ensjs/src/@types/pako.d.ts deleted file mode 100644 index 77bdb9368..000000000 --- a/packages/ensjs/src/@types/pako.d.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* eslint-disable max-classes-per-file */ -// Type definitions for pako 2.0 -// Project: https://github.com/nodeca/pako -// Definitions by: Caleb Eggensperger -// Muhammet Öztürk -// Thibault Poisson -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -enum constants { - // FlushValues - Z_NO_FLUSH = 0, - Z_PARTIAL_FLUSH = 1, - Z_SYNC_FLUSH = 2, - Z_FULL_FLUSH = 3, - Z_FINISH = 4, - Z_BLOCK = 5, - Z_TREES = 6, - // StrategyValues - Z_FILTERED = 1, - Z_HUFFMAN_ONLY = 2, - Z_RLE = 3, - Z_FIXED = 4, - Z_DEFAULT_STRATEGY = 0, - // ReturnCodes - Z_OK = 0, - Z_STREAM_END = 1, - Z_NEED_DICT = 2, - Z_ERRNO = -1, - Z_STREAM_ERROR = -2, - Z_DATA_ERROR = -3, - Z_BUF_ERROR = -5, -} - -type FlushValues = - | constants.Z_NO_FLUSH - | constants.Z_PARTIAL_FLUSH - | constants.Z_SYNC_FLUSH - | constants.Z_FINISH - | constants.Z_BLOCK - | constants.Z_TREES - -type StrategyValues = - | constants.Z_FILTERED - | constants.Z_HUFFMAN_ONLY - | constants.Z_RLE - | constants.Z_FIXED - | constants.Z_DEFAULT_STRATEGY - -type ReturnCodes = - | constants.Z_OK - | constants.Z_STREAM_END - | constants.Z_NEED_DICT - | constants.Z_ERRNO - | constants.Z_STREAM_ERROR - | constants.Z_DATA_ERROR - | constants.Z_BUF_ERROR - | constants.Z_DEFAULT_STRATEGY - -interface DeflateOptions { - level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined - windowBits?: number | undefined - memLevel?: number | undefined - strategy?: StrategyValues | undefined - dictionary?: any - raw?: boolean | undefined - chunkSize?: number | undefined - gzip?: boolean | undefined - header?: Header | undefined -} - -interface DeflateFunctionOptions { - level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined - windowBits?: number | undefined - memLevel?: number | undefined - strategy?: StrategyValues | undefined - dictionary?: any - raw?: boolean | undefined -} - -interface InflateOptions { - windowBits?: number | undefined - dictionary?: any - raw?: boolean | undefined - to?: 'string' | undefined - chunkSize?: number | undefined -} - -interface InflateFunctionOptions { - windowBits?: number | undefined - raw?: boolean | undefined - to?: 'string' | undefined -} - -interface Header { - text?: boolean | undefined - time?: number | undefined - os?: number | undefined - extra?: number[] | undefined - name?: string | undefined - comment?: string | undefined - hcrc?: boolean | undefined -} - -type Data = Uint8Array | ArrayBuffer - -declare module 'pako/dist/pako_inflate.min.js' { - /** - * Decompress data with inflate/ungzip and options. Autodetect format via wrapper header - * by default. That's why we don't provide separate ungzip method. - */ - function inflate( - data: Data, - options: InflateFunctionOptions & { to: 'string' }, - ): string - function inflate(data: Data, options?: InflateFunctionOptions): Uint8Array - - /** - * The same as inflate, but creates raw data, without wrapper (header and adler32 crc). - */ - function inflateRaw( - data: Data, - options: InflateFunctionOptions & { to: 'string' }, - ): string - function inflateRaw(data: Data, options?: InflateFunctionOptions): Uint8Array - - /** - * Just shortcut to inflate, because it autodetects format by header.content. Done for convenience. - */ - function ungzip( - data: Data, - options: InflateFunctionOptions & { to: 'string' }, - ): string - function ungzip(data: Data, options?: InflateFunctionOptions): Uint8Array - - // https://github.com/nodeca/pako/blob/893381abcafa10fa2081ce60dae7d4d8e873a658/lib/inflate.js - class Inflate { - constructor(options?: InflateOptions) - header?: Header | undefined - - err: ReturnCodes - - msg: string - - result: Uint8Array | string - onData(chunk: Data): void - onEnd(status: number): void - push(data: Data, mode?: FlushValues | boolean): boolean - } -} - -declare module 'pako/dist/pako_deflate.min.js' { - /** - * Compress data with deflate algorithm and options. - */ - function deflate( - data: Data | string, - options?: DeflateFunctionOptions, - ): Uint8Array - - /** - * The same as deflate, but creates raw data, without wrapper (header and adler32 crc). - */ - function deflateRaw( - data: Data | string, - options?: DeflateFunctionOptions, - ): Uint8Array - - /** - * The same as deflate, but create gzip wrapper instead of deflate one. - */ - function gzip( - data: Data | string, - options?: DeflateFunctionOptions, - ): Uint8Array - - class Deflate { - constructor(options?: DeflateOptions) - err: ReturnCodes - - msg: string - - result: Uint8Array - onData(chunk: Data): void - onEnd(status: number): void - push(data: Data | string, mode?: FlushValues | boolean): boolean - } -} diff --git a/packages/ensjs/src/functions/dns/getDnsImportData.test.ts b/packages/ensjs/src/actions/dns/getDnsImportData.test.ts similarity index 64% rename from packages/ensjs/src/functions/dns/getDnsImportData.test.ts rename to packages/ensjs/src/actions/dns/getDnsImportData.test.ts index 5a48bf470..9079203c6 100644 --- a/packages/ensjs/src/functions/dns/getDnsImportData.test.ts +++ b/packages/ensjs/src/actions/dns/getDnsImportData.test.ts @@ -1,13 +1,25 @@ import { SignedSet } from '@ensdomains/dnsprovejs' import { toBytes } from 'viem' -import { expect, it, vi } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getDnsImportData, { type RrSetWithSig } from './getDnsImportData.js' +import { beforeAll, expect, it, vi } from 'vitest' +import { publicClient, testClient } from '../../test/addTestContracts.js' +import { getDnsImportData, type RrSetWithSig } from './getDnsImportData.js' vi.setConfig({ testTimeout: 10000, }) +beforeAll(async () => { + const block = await publicClient.getBlock() + const anvilTime = Number(block.timestamp) + const realTime = Math.floor(Date.now() / 1000) + + if (anvilTime > realTime) { + // @ts-expect-error evm_setTime is a valid anvil RPC method not typed in viem + await testClient.request({ method: 'evm_setTime', params: [realTime] }) + await testClient.mine({ blocks: 1 }) + } +}) + const decodeProofs = (proofs: RrSetWithSig[]) => proofs.map((proof) => SignedSet.fromWire( diff --git a/packages/ensjs/src/functions/dns/getDnsImportData.ts b/packages/ensjs/src/actions/dns/getDnsImportData.ts similarity index 73% rename from packages/ensjs/src/functions/dns/getDnsImportData.ts rename to packages/ensjs/src/actions/dns/getDnsImportData.ts index 346ba0232..96c1a8165 100644 --- a/packages/ensjs/src/functions/dns/getDnsImportData.ts +++ b/packages/ensjs/src/actions/dns/getDnsImportData.ts @@ -1,11 +1,12 @@ import type { ProvableAnswer, SignedSet } from '@ensdomains/dnsprovejs' +import { dnssecImplVerifyRrSetSnippet } from '@ensdomains/ensjs-abi/dnssecImpl' import type * as packet from 'dns-packet' -import { type Client, type Hex, type Transport, toHex } from 'viem' +import { type Chain, type Client, type Hex, toHex } from 'viem' import { readContract } from 'viem/actions' -import type { ChainWithEns } from '../../contracts/consts.js' -import { dnssecImplVerifyRrSetSnippet } from '../../contracts/dnssecImpl.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { DnsNewerRecordTypeAvailableError } from '../../index.js' +import type { RequireClientContracts } from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { DnsNewerRecordTypeAvailableError } from '../../errors/dns.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' import type { Endpoint } from './types.js' export type GetDnsImportDataParameters = { @@ -15,19 +16,21 @@ export type GetDnsImportDataParameters = { endpoint?: Endpoint } +export type GetDnsImportDataReturnType = RrSetWithSig[] + +export type GetDnsImportDataErrorType = DnsNewerRecordTypeAvailableError | Error + export type RrSetWithSig = { rrset: Hex sig: Hex } -export type GetDnsImportDataReturnType = RrSetWithSig[] - // Compares two serial numbers using RFC1982 serial number math. const serialNumberGt = (i1: number, i2: number): boolean => (i1 < i2 && i2 - i1 > 0x7fffffff) || (i1 > i2 && i1 - i2 < 0x7fffffff) const encodeProofs = ( - proofs: SignedSet[], + proofs: SignedSet[], ): RrSetWithSig[] => proofs.map((proof) => ({ rrset: toHex(proof.toWire(true)), @@ -36,7 +39,7 @@ const encodeProofs = ( /** * Gets DNS import data, used for `importDnsName()` - * @param client - {@link ClientWithEns} + * @param client - {@link Client} * @param parameters - {@link GetDnsImportDataParameters} * @returns DNS import data object, used for proving the value of the `_ens` TXT record * @@ -54,22 +57,25 @@ const encodeProofs = ( * name: 'example.eth', * }) */ -const getDnsImportData = async ( - client: Client, +export async function getDnsImportData<_chain extends Chain>( + client: RequireClientContracts<_chain, 'ensDnssecImpl'>, { name, endpoint = 'https://cloudflare-dns.com/dns-query', }: GetDnsImportDataParameters, -): Promise => { +): Promise { + ASSERT_NO_TYPE_ERROR(client) const { DNSProver } = await import('@ensdomains/dnsprovejs') const prover = DNSProver.create(endpoint) const result = (await prover.queryWithProof( 'TXT', `_ens.${name}`, - )) as ProvableAnswer + )) as ProvableAnswer const allProofs = ( - result.proofs as SignedSet[] + result.proofs as SignedSet< + packet.DSAnswer | packet.DnskeyAnswer | packet.TxtAnswer + >[] ).concat([result.answer]) const rrsets = encodeProofs(allProofs) @@ -77,7 +83,7 @@ const getDnsImportData = async ( const [onchainRrData, inception] = await readContract(client, { abi: dnssecImplVerifyRrSetSnippet, address: getChainContractAddress({ - client, + chain: client.chain, contract: 'ensDnssecImpl', }), functionName: 'verifyRRSet', @@ -98,5 +104,3 @@ const getDnsImportData = async ( return rrsets } - -export default getDnsImportData diff --git a/packages/ensjs/src/functions/dns/getDnsOffchainData.test.ts b/packages/ensjs/src/actions/dns/getDnsOffchainData.test.ts similarity index 95% rename from packages/ensjs/src/functions/dns/getDnsOffchainData.test.ts rename to packages/ensjs/src/actions/dns/getDnsOffchainData.test.ts index af73756c3..962e1e074 100644 --- a/packages/ensjs/src/functions/dns/getDnsOffchainData.test.ts +++ b/packages/ensjs/src/actions/dns/getDnsOffchainData.test.ts @@ -1,26 +1,35 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { RequestListener } from 'node:http' -import { http, createPublicClient } from 'viem' +import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { - type MockedFunction, afterAll, beforeAll, beforeEach, describe, expect, it, + type MockedFunction, vi, } from 'vitest' -import { addEnsContracts } from '../../index.js' +import { addEnsL1Contracts } from '../../index.js' import { createHttpServer } from '../../test/createHttpServer.js' -import { createHandlerResponse } from '../../test/dns.js' -import getDnsOffchainData from './getDnsOffchainData.js' +import { getDnsOffchainData } from './getDnsOffchainData.js' vi.setConfig({ testTimeout: 10000, }) +type Handler = MockedFunction + +const createHandlerResponse = (handler: Handler, response: object) => { + handler.mockImplementation((_req, res) => { + res.writeHead(200, { 'Content-Type': 'application/dns-json' }) + res.end(JSON.stringify(response)) + res.destroy() + }) +} + const handler: MockedFunction = vi.fn() let closeServer: () => Promise let serverUrl: `http://${string}` = 'http://' @@ -40,7 +49,7 @@ beforeEach(() => { }) const mainnetPublicClient = createPublicClient({ - chain: addEnsContracts(mainnet), + chain: addEnsL1Contracts(mainnet), transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), }) @@ -559,7 +568,7 @@ describe('no eligible invalid records', () => { }) }) -it('real test', async () => { +it.skip('real test', async () => { const offchainData = await getDnsOffchainData(mainnetPublicClient, { name: 'ethleaderboard.xyz', strict: true, diff --git a/packages/ensjs/src/functions/dns/getDnsOffchainData.ts b/packages/ensjs/src/actions/dns/getDnsOffchainData.ts similarity index 78% rename from packages/ensjs/src/functions/dns/getDnsOffchainData.ts rename to packages/ensjs/src/actions/dns/getDnsOffchainData.ts index 3b0cd6851..4b841fdc0 100644 --- a/packages/ensjs/src/functions/dns/getDnsOffchainData.ts +++ b/packages/ensjs/src/actions/dns/getDnsOffchainData.ts @@ -1,5 +1,5 @@ -import { type Address, type Client, type Transport, isAddress } from 'viem' -import type { ChainWithEns } from '../../contracts/consts.js' +import { type Address, type Chain, isAddress } from 'viem' +import type { RequireClientContracts } from '../../clients/shared.js' import { DnsDnssecVerificationFailedError, DnsDnssecWildcardExpansionError, @@ -14,8 +14,8 @@ import { type DnsResponseItem, DnsResponseStatus, } from '../../utils/dns/misc.js' -import { getNameType } from '../../utils/getNameType.js' -import getAddressRecord from '../public/getAddressRecord.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { getAddressRecord } from '../public/getAddressRecord.js' import type { Endpoint } from './types.js' export type GetDnsOffchainDataParameters = { @@ -32,6 +32,15 @@ export type GetDnsOffchainDataReturnType = { extraData: string | null } | null +export type GetDnsOffchainDataErrorType = + | DnsDnssecVerificationFailedError + | DnsDnssecWildcardExpansionError + | DnsInvalidTxtRecordError + | DnsNoTxtRecordError + | DnsResponseStatusError + | UnsupportedNameTypeError + | Error + type ValidTextRecord = { isValid: true resolverAddress: Address @@ -39,10 +48,10 @@ type ValidTextRecord = { } type InvalidTextRecord = { isValid: false; recordData: string } -const checkValidEnsTxtRecord = async ( - client: Client, +async function checkValidEnsTxtRecord( + client: RequireClientContracts, record: DnsResponseItem, -): Promise => { +): Promise { if (record.type !== DnsRecordType.TXT) return null if (!record.data.startsWith('"ENS1 ')) return null @@ -78,18 +87,28 @@ const checkValidEnsTxtRecord = async ( /** * Gets the DNS offchain data for a name, via DNS record lookup + * @param client - {@link Client} * @param parameters - {@link GetDnsOffchainDataParameters} * @returns Resolver address and extra data, or null. {@link GetDnsOffchainDataReturnType} * * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' * import { getDnsOffchainData } from '@ensdomains/ensjs/dns' * - * const owner = await getDnsOffchainData({ name: 'ethleaderboard.xyz' }) + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const data = await getDnsOffchainData(client, { + * name: 'ethleaderboard.xyz', + * }) */ -const getDnsOffchainData = async ( - client: Client, +export async function getDnsOffchainData( + client: RequireClientContracts, { name, endpoint, strict }: GetDnsOffchainDataParameters, -): Promise => { +): Promise { const nameType = getNameType(name) if (nameType !== 'other-2ld' && nameType !== 'other-subname') @@ -148,5 +167,3 @@ const getDnsOffchainData = async ( throw error } } - -export default getDnsOffchainData diff --git a/packages/ensjs/src/functions/dns/getDnsOwner.test.ts b/packages/ensjs/src/actions/dns/getDnsOwner.test.ts similarity index 99% rename from packages/ensjs/src/functions/dns/getDnsOwner.test.ts rename to packages/ensjs/src/actions/dns/getDnsOwner.test.ts index 8cff1aa7b..5a53ba6d7 100644 --- a/packages/ensjs/src/functions/dns/getDnsOwner.test.ts +++ b/packages/ensjs/src/actions/dns/getDnsOwner.test.ts @@ -1,17 +1,17 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { RequestListener } from 'node:http' import { - type MockedFunction, afterAll, beforeAll, beforeEach, describe, expect, it, + type MockedFunction, vi, } from 'vitest' import { createHttpServer } from '../../test/createHttpServer.js' -import getDnsOwner from './getDnsOwner.js' +import { getDnsOwner } from './getDnsOwner.js' vi.setConfig({ testTimeout: 10000, diff --git a/packages/ensjs/src/functions/dns/getDnsOwner.ts b/packages/ensjs/src/actions/dns/getDnsOwner.ts similarity index 82% rename from packages/ensjs/src/functions/dns/getDnsOwner.ts rename to packages/ensjs/src/actions/dns/getDnsOwner.ts index 8b7f63424..9f0c1c0bf 100644 --- a/packages/ensjs/src/functions/dns/getDnsOwner.ts +++ b/packages/ensjs/src/actions/dns/getDnsOwner.ts @@ -9,7 +9,7 @@ import { import { UnsupportedNameTypeError } from '../../errors/general.js' import { getDnsTxtRecords } from '../../utils/dns/getDnsTxtRecords.js' import { DnsRecordType, DnsResponseStatus } from '../../utils/dns/misc.js' -import { getNameType } from '../../utils/getNameType.js' +import { getNameType } from '../../utils/name/getNameType.js' import type { Endpoint } from './types.js' export type GetDnsOwnerParameters = { @@ -23,6 +23,15 @@ export type GetDnsOwnerParameters = { export type GetDnsOwnerReturnType = Address | null +export type GetDnsOwnerErrorType = + | DnsDnssecVerificationFailedError + | DnsInvalidAddressChecksumError + | DnsInvalidTxtRecordError + | DnsNoTxtRecordError + | DnsResponseStatusError + | UnsupportedNameTypeError + | Error + /** * Gets the DNS owner of a name, via DNS record lookup * @param parameters - {@link GetDnsOwnerParameters} @@ -34,11 +43,11 @@ export type GetDnsOwnerReturnType = Address | null * const owner = await getDnsOwner({ name: 'ens.domains' }) * // '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' */ -const getDnsOwner = async ({ +export async function getDnsOwner({ name, endpoint, strict, -}: GetDnsOwnerParameters): Promise => { +}: GetDnsOwnerParameters): Promise { const nameType = getNameType(name) if (nameType !== 'other-2ld') @@ -70,10 +79,10 @@ const getDnsOwner = async ({ if (!addressRecord?.data) throw new DnsNoTxtRecordError() - if (!unwrappedAddressRecord!.match(/^a=0x[a-fA-F0-9]{40}$/g)) + if (!unwrappedAddressRecord?.match(/^a=0x[a-fA-F0-9]{40}$/g)) throw new DnsInvalidTxtRecordError({ record: unwrappedAddressRecord! }) - const address = unwrappedAddressRecord!.slice(2) + const address = unwrappedAddressRecord?.slice(2) const checksumAddress = getAddress(address) if (address !== checksumAddress) @@ -85,5 +94,3 @@ const getDnsOwner = async ({ throw error } } - -export default getDnsOwner diff --git a/packages/ensjs/src/functions/dns/importDnsName.test.ts b/packages/ensjs/src/actions/dns/importDnsName.test.ts similarity index 78% rename from packages/ensjs/src/functions/dns/importDnsName.test.ts rename to packages/ensjs/src/actions/dns/importDnsName.test.ts index 4d94eb12a..885f948e0 100644 --- a/packages/ensjs/src/functions/dns/importDnsName.test.ts +++ b/packages/ensjs/src/actions/dns/importDnsName.test.ts @@ -1,7 +1,6 @@ -import { afterEach } from 'node:test' import { type Address, type Hex, parseAbi, parseEther } from 'viem' -import { beforeAll, beforeEach, expect, it, vi } from 'vitest' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' +import { afterEach, beforeAll, beforeEach, expect, it, vi } from 'vitest' +import { getChainContractAddress } from '../../clients/shared.js' import { deploymentAddresses, publicClient, @@ -9,12 +8,13 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getOwner from '../public/getOwner.js' -import getResolver from '../public/getResolver.js' -import getDnsImportData, { +import { getResolver } from '../public/getResolver.js' +import { getOwner } from '../public/v1/getOwner.js' +import { type GetDnsImportDataReturnType, + getDnsImportData, } from './getDnsImportData.js' -import importDnsName from './importDnsName.js' +import { importDnsName } from './importDnsName.js' vi.setConfig({ testTimeout: 10000, @@ -57,7 +57,7 @@ afterEach(async () => { await testClient.revert({ id: snapshot }) }) -it('should import a DNS name with no address', async () => { +it.skip('should import a DNS name with no address', async () => { const tx = await importDnsName(walletClient, { name, dnsImportData, @@ -68,10 +68,10 @@ it('should import a DNS name with no address', async () => { expect(receipt.status).toBe('success') const owner = await getOwner(publicClient, { name }) - expect(owner!.owner).toBe(address) + expect(owner?.owner).toBe(address) }) -it('should import a DNS name with an address, using default resolver', async () => { +it.skip('should import a DNS name with an address, using default resolver', async () => { await testClient.impersonateAccount({ address }) await testClient.setBalance({ address, @@ -89,16 +89,16 @@ it('should import a DNS name with an address, using default resolver', async () expect(receipt.status).toBe('success') const owner = await getOwner(publicClient, { name }) - expect(owner!.owner).toBe(address) + expect(owner?.owner).toBe(address) const resolver = await getResolver(publicClient, { name }) expect(resolver).toBe( getChainContractAddress({ - client: publicClient, + chain: publicClient.chain, contract: 'ensPublicResolver', }), ) }) -it('should import a DNS name with an address, using a custom resolver', async () => { +it.skip('should import a DNS name with an address, using a custom resolver', async () => { await testClient.impersonateAccount({ address }) await testClient.setBalance({ address, @@ -119,19 +119,20 @@ it('should import a DNS name with an address, using a custom resolver', async () expect(receipt.status).toBe('success') const owner = await getOwner(publicClient, { name }) - expect(owner!.owner).toBe(address) + expect(owner?.owner).toBe(address) const resolver = await getResolver(publicClient, { name }) expect(resolver).toBe(resolverAddress) }) -it('should throw error if resolver is specified when claiming without an address', async () => { +it.skip('should throw error if resolver is specified when claiming without an address', async () => { await expect( + // @ts-expect-error intentionally passing resolverAddress without address to test runtime validation importDnsName(walletClient, { name, resolverAddress: address, dnsImportData: await getDnsImportData(publicClient, { name }), account: accounts[0], - } as any), + }), ).rejects.toThrowErrorMatchingInlineSnapshot(` [AdditionalParameterSpecifiedError: Additional parameter specified: resolverAddress diff --git a/packages/ensjs/src/functions/dns/importDnsName.ts b/packages/ensjs/src/actions/dns/importDnsName.ts similarity index 75% rename from packages/ensjs/src/functions/dns/importDnsName.ts rename to packages/ensjs/src/actions/dns/importDnsName.ts index 2643a5755..5fda67fb8 100644 --- a/packages/ensjs/src/functions/dns/importDnsName.ts +++ b/packages/ensjs/src/actions/dns/importDnsName.ts @@ -1,26 +1,31 @@ +import { + dnsRegistrarProveAndClaimSnippet, + dnsRegistrarProveAndClaimWithResolverSnippet, +} from '@ensdomains/ensjs-abi/dnsRegistrar' import { type Account, type Address, + type Chain, + encodeFunctionData, type Hash, type SendTransactionParameters, - type Transport, - encodeFunctionData, toHex, } from 'viem' import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { - dnsRegistrarProveAndClaimSnippet, - dnsRegistrarProveAndClaimWithResolverSnippet, -} from '../../contracts/dnsRegistrar.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' +import { packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' import { AdditionalParameterSpecifiedError } from '../../errors/general.js' import type { Prettify, SimpleTransactionRequest, WriteTransactionParameters, -} from '../../types.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' +} from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' import type { GetDnsImportDataReturnType } from './getDnsImportData.js' type BaseImportDnsNameDataParameters = { @@ -49,10 +54,12 @@ export type ImportDnsNameDataParameters = BaseImportDnsNameDataParameters & export type ImportDnsNameDataReturnType = SimpleTransactionRequest +type ImportDnsNameContracts = 'ensDnsRegistrar' | 'ensPublicResolver' + export type ImportDnsNameParameters< - TChain extends ChainWithEns, + TChain extends Chain, TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, + TChainOverride extends ChainWithContracts, > = Prettify< ImportDnsNameDataParameters & WriteTransactionParameters @@ -60,11 +67,10 @@ export type ImportDnsNameParameters< export type ImportDnsNameReturnType = Hash -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, +export type ImportDnsNameErrorType = AdditionalParameterSpecifiedError | Error + +export const makeFunctionData = ( + chain: ChainWithContracts, { name, dnsImportData, @@ -74,7 +80,7 @@ export const makeFunctionData = < ): ImportDnsNameDataReturnType => { const hexEncodedName = toHex(packetToBytes(name)) const dnsRegistrarAddress = getChainContractAddress({ - client: wallet, + chain, contract: 'ensDnsRegistrar', }) @@ -98,7 +104,10 @@ export const makeFunctionData = < const resolverAddress_ = resolverAddress || - getChainContractAddress({ client: wallet, contract: 'ensPublicResolver' }) + getChainContractAddress({ + chain, + contract: 'ensPublicResolver', + }) return { to: dnsRegistrarAddress, @@ -139,12 +148,12 @@ export const makeFunctionData = < * dnsImportData, * }) */ -async function importDnsName< - TChain extends ChainWithEns, +export async function importDnsName< + TChain extends Chain, TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, + TChainOverride extends ChainWithContracts, >( - wallet: ClientWithAccount, + wallet: RequireClientContracts, { name, address, @@ -153,19 +162,22 @@ async function importDnsName< ...txArgs }: ImportDnsNameParameters, ): Promise { - const data = makeFunctionData(wallet, { + ASSERT_NO_TYPE_ERROR(wallet) + const data = makeFunctionData(wallet.chain, { name, address, dnsImportData, resolverAddress, } as ImportDnsNameDataParameters) - const writeArgs = { + const sendTransactionAction = getAction( + wallet, + sendTransaction, + 'sendTransaction', + ) + return sendTransactionAction({ ...data, ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) + } as SendTransactionParameters) } importDnsName.makeFunctionData = makeFunctionData - -export default importDnsName diff --git a/packages/ensjs/src/functions/dns/types.ts b/packages/ensjs/src/actions/dns/types.ts similarity index 100% rename from packages/ensjs/src/functions/dns/types.ts rename to packages/ensjs/src/actions/dns/types.ts diff --git a/packages/ensjs/src/actions/public/ccip.test.ts b/packages/ensjs/src/actions/public/ccip.test.ts new file mode 100644 index 000000000..0aadf898a --- /dev/null +++ b/packages/ensjs/src/actions/public/ccip.test.ts @@ -0,0 +1,41 @@ +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' +import { describe, expect, it, vi } from 'vitest' +import { addEnsL1Contracts } from '../../index.js' +import { getRecords } from './getRecords.js' + +vi.setConfig({ + testTimeout: 30000, +}) + +const mainnetPublicClient = createPublicClient({ + chain: addEnsL1Contracts(mainnet), + transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), +}) + +describe('CCIP', () => { + describe('getRecords', () => { + it('should return records from a ccip-read name with incompliant resolver', async () => { + const result = await getRecords(mainnetPublicClient, { + name: 'alisha.beam.eco', + texts: ['email', 'description'], + contentHash: true, + coins: ['ltc', '60'], + }) + expect(result).toMatchInlineSnapshot(` + { + "coins": [ + { + "coinType": 60, + "symbol": "eth", + "value": "0x3A8C8D374AD15fE43E6239F6C694bff9Ee4CBbbf", + }, + ], + "contentHash": null, + "resolverAddress": "0x244fE34a508E16E12A221332f63E3a741C759D76", + "texts": [], + } + `) + }) + }) +}) diff --git a/packages/ensjs/src/functions/public/getAbiRecord.test.ts b/packages/ensjs/src/actions/public/getAbiRecord.test.ts similarity index 59% rename from packages/ensjs/src/functions/public/getAbiRecord.test.ts rename to packages/ensjs/src/actions/public/getAbiRecord.test.ts index 5438b8356..cfe3174c5 100644 --- a/packages/ensjs/src/functions/public/getAbiRecord.test.ts +++ b/packages/ensjs/src/actions/public/getAbiRecord.test.ts @@ -1,9 +1,7 @@ -import { RawContractError } from 'viem' -import { describe, expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' +import { assert, describe, expect, it } from 'vitest' import { publicClient } from '../../test/addTestContracts.js' import { generateSupportedContentTypes } from '../../utils/generateSupportedContentTypes.js' -import getAbiRecord from './getAbiRecord.js' +import { getAbiRecord } from './getAbiRecord.js' const dummyABI = [ { @@ -129,45 +127,37 @@ describe('getAbiRecord()', () => { const result = await getAbiRecord(publicClient, { name: 'with-type-1-abi.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(1) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(1) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return object for type 2 ABI', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-2-abi.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(2) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(2) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return object for type 4 ABI', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-4-abi.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(4) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(4) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return unresolved URI for type 8 ABI', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-8-abi.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(8) - expect(result.decoded).toBe(true) - expect(result.abi).toBe('https://example.com') - } + assert.isNotNull(result) + expect(result?.contentType).toBe(8) + expect(result?.decoded).toBe(true) + expect(result?.abi).toBe('https://example.com') }) it('should return null if unsupported contentType (returned as 0x from contract)', async () => { const result = await getAbiRecord(publicClient, { @@ -179,89 +169,55 @@ describe('getAbiRecord()', () => { const result = await getAbiRecord(publicClient, { name: 'with-type-all-abi.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(1) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(1) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return the result of type 2 abi if supportedContentTypes is zlib', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-all-abi.eth', supportedContentTypes: generateSupportedContentTypes('zlib'), }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(2) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(2) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return the result of type 4 abi if supportedContentTypes is cbor', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-all-abi.eth', supportedContentTypes: generateSupportedContentTypes('cbor'), }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(4) - expect(result.decoded).toBe(true) - expect(result.abi).toMatchObject(dummyABI) - } + assert.isNotNull(result) + expect(result?.contentType).toBe(4) + expect(result?.decoded).toBe(true) + expect(result?.abi).toMatchObject(dummyABI) }) it('should return the result of type 8 abi if supportedContentTypes is uri', async () => { const result = await getAbiRecord(publicClient, { name: 'with-type-all-abi.eth', supportedContentTypes: generateSupportedContentTypes('uri'), }) - expect(result).toBeTruthy() - if (result) { - expect(result.contentType).toBe(8) - expect(result.decoded).toBe(true) - expect(result.abi).toBe('https://example.com') - } + assert.isNotNull(result) + expect(result?.contentType).toBe(8) + expect(result?.decoded).toBe(true) + expect(result?.abi).toBe('https://example.com') }) it('should return null on error when strict is false', async () => { - await expect( - getAbiRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() + const result = await getAbiRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + }) + expect(result).toBeNull() }) it('should throw on error when strict is true', async () => { + // The name is neither registered on v1 nor reserved on v2, so the Universal + // Resolver reverts with ResolverNotFound; strict mode surfaces it. await expect( - getAbiRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] - `) + getAbiRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + strict: true, + }), + ).rejects.toThrowError('ResolverNotFound') }) }) diff --git a/packages/ensjs/src/actions/public/getAbiRecord.ts b/packages/ensjs/src/actions/public/getAbiRecord.ts new file mode 100644 index 000000000..1aafffd49 --- /dev/null +++ b/packages/ensjs/src/actions/public/getAbiRecord.ts @@ -0,0 +1,72 @@ +import type { Chain, EncodeFunctionDataErrorType } from 'viem' +import { encodeFunctionData, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { Prettify } from '../../types/index.js' +import type { ExcludeTE } from '../../types/internal.js' +import { + type DecodeAbiResultErrorType, + type DecodeAbiResultParameters, + type DecodeAbiResultReturnType, + decodeAbiResult, + type GetAbiParametersErrorType, + type GetAbiParametersParameters, + getAbiParameters, +} from '../../utils/coders/getAbi.js' +import { + type ResolveNameDataErrorType, + resolveNameData, +} from './resolveNameData.js' + +export type GetAbiRecordParameters = Prettify< + GetAbiParametersParameters & + DecodeAbiResultParameters & { + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] + } +> + +export type GetAbiRecordReturnType = DecodeAbiResultReturnType | null + +export type GetAbiRecordErrorType = + | ResolveNameDataErrorType + | EncodeFunctionDataErrorType + | GetAbiParametersErrorType + | DecodeAbiResultErrorType + +/** + * Gets the ABI record for a name + * @param client - {@link Client} + * @param parameters - {@link GetAbiRecordParameters} + * @returns ABI record for the name, or `null` if not found. {@link GetAbiRecordReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getAbiRecord } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getAbiRecord(client, { name: 'ens.eth' }) + * // TODO: real example + */ +export async function getAbiRecord( + client: RequireClientContracts, + { gatewayUrls, name, supportedContentTypes, strict }: GetAbiRecordParameters, +): Promise { + const resolveNameDataAction = getAction( + client as ExcludeTE, + resolveNameData, + 'resolveNameData', + ) + const result = await resolveNameDataAction({ + name, + data: encodeFunctionData(getAbiParameters({ name, supportedContentTypes })), + gatewayUrls, + strict, + }) + if (!result) return null + return decodeAbiResult(result.resolvedData, { strict }) +} diff --git a/packages/ensjs/src/functions/public/getAddressRecord.test.ts b/packages/ensjs/src/actions/public/getAddressRecord.test.ts similarity index 64% rename from packages/ensjs/src/functions/public/getAddressRecord.test.ts rename to packages/ensjs/src/actions/public/getAddressRecord.test.ts index 1bd5a4a3e..a426b2d8a 100644 --- a/packages/ensjs/src/functions/public/getAddressRecord.test.ts +++ b/packages/ensjs/src/actions/public/getAddressRecord.test.ts @@ -1,13 +1,12 @@ -import { http, RawContractError, createPublicClient } from 'viem' +import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { describe, expect, it } from 'vitest' -import { addEnsContracts } from '../../contracts/addEnsContracts.js' -import type { ClientWithEns } from '../../contracts/consts.js' +import { addEnsL1Contracts } from '../../index.js' import { publicClient } from '../../test/addTestContracts.js' -import getAddressRecord from './getAddressRecord.js' +import { getAddressRecord } from './getAddressRecord.js' const mainnetPublicClient = createPublicClient({ - chain: addEnsContracts(mainnet), + chain: addEnsL1Contracts(mainnet), transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), }) @@ -18,8 +17,8 @@ describe('getAddressRecord()', () => { }) expect(result).toMatchInlineSnapshot(` { - "id": 60, - "name": "eth", + "coinType": 60, + "symbol": "eth", "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", } `) @@ -31,8 +30,8 @@ describe('getAddressRecord()', () => { }) expect(result).toMatchInlineSnapshot(` { - "id": 61, - "name": "etcLegacy", + "coinType": 61, + "symbol": "etcLegacy", "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", } `) @@ -44,8 +43,8 @@ describe('getAddressRecord()', () => { }) expect(result).toMatchInlineSnapshot(` { - "id": 61, - "name": "etcLegacy", + "coinType": 61, + "symbol": "etcLegacy", "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", } `) @@ -57,8 +56,8 @@ describe('getAddressRecord()', () => { }) expect(result).toMatchInlineSnapshot(` { - "id": 61, - "name": "etcLegacy", + "coinType": 61, + "symbol": "etcLegacy", "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", } `) @@ -76,52 +75,26 @@ describe('getAddressRecord()', () => { }) expect(result).toMatchInlineSnapshot(` { - "id": 60, - "name": "eth", + "coinType": 60, + "symbol": "eth", "value": "0xde9ba5F62D6047C4a9cCF24455AA733cCC5B8F41", } `) }) it('should return null on error when strict is false', async () => { - await expect( - getAddressRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() + const result = await getAddressRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + }) + expect(result).toBeNull() }) it('should throw on error when strict is true', async () => { + // The name is not registered on v1 nor reserved on v2, so the Universal + // Resolver reverts with ResolverNotFound; strict mode surfaces it. await expect( - getAddressRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] - `) + getAddressRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + strict: true, + }), + ).rejects.toThrowError('ResolverNotFound') }) }) diff --git a/packages/ensjs/src/actions/public/getAddressRecord.ts b/packages/ensjs/src/actions/public/getAddressRecord.ts new file mode 100644 index 000000000..812850bbf --- /dev/null +++ b/packages/ensjs/src/actions/public/getAddressRecord.ts @@ -0,0 +1,87 @@ +import type { Chain, EncodeFunctionDataErrorType } from 'viem' +import { encodeFunctionData, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { Prettify } from '../../types/index.js' +import type { ExcludeTE } from '../../types/internal.js' +import { + type DecodeAddressResultErrorType, + type DecodeAddressResultParameters, + type DecodeAddressResultReturnType, + decodeAddressResult, + type GetAddressParametersErrorType, + type GetAddressParametersParameters, + getAddressParameters, +} from '../../utils/coders/getAddress.js' +import { + type ResolveNameDataErrorType, + resolveNameData, +} from './resolveNameData.js' + +export type GetAddressRecordParameters< + coin extends string | number | undefined = undefined, +> = Prettify< + GetAddressParametersParameters & + DecodeAddressResultParameters & { + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] + } +> + +export type GetAddressRecordReturnType< + coin extends string | number | undefined = undefined, +> = DecodeAddressResultReturnType + +export type GetAddressRecordErrorType = + | ResolveNameDataErrorType + | EncodeFunctionDataErrorType + | GetAddressParametersErrorType + | DecodeAddressResultErrorType + +/** + * Gets an address record for a name and specified coin + * @param client - {@link Client} + * @param parameters - {@link GetAddressRecordParameters} + * @returns Coin value object, or `null` if not found. {@link GetAddressRecordReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getAddressRecord } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getAddressRecord(client, { name: 'ens.eth', coin: 'ETH' }) + * // { id: 60, name: 'ETH , value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' } + */ +export async function getAddressRecord< + chain extends Chain, + coin extends string | number | undefined = undefined, +>( + client: RequireClientContracts, + { + gatewayUrls, + strict, + name, + bypassFormat, + coin, + }: GetAddressRecordParameters, +): Promise> { + const resolveNameDataAction = getAction( + client as ExcludeTE, + resolveNameData, + 'resolveNameData', + ) + const result = await resolveNameDataAction({ + name, + data: encodeFunctionData( + getAddressParameters({ name, bypassFormat, coin }), + ), + gatewayUrls, + strict, + }) + if (!result) return null + return decodeAddressResult(result.resolvedData, { strict, coin }) +} diff --git a/packages/ensjs/src/actions/public/getContentHashRecord.test.ts b/packages/ensjs/src/actions/public/getContentHashRecord.test.ts new file mode 100644 index 000000000..53682a8ba --- /dev/null +++ b/packages/ensjs/src/actions/public/getContentHashRecord.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest' +import { publicClient } from '../../test/addTestContracts.js' +import { getContentHashRecord } from './getContentHashRecord.js' + +describe('getContentHashRecord', () => { + it('should return null for a non-existent name', async () => { + const result = await getContentHashRecord(publicClient, { + name: 'test123123cool.eth', + }) + expect(result).toBeNull() + }) + it('should return null for a name with no contenthash record', async () => { + const result = await getContentHashRecord(publicClient, { + name: 'with-profile.eth', + }) + expect(result).toBeNull() + }) + it('should return the contenthash for a name with the record set', async () => { + const result = await getContentHashRecord(publicClient, { + name: 'with-contenthash.eth', + }) + expect(result).toMatchInlineSnapshot(` + { + "decoded": "bafybeiac5y4sk47yumr2xxqmaryqs4pszld2r2yaxeru666yf6pgmtv77q", + "protocolType": "ipfs", + } + `) + }) + it('should return null on error when strict is false', async () => { + const result = await getContentHashRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + strict: false, + }) + expect(result).toBeNull() + }) + it('should throw on error when strict is true', async () => { + // The name is neither registered on v1 nor reserved on v2, so the Universal + // Resolver reverts with ResolverNotFound; strict mode surfaces it. + await expect( + getContentHashRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + strict: true, + }), + ).rejects.toThrowError('ResolverNotFound') + }) +}) diff --git a/packages/ensjs/src/actions/public/getContentHashRecord.ts b/packages/ensjs/src/actions/public/getContentHashRecord.ts new file mode 100644 index 000000000..21f6a9b55 --- /dev/null +++ b/packages/ensjs/src/actions/public/getContentHashRecord.ts @@ -0,0 +1,70 @@ +import type { Chain, EncodeFunctionDataErrorType } from 'viem' +import { encodeFunctionData, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { Prettify } from '../../types/index.js' +import type { ExcludeTE } from '../../types/internal.js' +import { + type DecodeContentHashResultErrorType, + decodeContentHashResult, + type GetContentHashErrorType, + type GetContentHashParameters, + type GetContentHashReturnType, + getContentHashParameters, +} from '../../utils/coders/getContentHash.js' +import { + type ResolveNameDataErrorType, + resolveNameData, +} from './resolveNameData.js' + +export type GetContentHashRecordParameters = Prettify< + GetContentHashParameters & { + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] + } +> + +export type GetContentHashRecordReturnType = GetContentHashReturnType + +export type GetContentHashRecordErrorType = + | GetContentHashErrorType + | ResolveNameDataErrorType + | EncodeFunctionDataErrorType + | DecodeContentHashResultErrorType + +/** + * Gets the content hash record for a name + * @param client - {@link Client} + * @param parameters - {@link GetContentHashRecordParameters} + * @returns Content hash object, or `null` if not found. {@link GetContentHashRecordReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getContentHashRecord } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getContentHashRecord(client, { name: 'ens.eth' }) + * // { protocolType: 'ipfs', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } + */ +export async function getContentHashRecord( + client: RequireClientContracts, + { gatewayUrls, name, strict }: GetContentHashRecordParameters, +): Promise { + const resolveNameDataAction = getAction( + client as ExcludeTE, + resolveNameData, + 'resolveNameData', + ) + const result = await resolveNameDataAction({ + name, + data: encodeFunctionData(getContentHashParameters({ name })), + gatewayUrls, + strict, + }) + if (!result) return null + return decodeContentHashResult(result.resolvedData, { strict }) +} diff --git a/packages/ensjs/src/actions/public/getCredentials.ts b/packages/ensjs/src/actions/public/getCredentials.ts new file mode 100644 index 000000000..b49fd4d0b --- /dev/null +++ b/packages/ensjs/src/actions/public/getCredentials.ts @@ -0,0 +1,65 @@ +import type { Chain } from 'viem' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Prettify } from '../../types/index.js' +import { + type GetTextRecordErrorType, + type GetTextRecordParameters, + getTextRecord, +} from './getTextRecord.js' + +export type GetCredentialsParameters = Prettify< + Omit +> + +export type GetCredentialsReturnType = ExternalCredential[] | null + +export type GetCredentialsErrorType = GetTextRecordErrorType | ErrorType + +export type ExternalCredential = { + url: string +} + +const parseCredentials = (credentials: string[]) => { + const externalCredentials: ExternalCredential[] = [] + for (const credential of credentials) { + if (URL.canParse(credential)) externalCredentials.push({ url: credential }) + } + + return externalCredentials +} + +/** + * Gets credentials for a name. + * @param client - {@link Client} + * @param parameters - {@link GetCredentialsParameters} + * @returns Credentials, or null if none are found. {@link GetCredentialsReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getCredentials } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getCredentials(client, { name: 'ens.eth' }) + * // [{ url: 'https://example.com' }] + */ +export async function getCredentials( + client: RequireClientContracts, + { gatewayUrls, strict, name }: GetCredentialsParameters, +): Promise { + const result = await getTextRecord(client, { + name, + key: 'verifications', + gatewayUrls, + strict, + }) + if (!result) return null + + const credential = JSON.parse(result) as string[] + return parseCredentials(credential) +} diff --git a/packages/ensjs/src/actions/public/getName.test.ts b/packages/ensjs/src/actions/public/getName.test.ts new file mode 100644 index 000000000..7b2f95471 --- /dev/null +++ b/packages/ensjs/src/actions/public/getName.test.ts @@ -0,0 +1,246 @@ +import { universalResolverReverseSnippet } from '@ensdomains/ensjs-abi/universalResolver' +import { + type Address, + BaseError, + bytesToHex, + type Client, + ContractFunctionRevertedError, + encodeErrorResult, + type Hex, + type PublicClient, + type Transport, +} from 'viem' +import { mainnet } from 'viem/chains' +import { packetToBytes } from 'viem/ens' +import { + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + type MockedFunction, + vi, +} from 'vitest' +import type { ChainWithContracts } from '../../clients/shared.js' +import { addEnsL1Contracts } from '../../index.js' +import { + deploymentAddresses, + publicClient, + testClient, + waitForTransaction, + walletClient, +} from '../../test/addTestContracts.js' +import { createSubname } from '../wallet/createSubname.js' +import { setAddressRecord } from '../wallet/setAddressRecord.js' +import { setPrimaryName } from '../wallet/setPrimaryName.js' +import { getName } from './getName.js' + +let snapshot: Hex +let accounts: Address[] + +const mockReadContract = vi.fn() as MockedFunction +const mockClient = { + readContract: mockReadContract, + chain: addEnsL1Contracts(mainnet), +} as unknown as Client> + +beforeAll(async () => { + accounts = await walletClient.getAddresses() +}) + +beforeEach(async () => { + snapshot = await testClient.snapshot() + vi.resetAllMocks() +}) + +afterEach(async () => { + await testClient.revert({ id: snapshot }) +}) + +describe('getName', () => { + it('should get a primary name from an address', async () => { + const result = await getName(publicClient, { + address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', + }) + // Reverse resolution runs through the Universal Resolver, which reports the + // ENSv1 mirror resolver for these v1 names (forward and reverse). + expect(result).toMatchInlineSnapshot(` + { + "match": true, + "name": "with-profile.eth", + "normalized": true, + "resolverAddress": "${deploymentAddresses.ENSV1Resolver}", + "reverseResolverAddress": "${deploymentAddresses.ENSV1Resolver}", + } + `) + }) + it('should return null for an address with no primary name', async () => { + const result = await getName(publicClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + }) + expect(result).toBeNull() + }) + it('should return null for a name with no forward resolution when allowMismatch is false', async () => { + const tx = await setPrimaryName(walletClient, { + name: 'with-profile.eth', + account: accounts[0], + }) + await waitForTransaction(tx) + + const result = await getName(publicClient, { + address: accounts[0], + }) + expect(result).toBeNull() + }) + it('should return with a false match for a name with no forward resolution when allowMismatch is true', async () => { + const tx = await setPrimaryName(walletClient, { + name: 'with-profile.eth', + account: accounts[0], + }) + await waitForTransaction(tx) + + const result = await getName(publicClient, { + address: accounts[0], + allowMismatch: true, + }) + expect(result).toMatchInlineSnapshot(` + { + "match": false, + "name": "with-profile.eth", + "normalized": true, + "resolverAddress": null, + "reverseResolverAddress": null, + } + `) + }) + it('should return null on known error when strict is false', async () => { + mockReadContract.mockImplementation(async () => { + throw new ContractFunctionRevertedError({ + abi: universalResolverReverseSnippet, + functionName: 'reverse', + data: encodeErrorResult({ + abi: universalResolverReverseSnippet, + errorName: 'ResolverNotFound', + args: [bytesToHex(packetToBytes('test.eth'))], + }), + }) + }) + const result = await getName(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + }) + expect(result).toBeNull() + }) + it('should throw on error when strict is true', async () => { + const error = new ContractFunctionRevertedError({ + abi: universalResolverReverseSnippet, + functionName: 'reverse', + data: encodeErrorResult({ + abi: universalResolverReverseSnippet, + errorName: 'ResolverNotFound', + args: [bytesToHex(packetToBytes('test.eth'))], + }), + }) + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + getName(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + strict: true, + }), + ).rejects.toThrowError(error) + }) + it('should throw on unknown error when strict is false', async () => { + const error = new BaseError('Unknown error') + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + getName(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + strict: false, + }), + ).rejects.toThrowError(error) + }) + it('should throw on unknown error when strict is true', async () => { + const error = new BaseError('Unknown error') + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + getName(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + strict: true, + }), + ).rejects.toThrowError(error) + }) + it('returns null for a name that is not normalized', async () => { + const tx1 = await createSubname(walletClient, { + name: 'suB.with-profile.eth', + contract: 'registry', + owner: accounts[0], + resolverAddress: deploymentAddresses.PublicResolver, + account: accounts[2], + }) + await waitForTransaction(tx1) + const tx2 = await setAddressRecord(walletClient, { + name: 'suB.with-profile.eth', + coin: 'eth', + resolverAddress: deploymentAddresses.PublicResolver, + value: accounts[0], + account: accounts[0], + }) + await waitForTransaction(tx2) + const tx3 = await setPrimaryName(walletClient, { + name: 'suB.with-profile.eth', + account: accounts[0], + }) + await waitForTransaction(tx3) + + const result = await getName(publicClient, { + address: accounts[0], + }) + + expect(result).toBeNull() + }) + + it('returns unnormalized name when allowUnnormalized is true', async () => { + const tx1 = await createSubname(walletClient, { + name: 'suB.with-profile.eth', + contract: 'registry', + owner: accounts[0], + resolverAddress: deploymentAddresses.PublicResolver, + account: accounts[2], + }) + await waitForTransaction(tx1) + const tx2 = await setAddressRecord(walletClient, { + name: 'suB.with-profile.eth', + coin: 'eth', + resolverAddress: deploymentAddresses.PublicResolver, + value: accounts[0], + account: accounts[0], + }) + await waitForTransaction(tx2) + const tx3 = await setPrimaryName(walletClient, { + name: 'suB.with-profile.eth', + account: accounts[0], + }) + await waitForTransaction(tx3) + + const result = await getName(publicClient, { + address: accounts[0], + allowUnnormalized: true, + }) + + expect(result).toMatchInlineSnapshot(` + { + "match": true, + "name": "suB.with-profile.eth", + "normalized": false, + "resolverAddress": "${deploymentAddresses.ENSV1Resolver}", + "reverseResolverAddress": "${deploymentAddresses.ENSV1Resolver}", + } + `) + }) +}) diff --git a/packages/ensjs/src/actions/public/getName.ts b/packages/ensjs/src/actions/public/getName.ts new file mode 100644 index 000000000..6c84fe0c1 --- /dev/null +++ b/packages/ensjs/src/actions/public/getName.ts @@ -0,0 +1,162 @@ +import { evmChainIdToCoinType } from '@ensdomains/address-encoder/utils' +import { + universalResolverReverseSnippet, + universalResolverReverseWithGatewaysSnippet, +} from '@ensdomains/ensjs-abi/universalResolver' +import type { + Address, + Chain, + GetAddressErrorType, + GetChainContractAddressErrorType, + ReadContractErrorType, + ToHexErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import type { PacketToBytesErrorType } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { isNullUniversalResolverError } from '../../utils/errors/isNullUniversalResolverError.js' +import { parseReverseAddressMismatchError } from '../../utils/errors/parseReverseAddressMismatchError.js' +import { + type NormalizeErrorType, + normalize, +} from '../../utils/name/normalize.js' +import { nullableAddress } from '../../utils/nullableAddress.js' + +type GetNameCoinTypeParameters = { + /** Coin type to use for reverse resolution */ + coinType?: number + chainId?: never +} + +type GetNameChainIdParameters = { + /** Chain ID to use for reverse resolution */ + chainId?: number + coinType?: never +} + +export type GetNameParameters = { + /** Address to get name for */ + address: Address + /** Whether or not to allow mismatched forward resolution */ + allowMismatch?: boolean + /** Whether or not to allow unnormalized name results (UNSAFE) */ + allowUnnormalized?: boolean + /** Whether or not to throw decoding errors */ + strict?: boolean + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] +} & (GetNameCoinTypeParameters | GetNameChainIdParameters) + +export type GetNameReturnType = { + /** Primary name for address */ + name: string + /** Indicates if forward resolution for name matches address */ + match: boolean + /** Indicates if name is normalized */ + normalized: boolean + /** Resolver address for reverse node */ + reverseResolverAddress: Address | null + /** Resolver address for resolved name */ + resolverAddress: Address | null +} | null + +export type GetNameErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | ToHexErrorType + | PacketToBytesErrorType + | GetAddressErrorType + | NormalizeErrorType + | ErrorType + +/** + * Gets the primary name for an address + * @param client - {@link Client} + * @param parameters - {@link GetNameParameters} + * @returns Name data object, or `null` if no primary name is set. {@link GetNameReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getName } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getName(client, { address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' }) + * // { name: 'nick.eth', match: true, reverseResolverAddress: '0xa2c122be93b0074270ebee7f6b7292c7deb45047', resolverAddress: '0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41' } + */ +export async function getName( + client: RequireClientContracts, + { + address, + allowUnnormalized, + strict, + gatewayUrls, + chainId, + coinType, + allowMismatch, + }: GetNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + const readContractAction = getAction(client, readContract, 'readContract') + + const parameters = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }), + abi: universalResolverReverseSnippet, + functionName: 'reverseWithGateways', + args: [address, chainId ? evmChainIdToCoinType(chainId) : coinType || 60n], + } as const + + let unnormalisedName: string | null = null + let resolverAddress: Address | null = null + let reverseResolverAddress: Address | null = null + let match = true + + try { + const [name, resolver, reverseResolver] = Array.isArray(gatewayUrls) + ? await readContractAction({ + ...parameters, + abi: universalResolverReverseWithGatewaysSnippet, + args: [...parameters.args, gatewayUrls] as const, + }) + : await readContractAction({ ...parameters, functionName: 'reverse' }) + if (!name) return null + unnormalisedName = name + resolverAddress = resolver + reverseResolverAddress = reverseResolver + } catch (error) { + if (strict) throw error + if (isNullUniversalResolverError(error)) return null + const errorArgs = parseReverseAddressMismatchError(error) + if (Array.isArray(errorArgs)) { + unnormalisedName = errorArgs[0] + if (allowMismatch) { + match = false + } else { + return null + } + } else throw error + } + + const normalizedName = normalize(unnormalisedName!) + const nameMatch = unnormalisedName === normalizedName + if (!nameMatch && !allowUnnormalized) return null + + return { + name: unnormalisedName!, + match, + normalized: nameMatch, + reverseResolverAddress: nullableAddress(reverseResolverAddress!), + resolverAddress: nullableAddress(resolverAddress!), + } +} diff --git a/packages/ensjs/src/actions/public/getNames.test.ts b/packages/ensjs/src/actions/public/getNames.test.ts new file mode 100644 index 000000000..8fec176be --- /dev/null +++ b/packages/ensjs/src/actions/public/getNames.test.ts @@ -0,0 +1,86 @@ +import { defaultReverseResolverResolveNamesSnippet } from '@ensdomains/ensjs-abi/defaultReverseResolver' +import type { Client, PublicClient, Transport } from 'viem' +import { mainnet } from 'viem/chains' +import { + beforeEach, + describe, + expect, + it, + type MockedFunction, + vi, +} from 'vitest' +import type { ChainWithContracts } from '../../clients/shared.js' +import { addEnsL1Contracts } from '../../index.js' +import { getNames } from './getNames.js' + +const mockReadContract = vi.fn() as MockedFunction +const mockClient = { + readContract: mockReadContract, + chain: addEnsL1Contracts(mainnet), +} as unknown as Client< + Transport, + ChainWithContracts<'ensDefaultReverseResolver'> +> + +// Arbitrary input addresses — `readContract` is mocked, so these are never +// resolved on-chain; they only need to be valid addresses passed through to the +// `resolveNames` call. +const addressA = '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' as const +const addressB = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' as const + +beforeEach(() => { + vi.resetAllMocks() +}) + +describe('getNames', () => { + it('returns an empty array without reading the chain when given no addresses', async () => { + const result = await getNames(mockClient, { addresses: [] }) + expect(result).toEqual([]) + expect(mockReadContract).not.toHaveBeenCalled() + }) + + it('reads `resolveNames` from the DefaultReverseResolver with the given addresses', async () => { + mockReadContract.mockResolvedValueOnce(['nick.eth', '']) + + await getNames(mockClient, { addresses: [addressA, addressB] }) + + expect(mockReadContract).toHaveBeenCalledTimes(1) + expect(mockReadContract).toHaveBeenCalledWith( + expect.objectContaining({ + abi: defaultReverseResolverResolveNamesSnippet, + functionName: 'resolveNames', + args: [[addressA, addressB]], + }), + ) + }) + + it('returns names aligned to the input addresses', async () => { + mockReadContract.mockResolvedValueOnce(['nick.eth', 'with-profile.eth']) + + const result = await getNames(mockClient, { + addresses: [addressA, addressB], + }) + + expect(result).toEqual(['nick.eth', 'with-profile.eth']) + }) + + it('maps empty-string results to null', async () => { + mockReadContract.mockResolvedValueOnce(['nick.eth', '', 'with-profile.eth']) + + const result = await getNames(mockClient, { + addresses: [addressA, addressB, addressA], + }) + + expect(result).toEqual(['nick.eth', null, 'with-profile.eth']) + }) + + it('returns all nulls when no addresses have a primary name', async () => { + mockReadContract.mockResolvedValueOnce(['', '']) + + const result = await getNames(mockClient, { + addresses: [addressA, addressB], + }) + + expect(result).toEqual([null, null]) + }) +}) diff --git a/packages/ensjs/src/actions/public/getNames.ts b/packages/ensjs/src/actions/public/getNames.ts new file mode 100644 index 000000000..19718aa8a --- /dev/null +++ b/packages/ensjs/src/actions/public/getNames.ts @@ -0,0 +1,84 @@ +import { defaultReverseResolverResolveNamesSnippet } from '@ensdomains/ensjs-abi/defaultReverseResolver' +import type { + Address, + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' + +export type GetNamesParameters = { + /** Addresses to get primary names for */ + addresses: readonly Address[] +} + +export type GetNamesReturnType = (string | null)[] + +export type GetNamesErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | ErrorType + +/** + * Gets the ENSIP-19 default (chain-agnostic) primary names for a batch of + * addresses by reading the `DefaultReverseResolver`'s `resolveNames`. + * + * This is the ENSIP-19 `default.reverse` resolver from `ens-contracts` (not a + * v2-specific contract — ENSv2 has no reverse resolver of its own). It is read + * directly because {@link getName} goes through the Universal Resolver's + * per-coin reverse lookup (defaulting to coinType `60`, i.e. `addr.reverse`), + * which does not surface the chain-agnostic `default.reverse` records this + * resolver holds. + * + * Note: results are NOT forward-verified. Callers that need a verified primary + * name should check forward resolution themselves. + * + * @param client - {@link Client} + * @param parameters - {@link GetNamesParameters} + * @returns Names aligned to `addresses` (`null` where no name is set). {@link GetNamesReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getNames } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getNames(client, { + * addresses: ['0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5'], + * }) + * // ['nick.eth'] + */ +export async function getNames( + client: RequireClientContracts, + { addresses }: GetNamesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + if (addresses.length === 0) return [] + + const resolverAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensDefaultReverseResolver', + }) + const readContractAction = getAction(client, readContract, 'readContract') + + const names = await readContractAction({ + address: resolverAddress, + abi: defaultReverseResolverResolveNamesSnippet, + functionName: 'resolveNames', + args: [addresses], + }) + + return names.map((name) => (name === '' ? null : name)) +} diff --git a/packages/ensjs/src/actions/public/getRecords.test.ts b/packages/ensjs/src/actions/public/getRecords.test.ts new file mode 100644 index 000000000..c5fdde1c8 --- /dev/null +++ b/packages/ensjs/src/actions/public/getRecords.test.ts @@ -0,0 +1,91 @@ +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' +import { describe, expect, it } from 'vitest' +import { addEnsL1Contracts } from '../../index.js' +import { publicClient } from '../../test/addTestContracts.js' +import { getRecords } from './getRecords.js' + +const mainnetPublicClient = createPublicClient({ + chain: addEnsL1Contracts(mainnet), + transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), +}) + +describe('getRecords()', () => { + it('works', async () => { + const result = await getRecords(publicClient, { + name: 'with-profile.eth', + texts: ['description', 'url'], + coins: ['60', 'etcLegacy', '0'], + }) + expect(result).toMatchInlineSnapshot(` + { + "coins": [ + { + "coinType": 60, + "symbol": "eth", + "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + }, + { + "coinType": 61, + "symbol": "etcLegacy", + "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + }, + ], + "resolverAddress": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", + "texts": [ + { + "key": "description", + "value": "Hello2", + }, + ], + } + `) + }) + it('works with oldest resolver', async () => { + const result = await getRecords(publicClient, { + name: 'with-oldest-resolver.eth', + texts: ['description', 'url'], + coins: ['60', 'etcLegacy', '0'], + }) + expect(result).toMatchInlineSnapshot(` + { + "coins": [], + "resolverAddress": "0x0000000000000000000000000000000000000000", + "texts": [], + } + `) + }) + it('works with oldest resolver - jessesum.eth', async () => { + const result = await getRecords(mainnetPublicClient, { + name: 'jessesum.eth', + texts: ['description', 'url'], + coins: ['60', 'etcLegacy', '0'], + }) + expect(result).toMatchInlineSnapshot(` + { + "coins": [ + { + "coinType": 60, + "symbol": "eth", + "value": "0x8c4Eb6988A199DAbcae0Ce31052b3f3aC591787e", + }, + ], + "resolverAddress": "0x1da022710dF5002339274AaDEe8D58218e9D6AB5", + "texts": [], + } + `) + }) + + it('returns null results when known resolver error', async () => { + const result = await getRecords(publicClient, { + name: 'thisnamedoesnotexist.eth', + coins: [60], + }) + expect(result).toMatchInlineSnapshot(` + { + "coins": [], + "resolverAddress": "0x0000000000000000000000000000000000000000", + } + `) + }) +}) diff --git a/packages/ensjs/src/actions/public/getRecords.ts b/packages/ensjs/src/actions/public/getRecords.ts new file mode 100644 index 000000000..344298695 --- /dev/null +++ b/packages/ensjs/src/actions/public/getRecords.ts @@ -0,0 +1,408 @@ +import { + type Abi, + type Address, + type Chain, + type EncodeFunctionDataErrorType, + encodeFunctionData, + type Hex, + type MulticallErrorType, + zeroAddress, +} from 'viem' +import { multicall } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import type { DecodedAddr, DecodedText, Prettify } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type DecodeAbiResultErrorType, + type DecodeAbiResultFromPrimitiveTypesErrorType, + type DecodeAbiResultReturnType, + decodeAbiResult, + decodeAbiResultFromPrimitiveTypes, + type GetAbiParametersErrorType, + getAbiParameters, +} from '../../utils/coders/getAbi.js' +import { + type DecodeAddressResultErrorType, + type DecodeAddressResultFromPrimitiveTypesErrorType, + decodeAddressResult, + decodeAddressResultFromPrimitiveTypes, + type GetAddressParametersErrorType, + getAddressParameters, +} from '../../utils/coders/getAddress.js' +import { + type DecodeContentHashResultErrorType, + type DecodeContentHashResultFromPrimitiveTypesErrorType, + decodeContentHashResult, + decodeContentHashResultFromPrimitiveTypes, + type GetContentHashErrorType, + type GetContentHashReturnType, + getContentHashParameters, +} from '../../utils/coders/getContentHash.js' +import { + type DecodeTextResultErrorType, + type DecodeTextResultFromPrimitiveTypesErrorType, + decodeTextResult, + decodeTextResultFromPrimitiveTypes, + type GetTextParametersErrorType, + getTextParameters, +} from '../../utils/coders/getText.js' +import { + type ResolveNameDataErrorType, + resolveNameData, +} from './resolveNameData.js' + +export type GetRecordsParameters< + texts extends readonly string[] | undefined = readonly string[], + coins extends readonly (string | number)[] | undefined = readonly ( + | string + | number + )[], + contentHash extends boolean | undefined = true, + abi extends boolean | undefined = true, +> = { + /** Name to get records for */ + name: string + /** Text record key array */ + texts?: texts + /** Coin record id/symbol array */ + coins?: coins + /** If true, will fetch content hash */ + contentHash?: contentHash + /** If true, will fetch ABI */ + abi?: abi + /** Optional specific resolver address, for fallback or for all results */ + resolver?: { + /** Resolver address */ + address: Address + /** If true, will only use resolver if main fetch fails */ + fallbackOnly?: boolean + } + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] + + /** Don't throw on invalid coinTypes */ + ignoreInvalidCoinTypes?: boolean +} + +type WithContentHashResult = { + /** Retrieved content hash record for name */ + contentHash: GetContentHashReturnType +} + +type WithAbiResult = { + /** Retrieved ABI record for name */ + abi: DecodeAbiResultReturnType +} + +type WithTextsResult = { + /** Retrieved text records for name */ + texts: DecodedText[] +} + +type WithCoinsResult = { + /** Retrieved coins for name */ + coins: DecodedAddr[] +} + +type CreateCallsErrorType = + | GetTextParametersErrorType + | DecodeTextResultFromPrimitiveTypesErrorType + | DecodeTextResultErrorType + | GetAddressParametersErrorType + | DecodeAddressResultFromPrimitiveTypesErrorType + | DecodeAddressResultErrorType + | GetContentHashErrorType + | DecodeContentHashResultFromPrimitiveTypesErrorType + | DecodeContentHashResultErrorType + | GetAbiParametersErrorType + | DecodeAbiResultFromPrimitiveTypesErrorType + | DecodeAbiResultErrorType + +const createCalls = < + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, +>({ + name, + texts, + coins, + abi, + contentHash, + ignoreInvalidCoinTypes, +}: Pick< + GetRecordsParameters, + 'name' | 'texts' | 'coins' | 'abi' | 'contentHash' | 'ignoreInvalidCoinTypes' +>) => [ + ...(texts ?? []).map( + (text) => + ({ + parameters: getTextParameters({ name, key: text }), + addToResult: ({ + currentResult, + data, + shouldDecodeFromPrimitiveTypes, + }: { + currentResult: GetRecordsReturnType + shouldDecodeFromPrimitiveTypes: shouldDecodeFromPrimitiveTypes + data: shouldDecodeFromPrimitiveTypes extends true ? string : Hex + }) => { + const result = shouldDecodeFromPrimitiveTypes + ? decodeTextResultFromPrimitiveTypes({ decodedData: data }) + : decodeTextResult(data as Hex, { strict: false }) + if (!result) return + + currentResult.texts.push({ key: text, value: result }) + }, + }) as const, + ), + ...(coins ?? []).map( + (coin) => + ({ + parameters: getAddressParameters({ + name, + coin, + ignoreInvalidCoinTypes, + }), + addToResult: ({ + currentResult, + data, + shouldDecodeFromPrimitiveTypes, + }: { + currentResult: GetRecordsReturnType + shouldDecodeFromPrimitiveTypes: shouldDecodeFromPrimitiveTypes + data: Hex + }) => { + try { + const result = shouldDecodeFromPrimitiveTypes + ? decodeAddressResultFromPrimitiveTypes({ + decodedData: data, + coin, + }) + : decodeAddressResult(data, { coin, strict: false }) + + if (!result) return + + currentResult.coins.push(result) + } catch (e) { + console.error(e) + // Don't panic if coming across an unknown coinType + return + } + }, + }) as const, + ), + ...(contentHash + ? ([ + { + parameters: getContentHashParameters({ name }), + addToResult: ({ + currentResult, + data, + shouldDecodeFromPrimitiveTypes, + }: { + currentResult: GetRecordsReturnType + shouldDecodeFromPrimitiveTypes: shouldDecodeFromPrimitiveTypes + data: Hex + }) => { + const result = shouldDecodeFromPrimitiveTypes + ? decodeContentHashResultFromPrimitiveTypes({ decodedData: data }) + : decodeContentHashResult(data, { strict: false }) + if (!result) return + + currentResult.contentHash = result + }, + }, + ] as const) + : []), + ...(abi + ? ([ + { + parameters: getAbiParameters({ name }), + addToResult: async ({ + currentResult, + data, + shouldDecodeFromPrimitiveTypes, + }: { + currentResult: GetRecordsReturnType + shouldDecodeFromPrimitiveTypes: shouldDecodeFromPrimitiveTypes + data: shouldDecodeFromPrimitiveTypes extends true + ? readonly [bigint, Hex] + : Hex + }) => { + const result = shouldDecodeFromPrimitiveTypes + ? await decodeAbiResultFromPrimitiveTypes({ + decodedData: data as readonly [bigint, Hex], + }) + : await decodeAbiResult(data as Hex, { strict: false }) + if (!result) return + + currentResult.abi = result + }, + }, + ] as const) + : []), +] + +const createEmptyResult = < + texts extends readonly string[] | undefined, + coins extends readonly (string | number)[] | undefined, + contentHash extends boolean | undefined, + abi extends boolean | undefined, +>({ + texts, + coins, + abi, + contentHash, +}: Pick< + GetRecordsParameters, + 'texts' | 'coins' | 'abi' | 'contentHash' +>) => + ({ + ...(texts ? { texts: [] as DecodedText[] } : {}), + ...(coins ? { coins: [] as DecodedAddr[] } : {}), + ...(contentHash ? { contentHash: null } : {}), + ...(abi ? { abi: null } : {}), + resolverAddress: zeroAddress as Address, + }) as GetRecordsReturnType + +export type GetRecordsReturnType< + texts extends readonly string[] | undefined = readonly string[], + coins extends readonly (string | number)[] | undefined = readonly ( + | string + | number + )[], + contentHash extends boolean | undefined = true, + abi extends boolean | undefined = true, +> = Prettify< + (contentHash extends true ? WithContentHashResult : {}) & + (abi extends true ? WithAbiResult : {}) & + (texts extends readonly string[] ? WithTextsResult : {}) & + (coins extends readonly (string | number)[] ? WithCoinsResult : {}) & { + /** Resolver address used for fetch */ + resolverAddress: Address + } +> + +export type GetRecordsErrorType = + | CreateCallsErrorType + | MulticallErrorType + | ResolveNameDataErrorType + | EncodeFunctionDataErrorType + | ErrorType + +/** + * Gets arbitrary records for a name + * @param client - {@link Client} + * @param parameters - {@link GetRecordsParameters} + * @returns Records data object. {@link GetRecordsReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getRecords } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getRecords(client, { + * name: 'ens.eth', + * texts: ['com.twitter', 'com.github'], + * coins: ['ETH'], + * contentHash: true, + * }) + * // { texts: [{ key: 'com.twitter', value: 'ensdomains' }, { key: 'com.github', value: 'ensdomains' }], coins: [{ id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }], contentHash: { protocolType: 'ipns', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } } + */ +export async function getRecords< + chain extends Chain, + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, +>( + client: RequireClientContracts, + { + name, + resolver, + texts, + coins, + contentHash, + abi, + gatewayUrls, + ignoreInvalidCoinTypes, + }: GetRecordsParameters, +): Promise> { + ASSERT_NO_TYPE_ERROR(client) + + const calls = createCalls({ + name, + texts, + coins, + contentHash, + abi, + ignoreInvalidCoinTypes, + }) + const currentResult = createEmptyResult({ texts, coins, contentHash, abi }) + + if (resolver?.address && !resolver.fallbackOnly) { + const multicallAction = getAction(client, multicall, 'multicall') + const result = await multicallAction({ + contracts: calls.map((c) => ({ + address: resolver.address, + ...c.parameters, + })), + }) + currentResult.resolverAddress = resolver.address + await Promise.all( + result.map(async (item, i) => { + if (item.status !== 'success') return + + const callItem = calls[i] + await callItem.addToResult({ + currentResult, + data: item.result as never, + shouldDecodeFromPrimitiveTypes: true, + }) + }), + ) + } else { + const resolveNameDataAction = getAction( + client, + resolveNameData, + 'resolveNameData', + ) + const result = await resolveNameDataAction({ + name, + data: calls.map((c) => encodeFunctionData(c?.parameters)), + gatewayUrls, + }) + if (!result) + return currentResult as GetRecordsReturnType< + texts, + coins, + contentHash, + abi + > + + currentResult.resolverAddress = result.resolverAddress + + await Promise.all( + result.resolvedData.map(async (item, i) => { + if (!item.success) return + + const callItem = calls[i] + await callItem.addToResult({ + currentResult, + data: item.returnData, + shouldDecodeFromPrimitiveTypes: false, + }) + }), + ) + } + + return currentResult as GetRecordsReturnType +} diff --git a/packages/ensjs/src/actions/public/getResolver.test.ts b/packages/ensjs/src/actions/public/getResolver.test.ts new file mode 100644 index 000000000..0d2200f88 --- /dev/null +++ b/packages/ensjs/src/actions/public/getResolver.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, +} from '../../test/addTestContracts.js' +import { getResolver } from './getResolver.js' + +describe('getResolver', () => { + it('should find the resolver for a name with a resolver', async () => { + const result = await getResolver(publicClient, { + name: 'with-profile.eth', + }) + // with-profile.eth is a v1 name pre-migrated onto v2 behind the ENSv1 mirror + // resolver; getResolver unwraps the composite mirror to the final, writable + // v1 resolver. + expect(result).toBe(deploymentAddresses.LegacyPublicResolver) + }) +}) diff --git a/packages/ensjs/src/actions/public/getResolver.ts b/packages/ensjs/src/actions/public/getResolver.ts new file mode 100644 index 000000000..fee8c8eb8 --- /dev/null +++ b/packages/ensjs/src/actions/public/getResolver.ts @@ -0,0 +1,106 @@ +import { compositeResolverGetResolver } from '@ensdomains/ensjs-abi/compositeResolver' +import { erc165SupportsInterfaceSnippet } from '@ensdomains/ensjs-abi/erc165' +import { universalResolverFindResolverSnippet } from '@ensdomains/ensjs-abi/universalResolver' +import { + type Address, + type Chain, + type GetChainContractAddressErrorType, + type ReadContractErrorType, + type ToHexErrorType, + toHex, + zeroAddress, +} from 'viem' +import { readContract } from 'viem/actions' +import { type PacketToBytesErrorType, packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' + +// ICompositeResolver interface id (ERC-165). A composite resolver (e.g. the +// ENSv1 mirror resolver the Universal Resolver reports for unmigrated v1 +// names) delegates to an underlying resolver, exposed via `getResolver(name)`. +const COMPOSITE_RESOLVER_INTERFACE_ID = '0xeea330f9' + +export type GetResolverParameters = { + /** Name to get resolver for */ + name: string +} + +export type GetResolverReturnType = Address | null + +export type GetResolverErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | ToHexErrorType + | PacketToBytesErrorType + +/** + * Gets the resolver address for a name. + * + * Returns the address records should be read from and written to. The Universal + * Resolver locates the resolver bound to the name; when that resolver is a + * composite resolver (it delegates to an underlying resolver, such as the ENSv1 + * mirror used for unmigrated v1 names), the underlying resolver is returned so + * callers get a writable resolver. A plain (non-composite) resolver is already + * final and is returned as-is. + * + * @param client - {@link Client} + * @param parameters - {@link GetResolverParameters} + * @returns Resolver address, or null if none is found. {@link GetResolverReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getResolver } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getResolver(client, { name: 'ens.eth' }) + * // 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 + */ +export async function getResolver( + client: RequireClientContracts, + { name }: GetResolverParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + const encodedName = toHex(packetToBytes(name)) + const [resolver] = await readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }), + abi: universalResolverFindResolverSnippet, + functionName: 'findResolver', + args: [encodedName], + }) + + if (resolver === zeroAddress) return null + + // A composite resolver delegates to an underlying resolver; unwrap it to the + // final (writable) resolver. Non-composite resolvers are already final. + const isComposite = await readContractAction({ + address: resolver, + abi: erc165SupportsInterfaceSnippet, + functionName: 'supportsInterface', + args: [COMPOSITE_RESOLVER_INTERFACE_ID], + }).catch(() => false) + + if (!isComposite) return resolver + + const [underlyingResolver] = await readContractAction({ + address: resolver, + abi: compositeResolverGetResolver, + functionName: 'getResolver', + args: [encodedName], + }) + + return underlyingResolver === zeroAddress ? null : underlyingResolver +} diff --git a/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.test.ts b/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.test.ts new file mode 100644 index 000000000..0da436e5e --- /dev/null +++ b/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.test.ts @@ -0,0 +1,137 @@ +import { registryResolverSnippet } from '@ensdomains/ensjs-abi/registry' +import { permissionedResolverNameSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { Client, PublicClient, Transport } from 'viem' +import { mainnet } from 'viem/chains' +import { + afterEach, + beforeEach, + describe, + expect, + it, + type MockedFunction, + vi, +} from 'vitest' +import type { ChainWithContracts } from '../../clients/shared.js' +import { addEnsL1Contracts } from '../../index.js' +import { + deploymentAddresses, + publicClient, + testClient, + waitForTransaction, + walletClient, +} from '../../test/addTestContracts.js' +import { setPrimaryName } from '../wallet/setPrimaryName.js' +import { getReverseRecordFromRegistry } from './getReverseRecordFromRegistry.js' + +let snapshot: `0x${string}` + +const mockReadContract = vi.fn() as MockedFunction +const mockClient = { + readContract: mockReadContract, + chain: addEnsL1Contracts(mainnet), +} as unknown as Client> + +beforeEach(async () => { + snapshot = await testClient.snapshot() + vi.resetAllMocks() +}) + +afterEach(async () => { + await testClient.revert({ id: snapshot }) +}) + +describe('getReverseRecordFromRegistry', () => { + it('returns the reverse record for an address with a primary name', async () => { + const result = await getReverseRecordFromRegistry(publicClient, { + address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', + }) + expect(result).toMatchInlineSnapshot(` + { + "name": "with-profile.eth", + "reverseResolverAddress": "${deploymentAddresses.PublicResolver}", + } + `) + }) + + it('returns null for an address with no reverse record', async () => { + const result = await getReverseRecordFromRegistry(publicClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + }) + expect(result).toBeNull() + }) + + it('returns the reverse record after setPrimaryName', async () => { + const [account] = await walletClient.getAddresses() + if (!account) throw new Error('No account') + + const tx = await setPrimaryName(walletClient, { + name: 'with-profile.eth', + account, + }) + await waitForTransaction(tx) + + const result = await getReverseRecordFromRegistry(publicClient, { + address: account, + }) + expect(result).not.toBeNull() + expect(result?.name).toBe('with-profile.eth') + expect(result?.reverseResolverAddress).toBe( + deploymentAddresses.PublicResolver, + ) + }) + + it('returns null when registry has no resolver for address', async () => { + mockReadContract.mockResolvedValueOnce( + '0x0000000000000000000000000000000000000000', + ) + + const result = await getReverseRecordFromRegistry(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + }) + expect(result).toBeNull() + expect(mockReadContract).toHaveBeenCalledTimes(1) + }) + + it('returns null when resolver returns empty name', async () => { + mockReadContract + .mockResolvedValueOnce('0xa2c122be93b0074270ebee7f6b7292c7deb45047') + .mockResolvedValueOnce('') + + const result = await getReverseRecordFromRegistry(mockClient, { + address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', + }) + expect(result).toBeNull() + expect(mockReadContract).toHaveBeenCalledTimes(2) + }) + + it('returns name and reverseResolverAddress when resolver has name', async () => { + const resolverAddress = '0xa2c122be93b0074270ebee7f6b7292c7deb45047' + mockReadContract + .mockResolvedValueOnce(resolverAddress) + .mockResolvedValueOnce('nick.eth') + + const result = await getReverseRecordFromRegistry(mockClient, { + address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5', + }) + expect(result).toEqual({ + name: 'nick.eth', + reverseResolverAddress: resolverAddress, + }) + expect(mockReadContract).toHaveBeenCalledTimes(2) + expect(mockReadContract).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + abi: registryResolverSnippet, + functionName: 'resolver', + }), + ) + expect(mockReadContract).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + address: resolverAddress, + abi: permissionedResolverNameSnippet, + functionName: 'name', + }), + ) + }) +}) diff --git a/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.ts b/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.ts new file mode 100644 index 000000000..1ac26db92 --- /dev/null +++ b/packages/ensjs/src/actions/public/getReverseRecordFromRegistry.ts @@ -0,0 +1,94 @@ +import { registryResolverSnippet } from '@ensdomains/ensjs-abi/registry' +import { permissionedResolverNameSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import { + type Address, + type Chain, + type GetChainContractAddressErrorType, + type ReadContractErrorType, + zeroAddress, +} from 'viem' +import { readContract } from 'viem/actions' +import { namehash } from 'viem/ens' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' + +export type GetReverseRecordFromRegistryParameters = { + /** Address to get the reverse name for */ + address: Address +} + +export type GetReverseRecordFromRegistryReturnType = { + /** Primary name for address */ + name: string + /** Resolver address for reverse node */ + reverseResolverAddress: Address +} | null + +export type GetReverseRecordFromRegistryErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | ErrorType + +/** + * Gets the reverse record (primary name) for an address by reading directly from + * the L1 ENS registry and resolver, bypassing the Universal Resolver and CCIP-read. + * + * Useful as a fallback when `getName` returns null due to Universal Resolver + * errors, but the reverse record still exists on-chain. + * + * @param client - {@link Client} + * @param parameters - {@link GetReverseRecordFromRegistryParameters} + * @returns Name and reverse resolver address, or `null` if no reverse record is set. {@link GetReverseRecordFromRegistryReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getReverseRecordFromRegistry } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getReverseRecordFromRegistry(client, { address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' }) + * // { name: 'nick.eth', reverseResolverAddress: '0xa2c122be93b0074270ebee7f6b7292c7deb45047' } + */ +export async function getReverseRecordFromRegistry( + client: RequireClientContracts, + { address }: GetReverseRecordFromRegistryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const reverseNode = `${address.toLowerCase().slice(2)}.addr.reverse` + const nodeHash = namehash(reverseNode) + const registryAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensLegacyRegistry', + }) + const readContractAction = getAction(client, readContract, 'readContract') + + const resolverAddress = await readContractAction({ + address: registryAddress, + abi: registryResolverSnippet, + functionName: 'resolver', + args: [nodeHash], + }) + + if (!resolverAddress || resolverAddress === zeroAddress) return null + + const name = await readContractAction({ + address: resolverAddress, + abi: permissionedResolverNameSnippet, + functionName: 'name', + args: [nodeHash], + }) + + if (!name || name === '') return null + + return { name, reverseResolverAddress: resolverAddress } +} diff --git a/packages/ensjs/src/functions/public/getSupportedInterfaces.test.ts b/packages/ensjs/src/actions/public/getSupportedInterfaces.test.ts similarity index 95% rename from packages/ensjs/src/functions/public/getSupportedInterfaces.test.ts rename to packages/ensjs/src/actions/public/getSupportedInterfaces.test.ts index b26ba1193..8e5972b6f 100644 --- a/packages/ensjs/src/functions/public/getSupportedInterfaces.test.ts +++ b/packages/ensjs/src/actions/public/getSupportedInterfaces.test.ts @@ -3,7 +3,7 @@ import { deploymentAddresses, publicClient, } from '../../test/addTestContracts.js' -import getSupportedInterfaces from './getSupportedInterfaces.js' +import { getSupportedInterfaces } from './getSupportedInterfaces.js' it('returns true for a single supported interface', async () => { const result = await getSupportedInterfaces(publicClient, { diff --git a/packages/ensjs/src/actions/public/getSupportedInterfaces.ts b/packages/ensjs/src/actions/public/getSupportedInterfaces.ts new file mode 100644 index 000000000..65634371c --- /dev/null +++ b/packages/ensjs/src/actions/public/getSupportedInterfaces.ts @@ -0,0 +1,68 @@ +import { erc165SupportsInterfaceSnippet } from '@ensdomains/ensjs-abi/erc165' +import type { Address, Chain, Hex, MulticallErrorType } from 'viem' +import { multicall } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' + +export type GetSupportedInterfacesParameters< + interfaces extends readonly Hex[], +> = { + address: Address + interfaces: interfaces +} + +export type GetSupportedInterfacesReturnType< + interfaces extends readonly Hex[], +> = { + -readonly [K in keyof interfaces]: boolean +} + +export type GetSupportedInterfacesErrorType = MulticallErrorType + +/** + * Gets the supported interfaces for any contract address. + * @param client - {@link Client} + * @param parameters - {@link GetSupportedInterfacesParameters} + * @returns Array of booleans matching the input array {@link GetSupportedInterfacesReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getSupportedInterfaces } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getSupportedInterfaces(client, { + * address: '0x58774Bb8acD458A640aF0B88238369A167546ef2', + * interfaces: ['0x2f435428', '0x23b872dd'], + * }) + * // [true, false] + */ +export async function getSupportedInterfaces< + chain extends Chain, + const interfaces extends readonly Hex[], +>( + client: RequireClientContracts, + { address, interfaces }: GetSupportedInterfacesParameters, +): Promise> { + ASSERT_NO_TYPE_ERROR(client) + + const multicallAction = getAction(client, multicall, 'multicall') + + const result = await multicallAction({ + contracts: interfaces.map((interfaceId) => ({ + address, + abi: erc165SupportsInterfaceSnippet, + functionName: 'supportsInterface', + args: [interfaceId], + })), + }) + + return result.map( + (r) => r.status === 'success' && r.result === true, + ) as GetSupportedInterfacesReturnType +} diff --git a/packages/ensjs/src/actions/public/getTextRecord.test.ts b/packages/ensjs/src/actions/public/getTextRecord.test.ts new file mode 100644 index 000000000..c19a87a16 --- /dev/null +++ b/packages/ensjs/src/actions/public/getTextRecord.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest' +import { publicClient } from '../../test/addTestContracts.js' +import { getTextRecord } from './getTextRecord.js' + +describe('getTextRecord()', () => { + it('should return a record from a key', async () => { + const result = await getTextRecord(publicClient, { + name: 'with-profile.eth', + key: 'description', + }) + expect(result).toBe('Hello2') + }) + + it('should return null for a non-existent key', async () => { + const result = await getTextRecord(publicClient, { + name: 'with-profile.eth', + key: 'thiskeydoesntexist', + }) + expect(result).toBeNull() + }) + it('should return null on error when strict is false', async () => { + const result = await getTextRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + key: 'description', + }) + expect(result).toBeNull() + }) + it('should throw on error when strict is true', async () => { + // The name is neither registered on v1 nor reserved on v2, so the Universal + // Resolver reverts with ResolverNotFound; strict mode surfaces it. + await expect( + getTextRecord(publicClient, { + name: 'thisnamedoesnotexist.eth', + key: 'description', + strict: true, + }), + ).rejects.toThrowError('ResolverNotFound') + }) +}) diff --git a/packages/ensjs/src/actions/public/getTextRecord.ts b/packages/ensjs/src/actions/public/getTextRecord.ts new file mode 100644 index 000000000..b91d13b92 --- /dev/null +++ b/packages/ensjs/src/actions/public/getTextRecord.ts @@ -0,0 +1,74 @@ +import type { Chain, EncodeFunctionDataErrorType } from 'viem' +import { encodeFunctionData, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import type { Prettify } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type DecodeTextResultErrorType, + type DecodeTextResultParameters, + type DecodeTextResultReturnType, + decodeTextResult, + type GetTextParametersErrorType, + type GetTextParametersParameters, + getTextParameters, +} from '../../utils/coders/getText.js' +import { + type ResolveNameDataErrorType, + resolveNameData, +} from './resolveNameData.js' + +export type GetTextRecordParameters = Prettify< + GetTextParametersParameters & + DecodeTextResultParameters & { + /** Batch gateway URLs to use for resolving CCIP-read requests. */ + gatewayUrls?: string[] + } +> + +export type GetTextRecordReturnType = DecodeTextResultReturnType + +export type GetTextRecordErrorType = + | ResolveNameDataErrorType + | EncodeFunctionDataErrorType + | GetTextParametersErrorType + | DecodeTextResultErrorType + +/** + * Gets a text record for a name. + * @param client - {@link Client} + * @param parameters - {@link GetTextRecordParameters} + * @returns Text record string, or null if none is found. {@link GetTextRecordReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getTextRecord } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getTextRecord(client, { name: 'ens.eth', key: 'com.twitter' }) + * // ensdomains + */ +export async function getTextRecord( + client: RequireClientContracts, + { gatewayUrls, strict, ...parameters }: GetTextRecordParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const resolveNameDataAction = getAction( + client, + resolveNameData, + 'resolveNameData', + ) + const result = await resolveNameDataAction({ + name: parameters.name, + data: encodeFunctionData(getTextParameters(parameters)), + gatewayUrls, + strict, + }) + if (!result) return null + return decodeTextResult(result.resolvedData, { strict }) +} diff --git a/packages/ensjs/src/actions/public/resolveNameData.test.ts b/packages/ensjs/src/actions/public/resolveNameData.test.ts new file mode 100644 index 000000000..171347680 --- /dev/null +++ b/packages/ensjs/src/actions/public/resolveNameData.test.ts @@ -0,0 +1,180 @@ +import { multicallSnippet } from '@ensdomains/ensjs-abi/multicall' +import { + universalResolverResolveSnippet, + universalResolverResolveWithGatewaysSnippet, +} from '@ensdomains/ensjs-abi/universalResolver' +import { + publicResolverSingleAddrSnippet, + publicResolverTextSnippet, +} from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + BaseError, + type Client, + ContractFunctionRevertedError, + encodeErrorResult, + encodeFunctionData, + encodeFunctionResult, + type Hex, + namehash, + type PublicClient, + type Transport, + zeroAddress, +} from 'viem' +import { mainnet } from 'viem/chains' +import { hexToBytes } from 'viem/utils' +import { beforeEach, expect, it, type MockedFunction, vi } from 'vitest' +import type { ChainWithContracts } from '../../clients/shared.js' +import { addEnsL1Contracts } from '../../index.js' +import { getAddressParameters, getTextParameters } from '../../utils/index.js' +import { bytesToPacket } from '../../utils/name/hexEncodedName.js' +import { resolveNameData } from './resolveNameData.js' + +const mockReadContract = vi.fn() as MockedFunction +const mockClient = { + readContract: mockReadContract, + chain: addEnsL1Contracts(mainnet), +} as unknown as Client> + +beforeEach(() => { + vi.clearAllMocks() +}) + +it('encodes labels larger than 255 bytes', async () => { + await resolveNameData(mockClient, { + name: `${'a'.repeat(300)}.eth`, + data: '0x', + }).catch(() => {}) + const [{ args }] = mockReadContract.mock.calls[0] + const name = bytesToPacket(hexToBytes(args?.[0] as Hex)) + expect(name).toMatchInlineSnapshot( + `"[5b7e0e47a96f32a88b4f14ca177982790807c40e1a105742ba0fc1babe1ef826].eth"`, + ) +}) + +it('uses gateways abi when gateways are provided', async () => { + await resolveNameData(mockClient, { + name: 'test.eth', + data: '0x', + gatewayUrls: ['https://gateway.example.com'], + }).catch(() => {}) + const [{ abi }] = mockReadContract.mock.calls[0] + expect(abi).toBe(universalResolverResolveWithGatewaysSnippet) +}) + +it('does not throw on known error when strict is false', async () => { + mockReadContract.mockImplementation(async () => { + throw new ContractFunctionRevertedError({ + abi: universalResolverResolveSnippet, + functionName: 'resolve', + data: encodeErrorResult({ + abi: universalResolverResolveSnippet, + errorName: 'ResolverNotFound', + args: [namehash('test.eth')], + }), + }) + }) + const result = await resolveNameData(mockClient, { + name: 'test.eth', + data: '0x', + strict: false, + }) + expect(result).toBeNull() +}) + +it('throws on known error when strict is true', async () => { + const error = new ContractFunctionRevertedError({ + abi: universalResolverResolveSnippet, + functionName: 'resolve', + data: encodeErrorResult({ + abi: universalResolverResolveSnippet, + errorName: 'ResolverNotFound', + args: [namehash('test.eth')], + }), + }) + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + resolveNameData(mockClient, { + name: 'test.eth', + data: '0x', + strict: true, + }), + ).rejects.toThrowError(error) +}) + +it('works with multicall', async () => { + const results = [ + encodeFunctionResult({ + abi: publicResolverTextSnippet, + functionName: 'text', + result: 'example@example.com', + }), + encodeFunctionResult({ + abi: publicResolverSingleAddrSnippet, + functionName: 'addr', + result: zeroAddress, + }), + ] + mockReadContract.mockImplementation( + async ({ functionName, args, address }) => { + if (functionName === 'resolve' && Array.isArray(args)) { + const data = encodeFunctionResult({ + abi: multicallSnippet, + functionName: 'multicall', + result: results, + }) + + return [data, address] + } + return undefined + }, + ) + + const calls = [ + encodeFunctionData(getTextParameters({ name: 'test.eth', key: 'email' })), + encodeFunctionData(getAddressParameters({ name: 'test.eth', coin: 'ETH' })), + ] + + const data = await resolveNameData(mockClient, { + name: 'test.eth', + strict: true, + data: calls, + }) + + expect(data?.resolverAddress?.toLowerCase()).toEqual( + '0x5a9236e72a66d3e08b83dcf489b4d850792b6009', + ) + + expect(data?.resolvedData).toEqual( + results.map((r) => ({ success: true, returnData: r })), + ) +}) + +it('throws on unknown error when strict is false', async () => { + const error = new BaseError('Unknown error') + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + resolveNameData(mockClient, { + name: 'test.eth', + data: '0x', + strict: false, + }), + ).rejects.toThrowError(error) +}) + +it('throws on unknown error when strict is true', async () => { + const error = new BaseError('Unknown error') + mockReadContract.mockImplementation(async () => { + throw error + }) + await expect( + resolveNameData(mockClient, { + name: 'test.eth', + data: '0x', + strict: false, + }), + ).rejects.toThrowError(error) +}) diff --git a/packages/ensjs/src/actions/public/resolveNameData.ts b/packages/ensjs/src/actions/public/resolveNameData.ts new file mode 100644 index 000000000..68883f459 --- /dev/null +++ b/packages/ensjs/src/actions/public/resolveNameData.ts @@ -0,0 +1,143 @@ +import { multicallSnippet } from '@ensdomains/ensjs-abi/multicall' +import { + universalResolverResolveSnippet, + universalResolverResolveWithGatewaysSnippet, +} from '@ensdomains/ensjs-abi/universalResolver' +import { + type Address, + type Chain, + decodeFunctionResult, + encodeFunctionData, + type GetChainContractAddressErrorType, + type Hex, + type ReadContractErrorType, + type ToHexErrorType, + toHex, + zeroAddress, +} from 'viem' +import { readContract } from 'viem/actions' +import { type PacketToBytesErrorType, packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { isNullUniversalResolverError } from '../../utils/errors/isNullUniversalResolverError.js' +import { + type GetNameWithSizedLabelsErrorType, + getNameWithSizedLabels, +} from '../../utils/name/getNameWithSizedLabels.js' + +export type ResolveNameDataParameters = { + name: string + data: data + strict?: boolean + gatewayUrls?: string[] +} + +type Result = { success: boolean; returnData: Hex } +type ResultArray = readonly Result[] + +export type ResolveNameDataReturnType = { + resolvedData: data extends Hex[] ? ResultArray : Hex + resolverAddress: Address +} | null + +export type ResolveNameDataErrorType = + | GetNameWithSizedLabelsErrorType + | ReadContractErrorType + | GetChainContractAddressErrorType + | ToHexErrorType + | PacketToBytesErrorType + +/** + * Resolves name and data with the universal resolver. + * @param client - {@link Client} + * @param parameters - {@link ResolveParameters} + * @returns {@link ResolveReturnType} + * @internal + */ +export async function resolveNameData< + chain extends Chain, + data extends Hex | Hex[], +>( + client: RequireClientContracts, + { name, data, strict, gatewayUrls }: ResolveNameDataParameters, +): Promise> { + ASSERT_NO_TYPE_ERROR(client) + + const nameWithSizedLabels = getNameWithSizedLabels(name) + const readContractAction = getAction(client, readContract, 'readContract') + + const baseParameters = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }), + functionName: 'resolve', + } as const + + try { + const [resolvedData, resolverAddress] = await (() => { + if (Array.isArray(data)) { + const multicallData = encodeFunctionData({ + abi: multicallSnippet, + functionName: 'multicall', + args: [data as Hex[]], + }) + + const arrayParameters = { + ...baseParameters, + abi: universalResolverResolveSnippet, + args: [toHex(packetToBytes(nameWithSizedLabels)), multicallData], + } as const + + return gatewayUrls + ? readContractAction({ + ...arrayParameters, + abi: universalResolverResolveWithGatewaysSnippet, + functionName: 'resolveWithGateways', + args: [...arrayParameters.args, gatewayUrls], + }) + : readContractAction(arrayParameters) + } + + const parameters = { + ...baseParameters, + abi: universalResolverResolveSnippet, + args: [toHex(packetToBytes(nameWithSizedLabels)), data as Hex], + } as const + + return gatewayUrls + ? readContractAction({ + ...parameters, + abi: universalResolverResolveWithGatewaysSnippet, + functionName: 'resolveWithGateways', + args: [...parameters.args, gatewayUrls] as const, + }) + : readContractAction(parameters) + })() + + if (resolverAddress === zeroAddress) return null + if (Array.isArray(data)) { + return { + resolvedData: decodeFunctionResult({ + abi: multicallSnippet, + data: resolvedData, + }).map((returnData): Result => { + if (returnData === '0x') { + return { success: false, returnData: returnData } + } + return { success: (returnData.length - 2) % 64 === 0, returnData } + }) as ResultArray, + resolverAddress, + } as ResolveNameDataReturnType + } + return { resolvedData, resolverAddress } as ResolveNameDataReturnType + } catch (error) { + if (strict) throw error + if (isNullUniversalResolverError(error)) return null + throw error + } +} diff --git a/packages/ensjs/src/functions/public/getAvailable.test.ts b/packages/ensjs/src/actions/public/v1/getAvailable.test.ts similarity index 74% rename from packages/ensjs/src/functions/public/getAvailable.test.ts rename to packages/ensjs/src/actions/public/v1/getAvailable.test.ts index 91e5c6b60..449fa77d0 100644 --- a/packages/ensjs/src/functions/public/getAvailable.test.ts +++ b/packages/ensjs/src/actions/public/v1/getAvailable.test.ts @@ -1,9 +1,10 @@ import { describe, expect, it } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getAvailable from './getAvailable.js' +import { publicClient } from '../../../test/addTestContracts.js' +import { getAvailable } from './getAvailable.js' describe('getAvailable', () => { it('should return false for a name that is unavailable', async () => { + // 'test123' is pre-seeded by seedTestNames via the legacy v1 controller const result = await getAvailable(publicClient, { name: 'test123.eth' }) expect(typeof result).toBe('boolean') expect(result).toBe(false) diff --git a/packages/ensjs/src/actions/public/v1/getAvailable.ts b/packages/ensjs/src/actions/public/v1/getAvailable.ts new file mode 100644 index 000000000..d23dd5d61 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getAvailable.ts @@ -0,0 +1,80 @@ +import { baseRegistrarAvailableSnippet } from '@ensdomains/ensjs-abi/v1/baseRegistrar' +import { + type Chain, + type GetChainContractAddressErrorType, + labelhash, + type ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import type { ErrorType } from '../../../errors/utils.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { getNameType } from '../../../utils/name/getNameType.js' + +export type GetAvailableParameters = { + /** Name to check availability for, only compatible for eth 2ld */ + name: string +} + +export type GetAvailableReturnType = boolean + +export type GetAvailableErrorType = + | UnsupportedNameTypeError + | ReadContractErrorType + | GetChainContractAddressErrorType + | ErrorType + +/** + * Gets the availability of a name to register on the legacy v1 BaseRegistrar. + * + * Calls `BaseRegistrarImplementation.available(uint256 id)` where `id` is the + * labelhash of the 2LD label as a `uint256`. Use this for the legacy v1 + * deployment; for v2, use `getAvailable` from `@ensdomains/ensjs/public/v2`. + * + * @param client - {@link Client} + * @param parameters - {@link GetAvailableParameters} + * @returns Availability as boolean. {@link GetAvailableReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getAvailable } from '@ensdomains/ensjs/public/v1' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getAvailable(client, { name: 'ens.eth' }) + * // false + */ +export async function getAvailable( + client: RequireClientContracts, + { name }: GetAvailableParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const labels = name.split('.') + const nameType = getNameType(name) + if (nameType !== 'eth-2ld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld'], + details: 'Currently only eth-2ld names can be checked for availability', + }) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensBaseRegistrarImplementation', + }), + abi: baseRegistrarAvailableSnippet, + functionName: 'available', + args: [BigInt(labelhash(labels[0]))], + }) +} diff --git a/packages/ensjs/src/actions/public/v1/getExpiry.test.ts b/packages/ensjs/src/actions/public/v1/getExpiry.test.ts new file mode 100644 index 000000000..833a012d5 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getExpiry.test.ts @@ -0,0 +1,46 @@ +import { assert, describe, expect, it } from 'vitest' +import { publicClient } from '../../../test/addTestContracts.js' +import { getExpiry } from './getExpiry.js' + +describe('getExpiry', () => { + it('should get the expiry for a .eth name with no other args', async () => { + const result = await getExpiry(publicClient, { name: 'with-profile.eth' }) + assert.isNotNull(result) + expect(result?.expiry).toBeTypeOf('bigint') + expect(result?.gracePeriod).toBe(7776000) + expect(result?.status).toBe('active') + }) + it('should get the expiry for a wrapped name', async () => { + const result = await getExpiry(publicClient, { + name: 'wrapped.eth', + contract: 'nameWrapper', + }) + + assert.isNotNull(result) + expect(result?.expiry).toBeTypeOf('bigint') + expect(result?.gracePeriod).toBe(0) + expect(result?.status).toBe('active') + }) + it('should return null for a non .eth name if not wrapped', async () => { + const result = await getExpiry(publicClient, { + name: 'sub.with-profile.eth', + }) + expect(result).toBeNull() + }) + it('should throw an error for a non .eth name if registrar is specified', async () => { + await expect( + getExpiry(publicClient, { + name: 'sub.with-profile.eth', + contract: 'registrar', + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [UnsupportedNameTypeError: Unsupported name type: eth-subname + + - Supported name types: eth-2ld, tld + + Details: Only the expiry of eth-2ld names can be fetched when using the registrar contract + + Version: @ensdomains/ensjs@1.0.0-mock.0] + `) + }) +}) diff --git a/packages/ensjs/src/actions/public/v1/getExpiry.ts b/packages/ensjs/src/actions/public/v1/getExpiry.ts new file mode 100644 index 000000000..772112003 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getExpiry.ts @@ -0,0 +1,169 @@ +import { multicallGetCurrentBlockTimestampSnippet } from '@ensdomains/ensjs-abi/multicall' +import { + baseRegistrarGracePeriodSnippet, + baseRegistrarNameExpiresSnippet, +} from '@ensdomains/ensjs-abi/v1/baseRegistrar' +import { nameWrapperGetDataSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Chain, + type GetChainContractAddressErrorType, + type LabelhashErrorType, + labelhash, + type MulticallErrorType, + type NamehashErrorType, + namehash, +} from 'viem' +import { multicall } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import type { Prettify } from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { getNameType } from '../../../utils/name/getNameType.js' +import { checkIsDotEth } from '../../../utils/name/validation.js' + +type ContractOption = 'registrar' | 'nameWrapper' +type ExpiryStatus = 'active' | 'expired' | 'gracePeriod' + +export type GetExpiryParameters = Prettify<{ + /** Name to get expiry for */ + name: string + /** Optional specific contract to use to get expiry */ + contract?: ContractOption +}> + +export type GetExpiryReturnType = Prettify<{ + /** Expiry value */ + expiry: bigint + /** Grace period value (in seconds) */ + gracePeriod: number + /** Status of name */ + status: ExpiryStatus +} | null> + +export type GetExpiryErrorType = + | MulticallErrorType + | GetChainContractAddressErrorType + | NamehashErrorType + | LabelhashErrorType + +/** + * Gets the expiry for a name + * @param client - {@link Client} + * @param parameters - {@link GetExpiryParameters} + * @returns Expiry object, or `null` if no expiry. {@link GetExpiryReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getExpiry } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getExpiry(client, { name: 'ens.eth' }) + * // { expiry: { date: Date, value: 1913933217n }, gracePeriod: 7776000, status: 'active' } + + */ +export async function getExpiry( + client: RequireClientContracts< + chain, + | 'ensNameWrapper' + | 'ensBaseRegistrarImplementation' + | 'ensRegistry' + | 'multicall3' + >, + { name, contract: contractOption }: GetExpiryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const { chain } = client + const labels = name.split('.') + const contract = (() => { + if (contractOption) { + if (contractOption === 'registrar' && !checkIsDotEth(labels)) + throw new UnsupportedNameTypeError({ + nameType: getNameType(name), + supportedNameTypes: ['eth-2ld', 'tld'], + details: + 'Only the expiry of eth-2ld names can be fetched when using the registrar contract', + }) + return contractOption + } + if (checkIsDotEth(labels)) return 'registrar' + return 'nameWrapper' + })() + + const multicallAction = getAction(client, multicall, 'multicall') + + const getCurrentBlockTimestampParameters = { + address: getChainContractAddress({ + chain, + contract: 'multicall3', + }), + abi: multicallGetCurrentBlockTimestampSnippet, + functionName: 'getCurrentBlockTimestamp', + } as const + + const [currentBlockTimestamp, expiry, gracePeriod] = await (async () => { + if (contract === 'nameWrapper') { + const [timestamp_, [, , expiry_]] = await multicallAction({ + contracts: [ + getCurrentBlockTimestampParameters, + { + address: getChainContractAddress({ + chain, + contract: 'ensNameWrapper', + }), + abi: nameWrapperGetDataSnippet, + functionName: 'getData', + args: [BigInt(namehash(name))], + }, + ], + allowFailure: false, + }) + return [timestamp_, expiry_, 0n] as const + } + + return await multicallAction({ + contracts: [ + getCurrentBlockTimestampParameters, + { + address: getChainContractAddress({ + chain, + contract: 'ensBaseRegistrarImplementation', + }), + abi: baseRegistrarNameExpiresSnippet, + functionName: 'nameExpires', + args: [BigInt(labelhash(labels[0]))], + }, + { + address: getChainContractAddress({ + chain, + contract: 'ensBaseRegistrarImplementation', + }), + abi: baseRegistrarGracePeriodSnippet, + functionName: 'GRACE_PERIOD', + }, + ], + allowFailure: false, + }) + })() + + if (expiry === 0n) return null + + const status = (() => { + if (currentBlockTimestamp > expiry + gracePeriod) return 'expired' + if (currentBlockTimestamp > expiry) return 'gracePeriod' + return 'active' + })() + + return { + expiry, + gracePeriod: Number(gracePeriod), + status, + } +} diff --git a/packages/ensjs/src/functions/public/getOwner.test.ts b/packages/ensjs/src/actions/public/v1/getOwner.test.ts similarity index 75% rename from packages/ensjs/src/functions/public/getOwner.test.ts rename to packages/ensjs/src/actions/public/v1/getOwner.test.ts index cfaf940b7..33ec1ea5d 100644 --- a/packages/ensjs/src/functions/public/getOwner.test.ts +++ b/packages/ensjs/src/actions/public/v1/getOwner.test.ts @@ -1,9 +1,6 @@ import { describe, expect, it } from 'vitest' -import { - deploymentAddresses, - publicClient, -} from '../../test/addTestContracts.js' -import getOwner from './getOwner.js' +import { publicClient } from '../../../test/addTestContracts.js' +import { getOwner } from './getOwner.js' describe('getOwner', () => { it('should return correct ownership level and values for a wrapped .eth name', async () => { @@ -16,14 +13,10 @@ describe('getOwner', () => { `) }) it('should return correct ownership level and values for an expired wrapped .eth name', async () => { - const result = await getOwner(publicClient, { name: 'expired-wrapped.eth' }) - expect(result).toMatchInlineSnapshot(` - { - "owner": "${deploymentAddresses.NameWrapper}", - "ownershipLevel": "registrar", - "registrant": null, - } - `) + const result = await getOwner(publicClient, { + name: 'expired-wrapped.eth', + }) + expect(result).toMatchInlineSnapshot('null') // expect(result).toEqual({ // ownershipLevel: 'nameWrapper', // owner: '0x0000000000000000000000000000000000000000', @@ -42,13 +35,7 @@ describe('getOwner', () => { }) it('should return correct ownership level and values for an expired unwrapped .eth name', async () => { const result = await getOwner(publicClient, { name: 'expired.eth' }) - expect(result).toMatchInlineSnapshot(` - { - "owner": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", - "ownershipLevel": "registrar", - "registrant": null, - } - `) + expect(result).toMatchInlineSnapshot('null') }) describe('subname', () => { it('should return correct ownership level and values for a unwrapped name', async () => { @@ -77,12 +64,7 @@ describe('getOwner', () => { const result = await getOwner(publicClient, { name: 'test.expired-wrapped.eth', }) - expect(result).toMatchInlineSnapshot(` - { - "owner": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - "ownershipLevel": "nameWrapper", - } - `) + expect(result).toMatchInlineSnapshot('null') }) }) }) diff --git a/packages/ensjs/src/actions/public/v1/getOwner.ts b/packages/ensjs/src/actions/public/v1/getOwner.ts new file mode 100644 index 000000000..1d5c41876 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getOwner.ts @@ -0,0 +1,275 @@ +import { + type Address, + type Chain, + type MulticallErrorType, + type NamehashErrorType, + namehash, + type ReadContractErrorType, + type ReadContractParameters, + type ReadContractReturnType, + zeroAddress, +} from 'viem' +import { multicall, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { isNullRegistrarOwnerOfError } from '../../../utils/errors/isNullRegistrarOwnerOfError.js' +import { checkIsDotEth } from '../../../utils/name/validation.js' +import { nullableAddress } from '../../../utils/nullableAddress.js' +import { + type GetContractSpecificOwnerParametersErrorType, + getContractSpecificOwnerParameters, + type OwnerContract, +} from '../../../utils/ownerFromContract.js' + +export type GetOwnerParameters< + contract extends OwnerContract | undefined = undefined, +> = { + /** Name to get owner for */ + name: string + /** Optional specific contract to get ownership value from */ + contract?: contract +} + +type BaseGetOwnerReturnType = { + /** Owner of the name */ + owner?: Address | null + /** Registrant of the name (registrar owner) */ + registrant?: Address | null + /** The contract level that the ownership is on */ + ownershipLevel: 'registry' | 'registrar' | 'nameWrapper' +} + +type RegistrarOnlyOwnership = { + owner?: never + registrant: Address + ownershipLevel: 'registrar' +} + +type WrappedOwnership = { + owner: Address + registrant?: never + ownershipLevel: 'nameWrapper' +} + +type UnwrappedEth2ldOwnership = { + registrant: Address | null + owner: Address + ownershipLevel: 'registrar' +} + +type UnwrappedOwnership = { + owner: Address + registrant?: never + ownershipLevel: 'registry' +} + +export type GetOwnerReturnType< + contract extends OwnerContract | undefined = undefined, +> = + | (BaseGetOwnerReturnType & + (contract extends 'registrar' + ? RegistrarOnlyOwnership + : contract extends 'nameWrapper' + ? WrappedOwnership + : contract extends 'registry' + ? UnwrappedOwnership + : WrappedOwnership | UnwrappedEth2ldOwnership | UnwrappedOwnership)) + | null + +export type GetOwnerErrorType = + | NamehashErrorType + | ReadContractErrorType + | GetContractSpecificOwnerParametersErrorType + | MulticallErrorType + +/** + * Gets the owner(s) of a name. + * @param client - {@link Client} + * @param parameters - {@link GetOwnerParameters} + * @returns Owner data object, or `null` if no owners exist. {@link GetOwnerReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getOwner } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getOwner(client, { name: 'ens.eth' }) + * // { owner: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', registrant: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', ownershipLevel: 'registrar } + */ +export async function getOwner< + chain extends Chain, + contract extends OwnerContract | undefined = undefined, +>( + client: RequireClientContracts< + chain, + | 'ensNameWrapper' + | 'ensLegacyRegistry' + | 'ensBaseRegistrarImplementation' + | 'multicall3' + >, + { name, contract: contract_ }: GetOwnerParameters, +): Promise> { + ASSERT_NO_TYPE_ERROR(client) + + const node = namehash(name) + const labels = name.split('.') + + if (contract_ || labels.length === 1) { + const contract = contract_ || 'registry' + const readContractAction = getAction(client, readContract, 'readContract') + const params = getContractSpecificOwnerParameters(client.chain, { + contract, + node, + labels, + }) + type OwnerParameters = Extract + type OwnerReturnType = ReadContractReturnType< + OwnerParameters['abi'], + 'owner', + OwnerParameters['args'] + > + type OwnerOfParameters = Extract + type OwnerOfReturnType = ReadContractReturnType< + OwnerOfParameters['abi'], + 'ownerOf', + OwnerOfParameters['args'] + > + const result = (await readContractAction( + params as ReadContractParameters, + )) as OwnerReturnType | OwnerOfReturnType + + if (result === zeroAddress) return null + + if (contract === 'registrar') + return { + ownershipLevel: 'registrar', + registrant: result, + } as GetOwnerReturnType + + return { + owner: result, + ownershipLevel: contract, + } as GetOwnerReturnType + } + + const multicallAction = getAction(client, multicall, 'multicall') + const contractParameters = [ + getContractSpecificOwnerParameters(client.chain, { + contract: 'registry', + node, + labels, + }), + getContractSpecificOwnerParameters(client.chain, { + contract: 'nameWrapper', + node, + labels, + }), + ] as const + const is2ldEth = checkIsDotEth(labels) + const [registryOwner, nameWrapperOwner, registrarOwner] = ( + is2ldEth + ? await multicallAction({ + contracts: [ + ...contractParameters, + getContractSpecificOwnerParameters(client.chain, { + contract: 'registrar', + labels, + }), + ], + allowFailure: true, + }) + : await multicallAction({ + contracts: contractParameters, + allowFailure: true, + }) + ).map((result, index) => { + if (result.error) { + if (index !== 2) throw result.error + if (!isNullRegistrarOwnerOfError(result.error)) throw result.error + return null + } + return nullableAddress(result.result) + }) + + const nameWrapperAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }) + + // check for only .eth names + if (labels[labels.length - 1] === 'eth') { + // if the owner on the registrar is the namewrapper, then the namewrapper owner is the owner + // there is no "registrant" for wrapped names + if (registrarOwner === nameWrapperAddress) { + return { + owner: nameWrapperOwner, + ownershipLevel: 'nameWrapper', + } as GetOwnerReturnType + } + // if there is a registrar owner, then it's not a subdomain but we have also passed the namewrapper clause + // this means that it's an unwrapped second-level name + // the registrant is the owner of the NFT + // the owner is the controller of the records + if (registrarOwner) { + return { + registrant: registrarOwner, + owner: registryOwner, + ownershipLevel: 'registrar', + } as GetOwnerReturnType + } + if (registryOwner !== null) { + // if there is no registrar owner, but the label length is two, then the domain is an expired 2LD .eth + // so we still want to return the ownership values + if (labels.length === 2) { + return { + registrant: null, + owner: registryOwner, + ownershipLevel: 'registrar', + } as GetOwnerReturnType + } + // this means that the subname is wrapped + if (registryOwner === nameWrapperAddress && nameWrapperOwner !== null) { + return { + owner: nameWrapperOwner, + ownershipLevel: 'nameWrapper', + } as GetOwnerReturnType + } + // unwrapped subnames do not have NFTs associated, so do not have a registrant + return { + owner: registryOwner, + ownershipLevel: 'registry', + } as GetOwnerReturnType + } + // .eth names with no registrar owner are either unregistered or expired + return null + } + + // non .eth names inherit the owner from the registry + // there will only ever be an owner for non .eth names, not a registrant + // this is because for unwrapped names, there is no associated NFT + // and for wrapped names, owner and registrant are the same thing + if (registryOwner === nameWrapperAddress && nameWrapperOwner !== null) { + return { + owner: nameWrapperOwner, + ownershipLevel: 'nameWrapper', + } as GetOwnerReturnType + } + + // for unwrapped non .eth names, the owner is the registry owner + if (registryOwner !== null) { + return { + owner: registryOwner, + ownershipLevel: 'registry', + } as GetOwnerReturnType + } + + // for anything else, return + return null +} diff --git a/packages/ensjs/src/actions/public/v1/getPrice.test.ts b/packages/ensjs/src/actions/public/v1/getPrice.test.ts new file mode 100644 index 000000000..dbeb29264 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getPrice.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' +import { publicClient as publicClientL2 } from '../../../test/addTestContracts.js' +import { getPrice } from './getPrice.js' + +describe('getPrice', () => { + it('should return a base and premium price for a name', async () => { + const result = await getPrice(publicClientL2, { + nameOrNames: 'test123.eth', + duration: 86400, + }) + expect(result).toBeTruthy() + expect(result.base).toBeGreaterThan(0n) + expect(result.premium).toBe(0n) + }) + + it('should return a base and premium price for an array of names', async () => { + const result = await getPrice(publicClientL2, { + nameOrNames: ['test123.eth', 'another.eth'], + duration: 86400, + }) + expect(result).toBeTruthy() + expect(result.base).toBeGreaterThan(0n) + expect(result.premium).toBe(0n) + }) + + it('should allow labels as inputs', async () => { + const result = await getPrice(publicClientL2, { + nameOrNames: 'test123', + duration: 86400, + }) + expect(result).toBeTruthy() + expect(result.base).toBeGreaterThan(0n) + expect(result.premium).toBe(0n) + }) +}) diff --git a/packages/ensjs/src/actions/public/v1/getPrice.ts b/packages/ensjs/src/actions/public/v1/getPrice.ts new file mode 100644 index 000000000..8dd2d2c5b --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getPrice.ts @@ -0,0 +1,130 @@ +import { ethRegistrarControllerRentPriceSnippet } from '@ensdomains/ensjs-abi/v1/ethRegistrarController' +import type { + Chain, + GetChainContractAddressErrorType, + MulticallErrorType, + ReadContractErrorType, +} from 'viem' +import { multicall, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { getNameType } from '../../../utils/name/getNameType.js' + +export type GetPriceParameters = { + /** Name, or array of names, to get price for */ + nameOrNames: string | string[] + /** Duration in seconds to get price for */ + duration: bigint | number +} + +export type GetPriceReturnType = { + /** Price base value (wei) */ + base: bigint + /** Price premium (wei) */ + premium: bigint +} + +export type GetPriceErrorType = + | UnsupportedNameTypeError + | GetChainContractAddressErrorType + | ReadContractErrorType + | MulticallErrorType + | TypeError + +const extractLabel = (name: string): string => { + const nameType = getNameType(name) + if (nameType !== 'eth-2ld' && nameType !== 'tld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld', 'tld'], + details: 'Currently only the price of eth-2ld names can be fetched', + }) + return name.split('.')[0] +} + +/** + * Gets the registration price of a name, or array of names, for a given duration. + * + * Calls `ETHRegistrarController.rentPrice(label, duration)` on the legacy v1 controller. + * Pricing is ETH-native (no payment token), and `premium` reflects the post-expiry + * exponential-decay premium curve on the legacy price oracle. + * + * When given an array of names, the reads are batched into a single Multicall3 + * round-trip and the returned `base`/`premium` are summed across all names (atomic + * against one block). + * + * @param client - {@link Client} + * @param parameters - {@link GetPriceParameters} + * @returns Price data object. {@link GetPriceReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getPrice } from '@ensdomains/ensjs/public/v1' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getPrice(client, { nameOrNames: 'ens.eth', duration: 31536000 }) + * // { base: 352828971668930335n, premium: 0n } + */ +export async function getPrice( + client: RequireClientContracts, + { nameOrNames, duration }: GetPriceParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const controllerAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensEthRegistrarController', + }) + + const durationBigInt = BigInt(duration) + + if (!Array.isArray(nameOrNames)) { + const { base, premium } = await getAction( + client, + readContract, + 'readContract', + )({ + address: controllerAddress, + abi: ethRegistrarControllerRentPriceSnippet, + functionName: 'rentPrice', + args: [extractLabel(nameOrNames), durationBigInt], + }) + return { base, premium } + } + + const labels = nameOrNames.map(extractLabel) + + const results = await getAction( + client, + multicall, + 'multicall', + )({ + allowFailure: false, + contracts: labels.map( + (label) => + ({ + address: controllerAddress, + abi: ethRegistrarControllerRentPriceSnippet, + functionName: 'rentPrice', + args: [label, durationBigInt], + }) as const, + ), + }) + + let totalBase = 0n + let totalPremium = 0n + for (const { base, premium } of results) { + totalBase += base + totalPremium += premium + } + + return { base: totalBase, premium: totalPremium } +} diff --git a/packages/ensjs/src/functions/public/getWrapperData.test.ts b/packages/ensjs/src/actions/public/v1/getWrapperData.test.ts similarity index 55% rename from packages/ensjs/src/functions/public/getWrapperData.test.ts rename to packages/ensjs/src/actions/public/v1/getWrapperData.test.ts index 981578bad..5075e89ea 100644 --- a/packages/ensjs/src/functions/public/getWrapperData.test.ts +++ b/packages/ensjs/src/actions/public/v1/getWrapperData.test.ts @@ -1,6 +1,6 @@ -import { describe, expect, it } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getWrapperData from './getWrapperData.js' +import { assert, describe, expect, it } from 'vitest' +import { publicClient } from '../../../test/addTestContracts.js' +import { getWrapperData } from './getWrapperData.js' describe('getWrapperData', () => { it('should return null for an unwrapped name', async () => { @@ -13,11 +13,9 @@ describe('getWrapperData', () => { const result = await getWrapperData(publicClient, { name: 'test.wrapped-with-subnames.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.fuses.child.CAN_DO_EVERYTHING).toBe(true) - expect(result.fuses.value).toBe(0) - } + assert.isNotNull(result) + expect(result?.fuses.child.CAN_DO_EVERYTHING).toBe(true) + expect(result?.fuses.value).toBe(0) }) // it('should return with other correct fuses', async () => { // const tx = await ensInstance.setFuses('wrapped.eth', { @@ -47,34 +45,14 @@ describe('getWrapperData', () => { name: 'wrapped.eth', }) expect(result).toBeTruthy() - if (result) { - expect(result.expiry!.date).toBeInstanceOf(Date) - expect(typeof result.expiry!.value).toBe('bigint') - expect(Number.isNaN(result.expiry!.date.getTime())).toBe(false) - } + assert.isNotNull(result) + expect(result?.expiry).toBeTypeOf('bigint') }) it('should return correct expiry for large expiry', async () => { const result = await getWrapperData(publicClient, { name: 'wrapped-big-duration.eth', }) - expect(result).toBeTruthy() - if (result) { - expect(result.expiry!.date).toBeInstanceOf(Date) - expect(typeof result.expiry!.value).toBe('bigint') - expect(result.expiry!.date.getFullYear()).toBe(275760) - expect(Number.isNaN(result.expiry!.date.getTime())).toBe(false) - } - }) - it('should return correct max expiry for expiry larger than maximum for date', async () => { - const result = await getWrapperData(publicClient, { - name: 'wrapped-max-duration.eth', - }) - expect(result).toBeTruthy() - if (result) { - expect(result.expiry!.date).toBeInstanceOf(Date) - expect(typeof result.expiry!.value).toBe('bigint') - expect(result.expiry!.date.getFullYear()).toBe(275760) - expect(Number.isNaN(result.expiry!.date.getTime())).toBe(false) - } + assert.isNotNull(result) + expect(result?.expiry).toBeTypeOf('bigint') }) }) diff --git a/packages/ensjs/src/actions/public/v1/getWrapperData.ts b/packages/ensjs/src/actions/public/v1/getWrapperData.ts new file mode 100644 index 000000000..b00176272 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getWrapperData.ts @@ -0,0 +1,86 @@ +import { nameWrapperGetDataSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Address, + type Chain, + type GetChainContractAddressErrorType, + type NamehashErrorType, + namehash, + type ReadContractErrorType, + zeroAddress, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import type { Prettify } from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { type DecodedFuses, decodeFuses } from '../../../utils/fuses.js' + +export type GetWrapperDataParameters = { + /** Name to get wrapper data for */ + name: string +} + +export type GetWrapperDataReturnType = Prettify<{ + /** Fuse object */ + fuses: DecodedFuses & { + value: number + } + /** Expiry of the name */ + expiry: bigint | null + /** Owner of the name */ + owner: Address +} | null> + +export type GetWrapperDataErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | NamehashErrorType + +/** + * Gets the wrapper data for a name. + * @param client - {@link Client} + * @param parameters - {@link GetWrapperDataParameters} + * @returns Wrapper data object, or null if name is not wrapped. {@link GetWrapperDataReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getWrapperData } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getWrapperData(client, { name: 'ilikelasagna.eth' }) + */ +export async function getWrapperData( + client: RequireClientContracts, + { name }: GetWrapperDataParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + const [owner, fuses, expiry] = await readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + abi: nameWrapperGetDataSnippet, + functionName: 'getData', + args: [BigInt(namehash(name))], + }) + + if (owner === zeroAddress) return null + + const fuseObj = decodeFuses(fuses) + return { + fuses: { + ...fuseObj, + value: fuses, + }, + expiry: expiry > 0 ? expiry * 1000n : null, + owner, + } +} diff --git a/packages/ensjs/src/functions/public/getWrapperName.test.ts b/packages/ensjs/src/actions/public/v1/getWrapperName.test.ts similarity index 84% rename from packages/ensjs/src/functions/public/getWrapperName.test.ts rename to packages/ensjs/src/actions/public/v1/getWrapperName.test.ts index 9ed4cc659..e5f7d9ed4 100644 --- a/packages/ensjs/src/functions/public/getWrapperName.test.ts +++ b/packages/ensjs/src/actions/public/v1/getWrapperName.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getWrapperName from './getWrapperName.js' +import { publicClient } from '../../../test/addTestContracts.js' +import { getWrapperName } from './getWrapperName.js' describe('getWrapperName()', () => { it('should return name for existing name', async () => { diff --git a/packages/ensjs/src/actions/public/v1/getWrapperName.ts b/packages/ensjs/src/actions/public/v1/getWrapperName.ts new file mode 100644 index 000000000..b975e8879 --- /dev/null +++ b/packages/ensjs/src/actions/public/v1/getWrapperName.ts @@ -0,0 +1,74 @@ +import { nameWrapperNamesSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Chain, + type GetChainContractAddressErrorType, + type HexToBytesErrorType, + hexToBytes, + type NamehashErrorType, + namehash, + type ReadContractErrorType, + type TrimErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction, trim } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type BytesToPacketErrorType, + bytesToPacket, +} from '../../../utils/name/hexEncodedName.js' + +export type GetWrapperNameParameters = { + /** Name with unknown labels, e.g. "[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth" */ + name: string +} + +export type GetWrapperNameReturnType = string | null + +export type GetWrapperNameErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | NamehashErrorType + | TrimErrorType + | BytesToPacketErrorType + | HexToBytesErrorType + +/** + * Gets the full name for a name with unknown labels from the NameWrapper. + * @param client - {@link Client} + * @param parameters - {@link GetWrapperNameParameters} + * @returns Full name, or null if name was not found. {@link GetWrapperNameReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getWrapperName } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getWrapperName(client, { name: '[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth' }) + * // wrapped.eth + */ +export async function getWrapperName( + client: RequireClientContracts, + { name }: GetWrapperNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + const result = await readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + abi: nameWrapperNamesSnippet, + functionName: 'names', + args: [namehash(name)], + }) + if (!result || result === '0x' || trim(result) === '0x00') return null + return bytesToPacket(hexToBytes(result)) +} diff --git a/packages/ensjs/src/actions/public/v2/applyDiscount.test.ts b/packages/ensjs/src/actions/public/v2/applyDiscount.test.ts new file mode 100644 index 000000000..b7dbc551a --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/applyDiscount.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it } from 'vitest' +import { publicClient } from '../../../test/addTestContracts.js' +import { applyDiscount } from './applyDiscount.js' + +const ONE_YEAR = 31_557_600n +const TWENTY_EIGHT_DAYS = 86_400n * 28n + +describe('applyDiscount', () => { + it('returns the input unchanged at the 1y tier (no discount)', async () => { + const result = await applyDiscount(publicClient, { + value: 10_000n, + duration: ONE_YEAR, + }) + + expect(result).toBe(10_000n) + }) + + it('returns the input unchanged for short durations below the 1y tier', async () => { + const result = await applyDiscount(publicClient, { + value: 10_000n, + duration: TWENTY_EIGHT_DAYS, + }) + + expect(result).toBe(10_000n) + }) + + it('returns zero when value is zero (no discount on nothing)', async () => { + const result = await applyDiscount(publicClient, { + value: 0n, + duration: ONE_YEAR * 5n, + }) + + expect(result).toBe(0n) + }) + + it('discounts longer durations more aggressively than linearly scaling 1y', async () => { + const oneYear = await applyDiscount(publicClient, { + value: 10_000n, + duration: ONE_YEAR, + }) + + const fiveYears = await applyDiscount(publicClient, { + value: 10_000n * 5n, + duration: ONE_YEAR * 5n, + }) + + expect(fiveYears).toBeLessThan(oneYear * 5n) + expect(fiveYears).toBeGreaterThan(0n) + }) + + it('discount is monotonic — longer duration yields ≤ shorter duration at equal value', async () => { + const at1y = await applyDiscount(publicClient, { + value: 100_000n, + duration: ONE_YEAR, + }) + const at5y = await applyDiscount(publicClient, { + value: 100_000n, + duration: ONE_YEAR * 5n, + }) + const at10y = await applyDiscount(publicClient, { + value: 100_000n, + duration: ONE_YEAR * 10n, + }) + + expect(at5y).toBeLessThanOrEqual(at1y) + expect(at10y).toBeLessThanOrEqual(at5y) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/applyDiscount.ts b/packages/ensjs/src/actions/public/v2/applyDiscount.ts new file mode 100644 index 000000000..5fca29ea9 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/applyDiscount.ts @@ -0,0 +1,54 @@ +import { standardRentPriceOracleApplyDiscountSnippet } from '@ensdomains/ensjs-abi/v2/standardRentPriceOracle' +import type { + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type ApplyDiscountParameters = { + /** Undiscounted value (e.g. baseRate × duration), in standard units */ + value: bigint + /** Term length in seconds — selects the discount tier */ + duration: bigint +} + +export type ApplyDiscountReturnType = bigint + +export type ApplyDiscountErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + +/** + * Applies the oracle's duration-tiered discount to an arbitrary value. The + * oracle uses a step-function keyed on duration; doing this on-chain keeps the + * caller agnostic to the contract's discount denominator. + * + * @param client - {@link Client} + * @param parameters - {@link ApplyDiscountParameters} + * @returns The discounted value. {@link ApplyDiscountReturnType} + */ +export async function applyDiscount( + client: RequireClientContracts, + { value, duration }: ApplyDiscountParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensStandardRentPriceOracle', + }), + abi: standardRentPriceOracleApplyDiscountSnippet, + functionName: 'applyDiscount', + args: [value, duration], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getAvailable.test.ts b/packages/ensjs/src/actions/public/v2/getAvailable.test.ts new file mode 100644 index 000000000..0f05f722c --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getAvailable.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from 'vitest' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import { publicClient as publicClientL2 } from '../../../test/addTestContracts.js' +import { getAvailable } from './getAvailable.js' + +describe('getAvailable', () => { + describe('eth-2ld', () => { + it('should return false for a registered name', async () => { + // `test.eth` is registered by the devnet's --testNames + const result = await getAvailable(publicClientL2, { name: 'test.eth' }) + expect(result).toBe(false) + }) + + it('should return true for an unregistered name', async () => { + const result = await getAvailable(publicClientL2, { + name: 'available-name.eth', + }) + expect(result).toBe(true) + }) + + it('should return false for a RESERVED name', async () => { + // `reserved.eth` is reserved (no owner, no token) by the devnet. + // The registrar's isAvailable must treat reserved names as unavailable. + const result = await getAvailable(publicClientL2, { + name: 'reserved.eth', + }) + expect(result).toBe(false) + }) + }) + + describe('eth-subname (3LD and beyond)', () => { + it('should return false for a registered subname', async () => { + const result = await getAvailable(publicClientL2, { + name: 'sub2.parent.eth', + }) + expect(result).toBe(false) + }) + + it('should return true for an available subname', async () => { + const result = await getAvailable(publicClientL2, { + name: 'definitely-available-sub.parent.eth', + }) + expect(result).toBe(true) + }) + + it('should return false for a registered 3LD (sub1.sub2.parent.eth)', async () => { + const result = await getAvailable(publicClientL2, { + name: 'sub1.sub2.parent.eth', + }) + expect(result).toBe(false) + }) + + it('should return false for a registered 4LD (wallet.sub1.sub2.parent.eth)', async () => { + const result = await getAvailable(publicClientL2, { + name: 'wallet.sub1.sub2.parent.eth', + }) + expect(result).toBe(false) + }) + + it('should return true for a deep available subname under an existing parent', async () => { + // `available.sub1.sub2.parent.eth` is not registered, but its parent + // registry (sub1.sub2.parent.eth) exists. + const result = await getAvailable(publicClientL2, { + name: 'available.sub1.sub2.parent.eth', + }) + expect(result).toBe(true) + }) + + it('should return true for a subname under a parent with no subregistry', async () => { + // `test.eth` has no subregistry, so any subname under it is available. + const result = await getAvailable(publicClientL2, { + name: 'anything.test.eth', + }) + expect(result).toBe(true) + }) + }) + + it('should throw for unsupported name types', async () => { + // DNS 2LD, DNS subname, eth tld, non-eth tld, and root + for (const name of ['example.com', 'sub.example.com', 'eth', 'com', '']) { + await expect(getAvailable(publicClientL2, { name })).rejects.toThrowError( + UnsupportedNameTypeError, + ) + } + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getAvailable.ts b/packages/ensjs/src/actions/public/v2/getAvailable.ts new file mode 100644 index 000000000..547477b18 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getAvailable.ts @@ -0,0 +1,131 @@ +import { universalResolverV2FindParentRegistrySnippet } from '@ensdomains/ensjs-abi/universalResolver' +import { ethRegistrarAvailableSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import { permissionedRegistryGetStatusSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import type { + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { labelhash, toHex, zeroAddress } from 'viem' +import { readContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import type { ErrorType } from '../../../errors/utils.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { getNameType } from '../../../utils/name/getNameType.js' + +export type GetAvailableParameters = { + /** + * Name to check availability for. Only `.eth` names are supported: + * - `eth-2ld` names (e.g. `name.eth`) + * - `eth-subname` names of any depth (e.g. `sub.name.eth`) + * + * DNS names are not supported (they have both on-chain and off-chain + * import paths, so a single availability answer would be ambiguous). + */ + name: string +} + +export type GetAvailableReturnType = boolean + +export type GetAvailableErrorType = + | UnsupportedNameTypeError + | ReadContractErrorType + | GetChainContractAddressErrorType + | ErrorType + +/** + * Gets the availability of a `.eth` name to register. + * + * For `eth-2ld` names, availability is read from the `.eth` registrar via + * `isAvailable`, which accounts for grace periods and premium decay. + * + * For `eth-subname` names, the Universal Resolver V2's `findParentRegistry` + * is used to traverse the registry tree and locate the parent registry, then + * the leaf label's status is checked against it. + * + * @param client - {@link Client} + * @param parameters - {@link GetAvailableParameters} + * @returns Availability as boolean. {@link GetAvailableReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getAvailable } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * + * // eth-2ld via the .eth registrar + * const a = await getAvailable(client, { name: 'ens.eth' }) + * // false + * + * // eth-subname with auto-traversal via Universal Resolver + * const b = await getAvailable(client, { name: 'sub.ens.eth' }) + */ +export async function getAvailable( + client: RequireClientContracts< + chain, + 'ensEthRegistrar' | 'ensUniversalResolver' + >, + { name }: GetAvailableParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const labels = name.split('.') + const nameType = getNameType(name) + + if (nameType !== 'eth-2ld' && nameType !== 'eth-subname') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld', 'eth-subname'], + details: + 'Only .eth 2LD and subname names can be checked for availability', + }) + + const readContractAction = getAction(client, readContract, 'readContract') + const leafLabel = labels[0] + + // eth-2ld: availability comes from the .eth registrar (rent/premium aware). + if (nameType === 'eth-2ld') { + return readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensEthRegistrar', + }), + abi: ethRegistrarAvailableSnippet, + functionName: 'isAvailable', + args: [leafLabel], + }) + } + + // eth-subname: use the Universal Resolver to find the parent registry, then + // check the leaf label's status. A missing parent registry means the name is + // available. The configured `ensUniversalResolver` exposes `findParentRegistry` + // for both v1 and v2 names. + const universalResolverAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }) + const parentRegistry = await readContractAction({ + address: universalResolverAddress, + abi: universalResolverV2FindParentRegistrySnippet, + functionName: 'findParentRegistry', + args: [toHex(packetToBytes(name))], + }) + if (parentRegistry === zeroAddress) return true + + const status = await readContractAction({ + address: parentRegistry, + abi: permissionedRegistryGetStatusSnippet, + functionName: 'getStatus', + args: [BigInt(labelhash(leafLabel))], + }) + return status === 0 +} diff --git a/packages/ensjs/src/actions/public/v2/getBaseRates.test.ts b/packages/ensjs/src/actions/public/v2/getBaseRates.test.ts new file mode 100644 index 000000000..4438b9cd3 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getBaseRates.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' +import { publicClient } from '../../../test/addTestContracts.js' +import { getBaseRates } from './getBaseRates.js' + +// Pinned base-rate table from the devnet StandardRentPriceOracle deployment. +// Array is indexed by `label length - 1`; lengths 1-2 return 0 (oracle +// rejects labels shorter than 3 chars). Lengths beyond the table clamp to +// the tail entry. +const EXPECTED_RATES = [ + 0n, // length 1 + 0n, // length 2 + 20_280_377n, // length 3 + 5_070_095n, // length 4 + 253_505n, // length 5+ (clamps) +] as const + +describe('getBaseRates', () => { + it('returns the pinned per-length base-rate table from the oracle', async () => { + const rates = await getBaseRates(publicClient) + + expect(rates).toEqual(EXPECTED_RATES) + }) + + it('returns rates that are monotonically non-increasing across the valid (3+ char) range', async () => { + const rates = await getBaseRates(publicClient) + + for (let i = 3; i < rates.length; i++) { + expect(rates[i]).toBeLessThanOrEqual(rates[i - 1]) + } + }) + + it('returns zero rates for the two sub-3-character slots and positive thereafter', async () => { + const rates = await getBaseRates(publicClient) + + expect(rates[0]).toBe(0n) // length 1 + expect(rates[1]).toBe(0n) // length 2 + for (let i = 2; i < rates.length; i++) { + expect(rates[i]).toBeGreaterThan(0n) + } + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getBaseRates.ts b/packages/ensjs/src/actions/public/v2/getBaseRates.ts new file mode 100644 index 000000000..021ab1005 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getBaseRates.ts @@ -0,0 +1,44 @@ +import { standardRentPriceOracleGetBaseRatesSnippet } from '@ensdomains/ensjs-abi/v2/standardRentPriceOracle' +import type { + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetBaseRatesReturnType = readonly bigint[] + +export type GetBaseRatesErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + +/** + * Gets the per-codepoint base rate table from the StandardRentPriceOracle. The + * array is indexed by `label length - 1` (clamped to the last entry for longer + * labels), each value being the base rate per second in standard units. + * + * @param client - {@link Client} + * @returns The base rate table. {@link GetBaseRatesReturnType} + */ +export async function getBaseRates( + client: RequireClientContracts, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensStandardRentPriceOracle', + }), + abi: standardRentPriceOracleGetBaseRatesSnippet, + functionName: 'getBaseRates', + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getExpiry.test.ts b/packages/ensjs/src/actions/public/v2/getExpiry.test.ts new file mode 100644 index 000000000..568a3f095 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getExpiry.test.ts @@ -0,0 +1,25 @@ +import { expect, it } from 'vitest' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getExpiry } from './getExpiry.js' + +it('should return non-zero expiry for a V2 name', async () => { + const expiry = await getExpiry(client, { + name: 'parent.eth', + registryAddress: deploymentAddresses.ETHRegistry, + }) + + expect(expiry > 0n).toEqual(true) +}) + +it('should throw for a non-.eth name', async () => { + await expect( + getExpiry(client, { + name: 'test.com', + registryAddress: deploymentAddresses.ETHRegistry, + }), + ).rejects.toThrowError(UnsupportedNameTypeError) +}) diff --git a/packages/ensjs/src/actions/public/v2/getExpiry.ts b/packages/ensjs/src/actions/public/v2/getExpiry.ts new file mode 100644 index 000000000..4b226e719 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getExpiry.ts @@ -0,0 +1,62 @@ +import { permissionedRegistryGetExpirySnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { + type Address, + type Chain, + labelhash, + type ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { type GetChainContractAddressErrorType, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../../errors/general.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { getNameType } from '../../../utils/name/getNameType.js' +import { checkIsDotEth } from '../../../utils/name/validation.js' + +export type GetExpiryParameters = { + /** Name to get expiry for */ + name: string + /** Optional custom registry address (defaults to chain's ensRegistry) */ + registryAddress?: Address +} + +export type GetExpiryReturnType = bigint + +export type GetExpiryErrorType = + | UnsupportedNameTypeError + | GetChainContractAddressErrorType + | ReadContractErrorType + +export async function getExpiry( + client: RequireClientContracts, + { name, registryAddress }: GetExpiryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + const labels = name.split('.') + const nameType = getNameType(name) + if (!checkIsDotEth(labels) && nameType !== 'eth-subname') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld', 'eth-subname'], + details: 'Only .eth names can be looked up via the registry', + }) + + const readContractAction = getAction(client, readContract, 'readContract') + + const currentRegistry = + registryAddress ?? + getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }) + + const labelHash = BigInt(labelhash(labels[0])) + + return readContractAction({ + address: currentRegistry, + abi: permissionedRegistryGetExpirySnippet, + functionName: 'getExpiry', + args: [labelHash], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getNameRegistries.test.ts b/packages/ensjs/src/actions/public/v2/getNameRegistries.test.ts new file mode 100644 index 000000000..332c041a4 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRegistries.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getNameRegistries } from './getNameRegistries.js' + +describe('getNameRegistries', () => { + it('should return registries for a V2 name', async () => { + let registries: readonly string[] = [] + try { + registries = await getNameRegistries(client, { + name: 'test.eth', + address: deploymentAddresses.UniversalResolverV2, + }) + } catch (e) { + if ( + e instanceof Error && + e.message.includes('returned no data') && + e.message.includes('findRegistries') + ) { + return + } + throw e + } + + expect(registries.length).toBeGreaterThan(0) + expect(registries).toBeInstanceOf(Array) + registries.forEach((registry) => { + expect(registry).toMatch(/^0x[a-fA-F0-9]{40}$/) + }) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getNameRegistries.ts b/packages/ensjs/src/actions/public/v2/getNameRegistries.ts new file mode 100644 index 000000000..3c23c9ded --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRegistries.ts @@ -0,0 +1,58 @@ +import { universalResolverFindRegistriesSnippet } from '@ensdomains/ensjs-abi/universalResolver' +import type { + Address, + Chain, + NamehashErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { + type GetChainContractAddressErrorType, + getAction, + toHex, +} from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetNameRegistriesParameters = { + name: string + address?: Address +} + +export type GetNameRegistriesReturnType = readonly Address[] + +export type GetNameRegistriesErrorType = + | GetChainContractAddressErrorType + | ReadContractErrorType + | NamehashErrorType + +/** + * Find all registries in the ancestry of `name`. + * @param client - {@link Client} + * @param parameters - {@link GetNameRegistriesParameters} + * @returns Array of registry addresses. {@link GetNameRegistriesReturnType} + */ +export async function getNameRegistries( + client: RequireClientContracts, + { name, address }: GetNameRegistriesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const contractAddress = + address ?? + getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: contractAddress, + abi: universalResolverFindRegistriesSnippet, + functionName: 'findRegistries', + args: [toHex(packetToBytes(name))], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getNameRegistryAddress.ts b/packages/ensjs/src/actions/public/v2/getNameRegistryAddress.ts new file mode 100644 index 000000000..b9ff8cab8 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRegistryAddress.ts @@ -0,0 +1,69 @@ +import { registryGetSubregistrySnippet } from '@ensdomains/ensjs-abi/registry' +import type { Client } from 'viem' +import type { Address } from 'viem/accounts' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetNameRegistryAddressParameters = { + /** + * The parent registry address that manages this label. + * Example: the .eth registry address when querying `flo` for `flo.eth`. + */ + registryAddress: Address + + /** + * The label whose registry we want to look up. + * Example: "flo" for "flo.eth". + */ + label: string +} + +export type GetNameRegistryAddressReturnType = Address + +export type GetNameRegistryAddressErrorType = ReadContractErrorType + +/** + * Gets the subregistry (registry for a label) from a parent registry. + * + * This calls `getSubregistry(label)` on the given `registryAddress`. + * If no registry is set for the label, this typically returns the zero address. + * + * @param client - {@link Client} + * @param parameters - {@link GetNameRegistryAddressParameters} + * @returns Address of the subregistry, or the zero address if none is set. {@link GetNameRegistryAddressReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getNameRegistryAddress } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * + * // Example for label "flo" under the .eth registry + * const registryAddress = '0xEthRegistryAddress' as const + * const result = await getNameRegistryAddress(client, { + * registryAddress, + * label: 'flo', + * }) + * // result: Address of flo.eth's registry, or 0x000... if none set. + */ +export async function getNameRegistryAddress( + client: Client, + { registryAddress, label }: GetNameRegistryAddressParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: registryAddress, + abi: registryGetSubregistrySnippet, + functionName: 'getSubregistry', + args: [label], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.test.ts b/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.test.ts new file mode 100644 index 000000000..afc3028b9 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getNameRoleAccounts } from './getNameRolesAccounts.js' + +describe('getNameRoleAccounts', () => { + it('returns a map of accounts and roles', async () => { + const accounts = await getNameRoleAccounts(client, { + label: 'changerole', + registryAddress: deploymentAddresses.ETHRegistry, + }) + + expect(accounts.size).toBeGreaterThan(0) + expect( + accounts.get('0x70997970C51812dc3A010C7d01b50e0d17dc79C8'), + ).toBeDefined() + }) + + it('returns an empty map for a non-existent label', async () => { + const accounts = await getNameRoleAccounts(client, { + label: 'thislabeldoesnotexist12345', + fromBlock: 0n, + toBlock: 'latest', + registryAddress: deploymentAddresses.ETHRegistry, + }) + + expect(accounts).toEqual(new Map()) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.ts b/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.ts new file mode 100644 index 000000000..7bb333b62 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRolesAccounts.ts @@ -0,0 +1,91 @@ +import { + eacRolesEvents, + permissionedRegistryGetStateSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { + type Client, + type GetLogsErrorType, + type GetLogsParameters, + labelhash, + zeroAddress, +} from 'viem' +import type { Address } from 'viem/accounts' +import { getLogs, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { registryRoles } from '../../../utils/v2/index.js' +import { + decodeRoleCounts, + type RoleName, +} from '../../../utils/v2/roles/decodeRoleCounts.js' + +export type GetNameRolesAccountsParameters = { + registryAddress: Address + label: string +} & Pick + +export type GetNameRolesAccountsReturnType = Map< + Address, + RoleName[] +> + +export type GetNameRolesAccountsErrorType = GetLogsErrorType + +/** + * Get registry role accounts for a name + * @param client - {@link Client} + * @param parameters - {@link GetNameRolesAccountsParameters} + * @returns Filtered logs {@link GetNameRolesAccountsReturnType} + * + */ +export async function getNameRoleAccounts( + client: Client, + { registryAddress, label, ...params }: GetNameRolesAccountsParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + const getLogsAction = getAction(client, getLogs, 'getLogs') + + const labelHash = BigInt(labelhash(label)) + + const state = await readContractAction({ + address: registryAddress, + abi: permissionedRegistryGetStateSnippet, + functionName: 'getState', + args: [labelHash], + }) + + const resource = state.resource + + const logs = await getLogsAction({ + address: registryAddress, + events: eacRolesEvents, + fromBlock: params.fromBlock ?? 0n, + toBlock: params.toBlock ?? undefined, + // @ts-expect-error viem type error + args: { + resource, + }, + }) + + const roles = new Map[]>() + + for (const log of logs) { + if (log.args.account && log.args.account !== zeroAddress) { + if (log.eventName === 'EACRolesChanged') { + const bitmap = log.args.newRoleBitmap! + const counts = decodeRoleCounts(bitmap, registryRoles) + + roles.set( + log.args.account, + Object.keys(counts).filter( + (key) => counts[key as keyof typeof counts] === 1, + ), + ) + } + } + } + + return roles +} diff --git a/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.test.ts b/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.test.ts new file mode 100644 index 000000000..c6b9e5ff5 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getNameRolesForAccount } from './getNameRolesForAccount.js' + +describe('getNameRolesForAccount', () => { + it('returns roles for the owner of a registered name', async () => { + const result = await getNameRolesForAccount(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'parent', + account: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', + }) + + expect(result.decoded).toEqual([ + 'ROLE_CAN_TRANSFER_ADMIN', + 'ROLE_SET_RESOLVER', + 'ROLE_SET_RESOLVER_ADMIN', + 'ROLE_SET_SUBREGISTRY', + 'ROLE_SET_SUBREGISTRY_ADMIN', + ]) + }) + + it('returns SET_RESOLVER for user granted that role on changerole.eth', async () => { + const result = await getNameRolesForAccount(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + account: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', + }) + + expect(result.decoded).toEqual(['ROLE_SET_RESOLVER']) + }) + + it('returns empty roles for an account with no roles', async () => { + const result = await getNameRolesForAccount(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'parent', + account: '0x90F79bf6EB2c4f870365E785982E1f101E93b906', + }) + + expect(result.raw).toBe(0n) + expect(result.decoded).toEqual([]) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.ts b/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.ts new file mode 100644 index 000000000..e9b666d3e --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getNameRolesForAccount.ts @@ -0,0 +1,69 @@ +import { permissionedRegistryRolesSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { type Client, labelhash } from 'viem' +import type { Address } from 'viem/accounts' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { decodeRoleCounts, registryRoles } from '../../../utils/v2/index.js' + +export type GetNameRolesForAccountParameters = { + registryAddress: Address + account: Address + label: string +} + +export type GetNameRolesForAccountReturnType = { + decoded: Array + raw: bigint +} + +export type GetNameRolesForAccountErrorType = ReadContractErrorType + +/** + * Gets the registry's roles for an account. + * @param client - {@link Client} + * @param parameters - {@link GetRolesForAccountParameters} + * @returns Decoded roles bitmap and raw value. {@link GetRolesForAccountReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getRolesForAccount } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * + * const result = await getRolesForAccount(client, { + * registryAddress: '0x0f3eb298470639a96bd548cea4a648bc80b2cee2', + * account: '0x1234...abcd', + * }) + */ +export async function getNameRolesForAccount( + client: Client, + { registryAddress, account, label }: GetNameRolesForAccountParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + const roleBitmap = await readContractAction({ + address: registryAddress, + abi: permissionedRegistryRolesSnippet, + functionName: 'roles', + args: [BigInt(labelhash(label)), account], + }) + + const roleCounts = decodeRoleCounts(roleBitmap, registryRoles) + + const decoded = ( + Object.keys(roleCounts) as Array + ).filter((key) => roleCounts[key] !== 0) + + return { + decoded, + raw: roleBitmap, + } +} diff --git a/packages/ensjs/src/actions/public/v2/getOwner.test.ts b/packages/ensjs/src/actions/public/v2/getOwner.test.ts new file mode 100644 index 000000000..09da44591 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getOwner.test.ts @@ -0,0 +1,22 @@ +import { zeroAddress } from 'viem' +import { describe, expect, it } from 'vitest' +import { publicClient as client } from '../../../test/addTestContracts.js' +import { getOwner } from './getOwner.js' + +describe('getOwner', () => { + it('returns the owner address for a V2 name', async () => { + const owner = await getOwner(client, { + name: 'example.eth', + }) + + expect(owner).not.toEqual(zeroAddress) + }) + + it('returns zero address for a non-existent name', async () => { + const owner = await getOwner(client, { + name: 'thislabeldoesnotexistatall.eth', + }) + + expect(owner).toEqual(zeroAddress) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getOwner.ts b/packages/ensjs/src/actions/public/v2/getOwner.ts new file mode 100644 index 000000000..f2d3e484c --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getOwner.ts @@ -0,0 +1,55 @@ +import { universalResolverV2FindOwnerSnippet } from '@ensdomains/ensjs-abi/universalResolver' +import type { + Address, + Chain, + NamehashErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { + type GetChainContractAddressErrorType, + getAction, + toHex, +} from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetOwnerParameters = { + name: string +} + +export type GetOwnerReturnType = Address + +export type GetOwnerErrorType = + | GetChainContractAddressErrorType + | ReadContractErrorType + | NamehashErrorType + +/** + * Find the owner for a V2 name of any depth. + * @param client - {@link Client} + * @param parameters - {@link GetOwnerParameters} + * @returns The owner address, or the zero address if unowned or not found. {@link GetOwnerReturnType} + */ +export async function getOwner( + client: RequireClientContracts, + { name }: GetOwnerParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const contractAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensUniversalResolver', + }) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: contractAddress, + abi: universalResolverV2FindOwnerSnippet, + functionName: 'findOwner', + args: [toHex(packetToBytes(name))], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.test.ts b/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.test.ts new file mode 100644 index 000000000..5c2641329 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest' +import { publicClient } from '../../../test/addTestContracts.js' +import { getPremiumDecayParams } from './getPremiumDecayParams.js' + +describe('getPremiumDecayParams', () => { + it('reads the three immutable premium-decay constants from the oracle', async () => { + const { priceInitial, halvingPeriod, period } = + await getPremiumDecayParams(publicClient) + + expect(typeof priceInitial).toBe('bigint') + expect(typeof halvingPeriod).toBe('bigint') + expect(typeof period).toBe('bigint') + }) + + it('returns a positive initial premium', async () => { + const { priceInitial } = await getPremiumDecayParams(publicClient) + + expect(priceInitial).toBeGreaterThan(0n) + }) + + it('returns a halvingPeriod that strictly divides into the premium window', async () => { + const { halvingPeriod, period } = await getPremiumDecayParams(publicClient) + + expect(halvingPeriod).toBeGreaterThan(0n) + expect(period).toBeGreaterThan(0n) + expect(period).toBeGreaterThanOrEqual(halvingPeriod) + // Premium decays via halvings; the window should contain at least one halving step. + expect(period / halvingPeriod).toBeGreaterThanOrEqual(1n) + }) + + it('returns stable values across calls (constants are immutable)', async () => { + const a = await getPremiumDecayParams(publicClient) + const b = await getPremiumDecayParams(publicClient) + + expect(a).toEqual(b) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.ts b/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.ts new file mode 100644 index 000000000..3b08841a4 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getPremiumDecayParams.ts @@ -0,0 +1,75 @@ +import { standardRentPriceOracleConstantsSnippet } from '@ensdomains/ensjs-abi/v2/standardRentPriceOracle' +import type { + Chain, + GetChainContractAddressErrorType, + MulticallErrorType, +} from 'viem' +import { multicall } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetPremiumDecayParamsReturnType = { + /** Starting value of the expiry-premium exponential decay, in standard units */ + priceInitial: bigint + /** Seconds for the premium to halve */ + halvingPeriod: bigint + /** Total premium window in seconds (premium reaches zero at this offset) */ + period: bigint +} + +export type GetPremiumDecayParamsErrorType = + | MulticallErrorType + | GetChainContractAddressErrorType + +/** + * Reads the StandardRentPriceOracle's immutable expiry-premium decay parameters + * (`PREMIUM_PRICE_INITIAL`, `PREMIUM_HALVING_PERIOD`, `PREMIUM_PERIOD`) in a + * single multicall. Useful for plotting the premium curve client-side without + * sampling `getPremiumPriceAfter` repeatedly. + * + * @param client - {@link Client} + * @returns Premium decay parameters. {@link GetPremiumDecayParamsReturnType} + */ +export async function getPremiumDecayParams( + client: RequireClientContracts, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const oracleAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensStandardRentPriceOracle', + }) + + const multicallAction = getAction(client, multicall, 'multicall') + + const [priceInitial, halvingPeriod, period] = await multicallAction({ + allowFailure: false, + contracts: [ + { + address: oracleAddress, + abi: standardRentPriceOracleConstantsSnippet, + functionName: 'PREMIUM_PRICE_INITIAL', + }, + { + address: oracleAddress, + abi: standardRentPriceOracleConstantsSnippet, + functionName: 'PREMIUM_HALVING_PERIOD', + }, + { + address: oracleAddress, + abi: standardRentPriceOracleConstantsSnippet, + functionName: 'PREMIUM_PERIOD', + }, + ], + }) + + return { + priceInitial, + halvingPeriod: BigInt(halvingPeriod), + period: BigInt(period), + } +} diff --git a/packages/ensjs/src/actions/public/v2/getRegisterPrice.test.ts b/packages/ensjs/src/actions/public/v2/getRegisterPrice.test.ts new file mode 100644 index 000000000..7b5da6be0 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRegisterPrice.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, +} from '../../../test/addTestContracts.js' +import { getRegisterPrice } from './getRegisterPrice.js' + +const paymentToken = deploymentAddresses.USDC + +// Per-second base rates from StandardRentPriceOracle.getBaseRates(), indexed +// by `label length - 1`. Lengths 1-2 are zero (oracle rejects sub-3-char labels); +// lengths beyond the table clamp to the tail entry (5+ chars). +const BASE_RATE_PER_SEC_3CHAR = 20_280_377n +const BASE_RATE_PER_SEC_4CHAR = 5_070_095n +const BASE_RATE_PER_SEC_5CHAR = 253_505n + +// USDC payment token ratio (numer / denom) from getPaymentTokenRatio() +const RATIO_NUMER = 1n +const RATIO_DENOM = 1_000_000n + +// Oracle enforces a 28-day minimum duration; use that as the unit duration for the tests +const ONE_DAY = 86400n +const MIN_DURATION = ONE_DAY * 28n + +// Ceiling division matching the contract's pricing computation +const ceilDiv = (a: bigint, b: bigint) => (a + b - 1n) / b + +const expectedBasePrice = (perSecRate: bigint, duration: bigint) => + ceilDiv(perSecRate * duration * RATIO_NUMER, RATIO_DENOM) + +describe('getRegisterPrice', () => { + it('should return the correct price for a 5+ char label', async () => { + const result = await getRegisterPrice(publicClient, { + label: 'test123', + duration: MIN_DURATION, + paymentToken, + }) + + // 7 chars -> 5+ char rate, no discount for short duration + expect(result.base).toBe( + expectedBasePrice(BASE_RATE_PER_SEC_5CHAR, MIN_DURATION), + ) + expect(result.premium).toBe(0n) + }) + + it('should return a higher price for a 4 char label', async () => { + const result = await getRegisterPrice(publicClient, { + label: 'wxyz', + duration: MIN_DURATION, + paymentToken, + }) + + expect(result.base).toBe( + expectedBasePrice(BASE_RATE_PER_SEC_4CHAR, MIN_DURATION), + ) + expect(result.premium).toBe(0n) + }) + + it('should return the highest price for a 3 char label', async () => { + const result = await getRegisterPrice(publicClient, { + label: 'abc', + duration: MIN_DURATION, + paymentToken, + }) + + expect(result.base).toBe( + expectedBasePrice(BASE_RATE_PER_SEC_3CHAR, MIN_DURATION), + ) + expect(result.premium).toBe(0n) + }) + + it('should scale price linearly with duration (short durations, no discount)', async () => { + const oneUnit = await getRegisterPrice(publicClient, { + label: 'test123', + duration: MIN_DURATION, + paymentToken, + }) + + const twoUnits = await getRegisterPrice(publicClient, { + label: 'test123', + duration: MIN_DURATION * 2n, + paymentToken, + }) + + expect(oneUnit.base).toBe( + expectedBasePrice(BASE_RATE_PER_SEC_5CHAR, MIN_DURATION), + ) + expect(twoUnits.base).toBe( + expectedBasePrice(BASE_RATE_PER_SEC_5CHAR, MIN_DURATION * 2n), + ) + }) + + it('should enforce correct price ordering by label length', async () => { + const duration = MIN_DURATION + + const price3 = await getRegisterPrice(publicClient, { + label: 'abc', + duration, + paymentToken, + }) + const price4 = await getRegisterPrice(publicClient, { + label: 'abcd', + duration, + paymentToken, + }) + const price5 = await getRegisterPrice(publicClient, { + label: 'abcde', + duration, + paymentToken, + }) + + expect(price3.base).toBeGreaterThan(price4.base) + expect(price4.base).toBeGreaterThan(price5.base) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getRegisterPrice.ts b/packages/ensjs/src/actions/public/v2/getRegisterPrice.ts new file mode 100644 index 000000000..954c23ddb --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRegisterPrice.ts @@ -0,0 +1,70 @@ +import { ethRegistrarGetRegisterPriceSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import type { + Address, + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetRegisterPriceParameters = { + /** Label to get price for. Must be a bare label (e.g. `"foo"`), not a name (`"foo.eth"`). */ + label: string + /** Duration in seconds to get price for */ + duration: bigint + /** Payment token address */ + paymentToken: Address +} + +export type GetRegisterPriceReturnType = { + /** Price base value */ + base: bigint + /** Price premium */ + premium: bigint +} + +export type GetRegisterPriceErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + | TypeError + +/** + * Gets the registration price of a label for a given duration. + * + * Internally calls `ETHRegistrar.getRegisterPrice(label, duration, paymentToken)`. The + * registrar derives the `available` period (time elapsed since `expiry + GRACE_PERIOD`) + * from on-chain state and passes it to the rent price oracle; this means the returned + * `premium` already reflects the post-expiry exponential-decay premium curve. + * + * @param client - {@link Client} + * @param parameters - {@link GetRegisterPriceParameters} + * @returns Price data object. {@link GetRegisterPriceReturnType} + */ +export async function getRegisterPrice( + client: RequireClientContracts, + { label, duration, paymentToken }: GetRegisterPriceParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const [base, premium] = await getAction( + client, + readContract, + 'readContract', + )({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensEthRegistrar', + }), + abi: ethRegistrarGetRegisterPriceSnippet, + functionName: 'getRegisterPrice', + args: [label, duration, paymentToken], + }) + + return { base, premium } +} diff --git a/packages/ensjs/src/actions/public/v2/getRegistrationDate.test.ts b/packages/ensjs/src/actions/public/v2/getRegistrationDate.test.ts new file mode 100644 index 000000000..eac7c690f --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRegistrationDate.test.ts @@ -0,0 +1,37 @@ +import { expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getRegistrationDate } from './getRegistrationDate.js' + +it('should return a registration block timestamp for an existing V2 name', async () => { + const registeredAt = await getRegistrationDate(client, { + label: 'example', + registryAddress: deploymentAddresses.ETHRegistry, + }) + + expect(registeredAt).not.toBeNull() + expect(registeredAt).toBeTypeOf('bigint') + expect(registeredAt).toBeGreaterThan(0n) +}) + +it('should return null for a non-existing label', async () => { + const registeredAt = await getRegistrationDate(client, { + label: 'thisnamedoesnotexistatallitjustfortest', + registryAddress: deploymentAddresses.ETHRegistry, + }) + + expect(registeredAt).toEqual(null) +}) + +it('should return null if there is no register event in a specified block range', async () => { + const registeredAt = await getRegistrationDate(client, { + label: 'example', + registryAddress: deploymentAddresses.ETHRegistry, + fromBlock: 0n, + toBlock: 1n, + }) + + expect(registeredAt).toEqual(null) +}) diff --git a/packages/ensjs/src/actions/public/v2/getRegistrationDate.ts b/packages/ensjs/src/actions/public/v2/getRegistrationDate.ts new file mode 100644 index 000000000..84039804b --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRegistrationDate.ts @@ -0,0 +1,96 @@ +import { permissionedRegistryGetTokenIdSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { + type Address, + type Chain, + type GetBlockErrorType, + type GetLogsErrorType, + labelhash, + type ReadContractErrorType, +} from 'viem' +import { getBlock, getLogs, readContract } from 'viem/actions' +import { type GetChainContractAddressErrorType, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetRegistrationDateParameters = { + /** Label to get registration date for */ + label: string + /** Optional custom registry address (ETHRegistry) */ + registryAddress?: Address + /** Block range start */ + fromBlock?: bigint + /** Block range end */ + toBlock?: bigint +} + +export type GetRegistrationDateReturnType = bigint | null + +export type GetRegistrationDateErrorType = + | GetChainContractAddressErrorType + | ReadContractErrorType + | GetLogsErrorType + | GetBlockErrorType + +const LABEL_REGISTERED_EVENT = { + name: 'LabelRegistered' as const, + type: 'event' as const, + inputs: [ + { name: 'tokenId', type: 'uint256', indexed: true }, + { name: 'labelHash', type: 'bytes32', indexed: true }, + { name: 'label', type: 'string', indexed: false }, + { name: 'owner', type: 'address', indexed: false }, + { name: 'expiry', type: 'uint64', indexed: false }, + { name: 'sender', type: 'address', indexed: true }, + ], +} + +/** + * Get a block timestamp of when a name was registered on the V2 registry + * @returns registration timestamp or null + */ +export async function getRegistrationDate( + client: RequireClientContracts, + { label, registryAddress, fromBlock, toBlock }: GetRegistrationDateParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const getLogsAction = getAction(client, getLogs, 'getLogs') + const readContractAction = getAction(client, readContract, 'readContract') + + const registry = + registryAddress ?? + getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }) + + const tokenId = await readContractAction({ + address: registry, + abi: permissionedRegistryGetTokenIdSnippet, + functionName: 'getTokenId', + args: [BigInt(labelhash(label))], + }) + + const logs = await getLogsAction({ + address: registry, + event: LABEL_REGISTERED_EVENT, + args: { tokenId }, + fromBlock: fromBlock ?? 0n, + toBlock: toBlock ?? undefined, + }) + + const lastLog = logs.at(-1) + + if (!lastLog) return null + + const { blockNumber } = lastLog + + if (!blockNumber) return null + + const getBlockAction = getAction(client, getBlock, 'getBlock') + + const { timestamp } = await getBlockAction({ blockNumber }) + + return timestamp +} diff --git a/packages/ensjs/src/actions/public/v2/getRenewPrice.test.ts b/packages/ensjs/src/actions/public/v2/getRenewPrice.test.ts new file mode 100644 index 000000000..d268d904f --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRenewPrice.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, +} from '../../../test/addTestContracts.js' +import { applyDiscount } from './applyDiscount.js' +import { getRenewPrice } from './getRenewPrice.js' + +const renewerAddress = deploymentAddresses.ETHRegistrar +const paymentToken = deploymentAddresses.USDC + +const ONE_YEAR = 31_557_600n + +describe('getRenewPrice', () => { + describe('against a registered name', () => { + it('returns a positive renewal price for a 1-year extension', async () => { + const { amount } = await getRenewPrice(publicClient, { + renewerAddress, + label: 'example', + duration: ONE_YEAR, + paymentToken, + }) + + expect(typeof amount).toBe('bigint') + expect(amount).toBeGreaterThan(0n) + }) + + it('coerces a number duration to bigint without loss', async () => { + const asNumber = await getRenewPrice(publicClient, { + renewerAddress, + label: 'example', + duration: ONE_YEAR, + paymentToken, + }) + const asBigint = await getRenewPrice(publicClient, { + renewerAddress, + label: 'example', + duration: ONE_YEAR, + paymentToken, + }) + + expect(typeof asNumber.amount).toBe('bigint') + expect(asNumber.amount).toBe(asBigint.amount) + }) + + it('applies the oracle duration-tier discount to longer renewals', async () => { + const oneYear = await getRenewPrice(publicClient, { + renewerAddress, + label: 'example', + duration: ONE_YEAR, + paymentToken, + }) + + const twoYears = await getRenewPrice(publicClient, { + renewerAddress, + label: 'example', + duration: ONE_YEAR * 2n, + paymentToken, + }) + + // Base price scales linearly with duration before discount; the renewer + // then applies the oracle's duration-tier discount to the total. So + // twoYears = applyDiscount(oneYear × 2, 2y). + const expected = await applyDiscount(publicClient, { + value: oneYear.amount * 2n, + duration: ONE_YEAR * 2n, + }) + + expect(twoYears.amount).toBe(expected) + expect(twoYears.amount).toBeLessThan(oneYear.amount * 2n) + expect(twoYears.amount).toBeGreaterThan(oneYear.amount) + }) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getRenewPrice.ts b/packages/ensjs/src/actions/public/v2/getRenewPrice.ts new file mode 100644 index 000000000..36eaaccf3 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRenewPrice.ts @@ -0,0 +1,69 @@ +import { ethRegistrarGetRenewPriceSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import type { Address, Client, ReadContractErrorType } from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetRenewPriceParameters = { + /** + * Address of the renewer contract. Either the v2 `ETHRegistrar` (for names registered + * on v2) or the `ETHRenewerV1` (for unmigrated v1 names). Both expose the same + * `IETHRenewer` interface. + */ + renewerAddress: Address + /** Label to price renewal for. Must be a bare label (e.g. `"foo"`), not a name (`"foo.eth"`). */ + label: string + /** Renewal duration in seconds */ + duration: bigint + /** ERC-20 payment token address */ + paymentToken: Address +} + +export type GetRenewPriceReturnType = { + /** Renewal price */ + amount: bigint +} + +export type GetRenewPriceErrorType = ReadContractErrorType | TypeError + +/** + * Gets the renewal price of a label for a given duration. + * + * Internally calls `getRenewPrice(label, duration, paymentToken)` on a contract implementing + * `IETHRenewer` — either `ETHRegistrar` or `ETHRenewerV1`. The renewer fetches the current + * `expiry` and forwards it to the rent price oracle; the returned amount is the total + * cost (no separate premium for renewals). + * + * @param client - {@link Client} + * @param parameters - {@link GetRenewPriceParameters} + * @returns Renewal price in `paymentToken` units. {@link GetRenewPriceReturnType} + * + * @example + * import { getRenewPrice } from '@ensdomains/ensjs/public/v2' + * + * const price = await getRenewPrice(client, { + * renewerAddress: '0x...', + * label: 'example', + * duration: 31536000n, + * paymentToken: usdcAddress, + * }) + */ +export async function getRenewPrice( + client: Client, + { renewerAddress, label, duration, paymentToken }: GetRenewPriceParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const amount = await getAction( + client, + readContract, + 'readContract', + )({ + address: renewerAddress, + abi: ethRegistrarGetRenewPriceSnippet, + functionName: 'getRenewPrice', + args: [label, duration, paymentToken], + }) + + return { amount } +} diff --git a/packages/ensjs/src/actions/public/v2/getResolverName.ts b/packages/ensjs/src/actions/public/v2/getResolverName.ts new file mode 100644 index 000000000..6373cb7b1 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getResolverName.ts @@ -0,0 +1,49 @@ +import { permissionedResolverNameSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { Client } from 'viem' +import type { Address } from 'viem/accounts' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { getAction, padHex } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetResolverNameParameters = { + resolverAddress: Address +} + +export type GetResolverNameReturnType = string | null + +export type GetResolverNameErrorType = ReadContractErrorType + +/** + * Gets the name of a permissioned resolver. + * @param client - {@link Client} + * @param parameters - {@link GetResolverNameParameters} + * @returns Resolver address, or null if none is found. {@link GetResolverNameReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getResolverName } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getResolverName(client, { resolverAddress: '0x123' }) + * // l2name.eth + */ +export async function getResolverName( + client: Client, + { resolverAddress }: GetResolverNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: resolverAddress, + abi: permissionedResolverNameSnippet, + functionName: 'name', + args: [padHex('0x0', { size: 32 })], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getResource.test.ts b/packages/ensjs/src/actions/public/v2/getResource.test.ts new file mode 100644 index 000000000..3043e591a --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getResource.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getResource } from './getResource.js' + +describe('getResource', () => { + it('returns the resource ID for a registered label', async () => { + const resource = await getResource(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'example', + }) + + expect(resource).toEqual( + 50581729048717371578262585285492117321305276024434183435897035210102327476224n, + ) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getResource.ts b/packages/ensjs/src/actions/public/v2/getResource.ts new file mode 100644 index 000000000..6eb1d356a --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getResource.ts @@ -0,0 +1,51 @@ +import { permissionedRegistryGetResourceSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { type Address, type Chain, labelhash } from 'viem' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { type GetChainContractAddressErrorType, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetResourceParameters = { + /** Label to get the EAC resource ID for */ + label: string + /** Optional custom registry address (defaults to chain's ensRegistry) */ + registryAddress?: Address +} + +export type GetResourceReturnType = bigint + +export type GetResourceErrorType = + | GetChainContractAddressErrorType + | ReadContractErrorType + +/** + * Gets the EAC resource ID for a name in a V2 registry. + * @param client - {@link ClientWithEns} + * @param parameters - {@link GetResourceParameters} + * @returns The resource ID as a bigint. {@link GetResourceReturnType} + */ +export async function getResource( + client: RequireClientContracts, + { label, registryAddress }: GetResourceParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + const currentRegistry = + registryAddress ?? + getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }) + + const labelHash = BigInt(labelhash(label)) + + return readContractAction({ + address: currentRegistry, + abi: permissionedRegistryGetResourceSnippet, + functionName: 'getResource', + args: [labelHash], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getRoleCounts.test.ts b/packages/ensjs/src/actions/public/v2/getRoleCounts.test.ts new file mode 100644 index 000000000..7340cdc1b --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRoleCounts.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getRoleCounts } from './getRoleCounts.js' + +describe('getRoleCounts', () => { + it('returns role counts for a registered name', async () => { + const result = await getRoleCounts(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'parent', + }) + + expect(result.decoded).toEqual({ + ROLE_CAN_TRANSFER_ADMIN: 1, + ROLE_REGISTER_RESERVED: 0, + ROLE_REGISTER_RESERVED_ADMIN: 0, + ROLE_REGISTRAR: 0, + ROLE_REGISTRAR_ADMIN: 0, + ROLE_RENEW: 0, + ROLE_RENEW_ADMIN: 0, + ROLE_SET_PARENT: 0, + ROLE_SET_PARENT_ADMIN: 0, + ROLE_SET_RESOLVER: 1, + ROLE_SET_RESOLVER_ADMIN: 1, + ROLE_SET_SUBREGISTRY: 1, + ROLE_SET_SUBREGISTRY_ADMIN: 1, + ROLE_SET_URI: 0, + ROLE_SET_URI_ADMIN: 0, + ROLE_UNREGISTER: 0, + ROLE_UNREGISTER_ADMIN: 0, + ROLE_UPGRADE: 0, + ROLE_UPGRADE_ADMIN: 0, + ROLE_WAS_RESERVED: 0, + }) + }) + + it('returns higher count for SET_RESOLVER on changerole.eth (owner + user)', async () => { + const result = await getRoleCounts(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + }) + + expect(result.decoded).toEqual({ + ROLE_CAN_TRANSFER_ADMIN: 1, + ROLE_REGISTER_RESERVED: 0, + ROLE_REGISTER_RESERVED_ADMIN: 0, + ROLE_REGISTRAR: 0, + ROLE_REGISTRAR_ADMIN: 0, + ROLE_RENEW: 0, + ROLE_RENEW_ADMIN: 0, + ROLE_SET_PARENT: 0, + ROLE_SET_PARENT_ADMIN: 0, + ROLE_SET_RESOLVER: 2, + ROLE_SET_RESOLVER_ADMIN: 1, + ROLE_SET_SUBREGISTRY: 1, + ROLE_SET_SUBREGISTRY_ADMIN: 1, + ROLE_SET_URI: 0, + ROLE_SET_URI_ADMIN: 0, + ROLE_UNREGISTER: 0, + ROLE_UNREGISTER_ADMIN: 0, + ROLE_UPGRADE: 0, + ROLE_UPGRADE_ADMIN: 0, + ROLE_WAS_RESERVED: 0, + }) + }) + + it('returns zero counts for a non-existent label', async () => { + const result = await getRoleCounts(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'thislabeldoesnotexist12345', + }) + + expect(result.decoded).toEqual({ + ROLE_CAN_TRANSFER_ADMIN: 0, + ROLE_REGISTER_RESERVED: 0, + ROLE_REGISTER_RESERVED_ADMIN: 0, + ROLE_REGISTRAR: 0, + ROLE_REGISTRAR_ADMIN: 0, + ROLE_RENEW: 0, + ROLE_RENEW_ADMIN: 0, + ROLE_SET_PARENT: 0, + ROLE_SET_PARENT_ADMIN: 0, + ROLE_SET_RESOLVER: 0, + ROLE_SET_RESOLVER_ADMIN: 0, + ROLE_SET_SUBREGISTRY: 0, + ROLE_SET_SUBREGISTRY_ADMIN: 0, + ROLE_SET_URI: 0, + ROLE_SET_URI_ADMIN: 0, + ROLE_UNREGISTER: 0, + ROLE_UNREGISTER_ADMIN: 0, + ROLE_UPGRADE: 0, + ROLE_UPGRADE_ADMIN: 0, + ROLE_WAS_RESERVED: 0, + }) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getRoleCounts.ts b/packages/ensjs/src/actions/public/v2/getRoleCounts.ts new file mode 100644 index 000000000..0035eeaf6 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getRoleCounts.ts @@ -0,0 +1,62 @@ +import { permissionedRegistryRoleCountSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { type Client, labelhash } from 'viem' +import type { Address } from 'viem/accounts' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type DecodeRoleCountsReturnType, + decodeRoleCounts, + registryRoles, +} from '../../../utils/v2/index.js' + +export type GetRoleCountsParameters = { + registryAddress: Address + label: string +} + +export type GetRoleCountsReturnType = { + decoded: DecodeRoleCountsReturnType + raw: bigint +} + +export type GetRoleCountsErrorType = ReadContractErrorType + +/** + * Gets the registry's role counts for a name's label. + * @param client - {@link Client} + * @param parameters - {@link GetRoleCountsParameters} + * @returns Resolver address, or null if none is found. {@link GetRoleCountsReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { GetRoleCounts } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getRoleCounts(client, { resolverAddress: '0x0f3eb298470639a96bd548cea4a648bc80b2cee2', label: 'raffy' }) + */ +export async function getRoleCounts( + client: Client, + { registryAddress, label }: GetRoleCountsParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + const roleBitmap = await readContractAction({ + address: registryAddress, + abi: permissionedRegistryRoleCountSnippet, + functionName: 'roleCount', + args: [BigInt(labelhash(label))], + }) + + return { + decoded: decodeRoleCounts(roleBitmap, registryRoles), + raw: roleBitmap, + } +} diff --git a/packages/ensjs/src/actions/public/v2/getSubregistryHistory.ts b/packages/ensjs/src/actions/public/v2/getSubregistryHistory.ts new file mode 100644 index 000000000..195f2250c --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getSubregistryHistory.ts @@ -0,0 +1,89 @@ +import { subregistryUpdatedEventSnippet } from '@ensdomains/ensjs-abi/v2/userRegistry' +import { + type Client, + type GetLogsErrorType, + type GetLogsParameters, + labelhash, +} from 'viem' +import type { Address } from 'viem/accounts' +import { getLogs } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetSubregistryHistoryParameters = { + /** The registry address to query */ + registryAddress: Address + /** The label to get subregistry history for */ + label: string +} & Partial> + +export type SubregistryHistoryEntry = { + tokenId: bigint + subregistry: Address +} + +export type GetSubregistryHistoryReturnType = SubregistryHistoryEntry[] + +export type GetSubregistryHistoryErrorType = GetLogsErrorType + +/** + * Get the subregistry update history for a name. + * @param client - {@link Client} + * @param parameters - {@link GetSubregistryHistoryParameters} + * @returns Array of subregistry history entries. {@link GetSubregistryHistoryReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { getSubregistryHistory } from '@ensdomains/ensjs/public' + * + * const client = createPublicClient({ + * chain: mainnet, + * transport: http(), + * }) + * const history = await getSubregistryHistory(client, { + * registryAddress: '0x...', + * label: 'example', + * fromBlock: 0n, + * }) + * // [{ tokenId: 456n, subregistry: '0x...' }] + */ +export async function getSubregistryHistory( + client: Client, + { + registryAddress, + label, + fromBlock, + toBlock, + }: GetSubregistryHistoryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const getLogsAction = getAction(client, getLogs, 'getLogs') + + const tokenId = BigInt(labelhash(label)) + + const logs = await getLogsAction({ + address: registryAddress, + events: subregistryUpdatedEventSnippet, + fromBlock: fromBlock ?? 0n, + toBlock, + }) + + // Map logs to structured history entries + return logs + .filter( + ( + log, + ): log is typeof log & { + args: { tokenId: bigint; subregistry: Address } + } => + log.args?.tokenId !== undefined && + log.args?.subregistry !== undefined && + log.args.tokenId === tokenId, + ) + .map((log) => ({ + tokenId: log.args.tokenId, + subregistry: log.args.subregistry, + })) +} diff --git a/packages/ensjs/src/actions/public/v2/getTokenId.test.ts b/packages/ensjs/src/actions/public/v2/getTokenId.test.ts new file mode 100644 index 000000000..1758a91a8 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getTokenId.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, +} from '../../../test/addTestContracts.js' +import { getTokenId } from './getTokenId.js' + +describe('getTokenId', () => { + it('returns the token ID for a registered label', async () => { + const tokenId = await getTokenId(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'example', + }) + + expect(tokenId).toEqual( + 50581729048717371578262585285492117321305276024434183435897035210102327476224n, + ) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/getTokenId.ts b/packages/ensjs/src/actions/public/v2/getTokenId.ts new file mode 100644 index 000000000..8cee3b07b --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getTokenId.ts @@ -0,0 +1,51 @@ +import { permissionedRegistryGetTokenIdSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import { type Address, type Chain, labelhash } from 'viem' +import { type ReadContractErrorType, readContract } from 'viem/actions' +import { type GetChainContractAddressErrorType, getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../../clients/shared.js' +import { getChainContractAddress } from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetTokenIdParameters = { + /** Label to get the token ID for */ + label: string + /** Optional custom registry address (defaults to chain's ensRegistry) */ + registryAddress?: Address +} + +export type GetTokenIdReturnType = bigint + +export type GetTokenIdErrorType = + | GetChainContractAddressErrorType + | ReadContractErrorType + +/** + * Gets the ERC1155 token ID for a name in a V2 registry. + * @param client - {@link ClientWithEns} + * @param parameters - {@link GetTokenIdParameters} + * @returns The token ID as a bigint. {@link GetTokenIdReturnType} + */ +export async function getTokenId( + client: RequireClientContracts, + { label, registryAddress }: GetTokenIdParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + const currentRegistry = + registryAddress ?? + getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }) + + const labelHash = BigInt(labelhash(label)) + + return readContractAction({ + address: currentRegistry, + abi: permissionedRegistryGetTokenIdSnippet, + functionName: 'getTokenId', + args: [labelHash], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/getUnderlyingResolver.ts b/packages/ensjs/src/actions/public/v2/getUnderlyingResolver.ts new file mode 100644 index 000000000..6a4720328 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/getUnderlyingResolver.ts @@ -0,0 +1,53 @@ +import { compositeResolverGetResolver } from '@ensdomains/ensjs-abi/compositeResolver' +import type { Client, ReadContractErrorType } from 'viem' +import type { Address } from 'viem/accounts' +import { readContract } from 'viem/actions' +import { type PacketToBytesErrorType, packetToBytes } from 'viem/ens' +import { bytesToHex, getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type GetUnderlyingResolverParameters = { + resolverAddress: Address + + name: string +} + +export type GetUnderlyingResolverReturnType = readonly [Address, boolean] + +export type GetUnderlyingResolverErrorType = + | ReadContractErrorType + | PacketToBytesErrorType + +/** + * Finds the underlying non-mainnet resolver address via offchain lookup. + * @param client - {@link Client} + * @param parameters - {@link GetUnderlyingResolverParameters} + * @returns Resolver address, or null if none is found, and whether it's on mainnet or not. {@link GetUnderlyingResolverReturnType} + * + * @example + * import { createPublicClient, http } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getUnderlyingAddress } from '@ensdomains/ensjs/public/v2' + * + * const client = createPublicClient({ + * chain: addEnsContracts(mainnet), + * transport: http(), + * }) + * const result = await getUnderlyingAddress(client, { resolverAddress: '0x123' }) + * // ['0x352d7aA7a8bd0F6f31635BE5ceCb6Cebb6929A15', false] + */ +export async function getUnderlyingAddress( + client: Client, + { resolverAddress, name }: GetUnderlyingResolverParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + return await readContractAction({ + address: resolverAddress, + abi: compositeResolverGetResolver, + functionName: 'getResolver', + args: [bytesToHex(packetToBytes(name))], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/hasRoles.test.ts b/packages/ensjs/src/actions/public/v2/hasRoles.test.ts new file mode 100644 index 000000000..9b86b8fb8 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/hasRoles.test.ts @@ -0,0 +1,182 @@ +import { verifiableFactoryDeployProxySnippet } from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import type { Address } from 'viem' +import { encodeFunctionData, getAddress, keccak256 } from 'viem' +import { beforeAll, describe, expect, it } from 'vitest' +import { + publicClient as client, + deploymentAddresses, + waitForTransaction, + walletClient, +} from '../../../test/addTestContracts.js' +import { computeResolverResource, hasRoles } from './hasRoles.js' + +const RESOLVER_ROLES_ALL = + 0x1111111111111111111111111111111111111111111111111111111111111111n + +let resolverProxyAddress: `0x${string}` | undefined +let accounts: Address[] + +beforeAll(async () => { + accounts = await walletClient.getAddresses() + const proxyDeployTx = await walletClient.writeContract({ + address: deploymentAddresses.VerifiableFactory, + abi: verifiableFactoryDeployProxySnippet, + functionName: 'deployProxy', + args: [ + deploymentAddresses.PermissionedResolverImpl, + BigInt( + keccak256( + new TextEncoder().encode(`resolver-roles-test-${Date.now()}`), + ), + ), + encodeFunctionData({ + abi: [ + { + type: 'function', + inputs: [ + { name: 'admin', type: 'address' }, + { name: 'roleBitmap', type: 'uint256' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + }, + ], + functionName: 'initialize', + args: [accounts[0], RESOLVER_ROLES_ALL], + }), + ], + account: accounts[0], + }) + const proxyReceipt = await waitForTransaction(proxyDeployTx) + resolverProxyAddress = `0x${proxyReceipt.logs[3]?.topics[2]?.slice(26)}` +}) + +describe('hasRoles', () => { + // changerole.eth is registered by owner (0x70997970...) with ROLES.ALL. + // Then SET_RESOLVER is granted to user (0x3C44Cd...) and SET_SUBREGISTRY + // is revoked from user. + const owner = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' + const user = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' + + describe('registry mode', () => { + it('returns true when account has SET_SUBREGISTRY role', async () => { + const result = await hasRoles(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + roles: ['ROLE_SET_SUBREGISTRY'], + account: owner, + }) + + expect(result).toBe(true) + }) + + it('returns true when account has SET_RESOLVER role', async () => { + const result = await hasRoles(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + roles: ['ROLE_SET_RESOLVER'], + account: user, + }) + + expect(result).toBe(true) + }) + + it('returns true when account has CAN_TRANSFER_ADMIN role', async () => { + const result = await hasRoles(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + roles: ['ROLE_CAN_TRANSFER_ADMIN'], + account: owner, + }) + + expect(result).toBe(true) + }) + + it('returns true when owner has all three roles', async () => { + const result = await hasRoles(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + roles: [ + 'ROLE_SET_SUBREGISTRY', + 'ROLE_SET_RESOLVER', + 'ROLE_CAN_TRANSFER_ADMIN', + ], + account: owner, + }) + + expect(result).toBe(true) + }) + + it('returns false when account does not have the specified role', async () => { + // user had SET_SUBREGISTRY revoked + const result = await hasRoles(client, { + registryAddress: deploymentAddresses.ETHRegistry, + label: 'changerole', + roles: ['ROLE_SET_SUBREGISTRY'], + account: user, + }) + + expect(result).toBe(false) + }) + }) + + describe('resolver root mode', () => { + it('returns true when account has root-level ROLE_SET_ALIAS', async () => { + const result = await hasRoles(client, { + resolverAddress: resolverProxyAddress!, + roles: ['ROLE_SET_ALIAS'], + account: accounts[0], + }) + + expect(result).toBe(true) + }) + + it('returns false when account does not have root role', async () => { + const result = await hasRoles(client, { + resolverAddress: resolverProxyAddress!, + roles: ['ROLE_SET_ALIAS'], + account: getAddress('0x0000000000000000000000000000000000000001'), + }) + + expect(result).toBe(false) + }) + }) + + describe('resolver mode (with resource)', () => { + it('returns true when account has role for resource', async () => { + const result = await hasRoles(client, { + resolverAddress: resolverProxyAddress!, + resource: 0n, + roles: ['ROLE_SET_TEXT'], + account: accounts[0], + }) + + expect(result).toBe(true) + }) + + it('returns false when account does not have role for resource', async () => { + const result = await hasRoles(client, { + resolverAddress: resolverProxyAddress!, + resource: computeResolverResource( + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ), + roles: ['ROLE_SET_TEXT'], + account: getAddress('0x0000000000000000000000000000000000000001'), + }) + + expect(result).toBe(false) + }) + }) +}) + +describe('computeResolverResource', () => { + it('computes resource from hex inputs', () => { + const resource = computeResolverResource( + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000000', + ) + expect(typeof resource).toBe('bigint') + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/hasRoles.ts b/packages/ensjs/src/actions/public/v2/hasRoles.ts new file mode 100644 index 000000000..aa851269a --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/hasRoles.ts @@ -0,0 +1,184 @@ +import { eacHasRolesSnippet } from '@ensdomains/ensjs-abi/v2/enhancedAccessControl' +import { + permissionedResolverHasRolesSnippet, + permissionedResolverHasRootRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import { + type Address, + type Client, + labelhash, + type ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + encodeRoleBitmap, + type Role, +} from '../../../utils/v2/roles/encodeRoleBitmap.js' +import { + encodeResolverRoleBitmap, + type ResolverRole, +} from '../../../utils/v2/roles/resolverRoles.js' + +// ─── Registry mode ──────────────────────────────────────────────────── + +export type HasRolesRegistryParameters = { + /** The registry address to check */ + registryAddress: Address + /** The label to check roles for */ + label: string + /** The roles to check */ + roles: Role[] + /** The account address to check */ + account: Address +} + +// ─── Registry root mode ─────────────────────────────────────────────── + +export type HasRolesRegistryRootParameters = { + /** The registry address to check */ + registryAddress: Address + /** The roles to check at the registry root resource */ + roles: Role[] + /** The account address to check */ + account: Address +} + +// ─── Resolver root mode ─────────────────────────────────────────────── + +export type HasRolesResolverRootParameters = { + /** The resolver address to check */ + resolverAddress: Address + /** The resolver roles to check */ + roles: ResolverRole[] + /** The account address to check */ + account: Address +} + +// ─── Resolver mode (with resource) ──────────────────────────────────── + +export type HasRolesResolverParameters = { + /** The resolver address to check */ + resolverAddress: Address + /** The EAC resource to check roles on */ + resource: bigint + /** The resolver roles to check */ + roles: ResolverRole[] + /** The account address to check */ + account: Address +} + +// ─── Union type ─────────────────────────────────────────────────────── + +export type HasRolesParameters = + | HasRolesRegistryParameters + | HasRolesRegistryRootParameters + | HasRolesResolverRootParameters + | HasRolesResolverParameters + +export type HasRolesErrorType = ReadContractErrorType + +/** + * Check if an account has specific roles. + * + * Supports four modes based on the parameters passed: + * + * **Registry mode** (pass `registryAddress` + `label`): + * Check roles on a registry for a specific name. + * + * **Registry root mode** (pass `registryAddress` only): + * Check registry-wide roles at the root resource (resource = 0). + * + * **Resolver root mode** (pass `resolverAddress` only): + * Check root-level roles on a resolver (global, any name/record). + * + * **Resolver mode** (pass `resolverAddress` + `resource`): + * Check roles on a resolver for a specific resource. + * + * @param client - {@link Client} + * @param parameters - {@link HasRolesParameters} + * @returns Boolean indicating if the account has the roles. + * + * @example + * // Registry mode - check registry roles for a name + * const hasRole = await hasRoles(client, { + * registryAddress: '0x...', + * label: 'example', + * roles: ['ROLE_SET_SUBREGISTRY'], + * account: '0x...', + * }) + * + * @example + * // Registry root mode - check registry-wide root roles + * const isAdmin = await hasRoles(client, { + * registryAddress: '0x...', + * roles: ['ROLE_REGISTRAR_ADMIN'], + * account: '0x...', + * }) + * + * @example + * // Resolver root mode - check global resolver roles + * const canSetAlias = await hasRoles(client, { + * resolverAddress: '0x...', + * roles: ['ROLE_SET_ALIAS'], + * account: '0x...', + * }) + * + * @example + * // Resolver mode - check resolver roles for a specific resource + * const canSetText = await hasRoles(client, { + * resolverAddress: '0x...', + * resource: computeResolverResource(namehash('myname.eth'), '0x0000000000000000000000000000000000000000000000000000000000000000'), + * roles: ['ROLE_SET_TEXT'], + * account: '0x...', + * }) + */ +export async function hasRoles( + client: Client, + params: HasRolesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + // Registry mode: registryAddress + (label | root resource) + if ('registryAddress' in params) { + const { registryAddress, roles, account } = params + const resource = 'label' in params ? BigInt(labelhash(params.label)) : 0n + const rolesBitmap = encodeRoleBitmap(roles) + + return readContractAction({ + address: registryAddress, + abi: eacHasRolesSnippet, + functionName: 'hasRoles', + args: [resource, rolesBitmap, account], + }) + } + + // Resolver mode (with resource): resolverAddress + resource + if ('resource' in params) { + const { resolverAddress, resource, roles, account } = params + const roleBitmap = encodeResolverRoleBitmap(roles) + + return readContractAction({ + address: resolverAddress, + abi: permissionedResolverHasRolesSnippet, + functionName: 'hasRoles', + args: [resource, roleBitmap, account], + }) + } + + // Resolver root mode: resolverAddress only (no resource) + const { resolverAddress, roles, account } = params + const roleBitmap = encodeResolverRoleBitmap(roles) + + return readContractAction({ + address: resolverAddress, + abi: permissionedResolverHasRootRolesSnippet, + functionName: 'hasRootRoles', + args: [roleBitmap, account], + }) +} + +export { computeResolverResource } from '../../../utils/v2/roles/resolverResource.js' diff --git a/packages/ensjs/src/actions/public/v2/isPaymentToken.test.ts b/packages/ensjs/src/actions/public/v2/isPaymentToken.test.ts new file mode 100644 index 000000000..85a14f8a6 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/isPaymentToken.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, +} from '../../../test/addTestContracts.js' +import { isPaymentToken } from './isPaymentToken.js' + +describe('isPaymentToken', () => { + it('returns true for a configured payment token (USDC)', async () => { + const result = await isPaymentToken(publicClient, { + paymentToken: deploymentAddresses.USDC, + }) + + expect(result).toBe(true) + }) + + it.each([ + ['zero address', '0x0000000000000000000000000000000000000000'], + [ + 'arbitrary unsupported address', + '0x0000000000000000000000000000000000000001', + ], + ['registrar address (not an ERC-20)', deploymentAddresses.ETHRegistrar], + ['ERC-20 deployed but not configured (DAI)', deploymentAddresses.DAI], + ] as const)('returns false for %s', async (_, paymentToken) => { + const result = await isPaymentToken(publicClient, { + paymentToken, + }) + + expect(result).toBe(false) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/isPaymentToken.ts b/packages/ensjs/src/actions/public/v2/isPaymentToken.ts new file mode 100644 index 000000000..2759cf4b8 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/isPaymentToken.ts @@ -0,0 +1,53 @@ +import { standardRentPriceOracleIsPaymentTokenSnippet } from '@ensdomains/ensjs-abi/v2/standardRentPriceOracle' +import type { + Address, + Chain, + GetChainContractAddressErrorType, + ReadContractErrorType, +} from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { + getChainContractAddress, + type RequireClientContracts, +} from '../../../clients/shared.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type IsPaymentTokenParameters = { + /** ERC-20 token address to check */ + paymentToken: Address +} + +export type IsPaymentTokenReturnType = boolean + +export type IsPaymentTokenErrorType = + | ReadContractErrorType + | GetChainContractAddressErrorType + +/** + * Checks whether a token is accepted for register/renew payment. + * payment-token validation lives on the StandardRentPriceOracle (the registrar + * delegates pricing/payment to a swappable oracle). + * + * @param client - {@link Client} + * @param parameters - {@link IsPaymentTokenParameters} + * @returns `true` if the token is supported. {@link IsPaymentTokenReturnType} + */ +export async function isPaymentToken( + client: RequireClientContracts, + { paymentToken }: IsPaymentTokenParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const readContractAction = getAction(client, readContract, 'readContract') + + return readContractAction({ + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensStandardRentPriceOracle', + }), + abi: standardRentPriceOracleIsPaymentTokenSnippet, + functionName: 'isPaymentToken', + args: [paymentToken], + }) +} diff --git a/packages/ensjs/src/actions/public/v2/isRenewable.test.ts b/packages/ensjs/src/actions/public/v2/isRenewable.test.ts new file mode 100644 index 000000000..d8b5f89e6 --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/isRenewable.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, +} from '../../../test/addTestContracts.js' +import { isRenewable } from './isRenewable.js' + +const renewerAddress = deploymentAddresses.ETHRegistrar + +describe('isRenewable', () => { + it('returns true for a currently renewable registered label', async () => { + const renewable = await isRenewable(publicClient, { + renewerAddress, + label: 'example', + }) + + expect(renewable).toBe(true) + }) + + it('returns false for a label that was never registered', async () => { + const renewable = await isRenewable(publicClient, { + renewerAddress, + label: 'this-label-does-not-exist-on-the-devnet', + }) + + expect(renewable).toBe(false) + }) +}) diff --git a/packages/ensjs/src/actions/public/v2/isRenewable.ts b/packages/ensjs/src/actions/public/v2/isRenewable.ts new file mode 100644 index 000000000..a58edbecc --- /dev/null +++ b/packages/ensjs/src/actions/public/v2/isRenewable.ts @@ -0,0 +1,59 @@ +import { ethRegistrarIsRenewableSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import type { Address, Client, ReadContractErrorType } from 'viem' +import { readContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' + +export type IsRenewableParameters = { + /** + * Address of the renewer contract. Either the v2 `ETHRegistrar` (for names + * registered on v2) or the `ETHRenewerV1` (for unmigrated v1 names). Both + * expose the same `IETHRenewer` interface. + */ + renewerAddress: Address + /** Label to check (bare label, e.g. `"foo"`, not `"foo.eth"`). */ + label: string +} + +export type IsRenewableReturnType = boolean + +export type IsRenewableErrorType = ReadContractErrorType | TypeError + +/** + * Whether the renewer will renew a label right now, via its on-chain + * `isRenewable(label)`. + * + * Useful for the `ETHRenewerV1`, which only renews RESERVED (pre-migration) or + * in-grace names — an active, not-yet-migrated v1 name returns `false` (and + * `getRenewPrice`/`renew` would revert `NameNotRenewable`). For the v2 + * `ETHRegistrar` it reflects the name's registered/grace status. + * + * @param client - {@link Client} + * @param parameters - {@link IsRenewableParameters} + * @returns `true` if the label is currently renewable. {@link IsRenewableReturnType} + * + * @example + * import { isRenewable } from '@ensdomains/ensjs/public/v2' + * + * const renewable = await isRenewable(client, { + * renewerAddress: '0x...', + * label: 'example', + * }) + */ +export async function isRenewable( + client: Client, + { renewerAddress, label }: IsRenewableParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + return getAction( + client, + readContract, + 'readContract', + )({ + address: renewerAddress, + abi: ethRegistrarIsRenewableSnippet, + functionName: 'isRenewable', + args: [label], + }) +} diff --git a/packages/ensjs/src/functions/subgraph/client.test.ts b/packages/ensjs/src/actions/subgraph/client.test.ts similarity index 98% rename from packages/ensjs/src/functions/subgraph/client.test.ts rename to packages/ensjs/src/actions/subgraph/client.test.ts index afd21f0da..02c5a6591 100644 --- a/packages/ensjs/src/functions/subgraph/client.test.ts +++ b/packages/ensjs/src/actions/subgraph/client.test.ts @@ -1,4 +1,4 @@ -import { namehash } from 'viem' +import { namehash } from 'viem/ens' import { describe, expect, it } from 'vitest' import { requestMiddleware, responseMiddleware } from './client.js' @@ -159,7 +159,7 @@ describe('requestMiddleware', () => { describe('responseMiddleware', () => { it('should replace name with the namehash when there is an invalid name and id combo', () => { const response = { ...mockResponse } - responseMiddleware(response as any) + responseMiddleware(response as any, {} as any) expect(response.data.account.domains[0].name).toBe( namehash( '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb', diff --git a/packages/ensjs/src/functions/subgraph/client.ts b/packages/ensjs/src/actions/subgraph/client.ts similarity index 88% rename from packages/ensjs/src/functions/subgraph/client.ts rename to packages/ensjs/src/actions/subgraph/client.ts index 573c835fa..9ad9c609b 100644 --- a/packages/ensjs/src/functions/subgraph/client.ts +++ b/packages/ensjs/src/actions/subgraph/client.ts @@ -1,9 +1,9 @@ import type { Kind, SelectionNode, SelectionSetNode } from 'graphql' +import { parse, print, visit } from 'graphql/language/index.js' import type { RequestMiddleware, ResponseMiddleware } from 'graphql-request' import { GraphQLClient } from 'graphql-request' -import { parse, print, visit } from 'graphql/language/index.js' -import type { ClientWithEns } from '../../contracts/consts.js' -import { namehash } from '../../utils/normalise.js' +import { namehash } from 'viem/ens' +import type { ChainWithSubgraph } from '../../clients/l1.js' const generateSelection = (selection: string): SelectionNode => ({ kind: 'Field' as Kind.FIELD, @@ -54,11 +54,11 @@ export const requestMiddleware: RequestMiddleware = (request) => { } } -export const responseMiddleware: ResponseMiddleware = (response) => { +export const responseMiddleware: ResponseMiddleware = (response, _options) => { const traverse = (obj: Record) => { if (obj && typeof obj === 'object') { for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (Object.hasOwn(obj, key)) { const value = obj[key] if (value && typeof value === 'object') { @@ -96,7 +96,9 @@ export const responseMiddleware: ResponseMiddleware = (response) => { traverse(response) } -export const createSubgraphClient = ({ client }: { client: ClientWithEns }) => +export const createSubgraphClient = <_chain extends ChainWithSubgraph>(client: { + chain: _chain +}) => new GraphQLClient(client.chain.subgraphs.ens.url, { requestMiddleware, responseMiddleware, diff --git a/packages/ensjs/src/functions/subgraph/events.ts b/packages/ensjs/src/actions/subgraph/events.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/events.ts rename to packages/ensjs/src/actions/subgraph/events.ts diff --git a/packages/ensjs/src/functions/subgraph/filters.ts b/packages/ensjs/src/actions/subgraph/filters.ts similarity index 99% rename from packages/ensjs/src/functions/subgraph/filters.ts rename to packages/ensjs/src/actions/subgraph/filters.ts index 237c69fe5..c501d6ec9 100644 --- a/packages/ensjs/src/functions/subgraph/filters.ts +++ b/packages/ensjs/src/actions/subgraph/filters.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { P, match } from 'ts-pattern' +import { match, P } from 'ts-pattern' import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js' import type { InputMaybe, Scalars } from './types.js' import type { Name } from './utils.js' diff --git a/packages/ensjs/src/functions/subgraph/fragments.ts b/packages/ensjs/src/actions/subgraph/fragments.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/fragments.ts rename to packages/ensjs/src/actions/subgraph/fragments.ts diff --git a/packages/ensjs/src/functions/subgraph/getDecodedName.test.ts b/packages/ensjs/src/actions/subgraph/getDecodedName.test.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/getDecodedName.test.ts rename to packages/ensjs/src/actions/subgraph/getDecodedName.test.ts diff --git a/packages/ensjs/src/functions/subgraph/getDecodedName.ts b/packages/ensjs/src/actions/subgraph/getDecodedName.ts similarity index 92% rename from packages/ensjs/src/functions/subgraph/getDecodedName.ts rename to packages/ensjs/src/actions/subgraph/getDecodedName.ts index 7e49bd319..945c1a74f 100644 --- a/packages/ensjs/src/functions/subgraph/getDecodedName.ts +++ b/packages/ensjs/src/actions/subgraph/getDecodedName.ts @@ -1,11 +1,11 @@ import { gql } from 'graphql-request' -import type { ClientWithEns } from '../../contracts/consts.js' +import { namehash } from 'viem/ens' +import type { ChainWithSubgraph } from '../../clients/l1.js' import { checkIsDecrypted, decodeLabelhash, isEncodedLabelhash, -} from '../../utils/labels.js' -import { namehash } from '../../utils/normalise.js' +} from '../../utils/name/labels.js' import { createSubgraphClient } from './client.js' export type GetDecodedNameParameters = { @@ -17,6 +17,8 @@ export type GetDecodedNameParameters = { export type GetDecodedNameReturnType = string | null +export type GetDecodedNameErrorType = Error + type SubgraphResult = { namehashLookup?: { name: string } } & { @@ -43,7 +45,7 @@ type SubgraphResult = { * // ens.eth */ const getDecodedName = async ( - client: ClientWithEns, + client: { chain: ChainWithSubgraph }, { name, allowIncomplete }: GetDecodedNameParameters, ): Promise => { if (checkIsDecrypted(name)) return name @@ -51,7 +53,7 @@ const getDecodedName = async ( // also try to fetch the label names from any Domain entities that have a corresponding labelhash const labels = name.split('.') - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) let labelsQuery = '' for (let i = 0; i < labels.length; i += 1) { diff --git a/packages/ensjs/src/functions/subgraph/getNameHistory.test.ts b/packages/ensjs/src/actions/subgraph/getNameHistory.test.ts similarity index 85% rename from packages/ensjs/src/functions/subgraph/getNameHistory.test.ts rename to packages/ensjs/src/actions/subgraph/getNameHistory.test.ts index 6041e630f..bf3070136 100644 --- a/packages/ensjs/src/functions/subgraph/getNameHistory.test.ts +++ b/packages/ensjs/src/actions/subgraph/getNameHistory.test.ts @@ -15,8 +15,8 @@ it('returns the history of a name', async () => { }) if (!result) throw new Error('No result') expect(result.domainEvents.length).toBeGreaterThan(0) - expect(result.registrationEvents!.length).toBeGreaterThan(0) - expect(result.resolverEvents!.length).toBeGreaterThan(0) + expect(result.registrationEvents?.length).toBeGreaterThan(0) + expect(result.resolverEvents?.length).toBeGreaterThan(0) }) it('returns the history of a wrapped name', async () => { @@ -25,7 +25,7 @@ it('returns the history of a wrapped name', async () => { }) if (!result) throw new Error('No result') expect(result.domainEvents.length).toBeGreaterThan(0) - expect(result.registrationEvents!.length).toBeGreaterThan(0) + expect(result.registrationEvents?.length).toBeGreaterThan(0) expect(result.resolverEvents).not.toBeNull() }) @@ -45,10 +45,10 @@ it('returns the history of a name with a null addr record', async () => { }) if (!result) throw new Error('No result') expect(result.domainEvents.length).toBeGreaterThan(0) - expect(result.registrationEvents!.length).toBeGreaterThan(0) + expect(result.registrationEvents?.length).toBeGreaterThan(0) expect(result.resolverEvents).not.toBeNull() expect( - result.resolverEvents!.some( + result.resolverEvents?.some( (event) => event.type === 'MulticoinAddrChanged' && event.addr === null, ), ).toBe(true) diff --git a/packages/ensjs/src/functions/subgraph/getNameHistory.ts b/packages/ensjs/src/actions/subgraph/getNameHistory.ts similarity index 72% rename from packages/ensjs/src/functions/subgraph/getNameHistory.ts rename to packages/ensjs/src/actions/subgraph/getNameHistory.ts index 50a74355c..1be40c234 100644 --- a/packages/ensjs/src/functions/subgraph/getNameHistory.ts +++ b/packages/ensjs/src/actions/subgraph/getNameHistory.ts @@ -1,9 +1,6 @@ -import { getCoderByCoinType } from '@ensdomains/address-encoder' import { gql } from 'graphql-request' -import { hexToBytes, trim } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { decodeContentHash } from '../../utils/contentHash.js' -import { namehash } from '../../utils/normalise.js' +import { namehash } from 'viem/ens' +import type { ChainWithSubgraph } from '../../clients/l1.js' import { createSubgraphClient } from './client.js' import type { BaseResolverEvent, @@ -13,12 +10,28 @@ import type { RegistrationEvent, ResolverEvent, } from './events.js' +import { decodeResolverEvents } from './utils.js' export type GetNameHistoryParameters = { /** Name to get history for */ name: string + /** Only fetch the first n events */ + first?: number + /** Order direction */ + orderDirection?: 'asc' | 'desc' } +export type GetNameHistoryReturnType = { + /** Array of domain events */ + domainEvents: ReturnDomainEvent[] + /** Array of registration events */ + registrationEvents: ReturnRegistrationEvent[] | null + /** Array of resolver events */ + resolverEvents: ReturnResolverEvent[] | null +} | null + +export type GetNameHistoryErrorType = Error + type SubgraphResult = { domain?: { events: DomainEvent[] @@ -31,13 +44,18 @@ type SubgraphResult = { } } +// Nested `{ id }` references are flattened to their id string. ENSNode can +// return `null` for these references (e.g. a `NewResolver` event that clears +// the resolver), so the flattened value is `string | null`. type FlattenedEvent = { - [K in keyof TEvent]: TEvent[K] extends { id: string } ? string : TEvent[K] + [K in keyof TEvent]: TEvent[K] extends { id: string } + ? string | null + : TEvent[K] } type ReturnDomainEvent = FlattenedEvent type ReturnRegistrationEvent = FlattenedEvent -type ReturnResolverEvent = FlattenedEvent< +export type ReturnResolverEvent = FlattenedEvent< | Exclude | (BaseResolverEvent & { type: 'MulticoinAddrChanged' @@ -54,15 +72,6 @@ type ReturnResolverEvent = FlattenedEvent< }) > -export type GetNameHistoryReturnType = { - /** Array of domain events */ - domainEvents: ReturnDomainEvent[] - /** Array of registration events */ - registrationEvents: ReturnRegistrationEvent[] | null - /** Array of resolver events */ - resolverEvents: ReturnResolverEvent[] | null -} | null - /** * Gets the history of a name from the subgraph. * @param client - {@link ClientWithEns} @@ -82,15 +91,15 @@ export type GetNameHistoryReturnType = { * const result = await getNameHistory(client, { name: 'ens.eth' }) */ const getNameHistory = async ( - client: ClientWithEns, - { name }: GetNameHistoryParameters, + client: { chain: ChainWithSubgraph }, + { name, first, orderDirection }: GetNameHistoryParameters, ): Promise => { - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) const query = gql` - query getNameHistory($id: String!) { + query getNameHistory($id: String!, $first: Int, $orderDirection: OrderDirection) { domain(id: $id) { - events { + events(first: $first, orderDirection: $orderDirection) { id blockNumber transactionID @@ -138,7 +147,7 @@ const getNameHistory = async ( } } registration { - events { + events(first: $first, orderDirection: $orderDirection) { id blockNumber transactionID @@ -160,7 +169,7 @@ const getNameHistory = async ( } } resolver { - events { + events(first: $first, orderDirection: $orderDirection) { id blockNumber transactionID @@ -211,6 +220,8 @@ const getNameHistory = async ( const queryVars = { id: namehash(name), + first, + orderDirection, } const result = await subgraphClient.request( @@ -224,9 +235,11 @@ const getNameHistory = async ( (event: DomainEvent): ReturnDomainEvent => { switch (event.type) { case 'NewResolver': { + // ENSNode returns `resolver: null` when the resolver is cleared + // (set to the zero address), so guard against a missing reference. return { ...event, - resolver: event.resolver.id.split('-')[0], + resolver: event.resolver?.id.split('-')[0] ?? null, } } case 'NewOwner': @@ -236,7 +249,7 @@ const getNameHistory = async ( case 'NameUnwrapped': { return { ...event, - owner: event.owner.id, + owner: event.owner?.id ?? null, } } default: @@ -251,13 +264,13 @@ const getNameHistory = async ( case 'NameRegistered': { return { ...event, - registrant: event.registrant.id, + registrant: event.registrant?.id ?? null, } } case 'NameTransferred': { return { ...event, - newOwner: event.newOwner.id, + newOwner: event.newOwner?.id ?? null, } } default: @@ -266,56 +279,8 @@ const getNameHistory = async ( }, ) - const resolverEvents = result.domain?.resolver?.events.map( - (event: ResolverEvent): ReturnResolverEvent => { - switch (event.type) { - case 'AddrChanged': { - return { - ...event, - addr: event.addr.id, - } - } - case 'MulticoinAddrChanged': { - const { multiaddr, ...event_ } = event - const format = getCoderByCoinType(Number.parseInt(event.coinType)) - if (!format) { - return { - ...event_, - coinName: null, - decoded: false, - addr: multiaddr, - } - } - if (multiaddr === '0x' || trim(multiaddr) === '0x00') { - return { - ...event_, - coinName: format.name, - decoded: true, - addr: null, - } - } - return { - ...event_, - coinName: format.name, - decoded: true, - addr: format.encode(hexToBytes(multiaddr)), - } - } - case 'ContenthashChanged': { - const { decoded: contentHash, protocolType } = decodeContentHash( - event.hash, - ) || { protocolType: null, decoded: null } - return { - ...event, - decoded: contentHash !== null, - contentHash, - protocolType, - } - } - default: - return event - } - }, + const resolverEvents = decodeResolverEvents( + result.domain?.resolver?.events || [], ) return { diff --git a/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts b/packages/ensjs/src/actions/subgraph/getNamesForAddress.test.ts similarity index 98% rename from packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts rename to packages/ensjs/src/actions/subgraph/getNamesForAddress.test.ts index 24f8618e9..c303fecbb 100644 --- a/packages/ensjs/src/functions/subgraph/getNamesForAddress.test.ts +++ b/packages/ensjs/src/actions/subgraph/getNamesForAddress.test.ts @@ -4,9 +4,9 @@ import type { Address } from 'viem' import { beforeAll, describe, expect, it } from 'vitest' import { publicClient, walletClient } from '../../test/addTestContracts.js' import { GRACE_PERIOD_SECONDS } from '../../utils/consts.js' -import getExpiry from '../public/getExpiry.js' -import getOwner from '../public/getOwner.js' -import getWrapperData from '../public/getWrapperData.js' +import { getExpiry } from '../public/v1/getExpiry.js' +import { getOwner } from '../public/v1/getOwner.js' +import { getWrapperData } from '../public/v1/getWrapperData.js' import getNamesForAddress, { type NameWithRelation, } from './getNamesForAddress.js' @@ -20,7 +20,7 @@ beforeAll(async () => { const user4 = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65' let expiry: bigint -describe('check that concurrent names all have the same expiry date', () => { +describe.skip('check that concurrent names all have the same expiry date', () => { it.each([ ...Array.from({ length: 2 }, (_, i) => `concurrent-legacy-name-${i}.eth`), ...Array.from({ length: 4 }, (_, i) => { @@ -36,12 +36,12 @@ describe('check that concurrent names all have the same expiry date', () => { const owner = ownerData?.registrant ?? ownerData?.owner expect(owner).toEqual(user4) const expiryData = await getExpiry(publicClient, { name }) - const expiryValue = expiryData?.expiry?.value ?? 0n + const expiryValue = expiryData?.expiry ?? 0n if (!expiry) expiry = expiryValue const wrapperData = await getWrapperData(publicClient, { name }) const wrappedExpiryValue = - (wrapperData?.expiry?.value || 0n) - BigInt(GRACE_PERIOD_SECONDS) + (wrapperData?.expiry || 0n) - BigInt(GRACE_PERIOD_SECONDS) const expectedExpiry = ownerData?.ownershipLevel === 'nameWrapper' ? wrappedExpiryValue diff --git a/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts b/packages/ensjs/src/actions/subgraph/getNamesForAddress.ts similarity index 95% rename from packages/ensjs/src/functions/subgraph/getNamesForAddress.ts rename to packages/ensjs/src/actions/subgraph/getNamesForAddress.ts index b7e16b605..a9bd9ab71 100644 --- a/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts +++ b/packages/ensjs/src/actions/subgraph/getNamesForAddress.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { gql } from 'graphql-request' import type { Address } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' +import type { ChainWithSubgraph } from '../../clients/l1.js' import { FilterKeyRequiredError, InvalidFilterKeyError, @@ -15,12 +15,35 @@ import { getExpiryDateOrderFilter, } from './filters.js' import { - type SubgraphDomain, domainDetailsFragment, registrationDetailsFragment, + type SubgraphDomain, wrappedDomainDetailsFragment, } from './fragments.js' -import { type Name, makeNameObject } from './utils.js' +import { makeNameObject, type Name } from './utils.js' + +export type GetNamesForAddressParameters = { + /** Address to get names for */ + address: Address + /** Names to get, in relation to address */ + filter?: GetNamesForAddressFilter + /** Parameter to order names by (default: name) */ + orderBy?: GetNamesForAddressOrderBy + /** Direction to order names in (default: asc) */ + orderDirection?: 'asc' | 'desc' + /** Previous page of names, used for pagination */ + previousPage?: NameWithRelation[] + /** Page size (default: 100) */ + pageSize?: number +} + +export type GetNamesForAddressReturnType = NameWithRelation[] + +export type GetNamesForAddressErrorType = + | InvalidOrderByError + | InvalidFilterKeyError + | FilterKeyRequiredError + | Error const supportedOwnerFilters = [ 'owner', @@ -59,27 +82,10 @@ type GetNamesForAddressFilter = GetNamesForAddressRelation & { allowDeleted?: boolean } -export type GetNamesForAddressParameters = { - /** Address to get names for */ - address: Address - /** Names to get, in relation to address */ - filter?: GetNamesForAddressFilter - /** Parameter to order names by (default: name) */ - orderBy?: GetNamesForAddressOrderBy - /** Direction to order names in (default: asc) */ - orderDirection?: 'asc' | 'desc' - /** Previous page of names, used for pagination */ - previousPage?: NameWithRelation[] - /** Page size (default: 100) */ - pageSize?: number -} - export type NameWithRelation = Name & { relation: GetNamesForAddressRelation } -export type GetNamesForAddressReturnType = NameWithRelation[] - type SubgraphResult = { domains: SubgraphDomain[] } @@ -143,7 +149,7 @@ const getOrderByFilter = ({ * const result = await getNamesForAddress(client, { address: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }) */ const getNamesForAddress = async ( - client: ClientWithEns, + client: { chain: ChainWithSubgraph }, { address, filter: _filter, @@ -165,7 +171,7 @@ const getNamesForAddress = async ( ..._filter, } as const - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) const { allowExpired, @@ -179,7 +185,7 @@ const getNamesForAddress = async ( const ownerWhereFilters: DomainFilter[] = Object.entries(filters).reduce( (prev, [key, value]) => { if (value) { - if (!supportedOwnerFilters.includes(key as any)) + if (!supportedOwnerFilters.includes(key as keyof typeof filters)) throw new InvalidFilterKeyError({ filterKey: key, supportedFilterKeys: supportedOwnerFilters, diff --git a/packages/ensjs/src/actions/subgraph/getRecordHistory.ts b/packages/ensjs/src/actions/subgraph/getRecordHistory.ts new file mode 100644 index 000000000..d2f155e1a --- /dev/null +++ b/packages/ensjs/src/actions/subgraph/getRecordHistory.ts @@ -0,0 +1,126 @@ +import { gql } from 'graphql-request' +import { namehash } from 'viem' +import type { ChainWithSubgraph } from '../../clients/l1.js' +import { createSubgraphClient } from './client.js' +import type { ResolverEvent } from './events.js' +import type { ReturnResolverEvent } from './getNameHistory.js' +import { decodeResolverEvents } from './utils.js' + +type SubgraphResult = { + domain?: { + resolver?: { + events: ResolverEvent[] + } + } +} + +export type GetRecordHistoryReturnType = + | ReturnResolverEvent[] + | null + | undefined + +export type RecordKey = 'name' | 'texts' | 'coins' | 'abi' | 'contentHash' + +export type GetRecordHistoryParameters = { + name: string + key: key +} + +export type GetRecordHistoryErrorType = Error + +const events: Record = { + coins: ['AddrChanged', 'MulticoinAddrChanged'], + abi: ['AbiChanged'], + contentHash: ['ContenthashChanged'], + name: ['NameChanged'], + texts: ['TextChanged'], +} as const + +export const getRecordHistory = async ( + client: { chain: ChainWithSubgraph }, + { name, key }: GetRecordHistoryParameters, +): Promise => { + const subgraphClient = createSubgraphClient(client) + + const query = gql` + query getRecordHistory($id: String!) { + domain(id: $id) { + resolver { + events { + id + blockNumber + transactionID + type: __typename + ${ + key === 'name' + ? ` + ... on NameChanged { + name + } + ` + : '' + } + ${ + key === 'texts' + ? ` + ... on TextChanged { + key + value + } + ` + : '' + } + ${ + key === 'coins' + ? ` + ... on MulticoinAddrChanged { + coinType + multiaddr: addr + } + ... on AddrChanged { + addr { + id + } + } + ` + : '' + } + ${ + key === 'abi' + ? ` + ... on AbiChanged { + contentType + } + ` + : '' + } + ${ + key === 'contentHash' + ? ` + ... on ContenthashChanged { + hash + } + ` + : '' + } + } + } + } + } + ` + + const queryVars = { + id: namehash(name), + } + + const result = await subgraphClient.request( + query, + queryVars, + ) + + return decodeResolverEvents( + (result.domain?.resolver?.events || []).filter((el) => + events[key].includes(el.type), + ), + ) +} diff --git a/packages/ensjs/src/actions/subgraph/getResolvedNamesForAddress.ts b/packages/ensjs/src/actions/subgraph/getResolvedNamesForAddress.ts new file mode 100644 index 000000000..13cdb267e --- /dev/null +++ b/packages/ensjs/src/actions/subgraph/getResolvedNamesForAddress.ts @@ -0,0 +1,60 @@ +import type { Address, Hex } from 'viem' +import type { ChainWithSubgraph } from '../../clients/l1.js' +import { createSubgraphClient } from './client.js' + +type SubgraphResult = { + domains: { + name: string + resolver: { + coinTypes: string[] + } + }[] +} + +export type GetResolvedNamesForAddressParameters = { + address: Address +} + +export type GetResolvedNamesForAddressReturnType = { + name: string + coinTypes: string[] +}[] + +export type GetResolvedNamesForAddressErrorType = Error + +/** + * Gets domains from the subgraph that resolve to a given address. + * @param client - {@link ClientWithEns} + * @param parameters - {@link GetNamesResolvedToAddressParameters} + * @returns Name array. {@link GetNamesResolvedToAddressReturnType} + * + * @example + * const result = await getResolvedNamesForAddress(client, { address: '0xD3B282e9880cDcB1142830731cD83f7ac0e1043f' }) + */ +export const getResolvedNamesForAddress = async ( + client: { chain: ChainWithSubgraph }, + { address }: GetResolvedNamesForAddressParameters, +) => { + const subgraphClient = createSubgraphClient(client) + + const query = ` + query ($address: String!) { + domains(where: { resolvedAddress: $address }) { + name + resolver { + coinTypes + } + } + } + ` + + const result = await subgraphClient.request< + SubgraphResult, + GetResolvedNamesForAddressParameters + >(query, { address: address.toLowerCase() as Hex }) + + return result.domains.map((domain) => ({ + name: domain.name, + coinTypes: domain.resolver?.coinTypes ?? [], + })) +} diff --git a/packages/ensjs/src/functions/subgraph/getSubgraphRecords.test.ts b/packages/ensjs/src/actions/subgraph/getSubgraphRecords.test.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/getSubgraphRecords.test.ts rename to packages/ensjs/src/actions/subgraph/getSubgraphRecords.test.ts diff --git a/packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts b/packages/ensjs/src/actions/subgraph/getSubgraphRecords.ts similarity index 93% rename from packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts rename to packages/ensjs/src/actions/subgraph/getSubgraphRecords.ts index 19d4357fd..657aece47 100644 --- a/packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts +++ b/packages/ensjs/src/actions/subgraph/getSubgraphRecords.ts @@ -1,9 +1,9 @@ import type { GraphQLClient } from 'graphql-request' import { gql } from 'graphql-request' import type { Address, Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import type { DateWithValue } from '../../types.js' -import { namehash } from '../../utils/normalise.js' +import { namehash } from 'viem/ens' +import type { ChainWithSubgraph } from '../../clients/l1.js' +import type { DateWithValue } from '../../types/index.js' import { createSubgraphClient } from './client.js' export type GetSubgraphRecordsParameters = { @@ -24,6 +24,8 @@ export type GetSubgraphRecordsReturnType = { coins: string[] } | null +export type GetSubgraphRecordsErrorType = Error + const inheritedResolverQuery = gql` query getSubgraphRecords($id: String!) { domain(id: $id) { @@ -152,10 +154,10 @@ const getInheritedResolverResult = async ( * // } */ const getSubgraphRecords = async ( - client: ClientWithEns, + client: { chain: ChainWithSubgraph }, { name, resolverAddress }: GetSubgraphRecordsParameters, ): Promise => { - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) const id = namehash(name) const result = resolverAddress diff --git a/packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.test.ts b/packages/ensjs/src/actions/subgraph/getSubgraphRegistrant.test.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.test.ts rename to packages/ensjs/src/actions/subgraph/getSubgraphRegistrant.test.ts diff --git a/packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts b/packages/ensjs/src/actions/subgraph/getSubgraphRegistrant.ts similarity index 69% rename from packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts rename to packages/ensjs/src/actions/subgraph/getSubgraphRegistrant.ts index 278efb632..7b5768a56 100644 --- a/packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts +++ b/packages/ensjs/src/actions/subgraph/getSubgraphRegistrant.ts @@ -1,8 +1,8 @@ import { gql } from 'graphql-request' -import { type Address, getAddress, labelhash } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' +import { type Address, getAddress, namehash } from 'viem' +import type { ChainWithSubgraph } from '../../clients/l1.js' import { UnsupportedNameTypeError } from '../../errors/general.js' -import { getNameType } from '../../utils/getNameType.js' +import { getNameType } from '../../utils/name/getNameType.js' import { createSubgraphClient } from './client.js' export type GetSubgraphRegistrantParameters = { @@ -12,20 +12,26 @@ export type GetSubgraphRegistrantParameters = { export type GetSubgraphRegistrantReturnType = Address | null +export type GetSubgraphRegistrantErrorType = UnsupportedNameTypeError | Error + const query = gql` query getSubgraphRegistrant($id: String!) { - registration(id: $id) { - registrant { - id + domain(id: $id) { + registration { + registrant { + id + } } } } ` type SubgraphResult = { - registration?: { - registrant: { - id: Address + domain?: { + registration?: { + registrant: { + id: Address + } } } } @@ -49,11 +55,10 @@ type SubgraphResult = { * const result = await getSubgraphRegistrant(client, { name: 'ens.eth' }) * // 0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9 */ -const getSubgraphRegistrant = async ( - client: ClientWithEns, +const getSubgraphRegistrant = async <_chain extends ChainWithSubgraph>( + client: { chain: _chain }, { name }: GetSubgraphRegistrantParameters, ): Promise => { - const labels = name.split('.') const nameType = getNameType(name) if (nameType !== 'eth-2ld') throw new UnsupportedNameTypeError({ @@ -62,14 +67,14 @@ const getSubgraphRegistrant = async ( details: 'Registrant can only be fetched for eth-2ld names', }) - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) const result = await subgraphClient.request(query, { - id: labelhash(labels[0]), + id: namehash(name), }) - if (result?.registration?.registrant?.id) - return getAddress(result.registration.registrant.id) + if (result?.domain?.registration?.registrant?.id) + return getAddress(result.domain.registration.registrant.id) return null } diff --git a/packages/ensjs/src/functions/subgraph/getSubnames.test.ts b/packages/ensjs/src/actions/subgraph/getSubnames.test.ts similarity index 97% rename from packages/ensjs/src/functions/subgraph/getSubnames.test.ts rename to packages/ensjs/src/actions/subgraph/getSubnames.test.ts index 28ed156f6..22e79e108 100644 --- a/packages/ensjs/src/functions/subgraph/getSubnames.test.ts +++ b/packages/ensjs/src/actions/subgraph/getSubnames.test.ts @@ -44,7 +44,7 @@ it('does not include expired names by default - .eth 2ld', async () => { }) if (!result.length) throw new Error('No names found') for (const name of result) { - expect(name.expiryDate!.value).toBeGreaterThan(Date.now()) + expect(name.expiryDate?.value).toBeGreaterThan(Date.now()) } }) it('allows including expired names - .eth 2ld', async () => { @@ -55,7 +55,7 @@ it('allows including expired names - .eth 2ld', async () => { }) if (!result.length) throw new Error('No names found') const expiredNames = result.filter( - (name) => name.expiryDate!.value < Date.now(), + (name) => name.expiryDate && name.expiryDate?.value < Date.now(), ) expect(expiredNames.length).toBeGreaterThan(0) }) @@ -77,7 +77,7 @@ it('does not include expired names by default - other', async () => { const namesWithExpiry = result.filter((name) => name.expiryDate) expect(namesWithExpiry.length).toBeGreaterThan(0) for (const name of namesWithExpiry) { - expect(name.expiryDate!.value).toBeGreaterThan(Date.now()) + expect(name.expiryDate?.value).toBeGreaterThan(Date.now()) } }) it('allows including expired names - other', async () => { diff --git a/packages/ensjs/src/functions/subgraph/getSubnames.ts b/packages/ensjs/src/actions/subgraph/getSubnames.ts similarity index 93% rename from packages/ensjs/src/functions/subgraph/getSubnames.ts rename to packages/ensjs/src/actions/subgraph/getSubnames.ts index 23c26ed33..077f5cff2 100644 --- a/packages/ensjs/src/functions/subgraph/getSubnames.ts +++ b/packages/ensjs/src/actions/subgraph/getSubnames.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { gql } from 'graphql-request' -import type { ClientWithEns } from '../../contracts/consts.js' +import { namehash } from 'viem/ens' +import type { ChainWithSubgraph } from '../../clients/l1.js' import { InvalidOrderByError } from '../../errors/subgraph.js' import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { namehash } from '../../utils/normalise.js' import { createSubgraphClient } from './client.js' import { type DomainFilter, @@ -11,14 +11,12 @@ import { getExpiryDateOrderFilter, } from './filters.js' import { - type SubgraphDomain, domainDetailsWithoutParentFragment, registrationDetailsFragment, + type SubgraphDomain, wrappedDomainDetailsFragment, } from './fragments.js' -import { type Name, makeNameObject } from './utils.js' - -type GetSubnamesOrderBy = 'expiryDate' | 'name' | 'labelName' | 'createdAt' +import { makeNameObject, type Name } from './utils.js' export type GetSubnamesParameters = { /** Name to get subnames for */ @@ -41,6 +39,10 @@ export type GetSubnamesParameters = { export type GetSubnamesReturnType = Name[] +export type GetSubnamesErrorType = InvalidOrderByError | Error + +type GetSubnamesOrderBy = 'expiryDate' | 'name' | 'labelName' | 'createdAt' + type SubgraphResult = { domain?: { subdomains: SubgraphDomain[] @@ -103,8 +105,8 @@ const getOrderByFilter = ({ * }) * const result = await getSubnames(client, { name: 'ens.eth' }) */ -const getSubnames = async ( - client: ClientWithEns, +const getSubnames = async <_chain extends ChainWithSubgraph>( + client: { chain: _chain }, { name, searchString, @@ -116,7 +118,7 @@ const getSubnames = async ( previousPage, }: GetSubnamesParameters, ): Promise => { - const subgraphClient = createSubgraphClient({ client }) + const subgraphClient = createSubgraphClient(client) const whereFilters: DomainFilter[] = [] diff --git a/packages/ensjs/src/functions/subgraph/types.ts b/packages/ensjs/src/actions/subgraph/types.ts similarity index 100% rename from packages/ensjs/src/functions/subgraph/types.ts rename to packages/ensjs/src/actions/subgraph/types.ts diff --git a/packages/ensjs/src/functions/subgraph/utils.ts b/packages/ensjs/src/actions/subgraph/utils.ts similarity index 52% rename from packages/ensjs/src/functions/subgraph/utils.ts rename to packages/ensjs/src/actions/subgraph/utils.ts index f530ff88a..aff4a62d5 100644 --- a/packages/ensjs/src/functions/subgraph/utils.ts +++ b/packages/ensjs/src/actions/subgraph/utils.ts @@ -1,8 +1,15 @@ -import { type Address, type Hex, getAddress } from 'viem' -import type { DateWithValue } from '../../types.js' +import { + coinTypeToNameMap, + getCoderByCoinType, +} from '@ensdomains/address-encoder' +import { type Address, getAddress, type Hex, hexToBytes, trim } from 'viem' +import type { ReturnResolverEvent } from '../../subgraph.js' +import type { DateWithValue } from '../../types/index.js' +import { decodeContentHash } from '../../utils/contentHash.js' import { truncateFormat } from '../../utils/format.js' import { type DecodedFuses, decodeFuses } from '../../utils/fuses.js' -import { decryptName } from '../../utils/labels.js' +import { decryptName } from '../../utils/name/labels.js' +import type { ResolverEvent } from './events.js' import type { SubgraphDomain } from './fragments.js' export type Name = { @@ -90,3 +97,83 @@ export const makeNameObject = (domain: SubgraphDomain): Name => { resolvedAddress: getChecksumAddressOrNull(domain.resolvedAddress?.id), } } + +export const decodeResolverEvents = (resolverEvents: ResolverEvent[]) => { + return resolverEvents.map((event: ResolverEvent): ReturnResolverEvent => { + switch (event.type) { + case 'AddrChanged': { + return { + ...event, + addr: event.addr?.id ?? null, + } + } + case 'MulticoinAddrChanged': { + const { multiaddr, ...event_ } = event + try { + const format = getCoderByCoinType(Number.parseInt(event.coinType)) + if (!format) { + return { + ...event_, + coinName: null, + decoded: false, + addr: multiaddr, + } + } + if (multiaddr === '0x' || trim(multiaddr) === '0x00') { + return { + ...event_, + coinName: format.name, + decoded: true, + addr: null, + } + } + return { + ...event_, + coinName: format.name, + decoded: true, + addr: format.encode(hexToBytes(multiaddr)), + } + } catch (e) { + if (e instanceof Error) { + if ( + e.message.includes('Unsupported coin type') || + e.message.includes('Coin formatter not found') + ) { + return { + ...event_, + coinName: null, + decoded: false, + addr: multiaddr, + } + } + if (e.message.includes('Unrecognised address format')) { + return { + ...event_, + coinName: + coinTypeToNameMap[ + event.coinType as keyof typeof coinTypeToNameMap + ][0], + decoded: false, + addr: multiaddr, + } + } + } + throw e + } + } + case 'ContenthashChanged': { + const { decoded: contentHash, protocolType } = decodeContentHash( + event.hash, + ) || { protocolType: null, decoded: null } + return { + ...event, + decoded: contentHash !== null, + contentHash, + protocolType, + } + } + default: + return event + } + }) +} diff --git a/packages/ensjs/src/functions/wallet/clearRecords.test.ts b/packages/ensjs/src/actions/wallet/clearRecords.test.ts similarity index 74% rename from packages/ensjs/src/functions/wallet/clearRecords.test.ts rename to packages/ensjs/src/actions/wallet/clearRecords.test.ts index 11c0c28ae..c21d5956b 100644 --- a/packages/ensjs/src/functions/wallet/clearRecords.test.ts +++ b/packages/ensjs/src/actions/wallet/clearRecords.test.ts @@ -6,10 +6,10 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getResolver from '../public/getResolver.js' -import getText from '../public/getTextRecord.js' -import clearRecords from './clearRecords.js' -import setTextRecord from './setTextRecord.js' +import { getResolver } from '../public/getResolver.js' +import { getTextRecord } from '../public/getTextRecord.js' +import { clearRecords } from './clearRecords.js' +import { setTextRecord } from './setTextRecord.js' let snapshot: Hex let accounts: Address[] @@ -27,9 +27,10 @@ afterEach(async () => { }) it('should allow a name to be cleared', async () => { - const resolverAddress = (await getResolver(publicClient, { + const resolverAddress = await getResolver(publicClient, { name: 'wrapped.eth', - }))! + }) + if (!resolverAddress) throw new Error('resolver not found') const setTextTx = await setTextRecord(walletClient, { name: 'wrapped.eth', @@ -43,7 +44,7 @@ it('should allow a name to be cleared', async () => { const setTextReceipt = await waitForTransaction(setTextTx) expect(setTextReceipt.status).toBe('success') - const priorResponse = await getText(publicClient, { + const priorResponse = await getTextRecord(publicClient, { name: 'wrapped.eth', key: 'description', }) @@ -58,7 +59,7 @@ it('should allow a name to be cleared', async () => { const receipt = await waitForTransaction(tx) expect(receipt.status).toBe('success') - const response = await getText(publicClient, { + const response = await getTextRecord(publicClient, { name: 'wrapped.eth', key: 'description', }) diff --git a/packages/ensjs/src/actions/wallet/clearRecords.ts b/packages/ensjs/src/actions/wallet/clearRecords.ts new file mode 100644 index 000000000..c48f00fd0 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/clearRecords.ts @@ -0,0 +1,126 @@ +import type { + Account, + Address, + Chain, + Client, + NamehashErrorType, + Transport, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type ClearRecordsParametersErrorType, + clearRecordsParameters, +} from '../../utils/coders/clearRecords.js' + +// ================================ +// Write Parameters +// ================================ + +export type ClearRecordsWriteParametersParameters = { + /** The name to clear records for */ + name: string + /** The resolver address to use */ + resolverAddress: Address +} + +export type ClearRecordsWriteParametersReturnType< + chain extends Chain = Chain, + account extends Account = Account, +> = ReturnType> + +export type ClearRecordsWriteParametersErrorType = + | ClearRecordsParametersErrorType + | NamehashErrorType + +export const clearRecordsWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { name, resolverAddress }: ClearRecordsWriteParametersParameters, +) => { + const _clearRecordsParameters = clearRecordsParameters(name) + return { + address: resolverAddress, + chain: client.chain, + account: client.account, + ..._clearRecordsParameters, + } as const satisfies WriteContractParameters +} + +// ================================ +// Action +// ================================ + +export type ClearRecordsParameters< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +> = Prettify< + ClearRecordsWriteParametersParameters & + WriteTransactionParameters +> + +export type ClearRecordsReturnType = WriteContractReturnType + +export type ClearRecordsErrorType = + | ClearRecordsParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Clears the records for a name on a resolver. + * @param client - {@link Client} + * @param options - {@link ClearRecordsParameters} + * @returns Transaction hash. {@link ClearRecordsReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { clearRecords } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await clearRecords(wallet, { + * name: 'ens.eth', + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function clearRecords< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + resolverAddress, + ...txArgs + }: ClearRecordsParameters, +): Promise { + const writeParameters = clearRecordsWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + resolverAddress, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/commitName.test.ts b/packages/ensjs/src/actions/wallet/commitName.test.ts similarity index 53% rename from packages/ensjs/src/functions/wallet/commitName.test.ts rename to packages/ensjs/src/actions/wallet/commitName.test.ts index 7f484d11f..f7bb666a5 100644 --- a/packages/ensjs/src/functions/wallet/commitName.test.ts +++ b/packages/ensjs/src/actions/wallet/commitName.test.ts @@ -1,18 +1,17 @@ +import { ethRegistrarCommitmentsSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' import type { Address, Hex } from 'viem' import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' -import { ethRegistrarControllerCommitmentsSnippet } from '../../contracts/ethRegistrarController.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' +import { getChainContractAddress } from '../../clients/shared.js' import { publicClient, testClient, - waitForTransaction, walletClient, } from '../../test/addTestContracts.js' import { - type RegistrationParameters, - makeCommitment, -} from '../../utils/registerHelpers.js' -import commitName from './commitName.js' + type L2RegistrationParameters, + makeL2Commitment, +} from '../../utils/l2RegisterHelpers.js' +import { commitName } from './commitName.js' let snapshot: Hex let accounts: Address[] @@ -32,8 +31,8 @@ afterEach(async () => { const secret = `0x${'a'.repeat(64)}` as Hex it('should return a commit transaction and succeed', async () => { - const params: RegistrationParameters = { - name: 'wrapped-with-subnames.eth', + const params: L2RegistrationParameters = { + label: 'wrapped-with-subnames', duration: 31536000, owner: accounts[1], secret, @@ -43,17 +42,17 @@ it('should return a commit transaction and succeed', async () => { account: accounts[1], }) expect(tx).toBeTruthy() - const receipt = await waitForTransaction(tx) + const receipt = await publicClient.waitForTransactionReceipt({ hash: tx }) expect(receipt.status).toBe('success') - const commitment = await publicClient.readContract({ - abi: ethRegistrarControllerCommitmentsSnippet, - functionName: 'commitments', + const commitmentAt = await publicClient.readContract({ + abi: ethRegistrarCommitmentsSnippet, + functionName: 'commitmentAt', address: getChainContractAddress({ - client: publicClient, - contract: 'ensEthRegistrarController', + chain: publicClient.chain, + contract: 'ethRegistrar', }), - args: [makeCommitment(params)], + args: [makeL2Commitment(params)], }) - expect(commitment).toBeTruthy() + expect(commitmentAt).toBeGreaterThan(0n) }) diff --git a/packages/ensjs/src/actions/wallet/commitName.ts b/packages/ensjs/src/actions/wallet/commitName.ts new file mode 100644 index 000000000..77f16798a --- /dev/null +++ b/packages/ensjs/src/actions/wallet/commitName.ts @@ -0,0 +1,164 @@ +import { ethRegistrarCommitSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import type { + Account, + Chain, + GetChainContractAddressErrorType, + Hash, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../errors/general.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type L2RegistrationParameters, + type MakeL2CommitmentErrorType, + makeL2Commitment, +} from '../../utils/l2RegisterHelpers.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type WrappedLabelLengthCheckErrorType, + wrappedLabelLengthCheck, +} from '../../utils/wrapper.js' + +// ================================ +// Write Parameters +// ================================ + +export type CommitNameWriteParametersParameters = L2RegistrationParameters + +export type CommitNameWriteParametersReturnType< + chain extends Chain, + account extends Account, +> = ReturnType> + +export type CommitNameWriteParametersErrorType = + | UnsupportedNameTypeError + | WrappedLabelLengthCheckErrorType + | GetChainContractAddressErrorType + | MakeL2CommitmentErrorType + +export const commitNameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + args: CommitNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const nameType = getNameType(`${args.label}.eth`) + if (nameType !== 'eth-2ld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld'], + details: 'Only 2ld-eth name registration is supported', + }) + wrappedLabelLengthCheck(args.label) + return { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ethRegistrar', + }), + abi: ethRegistrarCommitSnippet, + functionName: 'commit', + args: [makeL2Commitment(args)], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters +} + +// ================================ +// Action +// ================================ + +export type CommitNameParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ethRegistrar'> | undefined, +> = Prettify< + CommitNameWriteParametersParameters & + WriteTransactionParameters +> + +export type CommitNameReturnType = Hash + +export type CommitNameErrorType = + | CommitNameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Commits a name to be registered + * @param client - {@link Client} + * @param options - {@link CommitNameOptions} + * @returns Transaction hash. {@link CommitNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { commitName } from '@ensdomains/ensjs/wallet' + * import { randomSecret } from '@ensdomains/ensjs/utils' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const secret = randomSecret() + * const hash = await commitName(wallet, { + * name: 'example.eth', + * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * duration: 31536000, // 1 year + * secret, + * }) + * // 0x... + */ +export async function commitName< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ethRegistrar'> | undefined, +>( + client: RequireClientContracts, + { + label, + owner, + duration, + secret, + resolverAddress, + subregistryAddress, + referrer, + ...txArgs + }: CommitNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = commitNameWriteParameters( + clientWithOverrides(client, txArgs), + { + label, + owner, + duration, + secret, + resolverAddress, + subregistryAddress, + referrer, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/createSubname.test.ts b/packages/ensjs/src/actions/wallet/createSubname.test.ts similarity index 92% rename from packages/ensjs/src/functions/wallet/createSubname.test.ts rename to packages/ensjs/src/actions/wallet/createSubname.test.ts index e5240eed5..4ef221629 100644 --- a/packages/ensjs/src/functions/wallet/createSubname.test.ts +++ b/packages/ensjs/src/actions/wallet/createSubname.test.ts @@ -1,21 +1,21 @@ -import type { Address, Hex } from 'viem' -import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' +import { registryOwnerSnippet } from '@ensdomains/ensjs-abi/registry' import { nameWrapperGetDataSnippet, nameWrapperOwnerOfSnippet, -} from '../../contracts/nameWrapper.js' -import { registryOwnerSnippet } from '../../contracts/registry.js' +} from '@ensdomains/ensjs-abi/v1/nameWrapper' +import type { Address, Hex } from 'viem' +import { namehash } from 'viem/ens' +import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' +import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' import { publicClient, testClient, waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import { namehash } from '../../utils/normalise.js' -import getWrapperData from '../public/getWrapperData.js' -import createSubname from './createSubname.js' -import setFuses from './setFuses.js' +import { getWrapperData } from '../public/v1/getWrapperData.js' +import { createSubname } from './createSubname.js' +import { setFuses } from './setFuses.js' let snapshot: Hex let accounts: Address[] @@ -128,8 +128,8 @@ it('should create a subname on the namewrapper with max expiry if pcc is burned expect(owner).toBe(accounts[0]) const data = await getWrapperData(publicClient, { name: 'test.wrapped.eth' }) - expect(data?.expiry?.value).toBeTruthy() - expect(data!.expiry!.value).toBe(parentWrapperData?.expiry?.value) + expect(data?.expiry).toBeTruthy() + expect(data?.expiry).toBe(parentWrapperData?.expiry) }) it('should throw an error when creating a wrapped subname with PCC burned with a parent name that has not burned CANNOT_UNWRAP', async () => { diff --git a/packages/ensjs/src/functions/wallet/createSubname.ts b/packages/ensjs/src/actions/wallet/createSubname.ts similarity index 58% rename from packages/ensjs/src/functions/wallet/createSubname.ts rename to packages/ensjs/src/actions/wallet/createSubname.ts index 913187fc2..6b2552c2e 100644 --- a/packages/ensjs/src/functions/wallet/createSubname.ts +++ b/packages/ensjs/src/actions/wallet/createSubname.ts @@ -1,20 +1,24 @@ +import { registrySetSubnodeRecordSnippet } from '@ensdomains/ensjs-abi/registry' +import { nameWrapperSetSubnodeRecordSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' import { type Account, type Address, + type Chain, + type Client, + type EncodeFunctionDataErrorType, + encodeFunctionData, + type GetChainContractAddressErrorType, type Hash, + type SendTransactionErrorType, type SendTransactionParameters, type Transport, - encodeFunctionData, } from 'viem' import { sendTransaction } from 'viem/actions' import type { - ChainWithEns, - ClientWithAccount, - ClientWithEns, -} from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperSetSubnodeRecordSnippet } from '../../contracts/nameWrapper.js' -import { registrySetSubnodeRecordSnippet } from '../../contracts/registry.js' + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' import { BaseError } from '../../errors/base.js' import { InvalidContractTypeError, @@ -25,35 +29,34 @@ import type { Prettify, SimpleTransactionRequest, WriteTransactionParameters, -} from '../../types.js' +} from '../../types/index.js' import { + ASSERT_NO_TYPE_ERROR, + EXCLUDE_TYPE_ERROR, +} from '../../types/internal.js' +import { + type EncodeFusesErrorType, type EncodeFusesInputObject, - ParentFuses, encodeFuses, + ParentFuses, } from '../../utils/fuses.js' -import { getNameType } from '../../utils/getNameType.js' -import { makeLabelNodeAndParent } from '../../utils/makeLabelNodeAndParent.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type MakeLabelNodeAndParentErrorType, + makeLabelNodeAndParent, +} from '../../utils/name/makeLabelNodeAndParent.js' import { + type ExpiryToBigIntErrorType, expiryToBigInt, + type MakeDefaultExpiryErrorType, makeDefaultExpiry, + type WrappedLabelLengthCheckErrorType, wrappedLabelLengthCheck, } from '../../utils/wrapper.js' -import getWrapperData from '../public/getWrapperData.js' - -type BaseCreateSubnameDataParameters = { - /** Subname to create */ - name: string - /** New owner of subname */ - owner: Address - /** Contract to create subname on */ - contract: 'registry' | 'nameWrapper' - /** Resolver address to set */ - resolverAddress?: Address - /** Expiry to set (only on NameWrapper) */ - expiry?: AnyDate - /** Fuses to set (only on NameWrapper) */ - fuses?: EncodeFusesInputObject -} +import { + type GetWrapperDataErrorType, + getWrapperData, +} from '../public/v1/getWrapperData.js' type RegistryCreateSubnameDataParameters = { contract: 'registry' @@ -67,39 +70,93 @@ type NameWrapperCreateSubnameDataParameters = { fuses?: EncodeFusesInputObject } -export type CreateSubnameDataParameters = BaseCreateSubnameDataParameters & - (RegistryCreateSubnameDataParameters | NameWrapperCreateSubnameDataParameters) +// ================================ +// Errors +// ================================ -export type CreateSubnameDataReturnType = SimpleTransactionRequest +class CreateSubnamePermissionDeniedError extends BaseError { + parentName: string -export type CreateSubnameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - CreateSubnameDataParameters & - WriteTransactionParameters -> + override name = 'CreateSubnamePermissionDeniedError' -export type CreateSubnameReturnType = Hash + constructor({ parentName }: { parentName: string }) { + super( + `Create subname error: ${parentName} as burned CANNOT_CREATE_SUBDOMAIN fuse`, + ) + this.parentName = parentName + } +} + +class CreateSubnameParentNotLockedError extends BaseError { + parentName: string + + override name = 'CreateSubnameParentNotLockedError' + + constructor({ parentName }: { parentName: string }) { + super( + `Create subname error: Cannot burn PARENT_CANNOT_CONTROL when ${parentName} has not burned CANNOT_UNWRAP fuse`, + ) + this.parentName = parentName + } +} +// ================================ +// Make function data +// ================================ + +export type MakeCreateSubnameFunctionDataParameters = { + /** Subname to create */ + name: string + /** New owner of subname */ + owner: Address + /** Contract to create subname on */ + contract: 'registry' | 'nameWrapper' + /** Resolver address to set */ + resolverAddress?: Address + /** Expiry to set (only on NameWrapper) */ + expiry?: AnyDate + /** Fuses to set (only on NameWrapper) */ + fuses?: EncodeFusesInputObject +} & ( + | RegistryCreateSubnameDataParameters + | NameWrapperCreateSubnameDataParameters +) + +export type MakeCreateSubnameFunctionDataReturnType = SimpleTransactionRequest -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, +export type MakeCreateSubnameFunctionDataErrorType = + | GetChainContractAddressErrorType + | UnsupportedNameTypeError + | MakeLabelNodeAndParentErrorType + | EncodeFunctionDataErrorType + | WrappedLabelLengthCheckErrorType + | EncodeFusesErrorType + | ExpiryToBigIntErrorType + | MakeDefaultExpiryErrorType + | InvalidContractTypeError + +export const makeCreateSubnameFunctionData = < + chain extends Chain, + account extends Account | undefined, >( - wallet: ClientWithAccount, + wallet: RequireClientContracts< + chain, + 'ensPublicResolver' | 'ensNameWrapper', + account + >, { name, contract, owner, resolverAddress = getChainContractAddress({ - client: wallet, + chain: EXCLUDE_TYPE_ERROR(wallet).chain, contract: 'ensPublicResolver', }), expiry, fuses, - }: CreateSubnameDataParameters, -): CreateSubnameDataReturnType => { + }: MakeCreateSubnameFunctionDataParameters, +): MakeCreateSubnameFunctionDataReturnType => { + ASSERT_NO_TYPE_ERROR(wallet) + const nameType = getNameType(name) if (nameType === 'tld' || nameType === 'root') throw new UnsupportedNameTypeError({ @@ -118,7 +175,7 @@ export const makeFunctionData = < case 'registry': { return { to: getChainContractAddress({ - client: wallet, + chain: wallet.chain, contract: 'ensRegistry', }), data: encodeFunctionData({ @@ -136,7 +193,7 @@ export const makeFunctionData = < : makeDefaultExpiry(generatedFuses) return { to: getChainContractAddress({ - client: wallet, + chain: wallet.chain, contract: 'ensNameWrapper', }), data: encodeFunctionData({ @@ -162,39 +219,29 @@ export const makeFunctionData = < } } -class CreateSubnamePermissionDeniedError extends BaseError { - parentName: string - - override name = 'CreateSubnamePermissionDeniedError' - - constructor({ parentName }: { parentName: string }) { - super( - `Create subname error: ${parentName} as burned CANNOT_CREATE_SUBDOMAIN fuse`, - ) - this.parentName = parentName - } -} - -class CreateSubnameParentNotLockedError extends BaseError { - parentName: string - - override name = 'CreateSubnameParentNotLockedError' +// ================================ +// Check can create subname +// ================================ - constructor({ parentName }: { parentName: string }) { - super( - `Create subname error: Cannot burn PARENT_CANNOT_CONTROL when ${parentName} has not burned CANNOT_UNWRAP fuse`, - ) - this.parentName = parentName - } -} +type CheckCanCreateSubnameErrorType = + | GetWrapperDataErrorType + | CreateSubnamePermissionDeniedError + | EncodeFusesErrorType + | CreateSubnameParentNotLockedError const checkCanCreateSubname = async ( - wallet: ClientWithEns, + wallet: Client< + Transport, + ChainWithContracts<'ensPublicResolver' | 'ensNameWrapper'> + >, { name, fuses, contract, - }: Pick, + }: Pick< + MakeCreateSubnameFunctionDataParameters, + 'name' | 'contract' | 'fuses' + >, ): Promise => { if (contract !== 'nameWrapper') return @@ -214,6 +261,28 @@ const checkCanCreateSubname = async ( throw new CreateSubnameParentNotLockedError({ parentName }) } +// ================================ +// Create subname +// ================================ + +export type CreateSubnameParameters< + chain extends Chain, + account extends Account | undefined, + chainOverride extends + | ChainWithContracts<'ensPublicResolver' | 'ensNameWrapper'> + | undefined, +> = Prettify< + MakeCreateSubnameFunctionDataParameters & + WriteTransactionParameters +> + +export type CreateSubnameReturnType = Hash + +export type CreateSubnameErrorType = + | CheckCanCreateSubnameErrorType + | MakeCreateSubnameFunctionDataErrorType + | SendTransactionErrorType + /** * Creates a subname * @param wallet - {@link ClientWithAccount} @@ -237,12 +306,18 @@ const checkCanCreateSubname = async ( * }) * // 0x... */ -async function createSubname< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, +export async function createSubname< + chain extends Chain, + account extends Account | undefined, + chainOverride extends + | ChainWithContracts<'ensPublicResolver' | 'ensNameWrapper'> + | undefined = ChainWithContracts<'ensPublicResolver' | 'ensNameWrapper'>, >( - wallet: ClientWithAccount, + wallet: RequireClientContracts< + chain, + 'ensNameWrapper' | 'ensPublicResolver', + account + >, { name, contract, @@ -251,26 +326,29 @@ async function createSubname< expiry, fuses, ...txArgs - }: CreateSubnameParameters, + }: CreateSubnameParameters, ): Promise { + ASSERT_NO_TYPE_ERROR(wallet) + await checkCanCreateSubname(wallet, { name, fuses, contract }) - const data = makeFunctionData(wallet, { - name, - contract, - owner, - resolverAddress, - expiry, - fuses, - } as CreateSubnameDataParameters) + const data = makeCreateSubnameFunctionData( + wallet, + { + name, + contract, + owner, + resolverAddress, + expiry, + fuses, + } as MakeCreateSubnameFunctionDataParameters, + ) const writeArgs = { ...data, ...txArgs, - } as SendTransactionParameters + } as SendTransactionParameters return sendTransaction(wallet, writeArgs) } -createSubname.makeFunctionData = makeFunctionData - -export default createSubname +createSubname.makeFunctionData = makeCreateSubnameFunctionData diff --git a/packages/ensjs/src/functions/wallet/deleteSubname.test.ts b/packages/ensjs/src/actions/wallet/deleteSubname.test.ts similarity index 85% rename from packages/ensjs/src/functions/wallet/deleteSubname.test.ts rename to packages/ensjs/src/actions/wallet/deleteSubname.test.ts index c4402f996..69678671c 100644 --- a/packages/ensjs/src/functions/wallet/deleteSubname.test.ts +++ b/packages/ensjs/src/actions/wallet/deleteSubname.test.ts @@ -1,20 +1,19 @@ -import type { Address, Hex } from 'viem' -import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperOwnerOfSnippet } from '../../contracts/nameWrapper.js' import { registryOwnerSnippet, registryResolverSnippet, -} from '../../contracts/registry.js' +} from '@ensdomains/ensjs-abi/registry' +import { nameWrapperOwnerOfSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { type Address, type Hex, zeroAddress } from 'viem' +import { namehash } from 'viem/ens' +import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' +import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' import { publicClient, testClient, waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { namehash } from '../../utils/normalise.js' -import deleteSubname from './deleteSubname.js' +import { deleteSubname } from './deleteSubname.js' let snapshot: Hex let accounts: Address[] @@ -78,10 +77,10 @@ it('should allow deleting a subname on the registry by parent owner', async () = expect(receipt.status).toBe('success') const owner = await getOwner('test.with-subnames.eth') - expect(owner).toBe(EMPTY_ADDRESS) + expect(owner).toBe(zeroAddress) const resolver = await getResolver('test.with-subnames.eth') - expect(resolver).toBe(EMPTY_ADDRESS) + expect(resolver).toBe(zeroAddress) }) it('should allow deleting a subname on the namewrapper by parent owner', async () => { @@ -95,15 +94,15 @@ it('should allow deleting a subname on the namewrapper by parent owner', async ( expect(receipt.status).toBe('success') const owner = await getOwner('test.wrapped-with-subnames.eth') - expect(owner).toBe(EMPTY_ADDRESS) + expect(owner).toBe(zeroAddress) const resolver = await getResolver('test.wrapped-with-subnames.eth') - expect(resolver).toBe(EMPTY_ADDRESS) + expect(resolver).toBe(zeroAddress) const nameWrapperOwner = await getNameWrapperOwner( 'test.wrapped-with-subnames.eth', ) - expect(nameWrapperOwner).toBe(EMPTY_ADDRESS) + expect(nameWrapperOwner).toBe(zeroAddress) }) it('should allow deleting a subname on the namewrapper by name owner', async () => { @@ -118,15 +117,15 @@ it('should allow deleting a subname on the namewrapper by name owner', async () expect(receipt.status).toBe('success') const owner = await getOwner('addr.wrapped-with-subnames.eth') - expect(owner).toBe(EMPTY_ADDRESS) + expect(owner).toBe(zeroAddress) const resolver = await getResolver('addr.wrapped-with-subnames.eth') - expect(resolver).toBe(EMPTY_ADDRESS) + expect(resolver).toBe(zeroAddress) const nameWrapperOwner = await getNameWrapperOwner( 'addr.wrapped-with-subnames.eth', ) - expect(nameWrapperOwner).toBe(EMPTY_ADDRESS) + expect(nameWrapperOwner).toBe(zeroAddress) }) it('should not allow deleting top level domain', async () => { diff --git a/packages/ensjs/src/actions/wallet/deleteSubname.ts b/packages/ensjs/src/actions/wallet/deleteSubname.ts new file mode 100644 index 000000000..9957dcac7 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/deleteSubname.ts @@ -0,0 +1,241 @@ +import { + registrySetRecordSnippet, + registrySetSubnodeRecordSnippet, +} from '@ensdomains/ensjs-abi/registry' +import { + nameWrapperSetRecordSnippet, + nameWrapperSetSubnodeRecordSnippet, +} from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Account, + type Chain, + type GetChainContractAddressErrorType, + type Hash, + type NamehashErrorType, + namehash, + type WriteContractErrorType, + type WriteContractParameters, + zeroAddress, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { + InvalidContractTypeError, + UnsupportedNameTypeError, +} from '../../errors/general.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type MakeLabelNodeAndParentErrorType, + makeLabelNodeAndParent, +} from '../../utils/name/makeLabelNodeAndParent.js' + +// ================================ +// Write parameters +// ================================ + +export type DeleteSubnameWriteParametersParameters = { + /** Subname to delete */ + name: string + /** Contract to delete subname on */ + contract: 'registry' | 'nameWrapper' + /** If true, deletes via owner methods, otherwise will delete via parent owner methods */ + asOwner?: boolean +} + +export type DeleteSubnameWriteParametersReturnType = ReturnType< + typeof deleteSubnameWriteParameters +> + +export type DeleteSubnameWriteParametersErrorType = + | UnsupportedNameTypeError + | GetChainContractAddressErrorType + | NamehashErrorType + | MakeLabelNodeAndParentErrorType + | InvalidContractTypeError + +export const deleteSubnameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts< + chain, + 'ensRegistry' | 'ensNameWrapper', + account + >, + { name, contract, asOwner }: DeleteSubnameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const nameType = getNameType(name) + if (nameType !== 'eth-subname' && nameType !== 'other-subname') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-subname', 'other-subname'], + details: 'Cannot delete a name that is not a subname', + }) + + switch (contract) { + case 'registry': { + const baseParams = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }), + chain: client.chain, + account: client.account, + } as const + if (asOwner) + return { + ...baseParams, + abi: registrySetRecordSnippet, + functionName: 'setRecord', + args: [namehash(name), zeroAddress, zeroAddress, BigInt(0)], + } as const satisfies WriteContractParameters< + typeof registrySetRecordSnippet + > + + const { labelhash, parentNode } = makeLabelNodeAndParent(name) + return { + ...baseParams, + abi: registrySetSubnodeRecordSnippet, + functionName: 'setSubnodeRecord', + args: [parentNode, labelhash, zeroAddress, zeroAddress, BigInt(0)], + } as const satisfies WriteContractParameters< + typeof registrySetSubnodeRecordSnippet + > + } + case 'nameWrapper': { + const baseParams = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + chain: client.chain, + account: client.account, + } as const + if (asOwner) + return { + ...baseParams, + abi: nameWrapperSetRecordSnippet, + functionName: 'setRecord', + args: [namehash(name), zeroAddress, zeroAddress, BigInt(0)], + } as const satisfies WriteContractParameters< + typeof nameWrapperSetRecordSnippet + > + + const { label, parentNode } = makeLabelNodeAndParent(name) + return { + ...baseParams, + abi: nameWrapperSetSubnodeRecordSnippet, + functionName: 'setSubnodeRecord', + args: [ + parentNode, + label, + zeroAddress, + zeroAddress, + BigInt(0), + 0, + BigInt(0), + ], + } as const satisfies WriteContractParameters< + typeof nameWrapperSetSubnodeRecordSnippet + > + } + default: + throw new InvalidContractTypeError({ + contractType: contract, + supportedContractTypes: ['registry', 'nameWrapper'], + }) + } +} + +// ================================ +// Delete subname action +// ================================ + +export type DeleteSubnameParameters< + chain extends Chain, + account extends Account, + chainOverride extends + | ChainWithContracts<'ensRegistry' | 'ensNameWrapper'> + | undefined, +> = Prettify< + DeleteSubnameWriteParametersParameters & + WriteTransactionParameters +> + +export type DeleteSubnameReturnType = Hash + +export type DeleteSubnameErrorType = + | DeleteSubnameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deletes a subname + * @param client - {@link Client} + * @param options - {@link DeleteSubnameOptions} + * @returns Transaction hash. {@link DeleteSubnameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { deleteSubname } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnetWithEns, + * transport: custom(window.ethereum), + * }) + * const hash = await deleteSubname(wallet, { + * name: 'sub.ens.eth', + * contract: 'registry', + * }) + * // 0x... + */ +export async function deleteSubname< + chain extends Chain, + account extends Account, + chainOverride extends + | ChainWithContracts<'ensRegistry' | 'ensNameWrapper'> + | undefined, +>( + client: RequireClientContracts< + chain, + 'ensRegistry' | 'ensNameWrapper', + account + >, + { + name, + contract, + asOwner, + ...txArgs + }: DeleteSubnameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const data = deleteSubnameWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + contract, + asOwner, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...data, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/registerName.test.ts b/packages/ensjs/src/actions/wallet/registerName.test.ts similarity index 50% rename from packages/ensjs/src/functions/wallet/registerName.test.ts rename to packages/ensjs/src/actions/wallet/registerName.test.ts index 3a9278f01..2e845b784 100644 --- a/packages/ensjs/src/functions/wallet/registerName.test.ts +++ b/packages/ensjs/src/actions/wallet/registerName.test.ts @@ -1,18 +1,16 @@ import type { Address, Hex } from 'viem' +import { maxUint256, parseAbi } from 'viem' import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperOwnerOfSnippet } from '../../contracts/nameWrapper.js' import { + deploymentAddresses, publicClient, testClient, waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import { namehash } from '../../utils/normalise.js' -import type { RegistrationParameters } from '../../utils/registerHelpers.js' -import getPrice from '../public/getPrice.js' -import commitName from './commitName.js' -import registerName from './registerName.js' +import type { L2RegistrationParameters } from '../../utils/l2RegisterHelpers.js' +import { commitName } from './commitName.js' +import { registerName } from './registerName.js' let snapshot: Hex let accounts: Address[] @@ -31,21 +29,9 @@ afterEach(async () => { const secret = `0x${'a'.repeat(64)}` as Hex -const getNameWrapperOwner = async (name: string) => { - return publicClient.readContract({ - abi: nameWrapperOwnerOfSnippet, - functionName: 'ownerOf', - address: getChainContractAddress({ - client: publicClient, - contract: 'ensNameWrapper', - }), - args: [BigInt(namehash(name))], - }) -} - it('should return a registration transaction and succeed', async () => { - const params: RegistrationParameters = { - name: 'cool-swag.eth', + const params: L2RegistrationParameters = { + label: 'cool-swag', duration: 31536000, owner: accounts[1], secret, @@ -62,21 +48,38 @@ it('should return a registration transaction and succeed', async () => { await testClient.increaseTime({ seconds: 61 }) await testClient.mine({ blocks: 1 }) - const price = await getPrice(publicClient, { - nameOrNames: params.name, - duration: params.duration, + // Approve the ETH Registrar to spend USDC on behalf of accounts[1] + const approveTx = await walletClient.writeContract({ + address: deploymentAddresses.USDC, + abi: parseAbi([ + 'function approve(address spender, uint256 amount) returns (bool)', + ]), + functionName: 'approve', + args: [deploymentAddresses.ETHRegistrar, maxUint256], + account: accounts[1], + }) + const approveReceipt = await publicClient.waitForTransactionReceipt({ + hash: approveTx, }) - const total = price!.base + price!.premium + expect(approveReceipt.status).toBe('success') const tx = await registerName(walletClient, { ...params, account: accounts[1], - value: total, }) expect(tx).toBeTruthy() const receipt = await waitForTransaction(tx) expect(receipt.status).toBe('success') - const owner = await getNameWrapperOwner(params.name) - expect(owner).toBe(accounts[1]) + // TODO: add test for owner + // const owner = await publicClient.readContract({ + // abi: nameWrapperOwnerOfSnippet, + // functionName: 'ownerOf', + // address: getChainContractAddress({ + // chain: publicClient.chain, + // contract: 'ensNameWrapper', + // }), + // args: [BigInt(namehash(name))], + // }) + // expect(owner).toBe(params.owner) }) diff --git a/packages/ensjs/src/actions/wallet/registerName.ts b/packages/ensjs/src/actions/wallet/registerName.ts new file mode 100644 index 000000000..277e606c6 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/registerName.ts @@ -0,0 +1,198 @@ +import { ethRegistrarRegisterSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import { + type Account, + type Chain, + type GetChainContractAddressErrorType, + type Hash, + type WriteContractErrorType, + type WriteContractParameters, + zeroAddress, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction, padHex } from 'viem/utils' +import { + type ChainWithContracts, + getChainContractAddress, + type RequireClientContracts, +} from '../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../errors/general.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import type { + L2RegistrationParameters, + MakeL2CommitmentTupleErrorType, +} from '../../utils/l2RegisterHelpers.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type WrappedLabelLengthCheckErrorType, + wrappedLabelLengthCheck, +} from '../../utils/wrapper.js' + +// ================================ +// Write parameters +// ================================ + +export type RegisterNameWriteParametersParameters = L2RegistrationParameters + +export type RegisterNameWriteParametersReturnType< + chain extends Chain, + account extends Account, +> = ReturnType> + +export type RegisterNameWriteParametersErrorType = + | UnsupportedNameTypeError + | WrappedLabelLengthCheckErrorType + | GetChainContractAddressErrorType + | MakeL2CommitmentTupleErrorType + +export const registerNameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + registrationParams: RegisterNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const nameType = getNameType(`${registrationParams.label}.eth`) + if (nameType !== 'eth-2ld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld'], + details: 'Only 2ld-eth name registration is supported', + }) + + wrappedLabelLengthCheck(registrationParams.label) + + const usdc = getChainContractAddress({ + chain: client.chain, + contract: 'usdc', + }) + + return { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ethRegistrar', + }), + abi: ethRegistrarRegisterSnippet, + functionName: 'register', + args: [ + registrationParams.label, // V2 ETHRegistrar expects a label, not a full name + registrationParams.owner, + registrationParams.secret, + registrationParams.subregistryAddress || zeroAddress, + registrationParams.resolverAddress || zeroAddress, + BigInt(registrationParams.duration), + registrationParams.paymentToken || usdc, + registrationParams.referrer || padHex('0x', { size: 32 }), + ], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof ethRegistrarRegisterSnippet + > +} + +// ================================ +// Register name action +// ================================ + +export type RegisterNameParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ethRegistrar' | 'usdc'> | undefined, +> = Prettify< + RegisterNameWriteParametersParameters & + WriteTransactionParameters +> + +export type RegisterNameReturnType = Hash + +export type RegisterNameErrorType = + | RegisterNameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Registers a name on ENS + * @param client - {@link Client} + * @param options - {@link RegisterNameOptions} + * @returns Transaction hash. {@link RegisterNameReturnType} + * + * @example + * import { createPublicClient, createWalletClient, http, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { getPrice } from '@ensdomains/ensjs/public/v1' + * import { randomSecret } from '@ensdomains/ensjs/utils' + * import { commitName, registerName } from '@ensdomains/ensjs/wallet' + * + * const mainnetWithEns = addEnsContracts(mainnet) + * const publicClient = createPublicClient({ + * chain: mainnetWithEns, + * transport: http(), + * }) + * const wallet = createWalletClient({ + * chain: mainnetWithEns, + * transport: custom(window.ethereum), + * }) + * const secret = randomSecret() + * const params = { + * name: 'example.eth', + * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * duration: 31536000, // 1 year + * secret, + * } + * + * const commitmentHash = await commitName(wallet, params) + * await publicClient.waitForTransactionReceipt({ hash: commitmentHash }) // wait for commitment to finalise + * await new Promise((resolve) => setTimeout(resolve, 60 * 1_000)) // wait for commitment to be valid + * + * const { base, premium } = await getPrice(publicClient, { nameOrNames: params.name, duration: params.duration }) + * const value = (base + premium) * 110n / 100n // add 10% to the price for buffer + * const hash = await registerName(wallet, { ...params, value }) + * // 0x... + */ +export async function registerName< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ethRegistrar' | 'usdc'> | undefined, +>( + client: RequireClientContracts, + { + label, + owner, + duration, + secret, + resolverAddress, + subregistryAddress, + referrer, + paymentToken, + ...txArgs + }: RegisterNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = registerNameWriteParameters( + clientWithOverrides(client, txArgs), + { + label, + owner, + duration, + secret, + resolverAddress, + subregistryAddress, + paymentToken, + referrer, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/renewName.test.ts b/packages/ensjs/src/actions/wallet/renewName.test.ts new file mode 100644 index 000000000..ed125e5a2 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/renewName.test.ts @@ -0,0 +1,86 @@ +import { type Address, encodeFunctionData, zeroHash } from 'viem' +import { beforeAll, expect, it } from 'vitest' +import { getChainContractAddress } from '../../clients/shared.js' +import { + deploymentAddresses, + walletClient, +} from '../../test/addTestContracts.js' +import { clientWithOverrides } from '../../utils/clientWithOverrides.js' +import { renewNameWriteParameters } from './renewName.js' + +const paymentToken = deploymentAddresses.USDC + +let accounts: Address[] + +beforeAll(async () => { + accounts = await walletClient.getAddresses() +}) + +it('renewNameWriteParameters rejects non-eth-2ld names', () => { + expect(() => + renewNameWriteParameters( + clientWithOverrides(walletClient, { account: accounts[0] }), + { + name: 'foo.bar.eth', + duration: 31_536_000n, + paymentToken, + contract: 'ensEthRenewerV1', + }, + ), + ).toThrow() +}) + +it.each(['ensEthRegistrar', 'ensEthRenewerV1'] as const)( + 'renewNameWriteParameters encodes renew calldata for a 2ld name via %s', + (contract) => { + const writeParameters = renewNameWriteParameters( + clientWithOverrides(walletClient, { account: accounts[0] }), + { + name: 'example.eth', + duration: 31_536_000n, + paymentToken, + referrer: zeroHash, + contract, + }, + ) + const data = encodeFunctionData({ + abi: writeParameters.abi, + functionName: writeParameters.functionName, + args: writeParameters.args, + }) + + expect(data).toMatch(/^0x[0-9a-f]+$/i) + expect(data.length).toBeGreaterThan(10) + }, +) + +it('renewNameWriteParameters routes to the ETHRegistrar for v2 names', () => { + const { address } = renewNameWriteParameters( + clientWithOverrides(walletClient, { account: accounts[0] }), + { + name: 'example.eth', + duration: 31_536_000n, + paymentToken, + contract: 'ensEthRegistrar', + }, + ) + expect(address).toBe(deploymentAddresses.ETHRegistrar) +}) + +it('renewNameWriteParameters routes to the ETHRenewerV1 for v1 names', () => { + const { address } = renewNameWriteParameters( + clientWithOverrides(walletClient, { account: accounts[0] }), + { + name: 'example.eth', + duration: 31_536_000n, + paymentToken, + contract: 'ensEthRenewerV1', + }, + ) + expect(address).toBe( + getChainContractAddress({ + chain: walletClient.chain, + contract: 'ensEthRenewerV1', + }), + ) +}) diff --git a/packages/ensjs/src/actions/wallet/renewName.ts b/packages/ensjs/src/actions/wallet/renewName.ts new file mode 100644 index 000000000..e56c8a1d6 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/renewName.ts @@ -0,0 +1,199 @@ +import { ethRegistrarRenewSnippet } from '@ensdomains/ensjs-abi/v2/ethRegistrar' +import type { + Account, + Address, + Chain, + GetChainContractAddressErrorType, + Hex, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { zeroHash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { UnsupportedNameTypeError } from '../../errors/general.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { getNameType } from '../../utils/name/getNameType.js' + +// The two renewers share one `renew(label,duration,paymentToken,referrer)` ABI — +// only the target contract differs: `ensEthRegistrar` renews names registered on +// v2, `ensEthRenewerV1` renews unmigrated legacy (v1) names. The caller supplies +// which contract to use (from an indexer, or an on-chain owner lookup) — this +// action does no detection, it just builds the calldata. +type RenewerContract = 'ensEthRegistrar' | 'ensEthRenewerV1' + +export type RenewNameWriteParametersParameters = { + /** Full 2LD .eth name to renew (e.g. example.eth) */ + name: string + /** Renewal duration in seconds */ + duration: bigint + /** ERC-20 token used for payment (must be approved for the renewer) */ + paymentToken: Address + /** + * Referrer id (bytes32). Defaults to zero bytes32 when omitted. + */ + referrer?: Hex + /** + * Renewer contract to use: `ensEthRegistrar` for names registered on v2, + * `ensEthRenewerV1` for unmigrated legacy (v1) names. + */ + contract: RenewerContract +} + +export type RenewNameWriteParametersReturnType = ReturnType< + typeof renewNameWriteParameters +> + +export type RenewNameWriteParametersErrorType = + | UnsupportedNameTypeError + | GetChainContractAddressErrorType + +// ================================ +// Write parameters +// ================================ + +export const renewNameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + { + name, + duration, + paymentToken, + referrer = zeroHash, + contract, + }: RenewNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const nameType = getNameType(name) + if (nameType !== 'eth-2ld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld'], + details: 'Only 2ld-eth renewals are supported', + }) + + if (contract !== 'ensEthRegistrar' && contract !== 'ensEthRenewerV1') + throw new Error(`Unknown contract: ${contract}`) + + const [label] = name.split('.') + + const address = getChainContractAddress({ + chain: client.chain, + contract, + }) + + // Both renewers share an identical `renew` ABI, so a single snippet works for + // either contract. + return { + address, + functionName: 'renew', + args: [label, duration, paymentToken, referrer] as const, + chain: client.chain, + account: client.account, + abi: ethRegistrarRenewSnippet, + } as const satisfies WriteContractParameters +} + +// ================================ +// Renew name action +// ================================ + +export type RenewNameParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts | undefined, +> = Prettify< + RenewNameWriteParametersParameters & + WriteTransactionParameters +> + +export type RenewNameReturnType = WriteContractReturnType + +export type RenewNameErrorType = + | RenewNameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Renews a `.eth` 2LD via the given renewer contract: the v2 `ETHRegistrar` + * (`contract: 'ensEthRegistrar'`) for names registered on v2, or `ETHRenewerV1` + * (`contract: 'ensEthRenewerV1'`) for unmigrated legacy (v1) names. Both expose + * the same `renew(label,duration,paymentToken,referrer)` ERC-20 interface, so + * `contract` only selects the target address — resolve it from your data source + * (indexer) or an on-chain owner lookup ({@link getOwner}). + * + * Legacy `ETHRegistrarController`s were revoked at the v2 migration cutover, so an + * unmigrated v1 name can only be renewed through `ETHRenewerV1`, which renews it + * and syncs the underlying v1 BaseRegistrar. It renews a name while it still holds + * its pre-migration `RESERVED` slot (throughout the name's active life), or — once + * that slot has lapsed to `AVAILABLE` — while it is still unclaimed and within the + * v2 grace window; a name that was never reserved, has already migrated to v2, or + * has lapsed past grace reverts `NameNotRenewable`. + * + * Renews a single name — neither renewer is `Multicallable`, so there is no + * on-chain bulk-renewal path. + * + * @param client - {@link Client} + * @param options - {@link RenewNameParameters} + * @returns Transaction hash. {@link RenewNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { renewName } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * + * const hash = await renewName(wallet, { + * name: 'example.eth', + * duration: 31536000n, // 1 year + * paymentToken: usdcAddress, + * contract: 'ensEthRegistrar', + * }) + * // 0x... + */ +export async function renewName< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts | undefined, +>( + client: RequireClientContracts, + { + name, + duration, + paymentToken, + referrer, + contract, + ...txArgs + }: RenewNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = renewNameWriteParameters( + clientWithOverrides(client, txArgs), + { name, duration, paymentToken, referrer, contract }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setAbiRecord.test.ts b/packages/ensjs/src/actions/wallet/setAbiRecord.test.ts similarity index 67% rename from packages/ensjs/src/functions/wallet/setAbiRecord.test.ts rename to packages/ensjs/src/actions/wallet/setAbiRecord.test.ts index 8e630d9e9..6cb7cc215 100644 --- a/packages/ensjs/src/functions/wallet/setAbiRecord.test.ts +++ b/packages/ensjs/src/actions/wallet/setAbiRecord.test.ts @@ -6,10 +6,10 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import { encodeAbi } from '../../utils/encoders/encodeAbi.js' -import getAbiRecord from '../public/getAbiRecord.js' -import getResolver from '../public/getResolver.js' -import setAbiRecord from './setAbiRecord.js' +import type { AbiEncodeAs } from '../../utils/coders/encodeAbi.js' +import { getAbiRecord } from '../public/getAbiRecord.js' +import { getResolver } from '../public/getResolver.js' +import { setAbiRecord } from './setAbiRecord.js' let snapshot: Hex let accounts: Address[] @@ -47,12 +47,12 @@ const dummyABI = [ ] it('should allow an abi record to be set with json content type', async () => { - const encodedAbi = await encodeAbi({ encodeAs: 'json', data: dummyABI }) const tx = await setAbiRecord(walletClient, { - name: 'test123.eth', - encodedAbi, + name: 'with-subnames.eth', + data: dummyABI, + encodeAs: 'json', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -61,20 +61,20 @@ it('should allow an abi record to be set with json content type', async () => { expect(receipt.status).toBe('success') const response = await getAbiRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) - expect(response!.abi).toEqual(dummyABI) - expect(response!.contentType).toEqual(1) - expect(response!.decoded).toBe(true) + expect(response?.abi).toEqual(dummyABI) + expect(response?.contentType).toEqual(1) + expect(response?.decoded).toBe(true) }) it('should allow an abi record to be set with zlib content type', async () => { - const encodedAbi = await encodeAbi({ encodeAs: 'zlib', data: dummyABI }) const tx = await setAbiRecord(walletClient, { - name: 'test123.eth', - encodedAbi, + name: 'with-subnames.eth', + data: dummyABI, + encodeAs: 'zlib', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -83,20 +83,20 @@ it('should allow an abi record to be set with zlib content type', async () => { expect(receipt.status).toBe('success') const response = await getAbiRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) - expect(response!.abi).toEqual(dummyABI) - expect(response!.contentType).toEqual(2) - expect(response!.decoded).toBe(true) + expect(response?.abi).toEqual(dummyABI) + expect(response?.contentType).toEqual(2) + expect(response?.decoded).toBe(true) }) it('should allow an abi record to be set with cbor content type', async () => { - const encodedAbi = await encodeAbi({ encodeAs: 'cbor', data: dummyABI }) const tx = await setAbiRecord(walletClient, { - name: 'test123.eth', - encodedAbi, + name: 'with-subnames.eth', + data: dummyABI, + encodeAs: 'cbor', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -105,23 +105,20 @@ it('should allow an abi record to be set with cbor content type', async () => { expect(receipt.status).toBe('success') const response = await getAbiRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) - expect(response!.abi).toEqual(dummyABI) - expect(response!.contentType).toEqual(4) - expect(response!.decoded).toBe(true) + expect(response?.abi).toEqual(dummyABI) + expect(response?.contentType).toEqual(4) + expect(response?.decoded).toBe(true) }) it('should allow an abi record to be set with uri content type', async () => { - const encodedAbi = await encodeAbi({ - encodeAs: 'uri', - data: 'https://example.com', - }) const tx = await setAbiRecord(walletClient, { - name: 'test123.eth', - encodedAbi, + name: 'with-subnames.eth', + data: 'https://example.com', + encodeAs: 'uri', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -130,29 +127,25 @@ it('should allow an abi record to be set with uri content type', async () => { expect(receipt.status).toBe('success') const response = await getAbiRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) - expect(response!.abi).toEqual('https://example.com') - expect(response!.contentType).toEqual(8) - expect(response!.decoded).toBe(true) + expect(response?.abi).toEqual('https://example.com') + expect(response?.contentType).toEqual(8) + expect(response?.decoded).toBe(true) }) -type EncodeAs = Parameters[0]['encodeAs'] it.each([ ['json', 'with-type-1-abi.eth'], ['zlib', 'with-type-2-abi.eth'], ['cbor', 'with-type-4-abi.eth'], ['uri', 'with-type-8-abi.eth'], -] as [EncodeAs, string][])( +] as [AbiEncodeAs, string][])( 'should allow an abi record to be set to null with %s content type', async (encodeAs, name) => { - const encodedAbi = await encodeAbi({ - encodeAs, - data: null, - }) const tx = await setAbiRecord(walletClient, { name, - encodedAbi, + data: null, + encodeAs, resolverAddress: (await getResolver(publicClient, { name, }))!, diff --git a/packages/ensjs/src/actions/wallet/setAbiRecord.ts b/packages/ensjs/src/actions/wallet/setAbiRecord.ts new file mode 100644 index 000000000..8675f08c6 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setAbiRecord.ts @@ -0,0 +1,176 @@ +import type { + Account, + Address, + Chain, + Client, + NamehashErrorType, + Transport, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import type { AbiEncodeAs } from '../../utils/coders/encodeAbi.js' +import { + type SetAbiParameters, + type SetAbiParametersErrorType, + type SetAbiParametersReturnType, + setAbiParameters, +} from '../../utils/coders/setAbi.js' + +// export type SetAbiRecordParameters = +// Prettify< +// EncodeAbiParameters & { +// /** Name to set ABI for */ +// name: string; +// /** Resolver address to set ABI on */ +// resolverAddress: Address; +// } +// >; + +// export type SetAbiRecordOptions< +// encodeAs extends AbiEncodeAs, +// chain extends Chain | undefined, +// account extends Account | undefined, +// chainOverride extends Chain | undefined, +// > = Prettify< +// SetAbiRecordParameters & +// WriteTransactionParameters +// >; + +// export type SetAbiRecordReturnType = WriteContractReturnType; + +// export type SetAbiRecordErrorType = Error; + +// ================================ +// Write parameters +// ================================ + +export type SetAbiRecordWriteParametersParameters< + encodeAs extends AbiEncodeAs, +> = Prettify< + SetAbiParameters & { + /** Name to set ABI for */ + name: string + /** Resolver address to set ABI on */ + resolverAddress: Address + } +> + +export type SetAbiRecordWriteParametersReturnType = ReturnType< + typeof setAbiRecordWriteParameters +> + +export type SetAbiRecordWriteParametersErrorType = + | SetAbiParametersErrorType + | NamehashErrorType + +export const setAbiRecordWriteParameters = async < + encodeAs extends AbiEncodeAs, + chain extends Chain, + account extends Account, +>( + client: Client, + { + name, + data, + encodeAs, + resolverAddress, + }: SetAbiRecordWriteParametersParameters, +) => { + return { + address: resolverAddress, + chain: client.chain, + account: client.account, + ...(await setAbiParameters({ + name, + data, + encodeAs, + } as SetAbiParameters)), + } as const satisfies WriteContractParameters< + SetAbiParametersReturnType['abi'] + > +} + +// ================================ +// Action +// ================================ + +export type SetAbiRecordParameters< + encodeAs extends AbiEncodeAs, + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +> = SetAbiRecordWriteParametersParameters & + WriteTransactionParameters + +export type SetAbiRecordReturnType = WriteContractReturnType + +export type SetAbiRecordErrorType = + | SetAbiRecordWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets the ABI for a name on a resolver. + * @param client - {@link Client} + * @param options - {@link SetAbiRecordParameters} + * @returns Transaction hash. {@link SetAbiRecordReturnType} + * + * @example + * import abi from './abi.json' + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { encodeAbi } from '@ensdomains/ensjs/utils' + * import { setAbiRecord } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * + * const encodedAbi = await encodeAbi({ encodeAs: 'json', abi }) + * const hash = await setAbiRecord(wallet, { + * name: 'ens.eth', + * encodedAbi, + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setAbiRecord< + encodeAs extends AbiEncodeAs, + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + data, + encodeAs, + resolverAddress, + ...txArgs + }: SetAbiRecordParameters, +): Promise { + const writeParameters = await setAbiRecordWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + data, + encodeAs, + resolverAddress, + } as SetAbiRecordWriteParametersParameters, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setAddressRecord.test.ts b/packages/ensjs/src/actions/wallet/setAddressRecord.test.ts similarity index 73% rename from packages/ensjs/src/functions/wallet/setAddressRecord.test.ts rename to packages/ensjs/src/actions/wallet/setAddressRecord.test.ts index 3b6fe39ea..0444cea62 100644 --- a/packages/ensjs/src/functions/wallet/setAddressRecord.test.ts +++ b/packages/ensjs/src/actions/wallet/setAddressRecord.test.ts @@ -6,9 +6,9 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getAddressRecord from '../public/getAddressRecord.js' -import getResolver from '../public/getResolver.js' -import setAddressRecord from './setAddressRecord.js' +import { getAddressRecord } from '../public/getAddressRecord.js' +import { getResolver } from '../public/getResolver.js' +import { setAddressRecord } from './setAddressRecord.js' let snapshot: Hex let accounts: Address[] @@ -27,11 +27,11 @@ afterEach(async () => { it('should allow an eth address record to be set', async () => { const tx = await setAddressRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'eth', value: '0x42D63ae25990889E35F215bC95884039Ba354115', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -40,13 +40,13 @@ it('should allow an eth address record to be set', async () => { expect(receipt.status).toBe('success') const response = await getAddressRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'eth', }) expect(response).toMatchInlineSnapshot(` { - "id": 60, - "name": "eth", + "coinType": 60, + "symbol": "eth", "value": "0x42D63ae25990889E35F215bC95884039Ba354115", } `) @@ -54,11 +54,11 @@ it('should allow an eth address record to be set', async () => { it('should allow a multicoin address record to be set', async () => { const tx = await setAddressRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'etcLegacy', value: '0x42D63ae25990889E35F215bC95884039Ba354115', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -67,13 +67,13 @@ it('should allow a multicoin address record to be set', async () => { expect(receipt.status).toBe('success') const response = await getAddressRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'etcLegacy', }) expect(response).toMatchInlineSnapshot(` { - "id": 61, - "name": "etcLegacy", + "coinType": 61, + "symbol": "etcLegacy", "value": "0x42D63ae25990889E35F215bC95884039Ba354115", } `) @@ -81,11 +81,11 @@ it('should allow a multicoin address record to be set', async () => { it('should allow an eth record to be set to blank', async () => { const tx = await setAddressRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'eth', value: null, resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -94,7 +94,7 @@ it('should allow an eth record to be set to blank', async () => { expect(receipt.status).toBe('success') const response = await getAddressRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coin: 'eth', }) expect(response).toBeNull() @@ -120,3 +120,13 @@ it('should allow a multicoin record to be set to blank', async () => { }) expect(response).toBeNull() }) + +// it('uses DedicatedResolver if namehash is not provided', async () => { +// const tx = await setResolver(walletClient, { +// name: 'with-subnames.eth', +// contract: 'registry', +// resolverAddress: deploymentAddresses.DedicatedResolver, +// account: accounts[1], +// }) +// there will be a test that sets the name to DedicatedResolver and then sets a record, but tenv does not include Namechain stuff yet +// }) diff --git a/packages/ensjs/src/actions/wallet/setAddressRecord.ts b/packages/ensjs/src/actions/wallet/setAddressRecord.ts new file mode 100644 index 000000000..f49f8a668 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setAddressRecord.ts @@ -0,0 +1,170 @@ +import type { + Account, + Address, + Chain, + Client, + NamehashErrorType, + Transport, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type SetAddrParametersErrorType, + type SetAddrParametersReturnType, + setAddrParameters, +} from '../../utils/coders/setAddr.js' + +// export type SetAddressRecordParameters = { +// /** Name to set address record for */ +// name: string; +// /** Coin ticker or ID to set */ +// coin: string | number; +// /** Value to set, null if deleting */ +// value: Address | string | null; +// /** Resolver address to set address record on */ +// resolverAddress: Address; +// }; + +// export type SetAddressRecordOptions< +// chain extends Chain | undefined, +// account extends Account | undefined, +// chainOverride extends Chain | undefined, +// > = Prettify< +// SetAddressRecordParameters & +// WriteTransactionParameters +// >; + +// export type SetAddressRecordReturnType = WriteContractReturnType; + +// export type SetAddressRecordErrorType = Error; + +// ================================ +// Write parameters +// ================================ + +export type SetAddressRecordWriteParametersParameters = { + /** Name to set address record for */ + name: string + /** Coin ticker or ID to set */ + coin: string | number + /** Value to set, null if deleting */ + value: Address | string | null + /** Resolver address to set address record on */ + resolverAddress: Address +} + +export type SetAddressRecordWriteParametersReturnType = ReturnType< + typeof setAddressRecordWriteParameters +> + +export type SetAddressRecordWriteParametersErrorType = + | SetAddrParametersErrorType + | NamehashErrorType + +export const setAddressRecordWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + name, + coin, + value, + resolverAddress, + }: SetAddressRecordWriteParametersParameters, +) => { + return { + address: resolverAddress, + chain: client.chain, + account: client.account, + ...setAddrParameters({ + name, + coin, + value, + }), + } as const satisfies WriteContractParameters< + SetAddrParametersReturnType['abi'] + > +} + +// ================================ +// Action +// ================================ + +export type SetAddressRecordParameters< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +> = Prettify< + SetAddressRecordWriteParametersParameters & + WriteTransactionParameters +> + +export type SetAddressRecordReturnType = WriteContractReturnType + +export type SetAddressRecordErrorType = + | SetAddressRecordWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets an address record for a name on a resolver. + * @param client - {@link Client} + * @param options - {@link SetAddressRecordParameters} + * @returns Transaction hash. {@link SetAddressRecordReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setAddressRecord } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setAddressRecord(wallet, { + * name: 'ens.eth', + * coin: 'ETH', + * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setAddressRecord< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + coin, + value, + resolverAddress, + ...txArgs + }: SetAddressRecordParameters, +): Promise { + const data = setAddressRecordWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + coin, + value, + resolverAddress, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...data, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setChildFuses.test.ts b/packages/ensjs/src/actions/wallet/setChildFuses.test.ts similarity index 92% rename from packages/ensjs/src/functions/wallet/setChildFuses.test.ts rename to packages/ensjs/src/actions/wallet/setChildFuses.test.ts index cd120892d..deaae611f 100644 --- a/packages/ensjs/src/functions/wallet/setChildFuses.test.ts +++ b/packages/ensjs/src/actions/wallet/setChildFuses.test.ts @@ -1,7 +1,8 @@ +import { nameWrapperGetDataSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' import type { Address, Hex } from 'viem' +import { namehash } from 'viem/ens' import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperGetDataSnippet } from '../../contracts/nameWrapper.js' import { publicClient, testClient, @@ -9,9 +10,8 @@ import { walletClient, } from '../../test/addTestContracts.js' import { ChildFuses, ParentFuses } from '../../utils/fuses.js' -import { namehash } from '../../utils/normalise.js' -import setChildFuses from './setChildFuses.js' -import setFuses from './setFuses.js' +import { setChildFuses } from './setChildFuses.js' +import { setFuses } from './setFuses.js' let snapshot: Hex let accounts: Address[] diff --git a/packages/ensjs/src/actions/wallet/setChildFuses.ts b/packages/ensjs/src/actions/wallet/setChildFuses.ts new file mode 100644 index 000000000..678b7ca94 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setChildFuses.ts @@ -0,0 +1,150 @@ +import { nameWrapperSetChildFusesSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import type { + Account, + Chain, + GetChainContractAddressErrorType, + LabelhashErrorType, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { type NamehashErrorType, namehash } from 'viem' +import { writeContract } from 'viem/actions' +import { labelhash } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { RequireClientContracts } from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type EncodeFusesErrorType, + type EncodeFusesInputObject, + encodeFuses, +} from '../../utils/fuses.js' + +// ================================ +// Write parameters +// ================================ + +export type SetChildFusesWriteParametersParameters = { + /** Name to set child fuses for */ + name: string + /** Fuse object or number value to set to */ + fuses: EncodeFusesInputObject + /** Expiry to set for fuses */ + expiry?: number | bigint +} + +export type SetChildFusesWriteParametersReturnType = ReturnType< + typeof setChildFusesWriteParameters +> + +export type SetChildFusesWriteParametersErrorType = + | EncodeFusesErrorType + | LabelhashErrorType + | NamehashErrorType + | GetChainContractAddressErrorType + +export const setChildFusesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + { name, fuses, expiry }: SetChildFusesWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const encodedFuses = encodeFuses({ input: fuses }) + const labels = name.split('.') + const labelHash = labelhash(labels.shift()!) + const parentNode = namehash(labels.join('.')) + + return { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + abi: nameWrapperSetChildFusesSnippet, + functionName: 'setChildFuses', + args: [parentNode, labelHash, encodedFuses, BigInt(expiry ?? 0)], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof nameWrapperSetChildFusesSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetChildFusesParameters< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +> = Prettify< + SetChildFusesWriteParametersParameters & + WriteTransactionParameters +> + +export type SetChildFusesReturnType = WriteContractReturnType + +export type SetChildFusesErrorType = + | SetChildFusesWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType +/** + * Sets the fuses for a name as the parent. + * @param client - {@link Client} + * @param options - {@link SetChildFusesOptions} + * @returns Transaction hash. {@link SetChildFusesReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setChildFuses } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setChildFuses(wallet, { + * name: 'sub.ens.eth', + * fuses: { + * parent: { + * named: ['PARENT_CANNOT_CONTROL'], + * }, + * }, + * }) + * // 0x... + */ +export async function setChildFuses< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: RequireClientContracts, + { + name, + fuses, + expiry, + ...txArgs + }: SetChildFusesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const data = setChildFusesWriteParameters( + clientWithOverrides(client, txArgs), + { name, fuses, expiry }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...data, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setContentHashRecord.test.ts b/packages/ensjs/src/actions/wallet/setContentHashRecord.test.ts similarity index 82% rename from packages/ensjs/src/functions/wallet/setContentHashRecord.test.ts rename to packages/ensjs/src/actions/wallet/setContentHashRecord.test.ts index 20bf33a45..6e8f95213 100644 --- a/packages/ensjs/src/functions/wallet/setContentHashRecord.test.ts +++ b/packages/ensjs/src/actions/wallet/setContentHashRecord.test.ts @@ -6,9 +6,9 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getContentHashRecord from '../public/getContentHashRecord.js' -import getResolver from '../public/getResolver.js' -import setContentHashRecord from './setContentHashRecord.js' +import { getContentHashRecord } from '../public/getContentHashRecord.js' +import { getResolver } from '../public/getResolver.js' +import { setContentHashRecord } from './setContentHashRecord.js' let snapshot: Hex let accounts: Address[] @@ -27,11 +27,11 @@ afterEach(async () => { it('should allow a contenthash record to be set', async () => { const tx = await setContentHashRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', contentHash: 'ipns://k51qzi5uqu5dgox2z23r6e99oqency055a6xt92xzmyvpz8mwz5ycjavm0u150', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -40,7 +40,7 @@ it('should allow a contenthash record to be set', async () => { expect(receipt.status).toBe('success') const response = await getContentHashRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) expect(response).toMatchInlineSnapshot(` { @@ -55,7 +55,7 @@ it('should allow a contenthash record to be set to blank', async () => { name: 'with-contenthash.eth', contentHash: null, resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -64,7 +64,7 @@ it('should allow a contenthash record to be set to blank', async () => { expect(receipt.status).toBe('success') const response = await getContentHashRecord(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) expect(response).toBeNull() }) diff --git a/packages/ensjs/src/actions/wallet/setContentHashRecord.ts b/packages/ensjs/src/actions/wallet/setContentHashRecord.ts new file mode 100644 index 000000000..c88fc3b17 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setContentHashRecord.ts @@ -0,0 +1,133 @@ +import type { + Account, + Address, + Chain, + Client, + Hash, + NamehashErrorType, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type SetContentHashParametersErrorType, + type SetContentHashParametersReturnType, + setContentHashParameters, +} from '../../utils/coders/setContentHash.js' + +// ================================ +// Write Parameters +// ================================ + +export type SetContentHashRecordWriteParametersParameters = { + /** Name to set content hash for */ + name: string + /** Content hash value */ + contentHash: string | null + /** The resolver address to use */ + resolverAddress: Address +} + +export type SetContentHashRecordWriteParametersErrorType = + | SetContentHashParametersErrorType + | NamehashErrorType + +export const setContentHashRecordWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + name, + contentHash, + resolverAddress, + }: SetContentHashRecordWriteParametersParameters, +) => { + return { + address: resolverAddress, + chain: client.chain, + account: client.account, + ...setContentHashParameters({ + name, + contentHash, + }), + } as const satisfies WriteContractParameters< + SetContentHashParametersReturnType['abi'] + > +} + +// ================================ +// Action +// ================================ + +export type SetContentHashRecordParameters< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +> = Prettify< + SetContentHashRecordWriteParametersParameters & + WriteTransactionParameters +> + +export type SetContentHashRecordReturnType = Hash + +export type SetContentHashRecordErrorType = + | SetContentHashRecordWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType +/** + * Sets the content hash record for a name on a resolver. + * @param client - {@link Client} + * @param options - {@link SetContentHashRecordOptions} + * @returns Transaction hash. {@link SetContentHashRecordReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setContentHashRecord } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setContentHashRecord(wallet, { + * name: 'ens.eth', + * contentHash: 'ipns://k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw', + * }) + * // 0x... + */ +export async function setContentHashRecord< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + contentHash, + resolverAddress, + ...txArgs + }: SetContentHashRecordParameters, +): Promise { + const data = setContentHashRecordWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + contentHash, + resolverAddress, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...data, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setFuses.test.ts b/packages/ensjs/src/actions/wallet/setFuses.test.ts similarity index 97% rename from packages/ensjs/src/functions/wallet/setFuses.test.ts rename to packages/ensjs/src/actions/wallet/setFuses.test.ts index d3fba1069..797b84c87 100644 --- a/packages/ensjs/src/functions/wallet/setFuses.test.ts +++ b/packages/ensjs/src/actions/wallet/setFuses.test.ts @@ -1,7 +1,8 @@ +import { nameWrapperGetDataSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' import type { Address, Hex } from 'viem' +import { namehash } from 'viem/ens' import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest' import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperGetDataSnippet } from '../../contracts/nameWrapper.js' import { publicClient, testClient, @@ -14,8 +15,7 @@ import { UnnamedChildFuses, UnnamedParentFuses, } from '../../utils/fuses.js' -import { namehash } from '../../utils/normalise.js' -import setFuses from './setFuses.js' +import { setFuses } from './setFuses.js' let snapshot: Hex let accounts: Address[] diff --git a/packages/ensjs/src/actions/wallet/setFuses.ts b/packages/ensjs/src/actions/wallet/setFuses.ts new file mode 100644 index 000000000..2deb9c04b --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setFuses.ts @@ -0,0 +1,127 @@ +import { nameWrapperSetFusesSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import type { + Account, + Chain, + GetChainContractAddressErrorType, + Hash, + WriteContractParameters, +} from 'viem' +import { type NamehashErrorType, namehash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type EncodeChildFusesInputObject, + type EncodeFusesErrorType, + encodeFuses, +} from '../../utils/fuses.js' + +export type SetFusesWriteParametersParameters = { + /** Name to set fuses for */ + name: string + /** Fuse object to set to */ + fuses: EncodeChildFusesInputObject +} + +export type SetFusesWriteParametersReturnType = ReturnType< + typeof setFusesWriteParameters +> + +export type SetFusesWriteParametersErrorType = + | EncodeFusesErrorType + | GetChainContractAddressErrorType + | NamehashErrorType + +export const setFusesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + { name, fuses }: SetFusesWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const encodedFuses = encodeFuses({ restriction: 'child', input: fuses }) + return { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + abi: nameWrapperSetFusesSnippet, + functionName: 'setFuses', + args: [namehash(name), encodedFuses], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof nameWrapperSetFusesSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetFusesParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper'>, +> = Prettify< + SetFusesWriteParametersParameters & + WriteTransactionParameters +> + +export type SetFusesReturnType = Hash + +export type SetFusesErrorType = Error + +/** + * Sets the fuses for a name. + * @param client - {@link Client} + * @param options - {@link SetFusesParameters} + * @returns Transaction hash. {@link SetFusesReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setFuses } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setFuses(wallet, { + * name: 'sub.ens.eth', + * fuses: { + * named: ['CANNOT_TRANSFER'], + * }, + * }) + * // 0x... + */ +export async function setFuses< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper'>, +>( + client: RequireClientContracts, + { name, fuses, ...txArgs }: SetFusesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const data = setFusesWriteParameters(client, { + name, + fuses, + }) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...data, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setPrimaryName.test.ts b/packages/ensjs/src/actions/wallet/setPrimaryName.test.ts similarity index 83% rename from packages/ensjs/src/functions/wallet/setPrimaryName.test.ts rename to packages/ensjs/src/actions/wallet/setPrimaryName.test.ts index 71d29f848..c39278d24 100644 --- a/packages/ensjs/src/functions/wallet/setPrimaryName.test.ts +++ b/packages/ensjs/src/actions/wallet/setPrimaryName.test.ts @@ -1,15 +1,15 @@ +import { registrySetApprovalForAllSnippet } from '@ensdomains/ensjs-abi/registry' import type { Address, Hex } from 'viem' import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { registrySetApprovalForAllSnippet } from '../../contracts/registry.js' import { publicClient, testClient, waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getName from '../public/getName.js' -import setPrimaryName from './setPrimaryName.js' +import { getName } from '../public/getName.js' +import { setPrimaryName } from './setPrimaryName.js' let snapshot: Hex let accounts: Address[] @@ -28,7 +28,7 @@ afterEach(async () => { it('should return a transaction for a name and set successfully', async () => { const tx = await setPrimaryName(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', account: accounts[1], }) expect(tx).toBeTruthy() @@ -38,7 +38,7 @@ it('should return a transaction for a name and set successfully', async () => { const resolvedName = await getName(publicClient, { address: accounts[1], }) - expect(resolvedName!.name).toBe('test123.eth') + expect(resolvedName?.name).toBe('with-subnames.eth') }) it("should return a transaction for setting another address' name and succeed", async () => { @@ -57,7 +57,7 @@ it("should return a transaction for setting another address' name and succeed", expect(setApprovedForAllReceipt.status).toBe('success') const tx = await setPrimaryName(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', address: accounts[1], account: accounts[2], }) @@ -68,5 +68,5 @@ it("should return a transaction for setting another address' name and succeed", const resolvedName = await getName(publicClient, { address: accounts[1], }) - expect(resolvedName!.name).toBe('test123.eth') + expect(resolvedName?.name).toBe('with-subnames.eth') }) diff --git a/packages/ensjs/src/actions/wallet/setPrimaryName.ts b/packages/ensjs/src/actions/wallet/setPrimaryName.ts new file mode 100644 index 000000000..664ff833f --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setPrimaryName.ts @@ -0,0 +1,189 @@ +import { + reverseRegistrarSetNameForAddrSnippet, + reverseRegistrarSetNameSnippet, +} from '@ensdomains/ensjs-abi/reverseRegistrar' +import type { + Account, + Address, + Chain, + GetChainContractAddressErrorType, + Hash, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + ASSERT_NO_TYPE_ERROR, + EXCLUDE_TYPE_ERROR, +} from '../../types/internal.js' +import { clientWithOverrides } from '../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +type BaseSetPrimaryNameDataParameters = { + /** The name to set as primary */ + name: string + /** The address to set the primary name for */ + address?: Address + /** The resolver address to use */ + resolverAddress?: Address +} + +type SelfSetPrimaryNameDataParameters = { + address?: never + resolverAddress?: never +} + +type OtherSetPrimaryNameDataParameters = { + address: Address + resolverAddress?: Address +} + +export type SetPrimaryNameWriteParametersParameters = + BaseSetPrimaryNameDataParameters & + (SelfSetPrimaryNameDataParameters | OtherSetPrimaryNameDataParameters) + +export type SetPrimaryNameWriteParametersReturnType = ReturnType< + typeof setPrimaryNameWriteParameters +> + +export type SetPrimaryNameWriteParametersErrorType = + GetChainContractAddressErrorType + +export const setPrimaryNameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts< + chain, + 'ensPublicResolver' | 'ensReverseRegistrar', + account + >, + { + name, + address, + resolverAddress = getChainContractAddress({ + chain: EXCLUDE_TYPE_ERROR(client).chain, + contract: 'ensPublicResolver', + }), + }: SetPrimaryNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const reverseRegistrarAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensReverseRegistrar', + }) + + const baseParams = { + address: reverseRegistrarAddress, + account: client.account, + chain: client.chain, + } as const + + if (address) + return { + ...baseParams, + abi: reverseRegistrarSetNameForAddrSnippet, + functionName: 'setNameForAddr', + args: [address, client.account.address, resolverAddress, name], + } as const satisfies WriteContractParameters< + typeof reverseRegistrarSetNameForAddrSnippet + > + + return { + ...baseParams, + abi: reverseRegistrarSetNameSnippet, + functionName: 'setName', + args: [name], + } as const satisfies WriteContractParameters< + typeof reverseRegistrarSetNameSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetPrimaryNameParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts< + 'ensPublicResolver' | 'ensReverseRegistrar' + >, +> = Prettify< + SetPrimaryNameWriteParametersParameters & + WriteTransactionParameters +> + +export type SetPrimaryNameReturnType = Hash + +export type SetPrimaryNameErrorType = + | SetPrimaryNameWriteParametersErrorType + | WriteContractErrorType + +/** + * Sets a primary name for an address. + * @param client - {@link Client} + * @param options - {@link SetPrimaryNameOptions} + * @returns Transaction hash. {@link SetPrimaryNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setPrimaryName } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setPrimaryName(wallet, { + * name: 'ens.eth', + * }) + * // 0x... + */ +export async function setPrimaryName< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts< + 'ensPublicResolver' | 'ensReverseRegistrar' + >, +>( + client: RequireClientContracts< + chain, + 'ensPublicResolver' | 'ensReverseRegistrar', + account + >, + { + name, + address, + resolverAddress, + ...txArgs + }: SetPrimaryNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = setPrimaryNameWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + address, + resolverAddress, + } as SetPrimaryNameWriteParametersParameters, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setRecords.test.ts b/packages/ensjs/src/actions/wallet/setRecords.test.ts similarity index 74% rename from packages/ensjs/src/functions/wallet/setRecords.test.ts rename to packages/ensjs/src/actions/wallet/setRecords.test.ts index f7f7f4dbf..065e12da4 100644 --- a/packages/ensjs/src/functions/wallet/setRecords.test.ts +++ b/packages/ensjs/src/actions/wallet/setRecords.test.ts @@ -6,10 +6,9 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import { encodeAbi } from '../../utils/encoders/encodeAbi.js' -import getRecords from '../public/getRecords.js' -import getResolver from '../public/getResolver.js' -import setRecords from './setRecords.js' +import { getRecords } from '../public/getRecords.js' +import { getResolver } from '../public/getResolver.js' +import { setRecords, setRecordsWriteParameters } from './setRecords.js' let snapshot: Hex let accounts: Address[] @@ -48,9 +47,9 @@ const dummyABI = [ it('should return a transaction to the resolver and set successfully', async () => { const tx = await setRecords(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, coins: [ { @@ -59,7 +58,7 @@ it('should return a transaction to the resolver and set successfully', async () }, ], texts: [{ key: 'foo', value: 'bar' }], - abi: await encodeAbi({ encodeAs: 'json', data: dummyABI }), + abi: { encodeAs: 'json', data: dummyABI }, account: accounts[1], }) expect(tx).toBeTruthy() @@ -67,17 +66,17 @@ it('should return a transaction to the resolver and set successfully', async () expect(receipt.status).toBe('success') const records = await getRecords(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coins: ['etcLegacy'], texts: ['foo'], abi: true, }) - expect(records.abi!.abi).toStrictEqual(dummyABI) + expect(records.abi?.abi).toStrictEqual(dummyABI) expect(records.coins).toMatchInlineSnapshot(` [ { - "id": 61, - "name": "etcLegacy", + "coinType": 61, + "symbol": "etcLegacy", "value": "0x42D63ae25990889E35F215bC95884039Ba354115", }, ] @@ -93,9 +92,9 @@ it('should return a transaction to the resolver and set successfully', async () }) it('should return a transaction to the resolver and delete successfully', async () => { const setupTx = await setRecords(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, coins: [ { @@ -104,23 +103,23 @@ it('should return a transaction to the resolver and delete successfully', async }, ], texts: [{ key: 'foo', value: 'bar' }], - abi: await encodeAbi({ encodeAs: 'json', data: dummyABI }), + abi: { encodeAs: 'json', data: dummyABI }, account: accounts[1], }) await waitForTransaction(setupTx) const checkRecords = await getRecords(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coins: ['etcLegacy'], texts: ['foo'], abi: true, }) - expect(checkRecords.abi!.abi).not.toBeNull() + expect(checkRecords.abi?.abi).not.toBeNull() expect(checkRecords.coins).toHaveLength(1) expect(checkRecords.texts).toHaveLength(1) const tx = await setRecords(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, coins: [ { @@ -129,13 +128,13 @@ it('should return a transaction to the resolver and delete successfully', async }, ], texts: [{ key: 'foo', value: '' }], - abi: await encodeAbi({ encodeAs: 'json', data: null }), + abi: { encodeAs: 'json', data: null }, account: accounts[1], }) await waitForTransaction(tx) const records = await getRecords(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', coins: ['etcLegacy'], texts: ['foo'], abi: true, @@ -151,17 +150,17 @@ it('should return a transaction to the resolver and delete all abis successfully name: 'with-type-all-abi.eth', }))!, abi: [ - await encodeAbi({ encodeAs: 'json', data: null }), - await encodeAbi({ encodeAs: 'cbor', data: null }), - await encodeAbi({ encodeAs: 'zlib', data: null }), - await encodeAbi({ encodeAs: 'uri', data: null }), + { encodeAs: 'json', data: null }, + { encodeAs: 'cbor', data: null }, + { encodeAs: 'zlib', data: null }, + { encodeAs: 'uri', data: null }, ], account: accounts[1], }) await waitForTransaction(tx) const records = await getRecords(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', abi: true, }) expect(records.abi).toBeNull() @@ -169,9 +168,9 @@ it('should return a transaction to the resolver and delete all abis successfully it('should error if there are no records to set', async () => { await expect( setRecords(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], @@ -183,10 +182,10 @@ it('should error if there are no records to set', async () => { `) }) it('should not wrap with multicall if only setting a single record', async () => { - const encodedData = setRecords.makeFunctionData(walletClient, { - name: 'test123.eth', + const writeParameters = await setRecordsWriteParameters(walletClient, { + name: 'with-subnames.eth', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, coins: [ { @@ -195,6 +194,5 @@ it('should not wrap with multicall if only setting a single record', async () => }, ], }) - // 0x8b95dd71 is the function selector for setAddr(bytes32,uint256,bytes) - expect(encodedData.data.startsWith('0x8b95dd71')).toBe(true) + expect(writeParameters.functionName).toBe('setAddr') }) diff --git a/packages/ensjs/src/actions/wallet/setRecords.ts b/packages/ensjs/src/actions/wallet/setRecords.ts new file mode 100644 index 000000000..0f210e877 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setRecords.ts @@ -0,0 +1,172 @@ +import { publicResolverMulticallSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type Account, + type Address, + type Chain, + type Client, + type EncodeFunctionDataErrorType, + type EncodeFunctionDataParameters, + encodeFunctionData, + type NamehashErrorType, + type Transport, + type WriteContractErrorType, + type WriteContractParameters, + type WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import { NoRecordsSpecifiedError } from '../../errors/public.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { clientWithOverrides } from '../../utils/clientWithOverrides.js' +import { + type RecordOptions, + resolverMulticallParameters, +} from '../../utils/coders/resolverMulticallParameters.js' + +// ================================ +// Write parameters +// ================================ + +export type SetRecordsWriteParametersParameters = { + /** The name to set records for */ + name: string + /** The resolver address to set records on */ + resolverAddress: Address +} & RecordOptions + +export type SetRecordsWriteParametersReturnType = ReturnType< + typeof setRecordsWriteParameters +> + +export type SetRecordsWriteParametersErrorType = + | NoRecordsSpecifiedError + | NamehashErrorType + | EncodeFunctionDataErrorType + +export const setRecordsWriteParameters = async < + chain extends Chain, + account extends Account, +>( + client: Client, + { name, resolverAddress, ...records }: SetRecordsWriteParametersParameters, +) => { + const callArray = await resolverMulticallParameters({ + name, + ...records, + }) + if (callArray.length === 0) throw new NoRecordsSpecifiedError() + + const baseParams = { + address: resolverAddress, + account: client.account, + chain: client.chain, + } as const + + // Encode each call to bytes + const encodedCalls = callArray.map((call) => + encodeFunctionData(call as EncodeFunctionDataParameters), + ) + + if (callArray.length === 1) + return { + ...baseParams, + ...callArray[0], + } as WriteContractParameters + + // Multiple calls: multicall(calls) + return { + ...baseParams, + abi: publicResolverMulticallSnippet, + functionName: 'multicall', + args: [encodedCalls], + } as const satisfies WriteContractParameters< + typeof publicResolverMulticallSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetRecordsParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + SetRecordsWriteParametersParameters & + WriteTransactionParameters +> + +export type SetRecordsReturnType = WriteContractReturnType + +export type SetRecordsErrorType = + | SetRecordsWriteParametersErrorType + | WriteContractErrorType + +/** + * Sets multiple records for a name on a resolver. + * + * Individual calls include namehash, uses `multicall(calls)` for batching. + * + * @param client - {@link Client} + * @param options - {@link SetRecordsParameters} + * @returns Transaction hash. {@link SetRecordsReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setRecords } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setRecords(wallet, { + * name: 'ens.eth', + * coins: [ + * { + * coin: 'ETH', + * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * }, + * ], + * texts: [{ key: 'foo', value: 'bar' }], + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setRecords< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + resolverAddress, + clearRecords, + contentHash, + texts, + coins, + abi, + ...txArgs + }: SetRecordsParameters, +): Promise { + const writeParameters = await setRecordsWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + resolverAddress, + clearRecords, + contentHash, + texts, + coins, + abi, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setResolver.test.ts b/packages/ensjs/src/actions/wallet/setResolver.test.ts similarity index 95% rename from packages/ensjs/src/functions/wallet/setResolver.test.ts rename to packages/ensjs/src/actions/wallet/setResolver.test.ts index 413aad7b5..a03d64244 100644 --- a/packages/ensjs/src/functions/wallet/setResolver.test.ts +++ b/packages/ensjs/src/actions/wallet/setResolver.test.ts @@ -6,8 +6,8 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getResolver from '../public/getResolver.js' -import setResolver from './setResolver.js' +import { getResolver } from '../public/getResolver.js' +import { setResolver } from './setResolver.js' let snapshot: Hex let accounts: Address[] @@ -26,7 +26,7 @@ afterEach(async () => { it('should return a setResolver transaction to the registry and succeed', async () => { const tx = await setResolver(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', contract: 'registry', resolverAddress: '0xAEfF4f4d8e2cB51854BEa2244B3C5Fb36b41C7fC', account: accounts[1], @@ -36,7 +36,7 @@ it('should return a setResolver transaction to the registry and succeed', async expect(receipt.status).toBe('success') const resolver = await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }) expect(resolver).toBe('0xAEfF4f4d8e2cB51854BEa2244B3C5Fb36b41C7fC') }) diff --git a/packages/ensjs/src/actions/wallet/setResolver.ts b/packages/ensjs/src/actions/wallet/setResolver.ts new file mode 100644 index 000000000..c630bcd50 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setResolver.ts @@ -0,0 +1,173 @@ +import { registrySetResolverSnippet } from '@ensdomains/ensjs-abi/registry' +import { nameWrapperSetResolverSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import type { + Account, + Address, + Chain, + GetChainContractAddressErrorType, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { type NamehashErrorType, namehash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' + +export type SetResolverWriteParametersParameters = { + /** Name to set resolver for */ + name: string + /** Contract to set resolver on */ + contract: 'registry' | 'nameWrapper' + /** Resolver address to set */ + resolverAddress: Address +} + +export type SetResolverWriteParametersReturnType = ReturnType< + typeof setResolverWriteParameters +> + +export type SetResolverWriteParametersErrorType = + | ErrorType + | GetChainContractAddressErrorType + | NamehashErrorType + +// ================================ +// Write parameters +// ================================ + +export const setResolverWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts< + chain, + 'ensNameWrapper' | 'ensRegistry', + account + >, + { name, contract, resolverAddress }: SetResolverWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + if (contract !== 'registry' && contract !== 'nameWrapper') + throw new Error(`Unknown contract: ${contract}`) + + const address = getChainContractAddress({ + chain: client.chain, + contract: contract === 'nameWrapper' ? 'ensNameWrapper' : 'ensRegistry', + }) + + const args = [namehash(name), resolverAddress] as const + const functionName = 'setResolver' + + const baseParams = { + address, + functionName, + args, + chain: client.chain, + account: client.account, + } as const + + if (contract === 'nameWrapper') + return { + ...baseParams, + abi: nameWrapperSetResolverSnippet, + } as const satisfies WriteContractParameters< + typeof nameWrapperSetResolverSnippet + > + + return { + ...baseParams, + abi: registrySetResolverSnippet, + } as const satisfies WriteContractParameters< + typeof registrySetResolverSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetResolverParameters< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper' | 'ensRegistry'>, +> = Prettify< + SetResolverWriteParametersParameters & + WriteTransactionParameters +> + +export type SetResolverReturnType = WriteContractReturnType + +export type SetResolverErrorType = + | SetResolverWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets a resolver for a name. + * @param client - {@link Client} + * @param options - {@link SetResolverOptions} + * @returns Transaction hash. {@link SetResolverReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setResolver } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setResolver(wallet, { + * name: 'ens.eth', + * contract: 'registry', + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setResolver< + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper' | 'ensRegistry'>, +>( + client: RequireClientContracts< + chain, + 'ensNameWrapper' | 'ensRegistry', + account + >, + { + name, + contract, + resolverAddress, + ...txArgs + }: SetResolverParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = setResolverWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + contract, + resolverAddress, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/setTextRecord.test.ts b/packages/ensjs/src/actions/wallet/setTextRecord.test.ts similarity index 73% rename from packages/ensjs/src/functions/wallet/setTextRecord.test.ts rename to packages/ensjs/src/actions/wallet/setTextRecord.test.ts index a5b362ed3..b54d704d7 100644 --- a/packages/ensjs/src/functions/wallet/setTextRecord.test.ts +++ b/packages/ensjs/src/actions/wallet/setTextRecord.test.ts @@ -6,9 +6,9 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getResolver from '../public/getResolver.js' -import getText from '../public/getTextRecord.js' -import setTextRecord from './setTextRecord.js' +import { getResolver } from '../public/getResolver.js' +import { getTextRecord } from '../public/getTextRecord.js' +import { setTextRecord } from './setTextRecord.js' let snapshot: Hex let accounts: Address[] @@ -27,11 +27,11 @@ afterEach(async () => { it('should allow a text record to be set', async () => { const tx = await setTextRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', key: 'foo', value: 'bar', resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -39,8 +39,8 @@ it('should allow a text record to be set', async () => { const receipt = await waitForTransaction(tx) expect(receipt.status).toBe('success') - const response = await getText(publicClient, { - name: 'test123.eth', + const response = await getTextRecord(publicClient, { + name: 'with-subnames.eth', key: 'foo', }) expect(response).toBe('bar') @@ -48,11 +48,11 @@ it('should allow a text record to be set', async () => { it('should allow a text record to be set to blank', async () => { const tx = await setTextRecord(walletClient, { - name: 'test123.eth', + name: 'with-subnames.eth', key: 'url', value: null, resolverAddress: (await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-subnames.eth', }))!, account: accounts[1], }) @@ -60,8 +60,8 @@ it('should allow a text record to be set to blank', async () => { const receipt = await waitForTransaction(tx) expect(receipt.status).toBe('success') - const response = await getText(publicClient, { - name: 'test123.eth', + const response = await getTextRecord(publicClient, { + name: 'with-subnames.eth', key: 'url', }) expect(response).toBeNull() diff --git a/packages/ensjs/src/actions/wallet/setTextRecord.ts b/packages/ensjs/src/actions/wallet/setTextRecord.ts new file mode 100644 index 000000000..aaf89b1ca --- /dev/null +++ b/packages/ensjs/src/actions/wallet/setTextRecord.ts @@ -0,0 +1,137 @@ +import type { + Account, + Address, + Chain, + Client, + NamehashErrorType, + Transport, + WriteContractErrorType, + WriteContractParameters, + WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { + type SetTextParametersErrorType, + type SetTextParametersReturnType, + setTextParameters, +} from '../../utils/coders/setText.js' + +export type SetTextRecordWriteParametersParameters = { + /** The name to set a text record for */ + name: string + /** The text record key to set */ + key: string + /** The text record value to set */ + value: string | null + /** The resolver address to use */ + resolverAddress: Address +} + +export type SetTextRecordWriteParametersReturnType = ReturnType< + typeof setTextRecordWriteParameters +> + +export type SetTextRecordWriteParametersErrorType = + | SetTextParametersErrorType + | NamehashErrorType + +export const setTextRecordWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { name, key, value, resolverAddress }: SetTextRecordWriteParametersParameters, +) => { + return { + address: resolverAddress, + chain: client.chain, + account: client.account, + ...setTextParameters({ + name, + key, + value, + }), + } as const satisfies WriteContractParameters< + SetTextParametersReturnType['abi'] + > +} + +// ================================ +// Action +// ================================ + +export type SetTextRecordParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain, +> = Prettify< + SetTextRecordWriteParametersParameters & + WriteTransactionParameters +> + +export type SetTextRecordReturnType = WriteContractReturnType + +export type SetTextRecordErrorType = + | SetTextRecordWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets a text record for a name on a resolver. + * @param client - {@link Client} + * @param options - {@link SetTextRecordOptions} + * @returns Transaction hash. {@link SetTextRecordReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { setTextRecord } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await setTextRecord(wallet, { + * name: 'ens.eth', + * key: 'foo', + * value: 'bar', + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setTextRecord< + chain extends Chain, + account extends Account, + chainOverride extends Chain, +>( + client: Client, + { + name, + key, + value, + resolverAddress, + ...txArgs + }: SetTextRecordParameters, +): Promise { + const writeParameters = setTextRecordWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + key, + value, + resolverAddress, + }, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/transferName.test.ts b/packages/ensjs/src/actions/wallet/transferName.test.ts similarity index 93% rename from packages/ensjs/src/functions/wallet/transferName.test.ts rename to packages/ensjs/src/actions/wallet/transferName.test.ts index a1bcfc011..d06417230 100644 --- a/packages/ensjs/src/functions/wallet/transferName.test.ts +++ b/packages/ensjs/src/actions/wallet/transferName.test.ts @@ -6,8 +6,8 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getOwner from '../public/getOwner.js' -import transferName from './transferName.js' +import { getOwner } from '../public/v1/getOwner.js' +import { transferName } from './transferName.js' let snapshot: Hex let accounts: Address[] @@ -40,7 +40,7 @@ it('should allow a transfer on the registry', async () => { contract: 'registry', }) - expect(owner!.owner).toBe(accounts[1]) + expect(owner?.owner).toBe(accounts[1]) }) it('should allow a regular transfer on the registrar', async () => { const tx = await transferName(walletClient, { @@ -57,7 +57,7 @@ it('should allow a regular transfer on the registrar', async () => { name: 'test123.eth', contract: 'registrar', }) - expect(owner!.registrant).toBe(accounts[2]) + expect(owner?.registrant).toBe(accounts[2]) }) it('should allow a reclaim on the registrar', async () => { const changeRegistryOwnerTx = await transferName(walletClient, { @@ -87,7 +87,7 @@ it('should allow a reclaim on the registrar', async () => { name: 'test123.eth', contract: 'registry', }) - expect(owner!.owner).toBe(accounts[1]) + expect(owner?.owner).toBe(accounts[1]) }) it('should allow a transfer on the namewrapper', async () => { const tx = await transferName(walletClient, { @@ -104,7 +104,7 @@ it('should allow a transfer on the namewrapper', async () => { name: 'wrapped.eth', contract: 'nameWrapper', }) - expect(owner!.owner).toBe(accounts[2]) + expect(owner?.owner).toBe(accounts[2]) }) it('errors if unknown contract', async () => { await expect( @@ -159,7 +159,7 @@ describe('subnames/asParent', () => { name: 'test.with-subnames.eth', contract: 'registry', }) - expect(owner!.owner).toBe(accounts[1]) + expect(owner?.owner).toBe(accounts[1]) }) it('allows transferring a subname on the namewrapper', async () => { const tx = await transferName(walletClient, { @@ -177,7 +177,7 @@ describe('subnames/asParent', () => { name: 'test.wrapped-with-subnames.eth', contract: 'nameWrapper', }) - expect(owner!.owner).toBe(accounts[1]) + expect(owner?.owner).toBe(accounts[1]) }) it('erorrs when asParent is specified and contract is registrar', async () => { await expect( diff --git a/packages/ensjs/src/actions/wallet/transferName.ts b/packages/ensjs/src/actions/wallet/transferName.ts new file mode 100644 index 000000000..79054d412 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/transferName.ts @@ -0,0 +1,314 @@ +import { + registrySetOwnerSnippet, + registrySetSubnodeOwnerSnippet, +} from '@ensdomains/ensjs-abi/registry' +import { + baseRegistrarReclaimSnippet, + baseRegistrarSafeTransferFromSnippet, +} from '@ensdomains/ensjs-abi/v1/baseRegistrar' +import { + nameWrapperSafeTransferFromSnippet, + nameWrapperSetSubnodeOwnerSnippet, +} from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Account, + type Address, + type Chain, + type GetChainContractAddressErrorType, + type LabelhashErrorType, + labelhash, + type NamehashErrorType, + namehash, + type WriteContractErrorType, + type WriteContractParameters, + type WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { + AdditionalParameterSpecifiedError, + InvalidContractTypeError, + UnsupportedNameTypeError, +} from '../../errors/general.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Prettify, WriteTransactionParameters } from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type MakeLabelNodeAndParentErrorType, + makeLabelNodeAndParent, +} from '../../utils/name/makeLabelNodeAndParent.js' + +export type TransferNameSupportedContract = + | 'registry' + | 'nameWrapper' + | 'registrar' + +export type TransferNameWriteParametersParameters< + contract extends TransferNameSupportedContract, +> = { + /** Name to transfer */ + name: string + /** Transfer recipient */ + newOwnerAddress: Address + /** Contract to use for transfer */ + contract: contract + /** Reclaim ownership as registrant (registrar only) */ + reclaim?: contract extends 'registrar' ? boolean : never + /** Transfer name as the parent owner */ + asParent?: contract extends 'registrar' ? never : boolean +} + +export type TransferNameWriteParametersErrorType = + | AdditionalParameterSpecifiedError + | GetChainContractAddressErrorType + | MakeLabelNodeAndParentErrorType + | NamehashErrorType + | LabelhashErrorType + | InvalidContractTypeError + | UnsupportedNameTypeError + | ErrorType + +export const transferNameWriteParameters = < + contract extends TransferNameSupportedContract, + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts< + chain, + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation', + account + >, + { + name, + newOwnerAddress, + contract, + reclaim, + asParent, + }: TransferNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + if (reclaim && contract !== 'registrar') + throw new AdditionalParameterSpecifiedError({ + parameter: 'reclaim', + allowedParameters: ['name', 'newOwnerAddress', 'contract'], + details: + "Can't reclaim a name from any contract other than the registrar", + }) + switch (contract) { + case 'registry': { + const baseParams = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensRegistry', + }), + chain: client.chain, + account: client.account, + } as const + if (asParent) { + const { labelhash: labelhashId, parentNode } = + makeLabelNodeAndParent(name) + return { + ...baseParams, + abi: registrySetSubnodeOwnerSnippet, + functionName: 'setSubnodeOwner', + args: [parentNode, labelhashId, newOwnerAddress], + } as const satisfies WriteContractParameters< + typeof registrySetSubnodeOwnerSnippet + > + } + + return { + ...baseParams, + abi: registrySetOwnerSnippet, + functionName: 'setOwner', + args: [namehash(name), newOwnerAddress], + } as const satisfies WriteContractParameters< + typeof registrySetOwnerSnippet + > + } + case 'registrar': { + if (asParent) + throw new AdditionalParameterSpecifiedError({ + parameter: 'asParent', + allowedParameters: ['name', 'newOwnerAddress', 'contract', 'reclaim'], + details: "Can't transfer a name as the parent owner on the registrar", + }) + const nameType = getNameType(name) + if (nameType !== 'eth-2ld') + throw new UnsupportedNameTypeError({ + nameType, + supportedNameTypes: ['eth-2ld'], + details: + 'Only eth-2ld names can be transferred on the registrar contract', + }) + const labels = name.split('.') + const tokenId = BigInt(labelhash(labels[0])) + const baseParams = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensBaseRegistrarImplementation', + }), + chain: client.chain, + account: client.account, + } + if (reclaim) + return { + ...baseParams, + abi: baseRegistrarReclaimSnippet, + functionName: 'reclaim', + args: [tokenId, newOwnerAddress], + } as const satisfies WriteContractParameters< + typeof baseRegistrarReclaimSnippet + > + + return { + ...baseParams, + abi: baseRegistrarSafeTransferFromSnippet, + functionName: 'safeTransferFrom', + args: [client.account.address, newOwnerAddress, tokenId], + } as const satisfies WriteContractParameters< + typeof baseRegistrarSafeTransferFromSnippet + > + } + case 'nameWrapper': { + const baseParams = { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }), + chain: client.chain, + account: client.account, + } as const + if (asParent) { + const { label, parentNode } = makeLabelNodeAndParent(name) + return { + ...baseParams, + abi: nameWrapperSetSubnodeOwnerSnippet, + functionName: 'setSubnodeOwner', + args: [parentNode, label, newOwnerAddress, 0, BigInt(0)], + } as const satisfies WriteContractParameters< + typeof nameWrapperSetSubnodeOwnerSnippet + > + } + + return { + ...baseParams, + abi: nameWrapperSafeTransferFromSnippet, + functionName: 'safeTransferFrom', + args: [ + client.account.address, + newOwnerAddress, + BigInt(namehash(name)), + BigInt(1), + '0x', + ], + } as const satisfies WriteContractParameters< + typeof nameWrapperSafeTransferFromSnippet + > + } + default: + throw new InvalidContractTypeError({ + contractType: contract, + supportedContractTypes: ['registry', 'registrar', 'nameWrapper'], + }) + } +} + +// ================================ +// Action +// ================================ + +export type TransferNameParameters< + contract extends 'registry' | 'nameWrapper' | 'registrar', + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts< + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation' + >, +> = Prettify< + TransferNameWriteParametersParameters & + WriteTransactionParameters +> + +export type TransferNameReturnType = WriteContractReturnType + +export type TransferNameErrorType = + | TransferNameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Transfers a name to a new owner. + * @param client - {@link Client} + * @param options - {@link TransferNameOptions} + * @returns Transaction hash. {@link TransferNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { transferName } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await transferName(wallet, { + * name: 'ens.eth', + * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * contract: 'registry', + * }) + * // 0x... + */ +export async function transferName< + contract extends TransferNameSupportedContract, + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts< + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation' + >, +>( + client: RequireClientContracts< + chain, + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation', + account + >, + { + name, + newOwnerAddress, + contract, + reclaim, + asParent, + ...txArgs + }: TransferNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = transferNameWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + newOwnerAddress, + contract, + reclaim, + asParent, + } as TransferNameWriteParametersParameters, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/unwrapName.test.ts b/packages/ensjs/src/actions/wallet/unwrapName.test.ts similarity index 88% rename from packages/ensjs/src/functions/wallet/unwrapName.test.ts rename to packages/ensjs/src/actions/wallet/unwrapName.test.ts index cb4581e5c..ab64da563 100644 --- a/packages/ensjs/src/functions/wallet/unwrapName.test.ts +++ b/packages/ensjs/src/actions/wallet/unwrapName.test.ts @@ -6,8 +6,8 @@ import { waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getOwner from '../public/getOwner.js' -import unwrapName from './unwrapName.js' +import { getOwner } from '../public/v1/getOwner.js' +import { unwrapName } from './unwrapName.js' let snapshot: Hex let accounts: Address[] @@ -38,9 +38,9 @@ it('should return a .eth unwrap name transaction and succeed', async () => { const owner = await getOwner(publicClient, { name: 'wrapped.eth', }) - expect(owner!.owner).toBe(accounts[1]) - expect(owner!.registrant).toBe(accounts[1]) - expect(owner!.ownershipLevel).toBe('registrar') + expect(owner?.owner).toBe(accounts[1]) + expect(owner?.registrant).toBe(accounts[1]) + expect(owner?.ownershipLevel).toBe('registrar') }) it('should return a regular unwrap name transaction and succeed', async () => { const tx = await unwrapName(walletClient, { @@ -55,8 +55,8 @@ it('should return a regular unwrap name transaction and succeed', async () => { const owner = await getOwner(publicClient, { name: 'test.wrapped-with-subnames.eth', }) - expect(owner!.owner).toBe(accounts[1]) - expect(owner!.ownershipLevel).toBe('registry') + expect(owner?.owner).toBe(accounts[1]) + expect(owner?.ownershipLevel).toBe('registry') }) it('should error if newRegistrantAddress is not specified for .eth', async () => { await expect( diff --git a/packages/ensjs/src/actions/wallet/unwrapName.ts b/packages/ensjs/src/actions/wallet/unwrapName.ts new file mode 100644 index 000000000..5a86f81a9 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/unwrapName.ts @@ -0,0 +1,194 @@ +import { + nameWrapperUnwrapEth2ldSnippet, + nameWrapperUnwrapSnippet, +} from '@ensdomains/ensjs-abi/v1/nameWrapper' +import type { + Account, + Address, + Chain, + GetChainContractAddressErrorType, + Hash, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { + AdditionalParameterSpecifiedError, + RequiredParameterNotSpecifiedError, +} from '../../errors/general.js' +import type { ErrorType } from '../../errors/utils.js' +import type { + Eth2ldNameSpecifier, + GetNameType, + WriteTransactionParameters, +} from '../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../utils/clientWithOverrides.js' +import { getNameType } from '../../utils/name/getNameType.js' +import { + type MakeLabelNodeAndParentErrorType, + makeLabelNodeAndParent, +} from '../../utils/name/makeLabelNodeAndParent.js' + +export type UnwrapNameWriteParametersParameters = { + /** The name to unwrap */ + name: name + /** The recipient of the unwrapped name */ + newOwnerAddress: Address + /** The registrant of the unwrapped name (eth-2ld only) */ + newRegistrantAddress?: Address +} & (GetNameType extends Eth2ldNameSpecifier + ? { + newRegistrantAddress: Address + } + : { + newRegistrantAddress?: never + }) + +// type ChainWithContractDependencies = ChainWithContract<"ensNameWrapper">; + +export type UnwrapNameWriteParametersErrorType = + | MakeLabelNodeAndParentErrorType + | AdditionalParameterSpecifiedError + | RequiredParameterNotSpecifiedError + | GetChainContractAddressErrorType + | ErrorType + +export const unwrapNameWriteParameters = < + name extends string, + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts, + { + name, + newOwnerAddress, + newRegistrantAddress, + }: UnwrapNameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const { labelhash, parentNode } = makeLabelNodeAndParent(name) + const nameWrapperAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }) + const nameType = getNameType(name) + + if (nameType === 'eth-2ld') { + if (!newRegistrantAddress) + throw new RequiredParameterNotSpecifiedError({ + parameter: 'newRegistrantAddress', + details: 'Must provide newRegistrantAddress for eth-2ld names', + }) + + return { + address: nameWrapperAddress, + abi: nameWrapperUnwrapEth2ldSnippet, + functionName: 'unwrapETH2LD', + args: [labelhash, newRegistrantAddress, newOwnerAddress], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof nameWrapperUnwrapEth2ldSnippet + > + } + + if (newRegistrantAddress) + throw new AdditionalParameterSpecifiedError({ + parameter: 'newRegistrantAddress', + allowedParameters: ['name', 'newOwnerAddress'], + details: 'newRegistrantAddress can only be specified for eth-2ld names', + }) + + return { + address: nameWrapperAddress, + abi: nameWrapperUnwrapSnippet, + functionName: 'unwrap', + args: [parentNode, labelhash, newOwnerAddress], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters +} + +// ================================ +// Action +// ================================ + +export type UnwrapNameParameters< + name extends string, + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper'>, +> = UnwrapNameWriteParametersParameters & + WriteTransactionParameters + +export type UnwrapNameReturnType = Hash + +export type UnwrapNameErrorType = + | UnwrapNameWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Unwraps a name. + * @param client - {@link Client} + * @param options - {@link UnwrapNameParameters} + * @returns Transaction hash. {@link UnwrapNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { unwrapName } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await unwrapName(wallet, { + * name: 'example.eth', + * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * newRegistrantAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * }) + * // 0x... + */ +export async function unwrapName< + name extends string, + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper'>, +>( + client: RequireClientContracts, + { + name, + newOwnerAddress, + newRegistrantAddress, + ...txArgs + }: UnwrapNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = unwrapNameWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + newOwnerAddress, + newRegistrantAddress, + } as UnwrapNameWriteParametersParameters, + ) + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/clearRecords.test.ts b/packages/ensjs/src/actions/wallet/v2/clearRecords.test.ts new file mode 100644 index 000000000..7f913779b --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/clearRecords.test.ts @@ -0,0 +1,62 @@ +import { publicResolverTextSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { subregistryInitializeSnippet } from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import { encodeFunctionData, namehash } from 'viem' +import { expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, + waitForTransaction, + walletClient, +} from '../../../test/addTestContracts.js' +import { + RESOLVER_ROLE_CLEAR, + RESOLVER_ROLE_SET_TEXT, +} from '../../../utils/v2/roles/resolverRoles.js' +import { setTextRecord } from '../setTextRecord.js' +import { clearRecords } from './clearRecords.js' +import { deployVerifiableProxy } from './deployVerifiableProxy.js' + +it('should allow a name to be cleared v2', async () => { + const accounts = await walletClient.getAddresses() + const proxyDeployTx = await deployVerifiableProxy(walletClient, { + factoryAddress: deploymentAddresses.VerifiableFactory, + implAddress: deploymentAddresses.PermissionedResolverImpl, + callData: encodeFunctionData({ + abi: subregistryInitializeSnippet, + functionName: 'initialize', + args: [accounts[1], RESOLVER_ROLE_SET_TEXT | RESOLVER_ROLE_CLEAR], + }), + account: accounts[0], + }) + const proxyReceipt = await waitForTransaction(proxyDeployTx) + const resolverProxyAddress = + `0x${proxyReceipt.logs[3].topics[2]?.slice(26)}` as const + + const setRecordTx = await setTextRecord(walletClient, { + resolverAddress: resolverProxyAddress, + account: accounts[1], + key: 'description', + value: 'test', + name: 'test.eth', + }) + + let receipt = await waitForTransaction(setRecordTx) + expect(receipt.status).toBe('success') + + const tx = await clearRecords(walletClient, { + name: 'test.eth', + resolverAddress: resolverProxyAddress, + account: accounts[1], + }) + expect(tx).toBeTruthy() + receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + const description = await publicClient.readContract({ + address: resolverProxyAddress, + abi: publicResolverTextSnippet, + functionName: 'text', + args: [namehash('test.eth'), 'description'], + }) + expect(description).toBe('') +}) diff --git a/packages/ensjs/src/actions/wallet/v2/clearRecords.ts b/packages/ensjs/src/actions/wallet/v2/clearRecords.ts new file mode 100644 index 000000000..4adbcad52 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/clearRecords.ts @@ -0,0 +1,96 @@ +import { permissionedResolverClearRecordsSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { type NamehashErrorType, namehash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +export type ClearRecordsWriteParametersParameters = { + name: string + resolverAddress: Address +} + +export type ClearRecordsWriteParametersReturnType = ReturnType< + typeof clearRecordsWriteParameters +> + +export const clearRecordsWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { name, resolverAddress }: ClearRecordsWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + return { + address: resolverAddress, + abi: permissionedResolverClearRecordsSnippet, + functionName: 'clearRecords', + args: [namehash(name)], + chain: client.chain, + account: client.account, + } as const +} + +export type ClearRecordsParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + ClearRecordsWriteParametersParameters & + WriteTransactionParameters +> + +export type ClearRecordsReturnType = Hash + +export type ClearRecordsErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + | NamehashErrorType + +export async function clearRecords< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + name, + resolverAddress, + ...txArgs + }: ClearRecordsParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = clearRecordsWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + resolverAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/createSubname.ts b/packages/ensjs/src/actions/wallet/v2/createSubname.ts new file mode 100644 index 000000000..2f843cb94 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/createSubname.ts @@ -0,0 +1,170 @@ +import { userRegistryRegisterSnippet } from '@ensdomains/ensjs-abi/v2/userRegistry' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type CreateSubnameWriteParametersParameters = { + /** The parent registry address */ + registryAddress: Address + /** The label of the subname to create */ + label: string + /** The owner address of the new subname */ + owner: Address + /** The subregistry address for the new subname (use address(0) for no subregistry) */ + subregistryAddress: Address + /** The resolver address for the new subname */ + resolverAddress: Address + /** The role bitmap to grant to the owner */ + roleBitmap: bigint + /** The expiration timestamp in seconds (defaults to one year from now) */ + expires?: bigint +} + +export type CreateSubnameV2WriteParametersReturnType = ReturnType< + typeof createSubnameV2WriteParameters +> + +export const createSubnameV2WriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + registryAddress, + label, + owner, + subregistryAddress, + resolverAddress, + roleBitmap, + expires, + }: CreateSubnameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const expiryTimestamp = + expires ?? BigInt(Math.floor(Date.now() / 1000)) + 31536000n // 1 year + + return { + address: registryAddress, + abi: userRegistryRegisterSnippet, + functionName: 'register', + args: [ + label, + owner, + subregistryAddress, + resolverAddress, + roleBitmap, + expiryTimestamp, + ], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof userRegistryRegisterSnippet + > +} + +// ================================ +// Action +// ================================ + +export type CreateSubnameV2Parameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + CreateSubnameWriteParametersParameters & + WriteTransactionParameters +> + +export type CreateSubnameV2ReturnType = Hash + +export type CreateSubnameV2ErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Creates a subname in a UserRegistry using the register function (V2). + * @param client - {@link Client} + * @param parameters - {@link CreateSubnameV2Parameters} + * @returns Transaction hash. {@link CreateSubnameV2ReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { createSubnameV2 } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await createSubnameV2(wallet, { + * registryAddress: '0x...', // parent registry + * label: 'mysubname', + * owner: '0x...', + * subregistryAddress: '0x0000000000000000000000000000000000000000', + * resolverAddress: '0x...', + * roleBitmap: 0n, // or encoded role bitmap + * // expires: optional, defaults to one year from now + * }) + * // 0x... + */ +export async function createSubnameV2< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + registryAddress, + label, + owner, + subregistryAddress, + resolverAddress, + roleBitmap, + expires, + ...txArgs + }: CreateSubnameV2Parameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = createSubnameV2WriteParameters( + clientWithOverrides(client, txArgs), + { + registryAddress, + label, + owner, + subregistryAddress, + resolverAddress, + roleBitmap, + expires, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/deleteAlias.ts b/packages/ensjs/src/actions/wallet/v2/deleteAlias.ts new file mode 100644 index 000000000..2eef4ade0 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/deleteAlias.ts @@ -0,0 +1,131 @@ +import { permissionedResolverAliasSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { getAction, toHex } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type DeleteAliasWriteParametersParameters = { + /** The source name to remove the alias from */ + fromName: string + /** The resolver address */ + resolverAddress: Address +} + +export type DeleteAliasWriteParametersReturnType = ReturnType< + typeof deleteAliasWriteParameters +> + +export const deleteAliasWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { fromName, resolverAddress }: DeleteAliasWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const fromNameEncoded = toHex(packetToBytes(fromName)) + + return { + address: resolverAddress, + abi: permissionedResolverAliasSnippet, + functionName: 'setAlias', + args: [fromNameEncoded, '0x'], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof permissionedResolverAliasSnippet + > +} + +// ================================ +// Action +// ================================ + +export type DeleteAliasParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + DeleteAliasWriteParametersParameters & + WriteTransactionParameters +> + +export type DeleteAliasReturnType = Hash + +export type DeleteAliasErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deletes an alias by setting its target to empty bytes (V2). + * + * @param client - {@link Client} + * @param parameters - {@link DeleteAliasParameters} + * @returns Transaction hash. {@link DeleteAliasReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { deleteAlias } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await deleteAlias(wallet, { + * fromName: 'alias.eth', + * resolverAddress: '0x...', + * }) + * // 0x... + */ +export async function deleteAlias< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + fromName, + resolverAddress, + ...txArgs + }: DeleteAliasParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = deleteAliasWriteParameters( + clientWithOverrides(client, txArgs), + { + fromName, + resolverAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/deleteSubname.ts b/packages/ensjs/src/actions/wallet/v2/deleteSubname.ts new file mode 100644 index 000000000..56e39a2f3 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/deleteSubname.ts @@ -0,0 +1,136 @@ +import { permissionedRegistryUnregisterSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { labelhash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type DeleteSubnameWriteParametersParameters = { + /** The parent registry address that contains the subname */ + registryAddress: Address + /** The label of the subname to delete (e.g. "sub" for sub.example.eth) */ + label: string +} + +export type DeleteSubnameWriteParametersReturnType = ReturnType< + typeof deleteSubnameWriteParameters +> + +export const deleteSubnameWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { registryAddress, label }: DeleteSubnameWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + return { + address: registryAddress, + abi: permissionedRegistryUnregisterSnippet, + functionName: 'unregister', + args: [BigInt(labelhash(label))], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof permissionedRegistryUnregisterSnippet + > +} + +// ================================ +// Action +// ================================ + +export type DeleteSubnameParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + DeleteSubnameWriteParametersParameters & + WriteTransactionParameters +> + +export type DeleteSubnameReturnType = Hash + +export type DeleteSubnameErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deletes a subname by calling `unregister()` on the parent registry. + * + * In ENSv2, subnames are managed by their parent registry (PermissionedRegistry). + * The caller must have ROLE_UNREGISTER on the name's resource or on ROOT_RESOURCE. + * If the name is REGISTERED, this burns the ERC1155 token and invalidates all + * roles on its resource (via an `eacVersionId` + `tokenVersionId` bump). + * If the name is RESERVED, no burn or version bump occurs. In both cases the + * name becomes AVAILABLE immediately. + * + * Note: unregistering does not clear resolver records, the linked subregistry, + * or any deeper subnames — those must be cleaned up separately. + * + * @param client - {@link Client} + * @param parameters - {@link DeleteSubnameParameters} + * @returns Transaction hash. {@link DeleteSubnameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { deleteSubname } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await deleteSubname(wallet, { + * registryAddress: '0x...', // parent registry (e.g. example.eth's UserRegistry) + * label: 'sub', // deletes sub.example.eth + * }) + * // 0x... + */ +export async function deleteSubname< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + registryAddress, + label, + ...txArgs + }: DeleteSubnameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = deleteSubnameWriteParameters( + clientWithOverrides(client, txArgs), + { registryAddress, label }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/deleteSubnames.ts b/packages/ensjs/src/actions/wallet/v2/deleteSubnames.ts new file mode 100644 index 000000000..97ccfe69d --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/deleteSubnames.ts @@ -0,0 +1,117 @@ +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' +import { deleteSubnameWriteParameters } from './deleteSubname.js' + +// ================================ +// Write parameters +// ================================ + +export type DeleteSubnamesWriteParametersParameters = { + /** The parent registry address */ + registryAddress: Address + /** The labels of the subnames to delete */ + labels: string[] +} + +// ================================ +// Action +// ================================ + +export type DeleteSubnamesParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + DeleteSubnamesWriteParametersParameters & + WriteTransactionParameters +> + +export type DeleteSubnamesReturnType = Hash[] + +export type DeleteSubnamesErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deletes multiple subnames by calling `unregister()` for each one on the parent registry. + * + * In ENSv2 there is no batch unregister — each subname requires a separate + * `unregister()` call. This function sends them sequentially (each tx is awaited + * before the next is signed) and returns their hashes in input order. The caller + * must have ROLE_UNREGISTER on each name's resource or on ROOT_RESOURCE. + * + * For REGISTERED names this burns the ERC1155 token and invalidates roles; + * RESERVED names are unregistered without a burn. In both cases the name becomes + * AVAILABLE. Resolver records, linked subregistries, and deeper subnames are not + * cleaned up automatically. + * + * @param client - {@link Client} + * @param parameters - {@link DeleteSubnamesParameters} + * @returns Array of transaction hashes. {@link DeleteSubnamesReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { deleteSubnames } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hashes = await deleteSubnames(wallet, { + * registryAddress: '0x...', // parent registry + * labels: ['sub1', 'sub2', 'sub3'], + * }) + * // ['0x...', '0x...', '0x...'] + */ +export async function deleteSubnames< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + registryAddress, + labels, + ...txArgs + }: DeleteSubnamesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const overriddenClient = clientWithOverrides(client, txArgs) + const writeContractAction = getAction(client, writeContract, 'writeContract') + const hashes: Hash[] = [] + + for (const label of labels) { + const writeParameters = deleteSubnameWriteParameters(overriddenClient, { + registryAddress, + label, + }) + const hash = await writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) + hashes.push(hash) + } + + return hashes +} diff --git a/packages/ensjs/src/actions/wallet/v2/deploySubregistry.ts b/packages/ensjs/src/actions/wallet/v2/deploySubregistry.ts new file mode 100644 index 000000000..fbbbc251e --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/deploySubregistry.ts @@ -0,0 +1,176 @@ +import { + subregistryInitializeSnippet, + verifiableFactoryDeployProxySnippet, +} from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import type { + Account, + Address, + Chain, + Client, + EncodeFunctionDataErrorType, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { encodeFunctionData, keccak256, stringToBytes } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Constants +// ================================ + +const DEFAULT_ROLE_BITMAP = BigInt( + '0x1111111111111111111111111111111111111111111111111111111111111111', +) +const DEFAULT_SALT = BigInt(keccak256(stringToBytes(new Date().toISOString()))) + +// ================================ +// Write parameters +// ================================ + +export type DeploySubregistryWriteParametersParameters = { + /** The factory contract address */ + factoryAddress: Address + /** The subregistry implementation contract address */ + implAddress: Address + /** The admin address for the subregistry (defaults to client.account.address) */ + adminAddress?: Address + /** The role bitmap for the admin (defaults to full permissions) */ + roleBitmap?: bigint + /** + * The salt for proxy deployment. + * If omitted, a timestamp-based salt is generated via + * `keccak256(stringToBytes(new Date().toISOString()))`. + */ + salt?: bigint +} + +export type DeploySubregistryWriteParametersReturnType = ReturnType< + typeof deploySubregistryWriteParameters +> + +export type DeploySubregistryWriteParametersErrorType = + EncodeFunctionDataErrorType + +export const deploySubregistryWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + factoryAddress, + implAddress, + adminAddress, + roleBitmap = DEFAULT_ROLE_BITMAP, + salt = DEFAULT_SALT, + }: DeploySubregistryWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const finalAdminAddress = adminAddress ?? client.account.address + + const callData = encodeFunctionData({ + abi: subregistryInitializeSnippet, + functionName: 'initialize', + args: [finalAdminAddress, roleBitmap], + }) + + return { + address: factoryAddress, + abi: verifiableFactoryDeployProxySnippet, + functionName: 'deployProxy', + args: [implAddress, salt, callData], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof verifiableFactoryDeployProxySnippet + > +} + +// ================================ +// Action +// ================================ + +export type DeploySubregistryParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + DeploySubregistryWriteParametersParameters & + WriteTransactionParameters +> + +export type DeploySubregistryReturnType = Hash + +export type DeploySubregistryErrorType = + | DeploySubregistryWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deploys a subregistry contract via a verifiable proxy factory. + * @param client - {@link Client} + * @param options - {@link DeploySubregistryParameters} + * @returns Transaction hash. {@link DeploySubregistryReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { deploySubregistry } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await deploySubregistry(wallet, { + * factoryAddress: '0x24e32c34effb021cc360b6a4e1de2850dcc59956', + * implAddress: '0xc3ae19b222d527d3cdda617953ab878a35527e54', + * }) + * // 0x... + */ +export async function deploySubregistry< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + factoryAddress, + implAddress, + adminAddress, + roleBitmap, + salt, + ...txArgs + }: DeploySubregistryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = deploySubregistryWriteParameters( + clientWithOverrides(client, txArgs), + { + factoryAddress, + implAddress, + adminAddress, + roleBitmap, + salt, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/deployVerifiableProxy.ts b/packages/ensjs/src/actions/wallet/v2/deployVerifiableProxy.ts new file mode 100644 index 000000000..735f554c2 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/deployVerifiableProxy.ts @@ -0,0 +1,183 @@ +import { + proxyInitializeSnippet, + verifiableFactoryDeployProxySnippet, +} from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import type { + Account, + Address, + Chain, + Client, + EncodeFunctionDataErrorType, + Hash, + Hex, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { encodeFunctionData, keccak256, stringToBytes } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Constants +// ================================ + +const DEFAULT_ROLE_BITMAP = BigInt( + '0x1111111111111111111111111111111111111111111111111111111111111111', +) +const DEFAULT_SALT = BigInt(keccak256(stringToBytes(new Date().toISOString()))) + +// ================================ +// Write parameters +// ================================ + +export type DeployVerifiableProxyWriteParametersParameters = { + /** The factory contract address */ + factoryAddress: Address + /** The implementation contract address */ + implAddress: Address + /** + * The initialization calldata. + * If omitted, defaults to + * `initialize(client.account.address, DEFAULT_ROLE_BITMAP)`. + */ + callData?: Hex + /** + * The role bitmap granted to the proxy admin when generating default + * calldata. Ignored when `callData` is provided. Defaults to a bitmap + * containing every role. + */ + roleBitmap?: bigint + /** + * The salt for proxy deployment. + * If omitted, a timestamp-based salt is generated via + * `keccak256(stringToBytes(new Date().toISOString()))`. + */ + salt?: bigint +} + +export type DeployVerifiableProxyWriteParametersReturnType = ReturnType< + typeof deployVerifiableProxyWriteParameters +> + +export type DeployVerifiableProxyWriteParametersErrorType = + EncodeFunctionDataErrorType + +export const deployVerifiableProxyWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + factoryAddress, + implAddress, + callData, + roleBitmap = DEFAULT_ROLE_BITMAP, + salt = DEFAULT_SALT, + }: DeployVerifiableProxyWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const finalCallData = + callData ?? + encodeFunctionData({ + abi: proxyInitializeSnippet, + functionName: 'initialize', + args: [client.account.address, roleBitmap], + }) + + return { + address: factoryAddress, + abi: verifiableFactoryDeployProxySnippet, + functionName: 'deployProxy', + args: [implAddress, salt, finalCallData], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof verifiableFactoryDeployProxySnippet + > +} + +// ================================ +// Action +// ================================ + +export type DeployVerifiableProxyParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + DeployVerifiableProxyWriteParametersParameters & + WriteTransactionParameters +> + +export type DeployVerifiableProxyReturnType = Hash + +export type DeployVerifiableProxyErrorType = + | DeployVerifiableProxyWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Deploys a verifiable proxy contract. + * @param client - {@link Client} + * @param options - {@link DeployVerifiableProxyParameters} + * @returns Transaction hash. {@link DeployVerifiableProxyReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { deployVerifiableProxy } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await deployVerifiableProxy(wallet, { + * factoryAddress: '0x24e32c34effb021cc360b6a4e1de2850dcc59956', + * implAddress: '0xc3ae19b222d527d3cdda617953ab878a35527e54', + * }) + * // 0x... + */ +export async function deployVerifiableProxy< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + factoryAddress, + implAddress, + callData, + salt, + ...txArgs + }: DeployVerifiableProxyParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = deployVerifiableProxyWriteParameters( + clientWithOverrides(client, txArgs), + { + factoryAddress, + implAddress, + callData, + salt, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.test.ts b/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.test.ts new file mode 100644 index 000000000..9c73d88e5 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.test.ts @@ -0,0 +1,155 @@ +import { subregistryInitializeSnippet } from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import type { Address } from 'viem' +import { encodeFunctionData, namehash } from 'viem' +import { beforeAll, describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, + waitForTransaction, + walletClient, +} from '../../../test/addTestContracts.js' +import { + addrPart, + computeResolverResource, + textPart, +} from '../../../utils/v2/roles/resolverResource.js' +import { + RESOLVER_ROLE_SET_ADDR_ADMIN, + RESOLVER_ROLE_SET_ALIAS_ADMIN, + RESOLVER_ROLE_SET_TEXT_ADMIN, +} from '../../../utils/v2/roles/resolverRoles.js' +import { hasRoles } from '../../public/v2/hasRoles.js' +import { deployVerifiableProxy } from './deployVerifiableProxy.js' +import { grantResolverRoles } from './grantResolverRoles.js' + +// Admin roles needed to grant the corresponding non-admin roles to other accounts +const ADMIN_ROLES = + RESOLVER_ROLE_SET_TEXT_ADMIN | + RESOLVER_ROLE_SET_ADDR_ADMIN | + RESOLVER_ROLE_SET_ALIAS_ADMIN + +let resolverProxyAddress: Address +let accounts: Address[] + +beforeAll(async () => { + accounts = await walletClient.getAddresses() + + // Deploy a fresh PermissionedResolver proxy with admin roles for accounts[0] + const proxyDeployTx = await deployVerifiableProxy(walletClient, { + factoryAddress: deploymentAddresses.VerifiableFactory, + implAddress: deploymentAddresses.PermissionedResolverImpl, + callData: encodeFunctionData({ + abi: subregistryInitializeSnippet, + functionName: 'initialize', + args: [accounts[0], ADMIN_ROLES], + }), + account: accounts[0], + }) + const proxyReceipt = await waitForTransaction(proxyDeployTx) + resolverProxyAddress = + `0x${proxyReceipt.logs[3].topics[2]?.slice(26)}` as Address +}) + +describe('grantResolverRoles', () => { + describe('scope: root', () => { + it('grants root-level roles', async () => { + const tx = await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'root', + roles: ['ROLE_SET_ALIAS'], + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + const result = await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + roles: ['ROLE_SET_ALIAS'], + account: accounts[1], + }) + expect(result).toBe(true) + }) + }) + + describe('scope: name', () => { + it('grants name-scoped roles', async () => { + const tx = await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'name', + name: 'test.eth', + roles: ['ROLE_SET_TEXT'], + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + const resource = computeResolverResource( + namehash('test.eth'), + '0x0000000000000000000000000000000000000000000000000000000000000000', + ) + const result = await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }) + expect(result).toBe(true) + }) + }) + + describe('scope: text', () => { + it('grants text-key-scoped roles', async () => { + const tx = await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'text', + name: 'test.eth', + key: 'avatar', + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + const resource = computeResolverResource( + namehash('test.eth'), + textPart('avatar'), + ) + const result = await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }) + expect(result).toBe(true) + }) + }) + + describe('scope: addr', () => { + it('grants addr-cointype-scoped roles', async () => { + const tx = await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'addr', + name: 'test.eth', + coinType: 60n, + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + const resource = computeResolverResource( + namehash('test.eth'), + addrPart(60n), + ) + const result = await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_ADDR'], + account: accounts[1], + }) + expect(result).toBe(true) + }) + }) +}) diff --git a/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.ts b/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.ts new file mode 100644 index 000000000..6a135fd90 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/grantResolverRoles.ts @@ -0,0 +1,292 @@ +import { + permissionedResolverAuthorizeAddrRolesSnippet, + permissionedResolverAuthorizeNameRolesSnippet, + permissionedResolverAuthorizeTextRolesSnippet, + permissionedResolverGrantRootRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { + Account, + Address, + Chain, + Client, + Hash, + Hex, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { toHex } from 'viem' +import { writeContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' +import { + encodeResolverRoleBitmap, + type ResolverRole, +} from '../../../utils/v2/roles/resolverRoles.js' + +// ─── Parameter types ───────────────────────────────────────────────── + +type BaseParameters = { + /** The resolver address */ + resolverAddress: Address + /** The account to grant roles to */ + targetAccount: Address +} + +export type GrantResolverRolesRootParameters = BaseParameters & { + /** Grant roles globally (any name, any record type) */ + scope: 'root' + /** The resolver roles to grant */ + roles: ResolverRole[] +} + +export type GrantResolverRolesNameParameters = BaseParameters & { + /** + * Grant roles scoped to a specific name. + * Use `name: ""` for any name (equivalent to root). + */ + scope: 'name' + /** The name to grant roles for (dotted format, e.g. "myname.eth") */ + name: string + /** The resolver roles to grant */ + roles: ResolverRole[] +} + +export type GrantResolverRolesTextParameters = BaseParameters & { + /** Grant ROLE_SET_TEXT scoped to a specific text key on a name */ + scope: 'text' + /** The name to grant roles for (dotted format, use "" for any name) */ + name: string + /** The specific text key to grant ROLE_SET_TEXT for */ + key: string +} + +export type GrantResolverRolesAddrParameters = BaseParameters & { + /** Grant ROLE_SET_ADDR scoped to a specific coin type on a name */ + scope: 'addr' + /** The name to grant roles for (dotted format, use "" for any name) */ + name: string + /** The specific coin type to grant ROLE_SET_ADDR for (e.g., 60n for ETH) */ + coinType: bigint +} + +export type GrantResolverRolesBaseParameters = + | GrantResolverRolesRootParameters + | GrantResolverRolesNameParameters + | GrantResolverRolesTextParameters + | GrantResolverRolesAddrParameters + +export type GrantResolverRolesReturnType = Hash + +export type GrantResolverRolesErrorType = + | WriteContractErrorType + | ClientWithOverridesErrorType + +// ─── Write parameters ──────────────────────────────────────────────── + +function dnsEncode(name: string): Hex { + return toHex(packetToBytes(name)) +} + +export const grantResolverRolesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + params: GrantResolverRolesBaseParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const base = { + address: params.resolverAddress, + chain: client.chain, + account: client.account, + } as const + + switch (params.scope) { + case 'root': + return { + ...base, + abi: permissionedResolverGrantRootRolesSnippet, + functionName: 'grantRootRoles', + args: [encodeResolverRoleBitmap(params.roles), params.targetAccount], + } as const + + case 'name': + return { + ...base, + abi: permissionedResolverAuthorizeNameRolesSnippet, + functionName: 'authorizeNameRoles', + args: [ + dnsEncode(params.name), + encodeResolverRoleBitmap(params.roles), + params.targetAccount, + true, + ], + } as const + + case 'text': + return { + ...base, + abi: permissionedResolverAuthorizeTextRolesSnippet, + functionName: 'authorizeTextRoles', + args: [dnsEncode(params.name), params.key, params.targetAccount, true], + } as const + + case 'addr': + return { + ...base, + abi: permissionedResolverAuthorizeAddrRolesSnippet, + functionName: 'authorizeAddrRoles', + args: [ + dnsEncode(params.name), + params.coinType, + params.targetAccount, + true, + ], + } as const + } +} + +// ─── Action ────────────────────────────────────────────────────────── + +export type GrantResolverRolesParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + GrantResolverRolesBaseParameters & + WriteTransactionParameters +> + +/** + * Grant roles on a PermissionedResolver. + * + * The `scope` parameter determines the granularity of the grant: + * + * - **`'root'`**: Grant roles globally (any name, any record type). + * The caller must hold the admin variant of each role. + * + * - **`'name'`**: Grant roles scoped to a specific name. + * Use `name: ""` for any name (equivalent to root scope). + * + * - **`'text'`**: Grant `ROLE_SET_TEXT` for a specific text key on a name. + * The caller must have `ROLE_SET_TEXT` admin on the name's resource. + * + * - **`'addr'`**: Grant `ROLE_SET_ADDR` for a specific coin type on a name. + * The caller must have `ROLE_SET_ADDR` admin on the name's resource. + * + * @param client - Wallet client + * @param parameters - {@link GrantResolverRolesParameters} + * @returns Transaction hash. {@link GrantResolverRolesReturnType} + * + * @example + * // Grant roles globally + * const hash = await grantResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'root', + * roles: ['ROLE_SET_TEXT', 'ROLE_SET_ADDR'], + * }) + * + * @example + * // Grant roles scoped to a specific name + * const hash = await grantResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'name', + * name: 'myname.eth', + * roles: ['ROLE_SET_TEXT'], + * }) + * + * @example + * // Grant ROLE_SET_TEXT for a specific text key + * const hash = await grantResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'text', + * name: 'myname.eth', + * key: 'avatar', + * }) + * + * @example + * // Grant ROLE_SET_ADDR for a specific coin type + * const hash = await grantResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'addr', + * name: 'myname.eth', + * coinType: 60n, + * }) + */ +export async function grantResolverRoles< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + params: GrantResolverRolesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const { scope, resolverAddress, targetAccount, ...txArgs } = params + + let scopeParams: GrantResolverRolesBaseParameters + switch (scope) { + case 'root': + scopeParams = { + scope: 'root' as const, + resolverAddress, + targetAccount, + roles: params.roles, + } + break + case 'name': + scopeParams = { + scope: 'name' as const, + resolverAddress, + targetAccount, + name: params.name, + roles: params.roles, + } + break + case 'text': + scopeParams = { + scope: 'text' as const, + resolverAddress, + targetAccount, + name: params.name, + key: params.key, + } + break + case 'addr': + scopeParams = { + scope: 'addr' as const, + resolverAddress, + targetAccount, + name: params.name, + coinType: params.coinType, + } + break + } + + const writeParameters = grantResolverRolesWriteParameters( + clientWithOverrides(client, txArgs), + scopeParams, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/grantRoles.ts b/packages/ensjs/src/actions/wallet/v2/grantRoles.ts new file mode 100644 index 000000000..839ce9a9b --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/grantRoles.ts @@ -0,0 +1,67 @@ +import { + eacGrantRolesSnippet, + eacGrantRootRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/enhancedAccessControl' +import type { + Account, + Address, + Chain, + Client, + Transport, + WriteContractParameters, +} from 'viem' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + encodeRoleBitmap, + type Role, +} from '../../../utils/v2/roles/encodeRoleBitmap.js' + +export type GrantRolesWriteParametersParameters = { + registryAddress: Address + roles: Role[] + account: Address + resource: bigint +} + +export const grantRolesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + registryAddress, + roles, + account, + resource, + }: GrantRolesWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const isRootResource = resource === 0n + const roleBitmap = encodeRoleBitmap(roles) + + // ROOT_RESOURCE grants must use the dedicated `grantRootRoles` entrypoint; + // the general `grantRoles` reverts with `EACRootResourceNotAllowed`. Mirrors + // `revokeRolesWriteParameters`. + if (isRootResource) { + return { + address: registryAddress, + functionName: 'grantRootRoles', + abi: eacGrantRootRolesSnippet, + chain: client.chain, + account: client.account, + args: [roleBitmap, account] as const, + } as const satisfies WriteContractParameters< + typeof eacGrantRootRolesSnippet + > + } + + return { + address: registryAddress, + functionName: 'grantRoles', + abi: eacGrantRolesSnippet, + chain: client.chain, + account: client.account, + args: [resource, roleBitmap, account] as const, + } as const satisfies WriteContractParameters +} diff --git a/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.test.ts b/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.test.ts new file mode 100644 index 000000000..2e9a3c6cf --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.test.ts @@ -0,0 +1,245 @@ +import { subregistryInitializeSnippet } from '@ensdomains/ensjs-abi/v2/verifiableFactory' +import type { Address } from 'viem' +import { encodeFunctionData, namehash } from 'viem' +import { beforeAll, describe, expect, it } from 'vitest' +import { + deploymentAddresses, + publicClient, + waitForTransaction, + walletClient, +} from '../../../test/addTestContracts.js' +import { + addrPart, + computeResolverResource, + textPart, +} from '../../../utils/v2/roles/resolverResource.js' +import { + RESOLVER_ROLE_SET_ADDR_ADMIN, + RESOLVER_ROLE_SET_ALIAS_ADMIN, + RESOLVER_ROLE_SET_TEXT_ADMIN, +} from '../../../utils/v2/roles/resolverRoles.js' +import { hasRoles } from '../../public/v2/hasRoles.js' +import { deployVerifiableProxy } from './deployVerifiableProxy.js' +import { grantResolverRoles } from './grantResolverRoles.js' +import { revokeResolverRoles } from './revokeResolverRoles.js' + +// Admin roles needed to grant/revoke the corresponding non-admin roles +const ADMIN_ROLES = + RESOLVER_ROLE_SET_TEXT_ADMIN | + RESOLVER_ROLE_SET_ADDR_ADMIN | + RESOLVER_ROLE_SET_ALIAS_ADMIN + +let resolverProxyAddress: Address +let accounts: Address[] + +beforeAll(async () => { + accounts = await walletClient.getAddresses() + + const proxyDeployTx = await deployVerifiableProxy(walletClient, { + factoryAddress: deploymentAddresses.VerifiableFactory, + implAddress: deploymentAddresses.PermissionedResolverImpl, + callData: encodeFunctionData({ + abi: subregistryInitializeSnippet, + functionName: 'initialize', + args: [accounts[0], ADMIN_ROLES], + }), + account: accounts[0], + }) + const proxyReceipt = await waitForTransaction(proxyDeployTx) + resolverProxyAddress = + `0x${proxyReceipt.logs[3].topics[2]?.slice(26)}` as Address +}) + +describe('revokeResolverRoles', () => { + describe('scope: root', () => { + it('revokes root-level roles', async () => { + // Grant first + await waitForTransaction( + await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'root', + roles: ['ROLE_SET_ALIAS'], + account: accounts[0], + }), + ) + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + roles: ['ROLE_SET_ALIAS'], + account: accounts[1], + }), + ).toBe(true) + + // Revoke + const tx = await revokeResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'root', + roles: ['ROLE_SET_ALIAS'], + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + // Verify revoked + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + roles: ['ROLE_SET_ALIAS'], + account: accounts[1], + }), + ).toBe(false) + }) + }) + + describe('scope: name', () => { + it('revokes name-scoped roles', async () => { + // Grant first + await waitForTransaction( + await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'name', + name: 'revoke-test.eth', + roles: ['ROLE_SET_TEXT'], + account: accounts[0], + }), + ) + const resource = computeResolverResource( + namehash('revoke-test.eth'), + '0x0000000000000000000000000000000000000000000000000000000000000000', + ) + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }), + ).toBe(true) + + // Revoke + const tx = await revokeResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'name', + name: 'revoke-test.eth', + roles: ['ROLE_SET_TEXT'], + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + // Verify revoked + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }), + ).toBe(false) + }) + }) + + describe('scope: text', () => { + it('revokes text-key-scoped roles', async () => { + // Grant first + await waitForTransaction( + await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'text', + name: 'revoke-test.eth', + key: 'description', + account: accounts[0], + }), + ) + const resource = computeResolverResource( + namehash('revoke-test.eth'), + textPart('description'), + ) + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }), + ).toBe(true) + + // Revoke + const tx = await revokeResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'text', + name: 'revoke-test.eth', + key: 'description', + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + // Verify revoked + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_TEXT'], + account: accounts[1], + }), + ).toBe(false) + }) + }) + + describe('scope: addr', () => { + it('revokes addr-cointype-scoped roles', async () => { + // Grant first + await waitForTransaction( + await grantResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'addr', + name: 'revoke-test.eth', + coinType: 60n, + account: accounts[0], + }), + ) + const resource = computeResolverResource( + namehash('revoke-test.eth'), + addrPart(60n), + ) + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_ADDR'], + account: accounts[1], + }), + ).toBe(true) + + // Revoke + const tx = await revokeResolverRoles(walletClient, { + resolverAddress: resolverProxyAddress, + targetAccount: accounts[1], + scope: 'addr', + name: 'revoke-test.eth', + coinType: 60n, + account: accounts[0], + }) + const receipt = await waitForTransaction(tx) + expect(receipt.status).toBe('success') + + // Verify revoked + expect( + await hasRoles(publicClient, { + resolverAddress: resolverProxyAddress, + resource, + roles: ['ROLE_SET_ADDR'], + account: accounts[1], + }), + ).toBe(false) + }) + }) +}) diff --git a/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.ts b/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.ts new file mode 100644 index 000000000..c58e56b02 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/revokeResolverRoles.ts @@ -0,0 +1,289 @@ +import { + permissionedResolverAuthorizeAddrRolesSnippet, + permissionedResolverAuthorizeNameRolesSnippet, + permissionedResolverAuthorizeTextRolesSnippet, + permissionedResolverRevokeRootRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { + Account, + Address, + Chain, + Client, + Hash, + Hex, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { toHex } from 'viem' +import { writeContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' +import { + encodeResolverRoleBitmap, + type ResolverRole, +} from '../../../utils/v2/roles/resolverRoles.js' + +// ─── Parameter types ───────────────────────────────────────────────── + +type BaseParameters = { + /** The resolver address */ + resolverAddress: Address + /** The account to revoke roles from */ + targetAccount: Address +} + +export type RevokeResolverRolesRootParameters = BaseParameters & { + /** Revoke roles globally (any name, any record type) */ + scope: 'root' + /** The resolver roles to revoke */ + roles: ResolverRole[] +} + +export type RevokeResolverRolesNameParameters = BaseParameters & { + /** Revoke roles scoped to a specific name */ + scope: 'name' + /** The name to revoke roles for (dotted format, e.g. "myname.eth") */ + name: string + /** The resolver roles to revoke */ + roles: ResolverRole[] +} + +export type RevokeResolverRolesTextParameters = BaseParameters & { + /** Revoke ROLE_SET_TEXT scoped to a specific text key on a name */ + scope: 'text' + /** The name to revoke roles for (dotted format) */ + name: string + /** The specific text key to revoke ROLE_SET_TEXT for */ + key: string +} + +export type RevokeResolverRolesAddrParameters = BaseParameters & { + /** Revoke ROLE_SET_ADDR scoped to a specific coin type on a name */ + scope: 'addr' + /** The name to revoke roles for (dotted format) */ + name: string + /** The specific coin type to revoke ROLE_SET_ADDR for (e.g., 60n for ETH) */ + coinType: bigint +} + +export type RevokeResolverRolesBaseParameters = + | RevokeResolverRolesRootParameters + | RevokeResolverRolesNameParameters + | RevokeResolverRolesTextParameters + | RevokeResolverRolesAddrParameters + +export type RevokeResolverRolesReturnType = Hash + +export type RevokeResolverRolesErrorType = + | WriteContractErrorType + | ClientWithOverridesErrorType + +// ─── Write parameters ──────────────────────────────────────────────── + +function dnsEncode(name: string): Hex { + return toHex(packetToBytes(name)) +} + +export const revokeResolverRolesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + params: RevokeResolverRolesBaseParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const base = { + address: params.resolverAddress, + chain: client.chain, + account: client.account, + } as const + + switch (params.scope) { + case 'root': + return { + ...base, + abi: permissionedResolverRevokeRootRolesSnippet, + functionName: 'revokeRootRoles', + args: [encodeResolverRoleBitmap(params.roles), params.targetAccount], + } as const + + case 'name': + return { + ...base, + abi: permissionedResolverAuthorizeNameRolesSnippet, + functionName: 'authorizeNameRoles', + args: [ + dnsEncode(params.name), + encodeResolverRoleBitmap(params.roles), + params.targetAccount, + false, + ], + } as const + + case 'text': + return { + ...base, + abi: permissionedResolverAuthorizeTextRolesSnippet, + functionName: 'authorizeTextRoles', + args: [dnsEncode(params.name), params.key, params.targetAccount, false], + } as const + + case 'addr': + return { + ...base, + abi: permissionedResolverAuthorizeAddrRolesSnippet, + functionName: 'authorizeAddrRoles', + args: [ + dnsEncode(params.name), + params.coinType, + params.targetAccount, + false, + ], + } as const + } +} + +// ─── Action ────────────────────────────────────────────────────────── + +export type RevokeResolverRolesParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + RevokeResolverRolesBaseParameters & + WriteTransactionParameters +> + +/** + * Revoke roles on a PermissionedResolver. + * + * The `scope` parameter determines the granularity of the revocation: + * + * - **`'root'`**: Revoke roles globally (any name, any record type). + * The caller must hold the admin variant of each role. + * + * - **`'name'`**: Revoke roles scoped to a specific name. + * Computes the resource ID internally from the name. + * + * - **`'text'`**: Revoke `ROLE_SET_TEXT` for a specific text key on a name. + * Computes the resource ID internally from the name and key. + * + * - **`'addr'`**: Revoke `ROLE_SET_ADDR` for a specific coin type on a name. + * Computes the resource ID internally from the name and coin type. + * + * @param client - Wallet client + * @param parameters - {@link RevokeResolverRolesParameters} + * @returns Transaction hash. {@link RevokeResolverRolesReturnType} + * + * @example + * // Revoke roles globally + * const hash = await revokeResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'root', + * roles: ['ROLE_SET_ALIAS'], + * }) + * + * @example + * // Revoke roles scoped to a specific name + * const hash = await revokeResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'name', + * name: 'myname.eth', + * roles: ['ROLE_SET_TEXT'], + * }) + * + * @example + * // Revoke ROLE_SET_TEXT for a specific text key + * const hash = await revokeResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'text', + * name: 'myname.eth', + * key: 'avatar', + * }) + * + * @example + * // Revoke ROLE_SET_ADDR for a specific coin type + * const hash = await revokeResolverRoles(walletClient, { + * resolverAddress: '0x...', + * targetAccount: '0xOTHER', + * scope: 'addr', + * name: 'myname.eth', + * coinType: 60n, + * }) + */ +export async function revokeResolverRoles< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + params: RevokeResolverRolesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const { scope, resolverAddress, targetAccount, ...txArgs } = params + + let scopeParams: RevokeResolverRolesBaseParameters + switch (scope) { + case 'root': + scopeParams = { + scope: 'root' as const, + resolverAddress, + targetAccount, + roles: params.roles, + } + break + case 'name': + scopeParams = { + scope: 'name' as const, + resolverAddress, + targetAccount, + name: params.name, + roles: params.roles, + } + break + case 'text': + scopeParams = { + scope: 'text' as const, + resolverAddress, + targetAccount, + name: params.name, + key: params.key, + } + break + case 'addr': + scopeParams = { + scope: 'addr' as const, + resolverAddress, + targetAccount, + name: params.name, + coinType: params.coinType, + } + break + } + + const writeParameters = revokeResolverRolesWriteParameters( + clientWithOverrides(client, txArgs), + scopeParams, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/revokeRoles.ts b/packages/ensjs/src/actions/wallet/v2/revokeRoles.ts new file mode 100644 index 000000000..ead4b0f5a --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/revokeRoles.ts @@ -0,0 +1,177 @@ +import { + eacRevokeRolesSnippet, + eacRevokeRootRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/enhancedAccessControl' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' +import { + encodeRoleBitmap, + type Role, +} from '../../../utils/v2/roles/encodeRoleBitmap.js' + +// ================================ +// Write parameters +// ================================ + +export type RevokeRolesWriteParametersParameters = { + /** The registry address */ + registryAddress: Address + /** The roles to revoke */ + roles: Role[] + /** The account to revoke roles from */ + account: Address + /** The resource to revoke roles within (use 0 for ROOT_RESOURCE) */ + resource: bigint +} + +export type RevokeRolesWriteParametersReturnType = ReturnType< + typeof revokeRolesWriteParameters +> + +export type RevokeRolesWriteParametersErrorType = Error + +export const revokeRolesWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + registryAddress, + roles, + account: accountToRevoke, + resource, + }: RevokeRolesWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const isRootResource = resource === 0n + const roleBitmap = encodeRoleBitmap(roles) + + if (isRootResource) { + return { + address: registryAddress, + functionName: 'revokeRootRoles', + abi: eacRevokeRootRolesSnippet, + chain: client.chain, + account: client.account, + args: [roleBitmap, accountToRevoke] as const, + } as const satisfies WriteContractParameters< + typeof eacRevokeRootRolesSnippet + > + } + + return { + address: registryAddress, + functionName: 'revokeRoles', + abi: eacRevokeRolesSnippet, + chain: client.chain, + account: client.account, + args: [resource, roleBitmap, accountToRevoke] as const, + } as const satisfies WriteContractParameters +} + +// ================================ +// Action +// ================================ + +export type RevokeRolesParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + RevokeRolesWriteParametersParameters & + WriteTransactionParameters +> + +export type RevokeRolesReturnType = Hash + +export type RevokeRolesErrorType = + | RevokeRolesWriteParametersErrorType + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Revokes roles from an account in the Enhanced Access Control contract. + * @param client - {@link Client} + * @param options - {@link RevokeRolesParameters} + * @returns Transaction hash. {@link RevokeRolesReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { revokeRoles } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await revokeRoles(wallet, { + * registryAddress: '0x...', + * roles: ['OWNER'], + * account: '0x...', + * resource: 1n, + * }) + * // 0x... + * + * @example + * // Revoke root roles (resource = 0) + * const hash = await revokeRoles(wallet, { + * registryAddress: '0x...', + * roles: ['OWNER'], + * account: '0x...', + * resource: 0n, + * }) + * // 0x... + */ +export async function revokeRoles< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + registryAddress, + roles, + account: accountToRevoke, + resource, + ...txArgs + }: RevokeRolesParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = revokeRolesWriteParameters( + clientWithOverrides(client, txArgs), + { + registryAddress, + roles, + account: accountToRevoke, + resource, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/setAlias.ts b/packages/ensjs/src/actions/wallet/v2/setAlias.ts new file mode 100644 index 000000000..c1b558513 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/setAlias.ts @@ -0,0 +1,137 @@ +import { permissionedResolverAliasSnippet } from '@ensdomains/ensjs-abi/v2/permissionedResolver' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { writeContract } from 'viem/actions' +import { packetToBytes } from 'viem/ens' +import { getAction, toHex } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type SetAliasWriteParametersParameters = { + /** The source name to alias from */ + fromName: string + /** The target name to alias to */ + toName: string + /** The resolver address */ + resolverAddress: Address +} + +export type SetAliasWriteParametersReturnType = ReturnType< + typeof setAliasWriteParameters +> + +export const setAliasWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { fromName, toName, resolverAddress }: SetAliasWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const fromNameEncoded = toHex(packetToBytes(fromName)) + const toNameEncoded = toHex(packetToBytes(toName)) + + return { + address: resolverAddress, + abi: permissionedResolverAliasSnippet, + functionName: 'setAlias', + args: [fromNameEncoded, toNameEncoded], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof permissionedResolverAliasSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetAliasParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + SetAliasWriteParametersParameters & + WriteTransactionParameters +> + +export type SetAliasReturnType = Hash + +export type SetAliasErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets an alias from one name to another in the resolver (V2). + * + * @param client - {@link Client} + * @param parameters - {@link SetAliasParameters} + * @returns Transaction hash. {@link SetAliasReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { setAlias } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await setAlias(wallet, { + * fromName: 'alias.eth', + * toName: 'target.eth', + * resolverAddress: '0x...', + * }) + * // 0x... + */ +export async function setAlias< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + fromName, + toName, + resolverAddress, + ...txArgs + }: SetAliasParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = setAliasWriteParameters( + clientWithOverrides(client, txArgs), + { + fromName, + toName, + resolverAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/setResolver.ts b/packages/ensjs/src/actions/wallet/v2/setResolver.ts new file mode 100644 index 000000000..09f21fe73 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/setResolver.ts @@ -0,0 +1,139 @@ +import { permissionedRegistrySetResolverSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { labelhash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type SetResolverWriteParametersParameters = { + /** Label to set resolver for */ + label: string + /** The v2 registry address */ + registryAddress: Address + /** Resolver address to set */ + resolverAddress: Address +} + +export type SetResolverWriteParametersReturnType = ReturnType< + typeof setResolverWriteParameters +> + +export const setResolverWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + label, + registryAddress, + resolverAddress, + }: SetResolverWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const tokenId = BigInt(labelhash(label)) + + return { + address: registryAddress, + abi: permissionedRegistrySetResolverSnippet, + functionName: 'setResolver', + args: [tokenId, resolverAddress], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof permissionedRegistrySetResolverSnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetResolverParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + SetResolverWriteParametersParameters & + WriteTransactionParameters +> + +export type SetResolverReturnType = Hash + +export type SetResolverErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets a resolver for a v2 name. + * @param client - {@link Client} + * @param parameters - {@link SetResolverParameters} + * @returns Transaction hash. {@link SetResolverReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { setResolver } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await setResolver(wallet, { + * label: 'myname', + * registryAddress: '0x1234...', // v2 registry address + * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', + * }) + * // 0x... + */ +export async function setResolver< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + label, + registryAddress, + resolverAddress, + ...txArgs + }: SetResolverParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = setResolverWriteParameters( + clientWithOverrides(client, txArgs), + { + label, + registryAddress, + resolverAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/actions/wallet/v2/setSubregistry.ts b/packages/ensjs/src/actions/wallet/v2/setSubregistry.ts new file mode 100644 index 000000000..fb1306ba7 --- /dev/null +++ b/packages/ensjs/src/actions/wallet/v2/setSubregistry.ts @@ -0,0 +1,139 @@ +import { userRegistrySetSubregistrySnippet } from '@ensdomains/ensjs-abi/v2/userRegistry' +import type { + Account, + Address, + Chain, + Client, + Hash, + Transport, + WriteContractErrorType, + WriteContractParameters, +} from 'viem' +import { labelhash } from 'viem' +import { writeContract } from 'viem/actions' +import { getAction } from 'viem/utils' +import type { + Prettify, + WriteTransactionParameters, +} from '../../../types/index.js' +import { ASSERT_NO_TYPE_ERROR } from '../../../types/internal.js' +import { + type ClientWithOverridesErrorType, + clientWithOverrides, +} from '../../../utils/clientWithOverrides.js' + +// ================================ +// Write parameters +// ================================ + +export type SetSubregistryWriteParametersParameters = { + /** The parent registry address */ + registryAddress: Address + /** The label to set the subregistry for */ + label: string + /** The subregistry address to assign */ + subregistryAddress: Address +} + +export type SetSubregistryWriteParametersReturnType = ReturnType< + typeof setSubregistryWriteParameters +> + +export const setSubregistryWriteParameters = < + chain extends Chain, + account extends Account, +>( + client: Client, + { + registryAddress, + label, + subregistryAddress, + }: SetSubregistryWriteParametersParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const tokenId = BigInt(labelhash(label)) + + return { + address: registryAddress, + abi: userRegistrySetSubregistrySnippet, + functionName: 'setSubregistry', + args: [tokenId, subregistryAddress], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof userRegistrySetSubregistrySnippet + > +} + +// ================================ +// Action +// ================================ + +export type SetSubregistryParameters< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +> = Prettify< + SetSubregistryWriteParametersParameters & + WriteTransactionParameters +> + +export type SetSubregistryReturnType = Hash + +export type SetSubregistryErrorType = + | ClientWithOverridesErrorType + | WriteContractErrorType + +/** + * Sets the subregistry for a name in the parent registry. + * @param client - {@link Client} + * @param parameters - {@link SetSubregistryParameters} + * @returns Transaction hash. {@link SetSubregistryReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { setSubregistry } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: mainnet, + * transport: custom(window.ethereum), + * }) + * const hash = await setSubregistry(wallet, { + * registryAddress: '0x...', // parent registry + * label: 'myname', + * subregistryAddress: '0x...', // deployed subregistry + * }) + * // 0x... + */ +export async function setSubregistry< + chain extends Chain, + account extends Account, + chainOverride extends Chain | undefined, +>( + client: Client, + { + registryAddress, + label, + subregistryAddress, + ...txArgs + }: SetSubregistryParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = setSubregistryWriteParameters( + clientWithOverrides(client, txArgs), + { + registryAddress, + label, + subregistryAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/functions/wallet/wrapName.test.ts b/packages/ensjs/src/actions/wallet/wrapName.test.ts similarity index 88% rename from packages/ensjs/src/functions/wallet/wrapName.test.ts rename to packages/ensjs/src/actions/wallet/wrapName.test.ts index e17bdde73..ff21fa3bc 100644 --- a/packages/ensjs/src/functions/wallet/wrapName.test.ts +++ b/packages/ensjs/src/actions/wallet/wrapName.test.ts @@ -1,17 +1,17 @@ +import { registrySetApprovalForAllSnippet } from '@ensdomains/ensjs-abi/registry' import type { Address, Hex } from 'viem' import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest' import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { registrySetApprovalForAllSnippet } from '../../contracts/registry.js' import { publicClient, testClient, waitForTransaction, walletClient, } from '../../test/addTestContracts.js' -import getOwner from '../public/getOwner.js' -import getResolver from '../public/getResolver.js' -import getWrapperData from '../public/getWrapperData.js' -import wrapName from './wrapName.js' +import { getResolver } from '../public/getResolver.js' +import { getOwner } from '../public/v1/getOwner.js' +import { getWrapperData } from '../public/v1/getWrapperData.js' +import { wrapName } from './wrapName.js' let snapshot: Hex let accounts: Address[] @@ -50,7 +50,7 @@ const approve = async () => { describe('eth 2ld', () => { it('should return a wrap name transaction and succeed', async () => { const tx = await wrapName(walletClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', newOwnerAddress: accounts[2], account: accounts[1], }) @@ -59,14 +59,14 @@ describe('eth 2ld', () => { expect(receipt.status).toBe('success') const owner = await getOwner(publicClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', }) - expect(owner!.owner).toBe(accounts[2]) - expect(owner!.ownershipLevel).toBe('nameWrapper') + expect(owner?.owner).toBe(accounts[2]) + expect(owner?.ownershipLevel).toBe('nameWrapper') }) it('should allow initial fuses', async () => { const tx = await wrapName(walletClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', newOwnerAddress: accounts[2], fuses: { named: ['CANNOT_UNWRAP', 'CANNOT_SET_TTL'], @@ -78,13 +78,13 @@ describe('eth 2ld', () => { expect(receipt.status).toBe('success') const wrapperData = await getWrapperData(publicClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', }) - expect(wrapperData!.fuses.value).toBe(196625) + expect(wrapperData?.fuses.value).toBe(196625) }) it('should allow a non-default resolver address', async () => { const tx = await wrapName(walletClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', newOwnerAddress: accounts[2], resolverAddress: '0x42D63ae25990889E35F215bC95884039Ba354115', account: accounts[1], @@ -94,7 +94,7 @@ describe('eth 2ld', () => { expect(receipt.status).toBe('success') const resolver = await getResolver(publicClient, { - name: 'test123.eth', + name: 'with-contenthash.eth', }) expect(resolver).toBe('0x42D63ae25990889E35F215bC95884039Ba354115') }) @@ -135,8 +135,8 @@ describe('other', () => { const owner = await getOwner(publicClient, { name: 'test.with-subnames.eth', }) - expect(owner!.owner).toBe(accounts[2]) - expect(owner!.ownershipLevel).toBe('nameWrapper') + expect(owner?.owner).toBe(accounts[2]) + expect(owner?.ownershipLevel).toBe('nameWrapper') }) it('should allow a non-default resolver address', async () => { const tx = await wrapName(walletClient, { diff --git a/packages/ensjs/src/actions/wallet/wrapName.ts b/packages/ensjs/src/actions/wallet/wrapName.ts new file mode 100644 index 000000000..cf1cc88ae --- /dev/null +++ b/packages/ensjs/src/actions/wallet/wrapName.ts @@ -0,0 +1,235 @@ +import { baseRegistrarSafeTransferFromWithDataSnippet } from '@ensdomains/ensjs-abi/v1/baseRegistrar' +import { nameWrapperWrapSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Account, + type Address, + type Chain, + type EncodeAbiParametersErrorType, + encodeAbiParameters, + type GetChainContractAddressErrorType, + type LabelhashErrorType, + labelhash, + type ToHexErrorType, + toHex, + type WriteContractParameters, + type WriteContractReturnType, +} from 'viem' +import { writeContract } from 'viem/actions' +import { type PacketToBytesErrorType, packetToBytes } from 'viem/ens' +import { getAction } from 'viem/utils' +import type { + ChainWithContracts, + RequireClientContracts, +} from '../../clients/shared.js' +import { getChainContractAddress } from '../../clients/shared.js' +import { AdditionalParameterSpecifiedError } from '../../errors/general.js' +import type { + ErrorType, + WrappedLabelTooLargeError, +} from '../../errors/utils.js' +import type { + Eth2ldNameSpecifier, + GetNameType, + WriteTransactionParameters, +} from '../../types/index.js' +import { + ASSERT_NO_TYPE_ERROR, + EXCLUDE_TYPE_ERROR, +} from '../../types/internal.js' +import { clientWithOverrides } from '../../utils/clientWithOverrides.js' +import { + type EncodeChildFusesInputObject, + type EncodeFusesErrorType, + encodeFuses, +} from '../../utils/fuses.js' +import { checkIsDotEth } from '../../utils/name/validation.js' +import { + type WrappedLabelLengthCheckErrorType, + wrappedLabelLengthCheck, +} from '../../utils/wrapper.js' + +export type WrapNameWriteParameters = { + /** The name to wrap */ + name: name + /** The recipient of the wrapped name */ + newOwnerAddress: Address + /** Fuses to set on wrap (eth-2ld only) */ + fuses?: GetNameType extends Eth2ldNameSpecifier + ? EncodeChildFusesInputObject + : never + /** The resolver address to set on wrap */ + resolverAddress?: Address +} + +export type WrapNameWriteParametersErrorType = + | AdditionalParameterSpecifiedError + | WrappedLabelTooLargeError + | GetChainContractAddressErrorType + | WrappedLabelLengthCheckErrorType + | EncodeFusesErrorType + | LabelhashErrorType + | EncodeAbiParametersErrorType + | ToHexErrorType + | PacketToBytesErrorType + | ErrorType + +export const wrapNameWriteParameters = < + name extends string, + chain extends Chain, + account extends Account, +>( + client: RequireClientContracts< + chain, + 'ensPublicResolver' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation', + account + >, + { + name, + newOwnerAddress, + fuses, + resolverAddress = getChainContractAddress({ + chain: EXCLUDE_TYPE_ERROR(client).chain, + contract: 'ensPublicResolver', + }), + }: WrapNameWriteParameters, +) => { + ASSERT_NO_TYPE_ERROR(client) + + const labels = name.split('.') + const isEth2ld = checkIsDotEth(labels) + + const nameWrapperAddress = getChainContractAddress({ + chain: client.chain, + contract: 'ensNameWrapper', + }) + + if (isEth2ld) { + wrappedLabelLengthCheck(labels[0]) + const encodedFuses = fuses + ? encodeFuses({ restriction: 'child', input: fuses }) + : 0 + const tokenId = BigInt(labelhash(labels[0])) + + const data = encodeAbiParameters( + [ + { name: 'label', type: 'string' }, + { name: 'wrappedOwner', type: 'address' }, + { name: 'ownerControlledFuses', type: 'uint16' }, + { name: 'resolverAddress', type: 'address' }, + ], + [labels[0], newOwnerAddress, encodedFuses, resolverAddress], + ) + + return { + address: getChainContractAddress({ + chain: client.chain, + contract: 'ensBaseRegistrarImplementation', + }), + abi: baseRegistrarSafeTransferFromWithDataSnippet, + functionName: 'safeTransferFrom', + args: [client.account.address, nameWrapperAddress, tokenId, data], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters< + typeof baseRegistrarSafeTransferFromWithDataSnippet + > + } + + if (fuses) + throw new AdditionalParameterSpecifiedError({ + parameter: 'fuses', + allowedParameters: ['name', 'wrappedOwner', 'resolverAddress'], + details: 'Fuses cannot be initially set when wrapping non eth-2ld names', + }) + + labels.forEach((label) => wrappedLabelLengthCheck(label)) + return { + address: nameWrapperAddress, + abi: nameWrapperWrapSnippet, + functionName: 'wrap', + args: [toHex(packetToBytes(name)), newOwnerAddress, resolverAddress], + chain: client.chain, + account: client.account, + } as const satisfies WriteContractParameters +} + +// ================================ +// Action +// ================================ + +export type WrapNameParameters< + name extends string, + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts<'ensNameWrapper'>, +> = WrapNameWriteParameters & + WriteTransactionParameters + +export type WrapNameReturnType = WriteContractReturnType + +export type WrapNameErrorType = + | AdditionalParameterSpecifiedError + | WrappedLabelTooLargeError + | ErrorType + +/** + * Wraps a name. + * @param client - {@link Client} + * @param options - {@link WrapNameParameters} + * @returns Transaction hash. {@link WrapNameReturnType} + * + * @example + * import { createWalletClient, custom } from 'viem' + * import { mainnet } from 'viem/chains' + * import { addEnsContracts } from '@ensdomains/ensjs' + * import { wrapName } from '@ensdomains/ensjs/wallet' + * + * const wallet = createWalletClient({ + * chain: addEnsContracts(mainnet), + * transport: custom(window.ethereum), + * }) + * const hash = await wrapName(wallet, { + * name: 'ens.eth', + * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', + * }) + * // 0x... + */ +export async function wrapName< + name extends string, + chain extends Chain, + account extends Account, + chainOverride extends ChainWithContracts< + 'ensPublicResolver' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation' + >, +>( + client: RequireClientContracts< + chain, + 'ensPublicResolver' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation', + account + >, + { + name, + newOwnerAddress, + fuses, + resolverAddress, + ...txArgs + }: WrapNameParameters, +): Promise { + ASSERT_NO_TYPE_ERROR(client) + + const writeParameters = wrapNameWriteParameters( + clientWithOverrides(client, txArgs), + { + name, + newOwnerAddress, + fuses, + resolverAddress, + }, + ) + + const writeContractAction = getAction(client, writeContract, 'writeContract') + return writeContractAction({ + ...writeParameters, + ...txArgs, + } as WriteContractParameters) +} diff --git a/packages/ensjs/src/clients/decorators/public.ts b/packages/ensjs/src/clients/decorators/public.ts deleted file mode 100644 index e398ae4ca..000000000 --- a/packages/ensjs/src/clients/decorators/public.ts +++ /dev/null @@ -1,420 +0,0 @@ -import type { Account, Client, Transport } from 'viem' -import type { ChainWithEns } from '../../contracts/consts.js' -import batch, { - type BatchParameters, - type BatchReturnType, -} from '../../functions/public/batch.js' -import getAbiRecord, { - type GetAbiRecordParameters, - type GetAbiRecordReturnType, -} from '../../functions/public/getAbiRecord.js' -import getAddressRecord, { - type GetAddressRecordParameters, - type GetAddressRecordReturnType, -} from '../../functions/public/getAddressRecord.js' -import getAvailable, { - type GetAvailableParameters, - type GetAvailableReturnType, -} from '../../functions/public/getAvailable.js' -import getContentHashRecord, { - type GetContentHashRecordParameters, - type GetContentHashRecordReturnType, -} from '../../functions/public/getContentHashRecord.js' -import getExpiry, { - type GetExpiryParameters, - type GetExpiryReturnType, -} from '../../functions/public/getExpiry.js' -import getName, { - type GetNameParameters, - type GetNameReturnType, -} from '../../functions/public/getName.js' -import getOwner, { - type GetOwnerParameters, - type GetOwnerReturnType, -} from '../../functions/public/getOwner.js' -import getPrice, { - type GetPriceParameters, - type GetPriceReturnType, -} from '../../functions/public/getPrice.js' -import getRecords, { - type GetRecordsParameters, - type GetRecordsReturnType, -} from '../../functions/public/getRecords.js' -import getResolver, { - type GetResolverParameters, - type GetResolverReturnType, -} from '../../functions/public/getResolver.js' -import getTextRecord, { - type GetTextRecordParameters, - type GetTextRecordReturnType, -} from '../../functions/public/getTextRecord.js' -import getWrapperData, { - type GetWrapperDataParameters, - type GetWrapperDataReturnType, -} from '../../functions/public/getWrapperData.js' -import getWrapperName, { - type GetWrapperNameParameters, - type GetWrapperNameReturnType, -} from '../../functions/public/getWrapperName.js' - -export type EnsPublicActions = { - /** - * Batches multiple read functions into a single call. - * @param ...parameters - Array of {@link BatchFunctionResult} objects - * @returns Array of return values from each function - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions, getTextRecord, getAddressRecord } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.ensBatch( - * getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), - * getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), - * ) - * // ['ensdomains', { id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7 }] - */ - ensBatch: ( - ...parameters: TBatchFunctions - ) => Promise> - /** - * Gets the ABI record for a name - * @param parameters - {@link GetAbiRecordParameters} - * @returns ABI record for the name, or `null` if not found. {@link GetAbiRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getAbiRecord({ name: 'ens.eth' }) - * // TODO: real example - */ - getAbiRecord: ({ - name, - gatewayUrls, - strict, - supportedContentTypes, - }: GetAbiRecordParameters) => Promise - /** - * Gets an address record for a name and specified coin - * @param parameters - {@link GetAddressRecordParameters} - * @returns Coin value object, or `null` if not found. {@link GetAddressRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getAddressRecord({ name: 'ens.eth', coin: 'ETH' }) - * // { id: 60, name: 'ETH , value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' } - */ - getAddressRecord: ({ - name, - coin, - bypassFormat, - gatewayUrls, - strict, - }: GetAddressRecordParameters) => Promise - /** - * Gets the availability of a name to register - * @param parameters - {@link GetAvailableParameters} - * @returns Availability as boolean. {@link GetAvailableReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getAvailable({ name: 'ens.eth' }) - * // false - */ - getAvailable: ({ - name, - }: GetAvailableParameters) => Promise - /** - * Gets the content hash record for a name - * @param parameters - {@link GetContentHashRecordParameters} - * @returns Content hash object, or `null` if not found. {@link GetContentHashRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getContentHashRecord({ name: 'ens.eth' }) - * // { protocolType: 'ipfs', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } - */ - getContentHashRecord: ({ - name, - gatewayUrls, - strict, - }: GetContentHashRecordParameters) => Promise - /** - * Gets the expiry for a name - * @param parameters - {@link GetExpiryParameters} - * @returns Expiry object, or `null` if no expiry. {@link GetExpiryReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getExpiry({ name: 'ens.eth' }) - * // { expiry: { date: Date, value: 1913933217n }, gracePeriod: 7776000, status: 'active' } - */ - getExpiry: ({ - name, - contract, - }: GetExpiryParameters) => Promise - /** - * Gets the primary name for an address - * @param parameters - {@link GetNameParameters} - * @returns Name data object, or `null` if no primary name is set. {@link GetNameReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getName({ address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' }) - * // { name: 'nick.eth', match: true, reverseResolverAddress: '0xa2c122be93b0074270ebee7f6b7292c7deb45047', resolverAddress: '0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41' } - */ - getName: ({ - address, - allowMismatch, - gatewayUrls, - strict, - }: GetNameParameters) => Promise - /** - * Gets the owner(s) of a name. - * @param parameters - {@link GetOwnerParameters} - * @returns Owner data object, or `null` if no owners exist. {@link GetOwnerReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getOwner({ name: 'ens.eth' }) - * // { owner: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', registrant: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', ownershipLevel: 'registrar } - */ - getOwner: ({ - name, - contract, - }: GetOwnerParameters) => Promise - /** - * Gets the price of a name, or array of names, for a given duration. - * @param parameters - {@link GetPriceParameters} - * @returns Price data object. {@link GetPriceReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getPrice({ nameOrNames: 'ens.eth' }) - * // { base: 352828971668930335n, premium: 0n } - */ - getPrice: ({ - nameOrNames, - duration, - }: GetPriceParameters) => Promise - /** - * Gets arbitrary records for a name - * @param parameters - {@link GetRecordsParameters} - * @returns Records data object. {@link GetRecordsReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getRecords({ - * name: 'ens.eth', - * texts: ['com.twitter', 'com.github'], - * coins: ['ETH'], - * contentHash: true, - * }) - * // { texts: [{ key: 'com.twitter', value: 'ensdomains' }, { key: 'com.github', value: 'ensdomains' }], coins: [{ id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }], contentHash: { protocolType: 'ipns', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } } - */ - getRecords: < - const TTexts extends readonly string[] = readonly string[], - const TCoins extends readonly (string | number)[] = readonly ( - | string - | number - )[], - const TContentHash extends boolean = true, - const TAbi extends boolean = true, - >({ - name, - texts, - coins, - contentHash, - abi, - resolver, - gatewayUrls, - }: GetRecordsParameters) => Promise< - GetRecordsReturnType - > - /** - * Gets the resolver address for a name. - * @param parameters - {@link GetResolverParameters} - * @returns Resolver address, or null if none is found. {@link GetResolverReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getResolver({ name: 'ens.eth' }) - * // 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 - */ - getResolver: ({ - name, - }: GetResolverParameters) => Promise - /** - * Gets a text record for a name. - * @param parameters - {@link GetTextRecordParameters} - * @returns Text record string, or null if none is found. {@link GetTextRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getTextRecord({ name: 'ens.eth', key: 'com.twitter' }) - * // ensdomains - */ - getTextRecord: ({ - name, - key, - gatewayUrls, - strict, - }: GetTextRecordParameters) => Promise - /** - * Gets the wrapper data for a name. - * @param parameters - {@link GetWrapperDataParameters} - * @returns Wrapper data object, or null if name is not wrapped. {@link GetWrapperDataReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getWrapperData({ name: 'ilikelasagna.eth' }) - */ - getWrapperData: ({ - name, - }: GetWrapperDataParameters) => Promise - /** - * Gets the full name for a name with unknown labels from the NameWrapper. - * @param parameters - {@link GetWrapperNameParameters} - * @returns Full name, or null if name was not found. {@link GetWrapperNameReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - * const result = await client.getWrapperName({ name: '[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth' }) - * // wrapped.eth - */ - getWrapperName: ({ - name, - }: GetWrapperNameParameters) => Promise -} - -/** - * Extends the viem client with ENS public actions - * @param client - The viem {@link Client} object to add the ENS public actions to - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' - * - * const clientWithEns = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensPublicActions) - */ -export const ensPublicActions = < - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, - TAccount extends Account | undefined = Account | undefined, ->( - client: Client, -): EnsPublicActions => ({ - ensBatch: (...parameters) => batch(client, ...parameters), - getAbiRecord: (parameters) => getAbiRecord(client, parameters), - getAddressRecord: (parameters) => getAddressRecord(client, parameters), - getAvailable: (parameters) => getAvailable(client, parameters), - getContentHashRecord: (parameters) => - getContentHashRecord(client, parameters), - getExpiry: (parameters) => getExpiry(client, parameters), - getName: (parameters) => getName(client, parameters), - getOwner: (parameters) => getOwner(client, parameters), - getPrice: (parameters) => getPrice(client, parameters), - getRecords: (parameters) => getRecords(client, parameters), - getResolver: (parameters) => getResolver(client, parameters), - getTextRecord: (parameters) => getTextRecord(client, parameters), - getWrapperData: (parameters) => getWrapperData(client, parameters), - getWrapperName: (parameters) => getWrapperName(client, parameters), -}) diff --git a/packages/ensjs/src/clients/decorators/subgraph.ts b/packages/ensjs/src/clients/decorators/subgraph.ts deleted file mode 100644 index bba67397c..000000000 --- a/packages/ensjs/src/clients/decorators/subgraph.ts +++ /dev/null @@ -1,195 +0,0 @@ -import type { Account, Client, Transport } from 'viem' -import type { ChainWithEns } from '../../contracts/consts.js' -import getDecodedName, { - type GetDecodedNameParameters, - type GetDecodedNameReturnType, -} from '../../functions/subgraph/getDecodedName.js' -import getNameHistory, { - type GetNameHistoryParameters, - type GetNameHistoryReturnType, -} from '../../functions/subgraph/getNameHistory.js' -import getNamesForAddress, { - type GetNamesForAddressParameters, - type GetNamesForAddressReturnType, -} from '../../functions/subgraph/getNamesForAddress.js' -import getSubgraphRecords, { - type GetSubgraphRecordsParameters, - type GetSubgraphRecordsReturnType, -} from '../../functions/subgraph/getSubgraphRecords.js' -import getSubgraphRegistrant, { - type GetSubgraphRegistrantParameters, - type GetSubgraphRegistrantReturnType, -} from '../../functions/subgraph/getSubgraphRegistrant.js' -import getSubnames, { - type GetSubnamesParameters, - type GetSubnamesReturnType, -} from '../../functions/subgraph/getSubnames.js' - -export type EnsSubgraphActions = { - /** - * Gets the full name for a name with unknown labels from the subgraph. - * @param parameters - {@link GetDecodedNameParameters} - * @returns Full name, or null if name was could not be filled. {@link GetDecodedNameReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getDecodedName({ name: '[5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da].eth' }) - * // ens.eth - */ - getDecodedName: ({ - name, - allowIncomplete, - }: GetDecodedNameParameters) => Promise - /** - * Gets the history of a name from the subgraph. - * @param parameters - {@link GetNameHistoryParameters} - * @returns History object, or null if name could not be found. {@link GetNameHistoryReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getNameHistory({ name: 'ens.eth' }) - */ - getNameHistory: ({ - name, - }: GetNameHistoryParameters) => Promise - /** - * Gets the names for an address from the subgraph. - * @param parameters - {@link GetNamesForAddressParameters} - * @returns Name array. {@link GetNamesForAddressReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getNamesForAddress({ address: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }) - */ - getNamesForAddress: ({ - address, - filter, - orderBy, - orderDirection, - pageSize, - previousPage, - }: GetNamesForAddressParameters) => Promise - /** - * Gets the records for a name from the subgraph - * @param parameters - {@link GetSubgraphRecordsParameters} - * @returns Record object, or null if name was not found. {@link GetSubgraphRecordsReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getSubgraphRecords({ name: 'ens.eth' }) - * // { - * // isMigrated: true, - * // createdAt: { date: 2019-08-26T05:09:01.000Z, value: 1566796141000 }, - * // texts: [ 'snapshot', 'url', 'avatar', 'com.twitter', 'com.github' ], - * // coins: [ '60' ] - * // } - */ - getSubgraphRecords: ({ - name, - resolverAddress, - }: GetSubgraphRecordsParameters) => Promise - /** - * Gets the name registrant from the subgraph. - * @param parameters - {@link GetSubgraphRegistrantParameters} - * @returns Registrant address, or null if name was not found. {@link GetSubgraphRegistrantReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getSubgraphRegistrant({ name: 'ens.eth' }) - * // 0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9 - */ - getSubgraphRegistrant: ({ - name, - }: GetSubgraphRegistrantParameters) => Promise - /** - * Gets the subnames for a name from the subgraph. - * @param parameters - {@link GetSubnamesParameters} - * @returns Subname array. {@link GetSubnamesReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - * const result = await client.getSubnames({ name: 'ens.eth' }) - */ - getSubnames: ({ - name, - searchString, - allowExpired, - allowDeleted, - orderBy, - orderDirection, - pageSize, - previousPage, - }: GetSubnamesParameters) => Promise -} - -/** - * Extends the viem client with ENS subgraph actions - * @param client - The viem {@link Client} object to add the ENS subgraph actions to - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' - * - * const clientWithEns = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }).extend(ensSubgraphActions) - */ -export const ensSubgraphActions = < - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, - TAccount extends Account | undefined = Account | undefined, ->( - client: Client, -): EnsSubgraphActions => ({ - getDecodedName: (parameters) => getDecodedName(client, parameters), - getNameHistory: (parameters) => getNameHistory(client, parameters), - getNamesForAddress: (parameters) => getNamesForAddress(client, parameters), - getSubgraphRecords: (parameters) => getSubgraphRecords(client, parameters), - getSubgraphRegistrant: (parameters) => - getSubgraphRegistrant(client, parameters), - getSubnames: (parameters) => getSubnames(client, parameters), -}) diff --git a/packages/ensjs/src/clients/decorators/wallet.ts b/packages/ensjs/src/clients/decorators/wallet.ts deleted file mode 100644 index c738282b4..000000000 --- a/packages/ensjs/src/clients/decorators/wallet.ts +++ /dev/null @@ -1,746 +0,0 @@ -import type { Account, Transport, WalletClient } from 'viem' -import type { ChainWithEns } from '../../contracts/consts.js' -import clearRecords, { - type ClearRecordsParameters, - type ClearRecordsReturnType, -} from '../../functions/wallet/clearRecords.js' -import commitName, { - type CommitNameParameters, - type CommitNameReturnType, -} from '../../functions/wallet/commitName.js' -import createSubname, { - type CreateSubnameParameters, - type CreateSubnameReturnType, -} from '../../functions/wallet/createSubname.js' -import deleteSubname, { - type DeleteSubnameParameters, - type DeleteSubnameReturnType, -} from '../../functions/wallet/deleteSubname.js' -import registerName, { - type RegisterNameParameters, - type RegisterNameReturnType, -} from '../../functions/wallet/registerName.js' -import renewNames, { - type RenewNamesParameters, - type RenewNamesReturnType, -} from '../../functions/wallet/renewNames.js' -import setAbiRecord, { - type SetAbiRecordParameters, - type SetAbiRecordReturnType, -} from '../../functions/wallet/setAbiRecord.js' -import setAddressRecord, { - type SetAddressRecordParameters, - type SetAddressRecordReturnType, -} from '../../functions/wallet/setAddressRecord.js' -import setChildFuses, { - type SetChildFusesParameters, - type SetChildFusesReturnType, -} from '../../functions/wallet/setChildFuses.js' -import setContentHashRecord, { - type SetContentHashRecordParameters, - type SetContentHashRecordReturnType, -} from '../../functions/wallet/setContentHashRecord.js' -import setFuses, { - type SetFusesParameters, - type SetFusesReturnType, -} from '../../functions/wallet/setFuses.js' -import setPrimaryName, { - type SetPrimaryNameParameters, - type SetPrimaryNameReturnType, -} from '../../functions/wallet/setPrimaryName.js' -import setRecords, { - type SetRecordsParameters, - type SetRecordsReturnType, -} from '../../functions/wallet/setRecords.js' -import setResolver, { - type SetResolverParameters, - type SetResolverReturnType, -} from '../../functions/wallet/setResolver.js' -import setTextRecord, { - type SetTextRecordParameters, - type SetTextRecordReturnType, -} from '../../functions/wallet/setTextRecord.js' -import transferName, { - type TransferNameParameters, - type TransferNameReturnType, -} from '../../functions/wallet/transferName.js' -import unwrapName, { - type UnwrapNameParameters, - type UnwrapNameReturnType, -} from '../../functions/wallet/unwrapName.js' -import wrapName, { - type WrapNameParameters, - type WrapNameReturnType, -} from '../../functions/wallet/wrapName.js' - -export type EnsWalletActions< - TChain extends ChainWithEns, - TAccount extends Account | undefined, -> = { - /** - * Clears the records for a name on a resolver. - * @param parameters - {@link ClearRecordsParameters} - * @returns Transaction hash. {@link ClearRecordsReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.clearRecords({ - * name: 'ens.eth', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - clearRecords: ({ - name, - resolverAddress, - ...txArgs - }: ClearRecordsParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Commits a name to be registered - * @param parameters - {@link CommitNameParameters} - * @returns Transaction hash. {@link CommitNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * import { randomSecret } from '@ensdomains/ensjs/utils' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const secret = randomSecret() - * const hash = await wallet.commitName({ - * name: 'example.eth', - * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * duration: 31536000, // 1 year - * secret, - * }) - * // 0x... - */ - commitName: ({ - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - ...txArgs - }: CommitNameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Creates a subname - * @param parameters - {@link CreateSubnameParameters} - * @returns Transaction hash. {@link CreateSubnameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.createSubname({ - * name: 'sub.ens.eth', - * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * contract: 'registry', - * }) - * // 0x... - */ - createSubname: ({ - name, - contract, - owner, - resolverAddress, - expiry, - fuses, - ...txArgs - }: CreateSubnameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Deletes a subname - * @param parameters - {@link DeleteSubnameParameters} - * @returns Transaction hash. {@link DeleteSubnameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.deleteSubname({ - * name: 'sub.ens.eth', - * contract: 'registry', - * }) - * // 0x... - */ - deleteSubname: ({ - name, - contract, - asOwner, - ...txArgs - }: DeleteSubnameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Registers a name on ENS - * @param parameters - {@link RegisterNameParameters} - * @returns Transaction hash. {@link RegisterNameReturnType} - * - * @example - * import { createPublicClient, createWalletClient, http, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions, ensWalletActions } from '@ensdomains/ensjs' - * import { randomSecret } from '@ensdomains/ensjs/utils' - * - * const mainnetWithEns = addEnsContracts(mainnet) - * const client = createPublicClient({ - * chain: mainnetWithEns, - * transport: http(), - * }).extend(ensPublicActions) - * const wallet = createWalletClient({ - * chain: mainnetWithEns, - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const secret = randomSecret() - * const params = { - * name: 'example.eth', - * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * duration: 31536000, // 1 year - * secret, - * } - * - * const commitmentHash = await wallet.commitName(params) - * await client.waitForTransactionReceipt({ hash: commitmentHash }) // wait for commitment to finalise - * await new Promise((resolve) => setTimeout(resolve, 60 * 1_000)) // wait for commitment to be valid - * - * const { base, premium } = await client.getPrice({ nameOrNames: params.name, duration: params.duration }) - * const value = (base + premium) * 110n / 100n // add 10% to the price for buffer - * const hash = await wallet.registerName({ ...params, value }) - * // 0x... - */ - registerName: ({ - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - value, - ...txArgs - }: RegisterNameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Renews a name or names for a specified duration. - * @param parameters - {@link RenewNamesParameters} - * @returns Transaction hash. {@link RenewNamesReturnType} - * - * @example - * import { createPublicClient, createWalletClient, http, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensPublicActions, ensWalletActions } from '@ensdomains/ensjs' - * - * const mainnetWithEns = addEnsContracts(mainnet) - * const client = createPublicClient({ - * chain: mainnetWithEns, - * transport: http(), - * }).extend(ensPublicActions) - * const wallet = createWalletClient({ - * chain: mainnetWithEns, - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * - * const duration = 31536000 // 1 year - * const { base, premium } = await client.getPrice({ - * nameOrNames: 'example.eth', - * duration, - * }) - * const value = (base + premium) * 110n / 100n // add 10% to the price for buffer - * const hash = await wallet.renewNames({ - * nameOrNames: 'example.eth', - * duration, - * value, - * }) - * // 0x... - */ - renewNames: ({ - nameOrNames, - duration, - value, - ...txArgs - }: RenewNamesParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets the ABI for a name on a resolver. - * @param parameters - {@link SetAbiRecordParameters} - * @returns Transaction hash. {@link SetAbiRecordReturnType} - * - * @example - * import abi from './abi.json' - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * import { encodeAbi } from '@ensdomains/ensjs/utils' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * - * const encodedAbi = await encodeAbi({ encodeAs: 'json', abi }) - * const hash = await wallet.setAbiRecord({ - * name: 'ens.eth', - * encodedAbi, - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setAbiRecord: ({ - name, - encodedAbi, - resolverAddress, - ...txArgs - }: SetAbiRecordParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets an address record for a name on a resolver. - * @param parameters - {@link SetAddressRecordParameters} - * @returns Transaction hash. {@link SetAddressRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setAddressRecord({ - * name: 'ens.eth', - * coin: 'ETH', - * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setAddressRecord: ({ - name, - coin, - value, - resolverAddress, - ...txArgs - }: SetAddressRecordParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets the fuses for a name as the parent. - * @param parameters - {@link SetChildFusesParameters} - * @returns Transaction hash. {@link SetChildFusesReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setChildFuses({ - * name: 'sub.ens.eth', - * fuses: { - * parent: { - * named: ['PARENT_CANNOT_CONTROl'], - * }, - * }, - * }) - * // 0x... - */ - setChildFuses: ({ - name, - fuses, - expiry, - ...txArgs - }: SetChildFusesParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets the content hash record for a name on a resolver. - * @param parameters - {@link SetContentHashRecordParameters} - * @returns Transaction hash. {@link SetContentHashRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setContentHashRecord({ - * name: 'ens.eth', - * value: 'ipns://k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setContentHashRecord: ({ - name, - contentHash, - resolverAddress, - ...txArgs - }: SetContentHashRecordParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets the fuses for a name. - * @param parameters - {@link SetFusesParameters} - * @returns Transaction hash. {@link SetFusesReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setFuses({ - * name: 'sub.ens.eth', - * fuses: { - * named: ['CANNOT_TRANSFER'], - * }, - * }) - * // 0x... - */ - setFuses: ({ - name, - fuses, - ...txArgs - }: SetFusesParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets a primary name for an address. - * @param parameters - {@link SetPrimaryNameParameters} - * @returns Transaction hash. {@link SetPrimaryNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setPrimaryName({ - * name: 'ens.eth', - * }) - * // 0x... - */ - setPrimaryName: ({ - name, - address, - resolverAddress, - ...txArgs - }: SetPrimaryNameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets multiple records for a name on a resolver. - * @param parameters - {@link SetRecordsParameters} - * @returns Transaction hash. {@link SetRecordsReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setRecords({ - * name: 'ens.eth', - * coins: [ - * { - * coin: 'ETH', - * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }, - * ], - * texts: [{ key: 'foo', value: 'bar' }], - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setRecords: ({ - name, - resolverAddress, - // eslint-disable-next-line @typescript-eslint/no-shadow - clearRecords, - contentHash, - texts, - coins, - abi, - ...txArgs - }: SetRecordsParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets a resolver for a name. - * @param parameters - {@link SetResolverParameters} - * @returns Transaction hash. {@link SetResolverReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setResolver({ - * name: 'ens.eth', - * contract: 'registry', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setResolver: ({ - name, - contract, - resolverAddress, - ...txArgs - }: SetResolverParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Sets a text record for a name on a resolver. - * @param parameters - {@link SetTextRecordParameters} - * @returns Transaction hash. {@link SetTextRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.setTextRecord({ - * name: 'ens.eth', - * key: 'foo', - * value: 'bar', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ - setTextRecord: ({ - name, - key, - value, - resolverAddress, - ...txArgs - }: SetTextRecordParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Transfers a name to a new owner. - * @param parameters - {@link TransferNameParameters} - * @returns Transaction hash. {@link TransferNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.transferName({ - * name: 'ens.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * contract: 'registry', - * }) - * // 0x... - */ - transferName: ({ - name, - newOwnerAddress, - contract, - reclaim, - asParent, - ...txArgs - }: TransferNameParameters< - TChain, - TAccount, - TChain - >) => Promise - /** - * Unwraps a name. - * @param parameters - {@link UnwrapNameParameters} - * @returns Transaction hash. {@link UnwrapNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.unwrapName({ - * name: 'example.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * newRegistrantAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }) - * // 0x... - */ - unwrapName: ({ - name, - newOwnerAddress, - newRegistrantAddress, - ...txArgs - }: UnwrapNameParameters< - TName, - TChain, - TAccount, - TChain - >) => Promise - /** - * Wraps a name. - * @param parameters - {@link WrapNameParameters} - * @returns Transaction hash. {@link WrapNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - * const hash = await wallet.wrapName({ - * name: 'ens.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }) - * // 0x... - */ - wrapName: ({ - name, - newOwnerAddress, - fuses, - resolverAddress, - ...txArgs - }: WrapNameParameters< - TName, - TChain, - TAccount, - TChain - >) => Promise -} - -/** - * Extends the viem client with ENS wallet actions - * @param client - The viem {@link WalletClient} object to add the ENS wallet actions to - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' - * - * const clientWithEns = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }).extend(ensWalletActions) - */ -export const ensWalletActions = < - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, - TAccount extends Account | undefined = Account | undefined, ->( - client: WalletClient, -): EnsWalletActions => ({ - clearRecords: (parameters) => clearRecords(client, parameters), - commitName: (parameters) => commitName(client, parameters), - createSubname: (parameters) => createSubname(client, parameters), - deleteSubname: (parameters) => deleteSubname(client, parameters), - registerName: (parameters) => registerName(client, parameters), - renewNames: (parameters) => renewNames(client, parameters), - setAbiRecord: (parameters) => setAbiRecord(client, parameters), - setAddressRecord: (parameters) => setAddressRecord(client, parameters), - setChildFuses: (parameters) => setChildFuses(client, parameters), - setContentHashRecord: (parameters) => - setContentHashRecord(client, parameters), - setFuses: (parameters) => setFuses(client, parameters), - setPrimaryName: (parameters) => setPrimaryName(client, parameters), - setRecords: (parameters) => setRecords(client, parameters), - setResolver: (parameters) => setResolver(client, parameters), - setTextRecord: (parameters) => setTextRecord(client, parameters), - transferName: (parameters) => transferName(client, parameters), - unwrapName: (parameters) => unwrapName(client, parameters), - wrapName: (parameters) => wrapName(client, parameters), -}) diff --git a/packages/ensjs/src/clients/l1.ts b/packages/ensjs/src/clients/l1.ts new file mode 100644 index 000000000..0a75fba5d --- /dev/null +++ b/packages/ensjs/src/clients/l1.ts @@ -0,0 +1,317 @@ +import { type Chain, type ChainContract, zeroAddress } from 'viem' +import type { + StringConcatenationOrder, + // biome-ignore lint/suspicious/noShadowRestrictedNames: yes + TypeError, +} from '../types/internal.js' +import type { AssertSupportedChain, SuggestedContracts } from './shared.js' + +// ================================ +// Supported chains +// ================================ + +export const supportedL1Chains = { + mainnet: 1, + sepolia: 11155111, +} as const + +const SupportedL1ChainIds = Object.values(supportedL1Chains) + +export type SupportedL1ChainId = + (typeof supportedL1Chains)[keyof typeof supportedL1Chains] + +export type AnySupportedL1Chain = Omit & { + id: SupportedL1ChainId +} + +// ================================ +// Supported contracts +// ================================ + +export const supportedL1Contracts = [ + // v1 + 'ensBaseRegistrarImplementation', + 'ensBulkRenewal', + 'ensLegacyDnsRegistrar', + 'ensLegacyDnssecImpl', + 'ensEthRegistrarController', + 'ensNameWrapper', + 'ensPublicResolver', + 'ensLegacyRegistry', + 'ensReverseRegistrar', + 'ensDefaultReverseResolver', + 'ensEthRenewerV1', + + // v2 + 'ensEthRegistrar', + 'usdc', + 'dai', + 'ensVerifiableFactory', + 'ensRegistry', + 'ensPermissionedResolverImpl', + 'ensUserRegistryImpl', + 'ensStandardRentPriceOracle', + 'ensHcaFactory', + 'ensLockedMigrationController', + 'ensUnlockedMigrationController', + 'ensMigrationHelper', + + // UR + 'ensUniversalResolver', +] as const + +export type SupportedL1Contract = (typeof supportedL1Contracts)[number] + +export const ensL1Contracts = { + [supportedL1Chains.mainnet]: { + ensBaseRegistrarImplementation: { + address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', + }, + ensBulkRenewal: { + address: '0xa12159e5131b1eEf6B4857EEE3e1954744b5033A', + }, + ensLegacyDnsRegistrar: { + address: '0xB32cB5677a7C971689228EC835800432B339bA2B', + }, + ensLegacyDnssecImpl: { + address: '0x0fc3152971714E5ed7723FAFa650F86A4BaF30C5', + }, + ensEthRegistrarController: { + address: '0x253553366Da8546fC250F225fe3d25d0C782303b', + }, + ensNameWrapper: { + address: '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401', + }, + ensPublicResolver: { + address: '0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63', + }, + ensLegacyRegistry: { + address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }, + ensReverseRegistrar: { + address: '0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb', + }, + ensDefaultReverseResolver: { + address: zeroAddress, + }, + ensUniversalResolver: { + address: '0x5a9236e72a66D3e08B83dcf489B4d850792B6009', + }, + ensPermissionedResolverImpl: { + address: zeroAddress, + }, + ensRegistry: { + address: zeroAddress, + }, + ensVerifiableFactory: { + address: zeroAddress, + }, + ensEthRegistrar: { + address: zeroAddress, + }, + ensEthRenewerV1: { + address: zeroAddress, + }, + usdc: { + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + }, + dai: { + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + }, + ensUserRegistryImpl: { + address: zeroAddress, + }, + ensStandardRentPriceOracle: { + address: zeroAddress, + }, + ensHcaFactory: { + address: zeroAddress, + }, + ensLockedMigrationController: { + address: zeroAddress, + }, + ensUnlockedMigrationController: { + address: zeroAddress, + }, + ensMigrationHelper: { + address: zeroAddress, + }, + }, + [supportedL1Chains.sepolia]: { + ensBaseRegistrarImplementation: { + address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', + }, + ensBulkRenewal: { + address: '0x7f86d816165BaF4fD68bFd9A0706601cDD666ac4', + }, + ensLegacyDnsRegistrar: { + address: '0x5a07C75Ae469Bf3ee2657B588e8E6ABAC6741b4f', + }, + ensLegacyDnssecImpl: { + address: '0xe62E4b6cE018Ad6e916fcC24545e20a33b9d8653', + }, + ensEthRegistrarController: { + address: '0xfb3cE5D01e0f33f41DbB39035dB9745962F1f968', + }, + ensNameWrapper: { + address: '0x0635513f179D50A207757E05759CbD106d7dFcE8', + }, + ensPublicResolver: { + address: '0x5239A812ec9A62F46dbb5de8f346C8eFe7553A9f', + }, + ensLegacyRegistry: { + address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }, + ensReverseRegistrar: { + address: '0xA0a1AbcDAe1a2a4A2EF8e9113Ff0e02DD81DC0C6', + }, + ensDefaultReverseResolver: { + address: '0x7cD0016F722f34394110738eEc10265b00c6C7d9', + }, + ensUniversalResolver: { + address: '0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe', + }, + ensPermissionedResolverImpl: { + address: '0x9EAe5C2730a7dD16BDD1DeE6421a1B91e3B0365e', + }, + ensRegistry: { + address: '0xBDC85dD5b15D7ecb354cd7cb6f2c50b4f2c4F0E2', + }, + ensVerifiableFactory: { + address: '0x10dC6333CDFe1FCEf624c6e0a8221b91804Cd7ef', + }, + ensEthRegistrar: { + address: '0xa88553F454b77203B0D036A05c894d555EAAa2Cc', + }, + ensEthRenewerV1: { + address: '0x4ad56feb5Fc7B8298db06E88fd5CBc41D64602Fa', + }, + usdc: { + address: '0x768F42455A2D082E23ceeF7d51e5787C82d67a39', + }, + dai: { + address: '0x5472C5725A00B7bA11F0794A79D08ade6F4683bD', + }, + ensUserRegistryImpl: { + address: '0x624a25d67B59D587752EbEc8DdeD8827dAe52050', + }, + ensStandardRentPriceOracle: { + address: '0x8914b66260EB8C4fff795650c3AE8Cd335958987', + }, + ensHcaFactory: { + address: '0x900FF7cF617Ef9D802178B4ef480491e3A782672', + }, + ensLockedMigrationController: { + address: '0x5c39E36a69A9897F08954c71aCB1F36E0Bd4f409', + }, + ensUnlockedMigrationController: { + address: '0x2FCf83232b93bD29C59dB18AaA1D4b62e9f9FC73', + }, + ensMigrationHelper: { + address: '0x1D8c7aA9862F9b823309Ad87A4864Fb27C575e85', + }, + }, +} as const satisfies Record< + SupportedL1ChainId, + Record +> + +// ================================ +// Supported subgraphs +// ================================ + +type EnsSubgraph = { + ens: { + url: string + } +} + +export type ChainWithSubgraph = { subgraphs: EnsSubgraph } + +export const ensL1Subgraphs = { + [supportedL1Chains.mainnet]: { + ens: { + url: 'https://api.alpha.blue.ensnode.io/subgraph', + }, + }, + [supportedL1Chains.sepolia]: { + ens: { + url: 'https://v1-graphql.ens.dev/subgraph', + }, + }, +} as const satisfies Record + +// ================================ +// Contracts +// ================================ + +// ================================ +// Assertions +// ================================ + +export type ChainWithEns< + chain extends AnySupportedL1Chain = AnySupportedL1Chain, +> = Omit & { + contracts: Omit< + chain['contracts'], + keyof (typeof ensL1Contracts)[chain['id']] + > & + (typeof ensL1Contracts)[chain['id']] + subgraphs: (typeof ensL1Subgraphs)[chain['id']] +} + +export const extendChainWithEns = ( + chain: AssertSupportedChain< + chain, + AnySupportedL1Chain, + typeof supportedL1Chains + >, +): ChainWithEns> => { + const initial = chain as AnySupportedL1Chain + + if (!SupportedL1ChainIds.includes(initial.id)) { + throw new Error(`Chain ${initial.name} is not supported`) + } + + return { + ...initial, + contracts: { + ...initial.contracts, + ...ensL1Contracts[initial.id], + }, + subgraphs: { + ...('subgraphs' in initial && typeof initial.subgraphs === 'object' + ? initial.subgraphs + : {}), + ...ensL1Subgraphs[initial.id], + }, + } as ChainWithEns> +} + +/** + * Type utility that enforces required contract dependencies on the chain while providing clear error messages + * @example + * ```ts + * // Action definition + * const myAction = async ( + * chain: RequireChainContracts, + * ) => { ... } + * + * // Will error + * myAction(mainnet) // TypeError<'Chain "mainnet" is missing required contracts: ensPublicResolver'> + * + * // Will not error + * myAction(extendChainWithEns(mainnet)) + * ``` + */ +export type RequireChainContracts< + chain extends Chain, + contracts extends SuggestedContracts, +> = chain extends Omit & { + contracts: { + [key in contracts]: ChainContract + } +} + ? chain + : TypeError<`Chain "${chain['name']}" is missing required contracts: ${StringConcatenationOrder}`> +// : TypeError<`Chain "${chain["name"]}" is missing required contracts: ${StringConcatenationOrder>, ", ">}`>; diff --git a/packages/ensjs/src/clients/public.ts b/packages/ensjs/src/clients/public.ts deleted file mode 100644 index 888295d2a..000000000 --- a/packages/ensjs/src/clients/public.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - type Client, - type ClientConfig, - type PublicRpcSchema, - type Transport, - createClient, -} from 'viem' -import { addEnsContracts } from '../contracts/addEnsContracts.js' -import type { - ChainWithBaseContracts, - ChainWithEns, -} from '../contracts/consts.js' -import type { Prettify } from '../types.js' -import { type EnsPublicActions, ensPublicActions } from './decorators/public.js' -import { - type EnsSubgraphActions, - ensSubgraphActions, -} from './decorators/subgraph.js' - -export type EnsPublicClientConfig< - TTransport extends Transport = Transport, - TChain extends ChainWithBaseContracts = ChainWithBaseContracts, -> = Pick< - ClientConfig, - 'batch' | 'key' | 'name' | 'pollingInterval' | 'transport' -> & { - chain: TChain -} - -export type EnsPublicClient< - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, -> = Prettify< - Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - EnsPublicActions & EnsSubgraphActions - > -> - -/** - * Creates a ENS Public Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * @param config - {@link EnsPublicClientConfig} - * @returns An ENS Public Client. {@link EnsPublicClient} - * - * @example - * import { http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { createEnsPublicClient } from '@ensdomains/ensjs' - * - * const client = createEnsPublicClient({ - * chain: mainnet, - * transport: http(), - * }) - */ -export const createEnsPublicClient = < - TTransport extends Transport, - TChain extends ChainWithBaseContracts, ->({ - batch, - chain, - key = 'ensPublic', - name = 'ENS Public Client', - transport, - pollingInterval, -}: EnsPublicClientConfig): EnsPublicClient< - TTransport, - ChainWithEns -> => { - return createClient({ - batch, - chain: addEnsContracts(chain), - key, - name, - pollingInterval, - transport, - type: 'ensPublicClient', - }) - .extend(ensPublicActions) - .extend(ensSubgraphActions) -} diff --git a/packages/ensjs/src/clients/shared.ts b/packages/ensjs/src/clients/shared.ts new file mode 100644 index 000000000..0ed010a43 --- /dev/null +++ b/packages/ensjs/src/clients/shared.ts @@ -0,0 +1,116 @@ +import type { Account, Chain, ChainContract, Client, Transport } from 'viem' +import { getChainContractAddress as viem_getChainContractAddress } from 'viem' +// biome-ignore lint/suspicious/noShadowRestrictedNames: error type with typed message +import type { StringConcatenationOrder, TypeError } from '../types/internal.js' + +export type AssertSupportedChain< + chain extends Chain, + supportedChain extends Chain, + chains extends Record, +> = chain extends supportedChain + ? chain + : TypeError<`${chain['name']} is not a supported chain, supported chains are: ${StringConcatenationOrder< + Extract, + ', ' + >}`> + +export type BaseChainContracts = { + [key in keyof NonNullable as string extends key + ? never + : key]: NonNullable[key] +} + +export type SuggestedContracts = keyof BaseChainContracts | (string & {}) + +/** + * Type utility that explicitly enforces the presence of required contracts on the chain + */ +export type ChainWithContracts< + contracts extends SuggestedContracts, + chain extends Chain = Chain, +> = Omit & { + contracts: { + [key in contracts]: ChainContract + } +} + +export type ExtractContracts = NonNullable< + chain extends { + contracts?: infer C + } + ? // ? C + { + [key in keyof C as string extends key ? never : key]: C[key] extends + | ChainContract + | undefined + ? C[key] + : C[key] extends { [sourceId: number]: ChainContract | undefined } + ? C[key][keyof C[key]] + : never + } + : never +> + +/** + * @see {viem_getChainContractAddress} + */ +export const getChainContractAddress = < + const chain extends Chain, + contracts extends ExtractContracts = ExtractContracts, + contract extends keyof contracts = keyof contracts, +>({ + blockNumber, + chain, + contract, +}: { + blockNumber?: bigint | undefined + chain: chain + contract: contract +}) => + viem_getChainContractAddress({ + chain, + contract: contract as string, + blockNumber, + }) as contracts[contract] extends ChainContract + ? contracts[contract]['address'] + : never + +/** + * Type utility that enforces required contract dependencies on the client while providing clear error messages + * @example + * ```ts + * // Action definition + * const myAction = async ( + * client: RequireClientContracts, + * ) => { ... } + * + * // Example clients + * const client = createPublicClient({ + * chain: mainnet, + * transport: http(), + * }) + * + * const clientWithensL1Contracts = createPublicClient({ + * // This adds the required contracts to the chain + * chain: extendChainWithEns(mainnet), + * transport: http(), + * }) + * + * // This will error + * myAction(client) // TypeError<'Chain "mainnet" is missing required contracts: ensPublicResolver'> + * + * // This will not error + * myAction(clientWithensL1Contracts) + * ``` + */ +export type RequireClientContracts< + chain extends Chain, + contracts extends SuggestedContracts, + account extends Account | undefined = Account | undefined, +> = chain extends Omit & { + contracts: { + [key in contracts]: ChainContract + } +} + ? Client + : TypeError<`Chain "${chain['name']}" is missing required contracts: ${StringConcatenationOrder}`> diff --git a/packages/ensjs/src/clients/subgraph.ts b/packages/ensjs/src/clients/subgraph.ts deleted file mode 100644 index 672ba647e..000000000 --- a/packages/ensjs/src/clients/subgraph.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - type Client, - type ClientConfig, - type PublicRpcSchema, - type Transport, - createClient, -} from 'viem' -import { addEnsContracts } from '../contracts/addEnsContracts.js' -import type { - ChainWithBaseContracts, - ChainWithEns, -} from '../contracts/consts.js' -import type { Prettify } from '../types.js' -import { - type EnsSubgraphActions, - ensSubgraphActions, -} from './decorators/subgraph.js' - -export type EnsSubgraphClientConfig< - TTransport extends Transport = Transport, - TChain extends ChainWithBaseContracts = ChainWithBaseContracts, -> = Pick< - ClientConfig, - 'batch' | 'key' | 'name' | 'pollingInterval' | 'transport' -> & { - chain: TChain -} - -export type EnsSubgraphClient< - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, -> = Prettify< - Client -> - -/** - * Creates a ENS Subgraph Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * @param config - {@link EnsSubgraphClientConfig} - * @returns An ENS Subgraph Client. {@link EnsSubgraphClient} - * - * @example - * import { http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { createEnsSubgraphClient } from '@ensdomains/ensjs' - * - * const client = createEnsSubgraphClient({ - * chain: mainnet, - * transport: http(), - * }) - */ -export const createEnsSubgraphClient = < - TTransport extends Transport, - TChain extends ChainWithBaseContracts, ->({ - batch, - chain, - key = 'ensSubgraph', - name = 'ENS Subgraph Client', - transport, - pollingInterval, -}: EnsSubgraphClientConfig): EnsSubgraphClient< - TTransport, - ChainWithEns -> => { - return createClient({ - batch, - chain: addEnsContracts(chain), - key, - name, - pollingInterval, - transport, - type: 'ensSubgraphClient', - }).extend(ensSubgraphActions) -} diff --git a/packages/ensjs/src/clients/wallet.ts b/packages/ensjs/src/clients/wallet.ts deleted file mode 100644 index 7a9ae3997..000000000 --- a/packages/ensjs/src/clients/wallet.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - type Account, - type Address, - type Client, - type ClientConfig, - type ParseAccount, - type Transport, - type WalletActions, - type WalletRpcSchema, - createWalletClient, -} from 'viem' -import { addEnsContracts } from '../contracts/addEnsContracts.js' -import type { - ChainWithBaseContracts, - ChainWithEns, - CheckedChainWithEns, -} from '../contracts/consts.js' -import type { Assign, Prettify } from '../types.js' -import { type EnsWalletActions, ensWalletActions } from './decorators/wallet.js' - -export type EnsWalletClientConfig< - TTransport extends Transport, - TChain extends ChainWithBaseContracts, - TAccountOrAddress extends Account | Address | undefined = - | Account - | Address - | undefined, -> = Assign< - Pick< - ClientConfig, - 'account' | 'chain' | 'key' | 'name' | 'pollingInterval' | 'transport' - >, - { - chain: TChain - } -> - -export type EnsWalletClient< - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, - TAccount extends Account | undefined = Account | undefined, -> = Prettify< - Client< - TTransport, - TChain, - TAccount, - WalletRpcSchema, - WalletActions & EnsWalletActions - > -> - -/** - * Creates an ENS Wallet Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * @param config - {@link EnsWalletClientConfig} - * @returns An ENS Wallet Client. {@link EnsWalletClient} - * - * @example - * import { custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { createEnsWalletClient } from '@ensdomains/ensjs' - * - * const client = createEnsWalletClient({ - * chain: mainnet, - * transport: custom(window.ethereum), - * }) - */ -export const createEnsWalletClient = < - TTransport extends Transport, - TChain extends ChainWithBaseContracts, - TAccountOrAddress extends Account | Address | undefined = undefined, ->({ - account, - chain, - key = 'ensWallet', - name = 'ENS Wallet Client', - transport, - pollingInterval, -}: EnsWalletClientConfig< - TTransport, - TChain, - TAccountOrAddress ->): EnsWalletClient< - TTransport, - CheckedChainWithEns, - ParseAccount -> => { - return createWalletClient({ - account, - chain: addEnsContracts(chain), - key, - name, - pollingInterval, - transport, - }).extend(ensWalletActions) -} diff --git a/packages/ensjs/src/contracts/addEnsContracts.ts b/packages/ensjs/src/contracts/addEnsL1Contracts.ts similarity index 57% rename from packages/ensjs/src/contracts/addEnsContracts.ts rename to packages/ensjs/src/contracts/addEnsL1Contracts.ts index 1e42ae8b9..7218af488 100644 --- a/packages/ensjs/src/contracts/addEnsContracts.ts +++ b/packages/ensjs/src/contracts/addEnsL1Contracts.ts @@ -1,12 +1,12 @@ import type { Chain } from 'viem' -import { NoChainError, UnsupportedChainError } from '../errors/contracts.js' import { - type CheckedChainWithEns, - type SupportedChain, - addresses, - subgraphs, - supportedChains, -} from './consts.js' + type AnySupportedL1Chain, + type ChainWithEns, + ensL1Contracts, + type SupportedL1ChainId, + supportedL1Chains, +} from '../clients/l1.js' +import { NoChainError, UnsupportedChainError } from '../errors/contracts.js' /** * Adds ENS contract addresses to the viem chain @@ -22,21 +22,25 @@ import { * transport: http(), * }) */ -export const addEnsContracts = (chain: TChain) => { +export const addEnsL1Contracts = ( + chain: chain, +) => { if (!chain) throw new NoChainError() - if (!supportedChains.includes(chain.id as SupportedChain)) + if ( + !Object.values(supportedL1Chains).includes(chain.id as SupportedL1ChainId) + ) throw new UnsupportedChainError({ chainId: chain.id, - supportedChains, + supportedChains: Object.values(supportedL1Chains), }) return { ...chain, contracts: { ...chain.contracts, - ...addresses[chain.id as SupportedChain], + ...ensL1Contracts[chain.id as SupportedL1ChainId], }, subgraphs: { - ...subgraphs[chain.id as SupportedChain], + ...ensL1Contracts[chain.id as SupportedL1ChainId], }, - } as unknown as CheckedChainWithEns + } as unknown as ChainWithEns } diff --git a/packages/ensjs/src/contracts/baseRegistrar.ts b/packages/ensjs/src/contracts/baseRegistrar.ts deleted file mode 100644 index 1235d8653..000000000 --- a/packages/ensjs/src/contracts/baseRegistrar.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { - erc721OwnerOfSnippet, - erc721SafeTransferFromSnippet, - erc721SafeTransferFromWithDataSnippet, -} from './erc721.js' - -export const baseRegistrarAvailableSnippet = [ - { - inputs: [ - { - name: 'id', - type: 'uint256', - }, - ], - name: 'available', - outputs: [ - { - name: 'available', - type: 'bool', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export const baseRegistrarNameExpiresSnippet = [ - { - inputs: [ - { - name: 'id', - type: 'uint256', - }, - ], - name: 'nameExpires', - outputs: [ - { - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export const baseRegistrarGracePeriodSnippet = [ - { - inputs: [], - name: 'GRACE_PERIOD', - outputs: [ - { - name: '', - type: 'uint256', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export const baseRegistrarReclaimSnippet = [ - { - inputs: [ - { - name: 'id', - type: 'uint256', - }, - { - name: 'owner', - type: 'address', - }, - ], - name: 'reclaim', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -export const baseRegistrarSafeTransferFromSnippet = [ - ...erc721SafeTransferFromSnippet, -] as const - -export const baseRegistrarSafeTransferFromWithDataSnippet = [ - ...erc721SafeTransferFromWithDataSnippet, -] as const - -export const baseRegistrarOwnerOfSnippet = [...erc721OwnerOfSnippet] as const diff --git a/packages/ensjs/src/contracts/consts.ts b/packages/ensjs/src/contracts/consts.ts deleted file mode 100644 index af67f61f6..000000000 --- a/packages/ensjs/src/contracts/consts.ts +++ /dev/null @@ -1,240 +0,0 @@ -import type { Account, Address, Chain, Client, Transport } from 'viem' -import type { Assign, Prettify } from '../types.js' - -type ChainContract = { - address: Address - blockCreated?: number -} - -export const supportedChains = [1, 5, 17000, 11155111] as const -export const supportedContracts = [ - 'ensBaseRegistrarImplementation', - 'ensBulkRenewal', - 'ensDnsRegistrar', - 'ensDnssecImpl', - 'ensEthRegistrarController', - 'ensNameWrapper', - 'ensPublicResolver', - 'ensRegistry', - 'ensReverseRegistrar', - 'ensUniversalResolver', -] as const - -export type SupportedChain = (typeof supportedChains)[number] -export type SupportedContract = (typeof supportedContracts)[number] - -export const addresses = { - 1: { - ensBaseRegistrarImplementation: { - address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', - }, - ensBulkRenewal: { - address: '0xa12159e5131b1eEf6B4857EEE3e1954744b5033A', - }, - ensDnsRegistrar: { - address: '0xB32cB5677a7C971689228EC835800432B339bA2B', - }, - ensDnssecImpl: { - address: '0x0fc3152971714E5ed7723FAFa650F86A4BaF30C5', - }, - ensEthRegistrarController: { - address: '0x253553366Da8546fC250F225fe3d25d0C782303b', - }, - ensNameWrapper: { - address: '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401', - }, - ensPublicResolver: { - address: '0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63', - }, - ensRegistry: { - address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - }, - ensReverseRegistrar: { - address: '0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb', - }, - ensUniversalResolver: { - address: '0xce01f8eee7E479C928F8919abD53E553a36CeF67', - }, - }, - 5: { - ensBaseRegistrarImplementation: { - address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', - }, - ensBulkRenewal: { - address: '0x6d9F26FfBcF1c6f0bAe9F2C1f7fBe8eE6B1d8d4d', - }, - ensDnsRegistrar: { - address: '0x8edc487D26F6c8Fa76e032066A3D4F87E273515d', - }, - ensDnssecImpl: { - address: '0xF427c4AdED8B6dfde604865c1a7E953B160C26f0', - }, - ensEthRegistrarController: { - address: '0xCc5e7dB10E65EED1BBD105359e7268aa660f6734', - }, - ensNameWrapper: { - address: '0x114D4603199df73e7D157787f8778E21fCd13066', - }, - ensPublicResolver: { - address: '0xd7a4F6473f32aC2Af804B3686AE8F1932bC35750', - }, - ensRegistry: { - address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - }, - ensReverseRegistrar: { - address: '0x6d9F26FfBcF1c6f0bAe9F2C1f7fBe8eE6B1d8d4d', - }, - ensUniversalResolver: { - address: '0x898A1182F3C2BBBF0b16b4DfEf63E9c3e9eB4821', - }, - }, - 17000: { - ensBaseRegistrarImplementation: { - address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', - }, - ensBulkRenewal: { - address: '0xbc4cfB363F948E64Cd73Da6438F64CB37E2e33d1', - }, - ensDnsRegistrar: { - address: '0x458d278AEd4cE82BAeC384170f39198b01B8351c', - }, - ensDnssecImpl: { - address: '0x283af0b28c62c092c9727f1ee09c02ca627eb7f5', - }, - ensEthRegistrarController: { - address: '0xF404D2F84BC1735f7D9948F032D61F5fFfD9D3C3', - }, - ensNameWrapper: { - address: '0xab50971078225D365994dc1Edcb9b7FD72Bb4862', - }, - ensPublicResolver: { - address: '0x5a692ffe769A9B3D0e61F7446F5cAED650044C36', - }, - ensRegistry: { - address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - }, - ensReverseRegistrar: { - address: '0x65EE0b0B030a76c95a7ff046C0e0c8f7A2d1B004', - }, - ensUniversalResolver: { - address: '0xa6ac935d4971e3cd133b950ae053becd16fe7f3b', - }, - }, - 11155111: { - ensBaseRegistrarImplementation: { - address: '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85', - }, - ensBulkRenewal: { - address: '0x4EF77b90762Eddb33C8Eba5B5a19558DaE53D7a1', - }, - ensDnsRegistrar: { - address: '0x5a07C75Ae469Bf3ee2657B588e8E6ABAC6741b4f', - }, - ensDnssecImpl: { - address: '0xe62E4b6cE018Ad6e916fcC24545e20a33b9d8653', - }, - ensEthRegistrarController: { - address: '0x4477cAc137F3353Ca35060E01E5aEb777a1Ca01B', - }, - ensNameWrapper: { - address: '0x0635513f179D50A207757E05759CbD106d7dFcE8', - }, - ensPublicResolver: { - address: '0x8948458626811dd0c23EB25Cc74291247077cC51', - }, - ensRegistry: { - address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - }, - ensReverseRegistrar: { - address: '0xCF75B92126B02C9811d8c632144288a3eb84afC8', - }, - ensUniversalResolver: { - address: '0xc8af999e38273d658be1b921b88a9ddf005769cc', - }, - }, -} as const satisfies Record< - SupportedChain, - Record -> - -type Subgraphs = { - ens: { - url: string - } -} - -export const subgraphs = { - 1: { - ens: { - url: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens', - }, - }, - 5: { - ens: { - url: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensgoerli', - }, - }, - 17000: { - ens: { - url: 'https://api.studio.thegraph.com/query/49574/ensholesky/version/latest', - }, - }, - 11155111: { - ens: { - url: 'https://api.studio.thegraph.com/query/49574/enssepolia/version/latest', - }, - }, -} as const satisfies Record - -type EnsChainContracts = { - ensBaseRegistrarImplementation: ChainContract - ensDnsRegistrar: ChainContract - ensEthRegistrarController: ChainContract - ensNameWrapper: ChainContract - ensPublicResolver: ChainContract - ensReverseRegistrar: ChainContract - ensBulkRenewal: ChainContract - ensDnssecImpl: ChainContract -} - -type BaseChainContracts = { - multicall3: ChainContract - ensUniversalResolver: ChainContract - ensRegistry: ChainContract -} - -export type ChainWithEns = Omit< - TChain, - 'contracts' -> & { - contracts: BaseChainContracts & EnsChainContracts - subgraphs: Subgraphs -} - -export type ChainWithBaseContracts = Assign< - Omit, - { - contracts: BaseChainContracts - } -> - -export type CheckedChainWithEns = - TChain['id'] extends SupportedChain - ? TChain['contracts'] extends BaseChainContracts - ? TChain & { - contracts: Prettify<(typeof addresses)[TChain['id']]> - subgraphs: (typeof subgraphs)[TChain['id']] - } - : never - : never - -export type ClientWithEns< - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, -> = Client - -export type ClientWithAccount< - TTransport extends Transport = Transport, - TChain extends ChainWithEns = ChainWithEns, - TAccount extends Account | undefined = Account | undefined, -> = Client diff --git a/packages/ensjs/src/contracts/erc1155.ts b/packages/ensjs/src/contracts/erc1155.ts deleted file mode 100644 index 45c5ba71d..000000000 --- a/packages/ensjs/src/contracts/erc1155.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const erc1155SafeTransferFromSnippet = [ - { - inputs: [ - { - name: 'from', - type: 'address', - }, - { - name: 'to', - type: 'address', - }, - { - name: 'id', - type: 'uint256', - }, - { - name: 'amount', - type: 'uint256', - }, - { - name: 'data', - type: 'bytes', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/ensjs/src/contracts/erc721.ts b/packages/ensjs/src/contracts/erc721.ts deleted file mode 100644 index 43ad8ab11..000000000 --- a/packages/ensjs/src/contracts/erc721.ts +++ /dev/null @@ -1,69 +0,0 @@ -export const erc721OwnerOfSnippet = [ - { - inputs: [ - { - name: 'id', - type: 'uint256', - }, - ], - name: 'ownerOf', - outputs: [ - { - name: 'owner', - type: 'address', - }, - ], - stateMutability: 'view', - type: 'function', - }, -] as const - -export const erc721SafeTransferFromSnippet = [ - { - inputs: [ - { - name: 'from', - type: 'address', - }, - { - name: 'to', - type: 'address', - }, - { - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const - -export const erc721SafeTransferFromWithDataSnippet = [ - { - inputs: [ - { - name: 'from', - type: 'address', - }, - { - name: 'to', - type: 'address', - }, - { - name: 'tokenId', - type: 'uint256', - }, - { - name: '_data', - type: 'bytes', - }, - ], - name: 'safeTransferFrom', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', - }, -] as const diff --git a/packages/ensjs/src/contracts/getChainContractAddress.ts b/packages/ensjs/src/contracts/getChainContractAddress.ts index 796c536d6..9fda64e1f 100644 --- a/packages/ensjs/src/contracts/getChainContractAddress.ts +++ b/packages/ensjs/src/contracts/getChainContractAddress.ts @@ -1,34 +1,41 @@ -import type { Address, Chain } from 'viem' -import { getChainContractAddress as _getChainContractAddress } from 'viem/utils' +import type { Address, Chain, Client, Transport } from 'viem' +import { + type GetChainContractAddressErrorType as viem_GetChainContractAddressErrorType, + getChainContractAddress as viem_getChainContractAddress, +} from 'viem/utils' -type ExtractContract = TClient extends { - chain: { contracts: infer C } -} - ? C extends Record - ? C - : never +type ExtractContracts = chain extends { contracts: infer C } + ? { [key in keyof C as string extends key ? never : key]: C[key] } : never +// ================================ +// Get chain contract address +// ================================ + +/** @deprecated */ +export type GetChainContractAddressErrorType = + viem_GetChainContractAddressErrorType + +/** @deprecated */ export const getChainContractAddress = < - const TClient extends { chain: Chain }, - TContracts extends ExtractContract = ExtractContract, - TContractName extends keyof TContracts = keyof TContracts, - TContract extends TContracts[TContractName] = TContracts[TContractName], + const chain extends Chain, + contracts extends ExtractContracts = ExtractContracts, + contractName extends keyof contracts = keyof contracts, >({ blockNumber, client, contract, }: { blockNumber?: bigint - client: TClient - contract: TContractName + client: Client + contract: contractName }) => - _getChainContractAddress({ + viem_getChainContractAddress({ blockNumber, - chain: client.chain, + chain: client.chain as Chain, contract: contract as string, - }) as TContract extends { address: infer A } + }) as contracts[contractName] extends { address: infer A } ? A extends Address ? A : never - : Address + : never diff --git a/packages/ensjs/src/contracts/index.ts b/packages/ensjs/src/contracts/index.ts index b998202ac..de1b97d11 100644 --- a/packages/ensjs/src/contracts/index.ts +++ b/packages/ensjs/src/contracts/index.ts @@ -1,51 +1,71 @@ export { + defaultReverseRegistrarSetNameForAddrWithSignatureSnippet, + defaultReverseRegistrarSetNameSnippet, +} from '@ensdomains/ensjs-abi/defaultReverseRegistrar' +export { + dnsRegistrarErrors, + dnsRegistrarProveAndClaimSnippet, + dnsRegistrarProveAndClaimWithResolverSnippet, +} from '@ensdomains/ensjs-abi/dnsRegistrar' +export { + dnssecImplAnchorsSnippet, + dnssecImplErrors, + dnssecImplVerifyRrSetSnippet, +} from '@ensdomains/ensjs-abi/dnssecImpl' +export { erc165SupportsInterfaceSnippet } from '@ensdomains/ensjs-abi/erc165' +export { + multicallGetCurrentBlockTimestampSnippet, + multicallTryAggregateSnippet, +} from '@ensdomains/ensjs-abi/multicall' +export { + registryGetResolverSnippet, + registryGetSubregistrySnippet, + registryOwnerSnippet, + registryResolverSnippet, + registrySetApprovalForAllSnippet, + registrySetOwnerSnippet, + registrySetRecordSnippet, + registrySetResolverSnippet, + registrySetSubnodeOwnerSnippet, + registrySetSubnodeRecordSnippet, +} from '@ensdomains/ensjs-abi/registry' +export { + reverseRegistrarSetNameForAddrSnippet, + reverseRegistrarSetNameSnippet, +} from '@ensdomains/ensjs-abi/reverseRegistrar' +export { + universalResolverErrors, + universalResolverFindResolverSnippet, + universalResolverResolveSnippet, + universalResolverResolveWithGatewaysSnippet, + universalResolverReverseSnippet, + universalResolverReverseWithGatewaysSnippet, +} from '@ensdomains/ensjs-abi/universalResolver' +export { + baseRegistrarAddControllerSnippet, baseRegistrarAvailableSnippet, + baseRegistrarControllersSnippet, baseRegistrarGracePeriodSnippet, baseRegistrarNameExpiresSnippet, baseRegistrarOwnerOfSnippet, + baseRegistrarOwnerSnippet, baseRegistrarReclaimSnippet, + baseRegistrarRegisterSnippet, baseRegistrarSafeTransferFromSnippet, baseRegistrarSafeTransferFromWithDataSnippet, -} from './baseRegistrar.js' +} from '@ensdomains/ensjs-abi/v1/baseRegistrar' export { bulkRenewalRenewAllSnippet, bulkRenewalRentPriceSnippet, -} from './bulkRenewal.js' -export { - addresses, - subgraphs, - supportedChains, - supportedContracts, - type ChainWithEns, - type CheckedChainWithEns, - type ClientWithAccount, - type ClientWithEns, - type SupportedChain, - type SupportedContract, -} from './consts.js' +} from '@ensdomains/ensjs-abi/v1/bulkRenewal' export { - dnsRegistrarErrors, - dnsRegistrarProveAndClaimSnippet, - dnsRegistrarProveAndClaimWithResolverSnippet, -} from './dnsRegistrar.js' -export { - dnssecImplAnchorsSnippet, - dnssecImplVerifyRrSetSnippet, -} from './dnssecImpl.js' -export { erc165SupportsInterfaceSnippet } from './erc165.js' -export { - ethRegistrarControllerCommitSnippet, ethRegistrarControllerCommitmentsSnippet, + ethRegistrarControllerCommitSnippet, ethRegistrarControllerErrors, ethRegistrarControllerRegisterSnippet, ethRegistrarControllerRenewSnippet, ethRegistrarControllerRentPriceSnippet, -} from './ethRegistrarController.js' -export { getChainContractAddress } from './getChainContractAddress.js' -export { - multicallGetCurrentBlockTimestampSnippet, - multicallTryAggregateSnippet, -} from './multicall.js' +} from '@ensdomains/ensjs-abi/v1/ethRegistrarController' export { nameWrapperErrors, nameWrapperGetDataSnippet, @@ -60,8 +80,9 @@ export { nameWrapperSetSubnodeRecordSnippet, nameWrapperUnwrapEth2ldSnippet, nameWrapperUnwrapSnippet, + nameWrapperWrapEth2ldSnippet, nameWrapperWrapSnippet, -} from './nameWrapper.js' +} from '@ensdomains/ensjs-abi/v1/nameWrapper' export { publicResolverAbiSnippet, publicResolverClearRecordsSnippet, @@ -74,25 +95,37 @@ export { publicResolverSetTextSnippet, publicResolverSingleAddrSnippet, publicResolverTextSnippet, -} from './publicResolver.js' +} from '@ensdomains/ensjs-abi/v1/publicResolver' export { - registryOwnerSnippet, - registryResolverSnippet, - registrySetApprovalForAllSnippet, - registrySetOwnerSnippet, - registrySetRecordSnippet, - registrySetResolverSnippet, - registrySetSubnodeOwnerSnippet, - registrySetSubnodeRecordSnippet, -} from './registry.js' + ethRegistrarAvailableSnippet as l2EthRegistrarAvailableSnippet, + ethRegistrarCommitmentsSnippet as l2EthRegistrarCommitmentsSnippet, + ethRegistrarCommitSnippet as l2EthRegistrarCommitSnippet, + ethRegistrarErrors as l2EthRegistrarErrors, + ethRegistrarGetRegisterPriceSnippet as l2EthRegistrarGetRegisterPriceSnippet, + ethRegistrarGetRemainingGracePeriodSnippet as l2EthRegistrarGetRemainingGracePeriodSnippet, + ethRegistrarGetRenewPriceSnippet as l2EthRegistrarGetRenewPriceSnippet, + ethRegistrarIsAvailableSnippet as l2EthRegistrarIsAvailableSnippet, + ethRegistrarIsRenewableSnippet as l2EthRegistrarIsRenewableSnippet, + ethRegistrarMakeCommitmentSnippet as l2EthRegistrarMakeCommitmentSnippet, + ethRegistrarRegisterSnippet as l2EthRegistrarRegisterSnippet, + ethRegistrarRenewSnippet as l2EthRegistrarRenewSnippet, +} from '@ensdomains/ensjs-abi/v2/ethRegistrar' export { - reverseRegistrarSetNameForAddrSnippet, - reverseRegistrarSetNameSnippet, -} from './reverseRegistrar.js' + permissionedRegistryRoleCountSnippet, + permissionedRegistrySetResolverSnippet, + permissionedRegistryUnregisterSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedRegistry' export { - universalResolverErrors, - universalResolverFindResolverSnippet, - universalResolverResolveArraySnippet, - universalResolverResolveSnippet, - universalResolverReverseSnippet, -} from './universalResolver.js' + permissionedResolverAliasSnippet, + permissionedResolverAuthorizeAddrRolesSnippet, + permissionedResolverAuthorizeDataRolesSnippet, + permissionedResolverAuthorizeNameRolesSnippet, + permissionedResolverAuthorizeTextRolesSnippet, + permissionedResolverGrantRootRolesSnippet, + permissionedResolverHasRolesSnippet, + permissionedResolverHasRootRolesSnippet, + permissionedResolverRevokeRolesSnippet, + permissionedResolverRevokeRootRolesSnippet, + permissionedResolverRolesSnippet, +} from '@ensdomains/ensjs-abi/v2/permissionedResolver' +export { getChainContractAddress } from './getChainContractAddress.js' diff --git a/packages/ensjs/src/dns.ts b/packages/ensjs/src/dns.ts index 62b5273c2..4e5573c1b 100644 --- a/packages/ensjs/src/dns.ts +++ b/packages/ensjs/src/dns.ts @@ -1,23 +1,27 @@ export { - default as getDnsImportData, + type GetDnsImportDataErrorType, type GetDnsImportDataParameters, type GetDnsImportDataReturnType, -} from './functions/dns/getDnsImportData.js' + getDnsImportData, +} from './actions/dns/getDnsImportData.js' export { - default as getDnsOffchainData, + type GetDnsOffchainDataErrorType, type GetDnsOffchainDataParameters, type GetDnsOffchainDataReturnType, -} from './functions/dns/getDnsOffchainData.js' + getDnsOffchainData, +} from './actions/dns/getDnsOffchainData.js' export { - default as getDnsOwner, + type GetDnsOwnerErrorType, type GetDnsOwnerParameters, type GetDnsOwnerReturnType, -} from './functions/dns/getDnsOwner.js' + getDnsOwner, +} from './actions/dns/getDnsOwner.js' export { - default as importDnsName, type ImportDnsNameDataParameters, type ImportDnsNameDataReturnType, + type ImportDnsNameErrorType, type ImportDnsNameParameters, type ImportDnsNameReturnType, -} from './functions/dns/importDnsName.js' -export type { Endpoint } from './functions/dns/types.js' + importDnsName, +} from './actions/dns/importDnsName.js' +export type { Endpoint } from './actions/dns/types.js' diff --git a/packages/ensjs/src/errors/base.ts b/packages/ensjs/src/errors/base.ts index 4a3f6e7da..7877f400d 100644 --- a/packages/ensjs/src/errors/base.ts +++ b/packages/ensjs/src/errors/base.ts @@ -24,9 +24,9 @@ export class BaseError extends Error { version = getVersion() - cause?: BaseError | Error + override cause?: BaseError | Error - constructor(shortMesage: string, args: BaseErrorParameters = {}) { + constructor(shortMessage: string, args: BaseErrorParameters = {}) { super() const details = @@ -35,7 +35,7 @@ export class BaseError extends Error { : (args.cause?.message ?? args.details!) this.message = [ - shortMesage || 'An error occurred', + shortMessage || 'An error occurred', '', ...(args.metaMessages ? [...args.metaMessages, ''] : []), ...(details ? [`Details: ${details}`, ''] : []), @@ -45,6 +45,6 @@ export class BaseError extends Error { if (args.cause) this.cause = args.cause this.details = details this.metaMessages = args.metaMessages - this.shortMessage = shortMesage + this.shortMessage = shortMessage } } diff --git a/packages/ensjs/src/errors/general.ts b/packages/ensjs/src/errors/general.ts index 608b5bcb9..2c29588e3 100644 --- a/packages/ensjs/src/errors/general.ts +++ b/packages/ensjs/src/errors/general.ts @@ -1,4 +1,4 @@ -import type { NameType } from '../types.js' +import type { NameType } from '../types/index.js' import { BaseError } from './base.js' export class AdditionalParameterSpecifiedError extends BaseError { diff --git a/packages/ensjs/src/errors/utils.ts b/packages/ensjs/src/errors/utils.ts index 6e7258cf2..2d203525c 100644 --- a/packages/ensjs/src/errors/utils.ts +++ b/packages/ensjs/src/errors/utils.ts @@ -1,5 +1,7 @@ import { BaseError } from './base.js' +export type ErrorType = Error & { name: name } + export class FusesOutOfRangeError extends BaseError { override name = 'FusesOutOfRangeError' diff --git a/packages/ensjs/src/errors/wallet.ts b/packages/ensjs/src/errors/wallet.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/ensjs/src/exports/chain.ts b/packages/ensjs/src/exports/chain.ts new file mode 100644 index 000000000..e2c5648fc --- /dev/null +++ b/packages/ensjs/src/exports/chain.ts @@ -0,0 +1,2 @@ +export * from '../clients/l1.js' +export * from '../clients/shared.js' diff --git a/packages/ensjs/src/exports/internal.ts b/packages/ensjs/src/exports/internal.ts new file mode 100644 index 000000000..f5f03e6d8 --- /dev/null +++ b/packages/ensjs/src/exports/internal.ts @@ -0,0 +1 @@ +export * from '../types/internal.js' diff --git a/packages/ensjs/src/exports/public.ts b/packages/ensjs/src/exports/public.ts new file mode 100644 index 000000000..aa55ab9a2 --- /dev/null +++ b/packages/ensjs/src/exports/public.ts @@ -0,0 +1,12 @@ +export * from '../actions/public/getAbiRecord.js' +export * from '../actions/public/getAddressRecord.js' +export * from '../actions/public/getContentHashRecord.js' +export * from '../actions/public/getCredentials.js' +export * from '../actions/public/getName.js' +export * from '../actions/public/getNames.js' +export * from '../actions/public/getRecords.js' +export * from '../actions/public/getResolver.js' +export * from '../actions/public/getReverseRecordFromRegistry.js' +export * from '../actions/public/getSupportedInterfaces.js' +export * from '../actions/public/getTextRecord.js' +export * from '../actions/public/resolveNameData.js' diff --git a/packages/ensjs/src/exports/public/v1.ts b/packages/ensjs/src/exports/public/v1.ts new file mode 100644 index 000000000..a49a571a0 --- /dev/null +++ b/packages/ensjs/src/exports/public/v1.ts @@ -0,0 +1,6 @@ +export * from '../../actions/public/v1/getAvailable.js' +export * from '../../actions/public/v1/getExpiry.js' +export * from '../../actions/public/v1/getOwner.js' +export * from '../../actions/public/v1/getPrice.js' +export * from '../../actions/public/v1/getWrapperData.js' +export * from '../../actions/public/v1/getWrapperName.js' diff --git a/packages/ensjs/src/exports/public/v2.ts b/packages/ensjs/src/exports/public/v2.ts new file mode 100644 index 000000000..992c42de9 --- /dev/null +++ b/packages/ensjs/src/exports/public/v2.ts @@ -0,0 +1,24 @@ +export * from '../../actions/public/v2/applyDiscount.js' +export * from '../../actions/public/v2/getAvailable.js' +export * from '../../actions/public/v2/getBaseRates.js' +export * from '../../actions/public/v2/getExpiry.js' +export * from '../../actions/public/v2/getNameRegistries.js' +export * from '../../actions/public/v2/getNameRegistryAddress.js' +export * from '../../actions/public/v2/getNameRolesAccounts.js' +export * from '../../actions/public/v2/getNameRolesForAccount.js' +export * from '../../actions/public/v2/getOwner.js' +export * from '../../actions/public/v2/getPremiumDecayParams.js' +export * from '../../actions/public/v2/getRegisterPrice.js' +export * from '../../actions/public/v2/getRegistrationDate.js' +export * from '../../actions/public/v2/getRenewPrice.js' +export * from '../../actions/public/v2/getResolverName.js' +export * from '../../actions/public/v2/getResource.js' +export * from '../../actions/public/v2/getRoleCounts.js' +export * from '../../actions/public/v2/getSubregistryHistory.js' +export * from '../../actions/public/v2/getTokenId.js' +export * from '../../actions/public/v2/getUnderlyingResolver.js' +export * from '../../actions/public/v2/hasRoles.js' +export * from '../../actions/public/v2/isPaymentToken.js' +export * from '../../actions/public/v2/isRenewable.js' +export * from '../../utils/v2/roles/resolverResource.js' +export * from '../../utils/v2/roles/resolverRoles.js' diff --git a/packages/ensjs/src/exports/wallet.ts b/packages/ensjs/src/exports/wallet.ts new file mode 100644 index 000000000..4b5fdc0a6 --- /dev/null +++ b/packages/ensjs/src/exports/wallet.ts @@ -0,0 +1,22 @@ +export * from '../actions/wallet/clearRecords.js' +export * from '../actions/wallet/commitName.js' +export * from '../actions/wallet/createSubname.js' +export * from '../actions/wallet/deleteSubname.js' +export * from '../actions/wallet/registerName.js' +export * from '../actions/wallet/renewName.js' +export * from '../actions/wallet/setAbiRecord.js' +export * from '../actions/wallet/setAddressRecord.js' +export * from '../actions/wallet/setChildFuses.js' +export * from '../actions/wallet/setContentHashRecord.js' +export * from '../actions/wallet/setFuses.js' +export * from '../actions/wallet/setPrimaryName.js' +export * from '../actions/wallet/setRecords.js' +export * from '../actions/wallet/setResolver.js' +export * from '../actions/wallet/setTextRecord.js' +export * from '../actions/wallet/transferName.js' +export * from '../actions/wallet/unwrapName.js' +export * from '../actions/wallet/v2/createSubname.js' +export * from '../actions/wallet/v2/deploySubregistry.js' +export * from '../actions/wallet/v2/deployVerifiableProxy.js' +export * from '../actions/wallet/v2/setSubregistry.js' +export * from '../actions/wallet/wrapName.js' diff --git a/packages/ensjs/src/exports/wallet/v2.ts b/packages/ensjs/src/exports/wallet/v2.ts new file mode 100644 index 000000000..34fbc103d --- /dev/null +++ b/packages/ensjs/src/exports/wallet/v2.ts @@ -0,0 +1,10 @@ +export * from '../../actions/wallet/v2/deleteAlias.js' +export * from '../../actions/wallet/v2/deleteSubname.js' +export * from '../../actions/wallet/v2/deleteSubnames.js' +export * from '../../actions/wallet/v2/grantResolverRoles.js' +export * from '../../actions/wallet/v2/grantRoles.js' +export * from '../../actions/wallet/v2/revokeResolverRoles.js' +export * from '../../actions/wallet/v2/revokeRoles.js' +export * from '../../actions/wallet/v2/setAlias.js' +export * from '../../actions/wallet/v2/setResolver.js' +export * from '../../actions/wallet/v2/setSubregistry.js' diff --git a/packages/ensjs/src/functions/public/_getAbi.test.ts b/packages/ensjs/src/functions/public/_getAbi.test.ts deleted file mode 100644 index 9c37ec0c5..000000000 --- a/packages/ensjs/src/functions/public/_getAbi.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import _getAbi from './_getAbi.js' - -it('does not propagate error when strict is false', async () => { - const result = await _getAbi.decode({} as ClientWithEns, '0x1234', { - strict: false, - }) - expect(result).toBeNull() -}) - -it('propagates error when strict is true', async () => { - await expect( - _getAbi.decode({} as ClientWithEns, '0x1234', { strict: true }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. - - Params: (uint256, bytes) - Data: 0x1234 (2 bytes) - - Version: viem@2.30.6] - `) -}) diff --git a/packages/ensjs/src/functions/public/_getAbi.ts b/packages/ensjs/src/functions/public/_getAbi.ts deleted file mode 100644 index 8caf34279..000000000 --- a/packages/ensjs/src/functions/public/_getAbi.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { - type Hex, - decodeFunctionResult, - encodeFunctionData, - hexToBytes, - hexToString, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { publicResolverAbiSnippet } from '../../contracts/publicResolver.js' -import type { - DecodedAbi, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { namehash } from '../../utils/normalise.js' - -export type InternalGetAbiParameters = { - /** Name to get ABI record for */ - name: string - /** Supported content types as bitwise - * ID 1: JSON - * ID 2: zlib compressed JSON - * ID 4: CBOR - * ID 8: URI - */ - supportedContentTypes?: bigint - /** Whether or not to throw decoding errors */ - strict?: boolean -} - -export type InternalGetAbiReturnType = Prettify - -const encode = ( - _client: ClientWithEns, - { - name, - supportedContentTypes = 0xfn, - }: Omit, -): SimpleTransactionRequest => { - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverAbiSnippet, - functionName: 'ABI', - args: [namehash(name), supportedContentTypes], - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex, - { strict }: Pick, -): Promise => { - if (data === '0x') return null - - try { - const [bigintContentType, encodedAbiData] = decodeFunctionResult({ - abi: publicResolverAbiSnippet, - functionName: 'ABI', - data, - }) - - if (!bigintContentType || !encodedAbiData) { - return null - } - - const contentType = Number(bigintContentType) - if (!contentType) { - return null - } - - let abiData: string | object - let decoded = false - switch (contentType) { - // JSON - case 1: - abiData = JSON.parse(hexToString(encodedAbiData)) - decoded = true - break - // zlib compressed JSON - case 2: { - const { inflate } = await import('pako/dist/pako_inflate.min.js') - abiData = JSON.parse( - inflate(hexToBytes(encodedAbiData), { to: 'string' }), - ) - decoded = true - break - } - // CBOR - case 4: { - const { cborDecode } = await import('@ensdomains/address-encoder/utils') - abiData = await cborDecode(hexToBytes(encodedAbiData).buffer) - decoded = true - break - } - // URI - case 8: - abiData = hexToString(encodedAbiData) - decoded = true - break - default: - try { - abiData = hexToString(encodedAbiData) - decoded = true - } catch { - abiData = encodedAbiData - } - } - - return { - contentType, - decoded, - abi: abiData, - } - } catch (error) { - if (strict) throw error - return null - } -} - -const _getAbi = generateFunction({ encode, decode }) - -export default _getAbi diff --git a/packages/ensjs/src/functions/public/_getAddr.test.ts b/packages/ensjs/src/functions/public/_getAddr.test.ts deleted file mode 100644 index 5ff83ee14..000000000 --- a/packages/ensjs/src/functions/public/_getAddr.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import _getAddr from './_getAddr.js' - -it('does not propagate error when strict is false', async () => { - const result = await _getAddr.decode({} as ClientWithEns, '0x1234', { - strict: false, - }) - expect(result).toBeNull() -}) - -it('propagates error when strict is true', async () => { - await expect( - _getAddr.decode({} as ClientWithEns, '0x1234', { strict: true }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. - - Params: (address) - Data: 0x1234 (2 bytes) - - Version: viem@2.30.6] - `) -}) diff --git a/packages/ensjs/src/functions/public/_getAddr.ts b/packages/ensjs/src/functions/public/_getAddr.ts deleted file mode 100644 index ecfbe4742..000000000 --- a/packages/ensjs/src/functions/public/_getAddr.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { - type Hex, - decodeFunctionResult, - encodeFunctionData, - hexToBytes, - trim, -} from 'viem' -import { namehash } from '../../utils/normalise.js' - -import type { ClientWithEns } from '../../contracts/consts.js' -import { - publicResolverMultiAddrSnippet, - publicResolverSingleAddrSnippet, -} from '../../contracts/publicResolver.js' -import type { - DecodedAddr, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { getCoderFromCoin } from '../../utils/normaliseCoinId.js' - -export type InternalGetAddrParameters = { - /** Name to get the address record for */ - name: string - /** Coin to get the address record for, can be either symbol (string) or coinId (number) (default: `60`) */ - coin?: string | number - /** Optionally return raw bytes value of address record (default: false) */ - bypassFormat?: boolean - /** Whether or not to throw decoding errors */ - strict?: boolean -} - -export type InternalGetAddrReturnType = Prettify - -const encode = ( - _client: ClientWithEns, - { name, coin = 60, bypassFormat }: Omit, -): SimpleTransactionRequest => { - const coder = getCoderFromCoin(coin) - if (coder.coinType === 60) { - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverSingleAddrSnippet, - functionName: 'addr', - args: [namehash(name)], - }), - } - } - - if (bypassFormat) { - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverMultiAddrSnippet, - functionName: 'addr', - args: [namehash(name), BigInt(coin)], - }), - } - } - - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverMultiAddrSnippet, - functionName: 'addr', - args: [namehash(name), BigInt(coder.coinType)], - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex, - { coin = 60, strict }: Pick, -): Promise => { - if (data === '0x') return null - - const coder = getCoderFromCoin(coin) - let response: Hex - - try { - if (coder.coinType === 60) { - response = decodeFunctionResult({ - abi: publicResolverSingleAddrSnippet, - functionName: 'addr', - data, - }) - } else { - response = decodeFunctionResult({ - abi: publicResolverMultiAddrSnippet, - functionName: 'addr', - data, - }) - } - - if (!response) return null - - const trimmed = trim(response) - if (trimmed === '0x' || trimmed === '0x0' || trimmed === '0x00') { - return null - } - - const decodedAddr = coder.encode(hexToBytes(response)) - - if (!decodedAddr) { - return null - } - - return { id: coder.coinType, name: coder.name, value: decodedAddr } - } catch (error) { - if (strict) throw error - return null - } -} - -const _getAddr = generateFunction({ encode, decode }) - -export default _getAddr diff --git a/packages/ensjs/src/functions/public/_getContentHash.test.ts b/packages/ensjs/src/functions/public/_getContentHash.test.ts deleted file mode 100644 index 3ef526d82..000000000 --- a/packages/ensjs/src/functions/public/_getContentHash.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import _getContentHash from './_getContentHash.js' - -it('does not propagate error when strict is false', async () => { - const result = await _getContentHash.decode({} as ClientWithEns, '0x1234', { - strict: false, - }) - expect(result).toBeNull() -}) - -it('propagates error when strict is true', async () => { - await expect( - _getContentHash.decode({} as ClientWithEns, '0x1234', { strict: true }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. - - Params: (bytes) - Data: 0x1234 (2 bytes) - - Version: viem@2.30.6] - `) -}) diff --git a/packages/ensjs/src/functions/public/_getContentHash.ts b/packages/ensjs/src/functions/public/_getContentHash.ts deleted file mode 100644 index b59f8d5cc..000000000 --- a/packages/ensjs/src/functions/public/_getContentHash.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { type Hex, decodeFunctionResult, encodeFunctionData } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { publicResolverContenthashSnippet } from '../../contracts/publicResolver.js' -import type { Prettify, SimpleTransactionRequest } from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { - type DecodedContentHash, - decodeContentHash, -} from '../../utils/contentHash.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { namehash } from '../../utils/normalise.js' - -export type InternalGetContentHashParameters = { - /** Name to get content hash record for */ - name: string - /** Whether or not to throw decoding errors */ - strict?: boolean -} - -export type InternalGetContentHashReturnType = - Prettify - -const encode = ( - _client: ClientWithEns, - { name }: Omit, -): SimpleTransactionRequest => { - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverContenthashSnippet, - functionName: 'contenthash', - args: [namehash(name)], - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex, - { strict }: Pick, -): Promise => { - if (data === '0x') return null - - try { - const response = decodeFunctionResult({ - abi: publicResolverContenthashSnippet, - functionName: 'contenthash', - data, - }) - - return decodeContentHash(response) - } catch (error) { - if (strict) throw error - return null - } -} - -const _getContentHash = generateFunction({ encode, decode }) - -export default _getContentHash diff --git a/packages/ensjs/src/functions/public/_getText.test.ts b/packages/ensjs/src/functions/public/_getText.test.ts deleted file mode 100644 index 958a9288c..000000000 --- a/packages/ensjs/src/functions/public/_getText.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import _getText from './_getText.js' - -it('does not propagate error when strict is false', async () => { - const result = await _getText.decode({} as ClientWithEns, '0x1234', { - strict: false, - }) - expect(result).toBeNull() -}) - -it('propagates error when strict is true', async () => { - await expect( - _getText.decode({} as ClientWithEns, '0x1234', { strict: true }), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. - - Params: (string) - Data: 0x1234 (2 bytes) - - Version: viem@2.30.6] - `) -}) diff --git a/packages/ensjs/src/functions/public/_getText.ts b/packages/ensjs/src/functions/public/_getText.ts deleted file mode 100644 index 0dfaa6e7c..000000000 --- a/packages/ensjs/src/functions/public/_getText.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { type Hex, decodeFunctionResult, encodeFunctionData } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { publicResolverTextSnippet } from '../../contracts/publicResolver.js' -import type { SimpleTransactionRequest } from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { namehash } from '../../utils/normalise.js' - -export type InternalGetTextParameters = { - /** Name to get text record for */ - name: string - /** Text record key to get */ - key: string - /** Whether or not to throw decoding errors */ - strict?: boolean -} - -export type InternalGetTextReturnType = string | null - -const encode = ( - _client: ClientWithEns, - { name, key }: Omit, -): SimpleTransactionRequest => { - return { - to: EMPTY_ADDRESS, - data: encodeFunctionData({ - abi: publicResolverTextSnippet, - functionName: 'text', - args: [namehash(name), key], - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex, - { strict }: Pick, -): Promise => { - if (data === '0x') return null - - try { - const response = decodeFunctionResult({ - abi: publicResolverTextSnippet, - functionName: 'text', - data, - }) - - if (!response) return null - - return response - } catch (error) { - if (strict) throw error - return null - } -} - -const _getText = generateFunction({ encode, decode }) - -export default _getText diff --git a/packages/ensjs/src/functions/public/batch.test.ts b/packages/ensjs/src/functions/public/batch.test.ts deleted file mode 100644 index 16d3f080e..000000000 --- a/packages/ensjs/src/functions/public/batch.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { http, createPublicClient } from 'viem' -import { mainnet } from 'viem/chains' -import { describe, expect, it } from 'vitest' -import { addEnsContracts } from '../../contracts/addEnsContracts.js' -import { - deploymentAddresses, - publicClient, -} from '../../test/addTestContracts.js' -import batch from './batch.js' -import getAddressRecord from './getAddressRecord.js' -import getName from './getName.js' -import getText from './getTextRecord.js' - -const mainnetPublicClient = createPublicClient({ - chain: addEnsContracts(mainnet), - transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), -}) - -describe('batch', () => { - it('should batch calls together', async () => { - const result = await batch( - publicClient, - getText.batch({ name: 'with-profile.eth', key: 'description' }), - getAddressRecord.batch({ name: 'with-profile.eth' }), - getName.batch({ address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' }), - ) - expect(result).toMatchInlineSnapshot(` - [ - "Hello2", - { - "id": 60, - "name": "eth", - "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - }, - { - "match": true, - "name": "with-profile.eth", - "resolverAddress": "${deploymentAddresses.LegacyPublicResolver}", - "reverseResolverAddress": "${deploymentAddresses.PublicResolver}", - }, - ] - `) - }) - it('should batch a single call', async () => { - const result = await batch( - publicClient, - getText.batch({ name: 'with-profile.eth', key: 'description' }), - ) - expect(result).toMatchInlineSnapshot(` - [ - "Hello2", - ] - `) - }) - it('should batch ccip', async () => { - const result = await batch( - mainnetPublicClient, - getText.batch({ name: '1.offchainexample.eth', key: 'email' }), - getText.batch({ name: '2.offchainexample.eth', key: 'email' }), - ) - expect(result).toMatchInlineSnapshot(` - [ - "nick@ens.domains", - "nick@ens.domains", - ] - `) - }) -}) diff --git a/packages/ensjs/src/functions/public/batch.ts b/packages/ensjs/src/functions/public/batch.ts deleted file mode 100644 index feee30951..000000000 --- a/packages/ensjs/src/functions/public/batch.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { FunctionNotBatchableError } from '../../errors/public.js' -import type { - SimpleTransactionRequest, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { - type BatchFunctionResult, - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import multicallWrapper from './multicallWrapper.js' - -type ExtractResult = TFunction extends { - decode: (...args: any[]) => Promise -} - ? U - : never - -export type BatchParameters = BatchFunctionResult[] - -export type BatchReturnType = { - [TFunctionName in keyof TFunctions]: ExtractResult -} - -const encode = ( - client: ClientWithEns, - ...items: BatchFunctionResult[] -): TransactionRequestWithPassthrough => { - const rawDataArr: SimpleTransactionRequest[] = items.map( - ({ args, encode: encodeRef }, i: number) => { - if (!encodeRef) throw new FunctionNotBatchableError({ functionIndex: i }) - return encodeRef(client, ...args) - }, - ) - const response = multicallWrapper.encode(client, { - transactions: rawDataArr, - }) - return { ...response, passthrough: rawDataArr } -} - -const decode = async ( - client: ClientWithEns, - data: Hex, - passthrough: TransactionRequestWithPassthrough[], - ...items: I -): Promise> => { - const response = await multicallWrapper.decode(client, data, passthrough) - if (!response) throw new Error('No response from multicall') - - return Promise.all( - response.map((ret, i: number) => { - if (passthrough[i].passthrough) { - return items[i].decode( - client, - ret.returnData, - passthrough[i].passthrough, - ...items[i].args, - ) - } - return items[i].decode(client, ret.returnData, ...items[i].args) - }), - ) as Promise> -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Batches multiple read functions into a single call. - * @param client - {@link ClientWithEns} - * @param ...parameters - Array of {@link BatchFunctionResult} objects - * @returns Array of return values from each function - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { batch, getTextRecord, getAddressRecord } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await batch( - * client, - * getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), - * getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), - * ) - * // ['ensdomains', { id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7 }] - */ -const batch = generateFunction({ - encode, - decode, -}) as (( - client: ClientWithEns, - ...args: I -) => Promise>) & - BatchableFunctionObject - -export default batch diff --git a/packages/ensjs/src/functions/public/ccip.test.ts b/packages/ensjs/src/functions/public/ccip.test.ts deleted file mode 100644 index 487b7b052..000000000 --- a/packages/ensjs/src/functions/public/ccip.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { http, createPublicClient } from 'viem' -import { holesky, mainnet } from 'viem/chains' -import { describe, expect, it, vi } from 'vitest' -import { addEnsContracts } from '../../contracts/addEnsContracts.js' -import { ccipRequest } from '../../utils/ccipRequest.js' -import batch from './batch.js' -import getAddressRecord from './getAddressRecord.js' -import getRecords from './getRecords.js' -import getText from './getTextRecord.js' - -vi.setConfig({ - testTimeout: 30000, -}) - -const mainnetPublicClient = createPublicClient({ - chain: addEnsContracts(mainnet), - transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), -}) - -const holeskyPublicClient = createPublicClient({ - chain: addEnsContracts(holesky), - transport: http('https://holesky.gateway.tenderly.co/5S00ox7ZN3mdGqaO74UDsg'), -}) - -describe('CCIP', () => { - describe('getRecords', () => { - it('should return records from a ccip-read name', async () => { - const result = await getRecords(holeskyPublicClient, { - name: 'offchainexample.eth', - texts: ['email', 'description'], - contentHash: true, - coins: ['btc', '60'], - }) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 0, - "name": "btc", - "value": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", - }, - { - "id": 60, - "name": "eth", - "value": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", - }, - ], - "contentHash": { - "decoded": "bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y", - "protocolType": "ipfs", - }, - "resolverAddress": "0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547", - "texts": [ - { - "key": "email", - "value": "vitalik@ethereum.org", - }, - { - "key": "description", - "value": "hello offchainresolver record", - }, - ], - } - `) - }) - it('should return records from a ccip-read name with custom ccipRequest', async () => { - const holeskyWithEns = addEnsContracts(holesky) - const holeskyPublicClientWithCustomCcipRequest = createPublicClient({ - chain: holeskyWithEns, - transport: http('https://holesky.gateway.tenderly.co'), - ccipRead: { - request: ccipRequest(holeskyWithEns), - }, - }) - const result = await getRecords( - holeskyPublicClientWithCustomCcipRequest, - { - name: 'offchainexample.eth', - texts: ['email', 'description'], - contentHash: true, - coins: ['btc', '60'], - }, - ) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 0, - "name": "btc", - "value": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", - }, - { - "id": 60, - "name": "eth", - "value": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", - }, - ], - "contentHash": { - "decoded": "bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y", - "protocolType": "ipfs", - }, - "resolverAddress": "0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547", - "texts": [ - { - "key": "email", - "value": "vitalik@ethereum.org", - }, - { - "key": "description", - "value": "hello offchainresolver record", - }, - ], - } - `) - }) - it('should return records from a ccip-read name with incompliant resolver', async () => { - const result = await getRecords(mainnetPublicClient, { - name: 'alisha.beam.eco', - texts: ['email', 'description'], - contentHash: true, - coins: ['ltc', '60'], - }) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 60, - "name": "eth", - "value": "0x3A8C8D374AD15fE43E6239F6C694bff9Ee4CBbbf", - }, - ], - "contentHash": null, - "resolverAddress": "0x244fE34a508E16E12A221332f63E3a741C759D76", - "texts": [], - } - `) - }) - }) - describe('batch', () => { - it('allows batch ccip', async () => { - const result = await batch( - holeskyPublicClient, - getAddressRecord.batch({ name: 'offchainexample.eth' }), - getAddressRecord.batch({ name: 'offchainexample.eth', coin: 'btc' }), - getText.batch({ name: 'offchainexample.eth', key: 'email' }), - ) - expect(result).toMatchInlineSnapshot(` - [ - { - "id": 60, - "name": "eth", - "value": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", - }, - { - "id": 0, - "name": "btc", - "value": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", - }, - "vitalik@ethereum.org", - ] - `) - }) - it('allows nested batch ccip', async () => { - const result = await batch( - holeskyPublicClient, - batch.batch(getAddressRecord.batch({ name: 'offchainexample.eth' })), - ) - expect(result).toMatchInlineSnapshot(` - [ - [ - { - "id": 60, - "name": "eth", - "value": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", - }, - ], - ] - `) - }) - }) -}) diff --git a/packages/ensjs/src/functions/public/getAbiRecord.ts b/packages/ensjs/src/functions/public/getAbiRecord.ts deleted file mode 100644 index 6c37f1a92..000000000 --- a/packages/ensjs/src/functions/public/getAbiRecord.ts +++ /dev/null @@ -1,87 +0,0 @@ -import type { BaseError, Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import type { - GenericPassthrough, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import _getAbi, { - type InternalGetAbiParameters, - type InternalGetAbiReturnType, -} from './_getAbi.js' -import universalWrapper from './universalWrapper.js' - -export type GetAbiRecordParameters = Prettify< - InternalGetAbiParameters & { - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] - } -> - -export type GetAbiRecordReturnType = Prettify - -const encode = ( - client: ClientWithEns, - { - name, - supportedContentTypes, - gatewayUrls, - }: Omit, -): SimpleTransactionRequest => { - const prData = _getAbi.encode(client, { name, supportedContentTypes }) - return universalWrapper.encode(client, { - name, - data: prData.data, - gatewayUrls, - }) -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { - strict, - gatewayUrls, - }: Pick, -): Promise => { - const urData = await universalWrapper.decode(client, data, passthrough, { - strict, - gatewayUrls, - }) - if (!urData) return null - return _getAbi.decode(client, urData.data, { strict }) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the ABI record for a name - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetAbiRecordParameters} - * @returns ABI record for the name, or `null` if not found. {@link GetAbiRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getAbiRecord } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getAbiRecord(client, { name: 'ens.eth' }) - * // TODO: real example - */ -const getAbiRecord = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name, strict, gatewayUrls, supportedContentTypes }: GetAbiRecordParameters, -) => Promise) & - BatchableFunctionObject - -export default getAbiRecord diff --git a/packages/ensjs/src/functions/public/getAddressRecord.ts b/packages/ensjs/src/functions/public/getAddressRecord.ts deleted file mode 100644 index 2594ab62d..000000000 --- a/packages/ensjs/src/functions/public/getAddressRecord.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { BaseError, Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import type { - GenericPassthrough, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import _getAddr, { - type InternalGetAddrParameters, - type InternalGetAddrReturnType, -} from './_getAddr.js' -import universalWrapper from './universalWrapper.js' - -export type GetAddressRecordParameters = Prettify< - InternalGetAddrParameters & { - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] - } -> - -export type GetAddressRecordReturnType = Prettify - -const encode = ( - client: ClientWithEns, - { - name, - coin, - gatewayUrls, - }: Omit, -): SimpleTransactionRequest => { - const prData = _getAddr.encode(client, { name, coin }) - return universalWrapper.encode(client, { - name, - data: prData.data, - gatewayUrls, - }) -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { - coin, - strict, - gatewayUrls, - }: Pick, -): Promise => { - const urData = await universalWrapper.decode(client, data, passthrough, { - strict, - gatewayUrls, - }) - if (!urData) return null - return _getAddr.decode(client, urData.data, { coin, strict }) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets an address record for a name and specified coin - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetAddressRecordParameters} - * @returns Coin value object, or `null` if not found. {@link GetAddressRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getAddressRecord } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getAddressRecord(client, { name: 'ens.eth', coin: 'ETH' }) - * // { id: 60, name: 'ETH , value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' } - */ -const getAddressRecord = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name, coin, bypassFormat, strict, gatewayUrls }: GetAddressRecordParameters, -) => Promise) & - BatchableFunctionObject - -export default getAddressRecord diff --git a/packages/ensjs/src/functions/public/getAvailable.ts b/packages/ensjs/src/functions/public/getAvailable.ts deleted file mode 100644 index c05228241..000000000 --- a/packages/ensjs/src/functions/public/getAvailable.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - labelhash, -} from 'viem' -import { baseRegistrarAvailableSnippet } from '../../contracts/baseRegistrar.js' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { UnsupportedNameTypeError } from '../../errors/general.js' -import type { SimpleTransactionRequest } from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { getNameType } from '../../utils/getNameType.js' - -export type GetAvailableParameters = { - /** Name to check availability for, only compatible for eth 2ld */ - name: string -} - -export type GetAvailableReturnType = boolean - -const encode = ( - client: ClientWithEns, - { name }: GetAvailableParameters, -): SimpleTransactionRequest => { - const labels = name.split('.') - const nameType = getNameType(name) - if (nameType !== 'eth-2ld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld'], - details: 'Currently only eth-2ld names can be checked for availability', - }) - - return { - to: getChainContractAddress({ - client, - contract: 'ensBaseRegistrarImplementation', - }), - data: encodeFunctionData({ - abi: baseRegistrarAvailableSnippet, - functionName: 'available', - args: [BigInt(labelhash(labels[0]))], - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, -): Promise => { - if (typeof data === 'object') throw data - const result = decodeFunctionResult({ - abi: baseRegistrarAvailableSnippet, - functionName: 'available', - data, - }) - return result -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the availability of a name to register - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetAvailableParameters} - * @returns Availability as boolean. {@link GetAvailableReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getAvailable } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getAvailable(client, { name: 'ens.eth' }) - * // false - */ -const getAvailable = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name }: GetAvailableParameters, -) => Promise) & - BatchableFunctionObject - -export default getAvailable diff --git a/packages/ensjs/src/functions/public/getContentHashRecord.test.ts b/packages/ensjs/src/functions/public/getContentHashRecord.test.ts deleted file mode 100644 index cc4c91cfb..000000000 --- a/packages/ensjs/src/functions/public/getContentHashRecord.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { RawContractError } from 'viem' -import { describe, expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import { publicClient } from '../../test/addTestContracts.js' -import getContentHashRecord from './getContentHashRecord.js' - -describe('getContentHashRecord', () => { - it('should return null for a non-existent name', async () => { - const result = await getContentHashRecord(publicClient, { - name: 'test123123cool.eth', - }) - expect(result).toBeNull() - }) - it('should return null for a name with no contenthash record', async () => { - const result = await getContentHashRecord(publicClient, { - name: 'with-profile.eth', - }) - expect(result).toBeNull() - }) - it('should return the contenthash for a name with the record set', async () => { - const result = await getContentHashRecord(publicClient, { - name: 'with-contenthash.eth', - }) - expect(result).toMatchInlineSnapshot(` - { - "decoded": "bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y", - "protocolType": "ipfs", - } - `) - }) - it('should return null on error when strict is false', async () => { - await expect( - getContentHashRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() - }) - it('should throw on error when strict is true', async () => { - await expect( - getContentHashRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] - `) - }) -}) diff --git a/packages/ensjs/src/functions/public/getContentHashRecord.ts b/packages/ensjs/src/functions/public/getContentHashRecord.ts deleted file mode 100644 index 952ca2d15..000000000 --- a/packages/ensjs/src/functions/public/getContentHashRecord.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { BaseError, Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import type { - GenericPassthrough, - Prettify, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import _getContentHash, { - type InternalGetContentHashParameters, - type InternalGetContentHashReturnType, -} from './_getContentHash.js' -import universalWrapper from './universalWrapper.js' - -export type GetContentHashRecordParameters = Prettify< - InternalGetContentHashParameters & { - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] - } -> - -export type GetContentHashRecordReturnType = - Prettify - -const encode = ( - client: ClientWithEns, - { name, gatewayUrls }: Omit, -): TransactionRequestWithPassthrough => { - const prData = _getContentHash.encode(client, { name }) - return universalWrapper.encode(client, { - name, - data: prData.data, - gatewayUrls, - }) -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { - strict, - gatewayUrls, - }: Pick, -): Promise => { - const urData = await universalWrapper.decode(client, data, passthrough, { - strict, - gatewayUrls, - }) - if (!urData) return null - return _getContentHash.decode(client, urData.data, { strict }) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the content hash record for a name - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetContentHashRecordParameters} - * @returns Content hash object, or `null` if not found. {@link GetContentHashRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getContentHashRecord } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getContentHashRecord(client, { name: 'ens.eth' }) - * // { protocolType: 'ipfs', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } - */ -const getContentHashRecord = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name, strict, gatewayUrls }: GetContentHashRecordParameters, -) => Promise) & - BatchableFunctionObject - -export default getContentHashRecord diff --git a/packages/ensjs/src/functions/public/getExpiry.test.ts b/packages/ensjs/src/functions/public/getExpiry.test.ts deleted file mode 100644 index d1354791e..000000000 --- a/packages/ensjs/src/functions/public/getExpiry.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getExpiry from './getExpiry.js' - -describe('getExpiry', () => { - it('should get the expiry for a .eth name with no other args', async () => { - const result = await getExpiry(publicClient, { name: 'with-profile.eth' }) - expect(result).toBeTruthy() - if (result) { - const { expiry, gracePeriod, status } = result - expect(expiry.date).toBeInstanceOf(Date) - expect(typeof expiry.value).toBe('bigint') - expect(gracePeriod).toBe(7776000) - expect(status).toBe('active') - } - }) - it('should get the expiry for a wrapped name', async () => { - const result = await getExpiry(publicClient, { - name: 'wrapped.eth', - contract: 'nameWrapper', - }) - - expect(result).toBeTruthy() - if (result) { - const { expiry, gracePeriod, status } = result - expect(expiry.date).toBeInstanceOf(Date) - expect(typeof expiry.value).toBe('bigint') - expect(gracePeriod).toBe(0) - expect(status).toBe('active') - } - }) - it('should return null for a non .eth name if not wrapped', async () => { - const result = await getExpiry(publicClient, { - name: 'sub.with-profile.eth', - }) - expect(result).toBeNull() - }) - it('should throw an error for a non .eth name if registrar is specified', async () => { - try { - await getExpiry(publicClient, { - name: 'sub.with-profile.eth', - contract: 'registrar', - }) - expect(false).toBeTruthy() - } catch { - expect(true).toBeTruthy() - } - }) -}) diff --git a/packages/ensjs/src/functions/public/getExpiry.ts b/packages/ensjs/src/functions/public/getExpiry.ts deleted file mode 100644 index 19192c11e..000000000 --- a/packages/ensjs/src/functions/public/getExpiry.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - labelhash, -} from 'viem' -import { - baseRegistrarGracePeriodSnippet, - baseRegistrarNameExpiresSnippet, -} from '../../contracts/baseRegistrar.js' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { multicallGetCurrentBlockTimestampSnippet } from '../../contracts/multicall.js' -import { nameWrapperGetDataSnippet } from '../../contracts/nameWrapper.js' -import type { - DateWithValue, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { makeSafeSecondsDate } from '../../utils/makeSafeSecondsDate.js' -import { namehash } from '../../utils/normalise.js' -import { checkIsDotEth } from '../../utils/validation.js' -import multicallWrapper from './multicallWrapper.js' - -type ContractOption = 'registrar' | 'nameWrapper' -type ExpiryStatus = 'active' | 'expired' | 'gracePeriod' - -export type GetExpiryParameters = Prettify<{ - /** Name to get expiry for */ - name: string - /** Optional specific contract to use to get expiry */ - contract?: ContractOption -}> - -export type GetExpiryReturnType = Prettify<{ - /** Expiry value */ - expiry: DateWithValue - /** Grace period value (in seconds) */ - gracePeriod: number - /** Status of name */ - status: ExpiryStatus -} | null> - -const getContractToUse = ( - contract: ContractOption | undefined, - labels: string[], -) => { - if (contract) return contract - if (checkIsDotEth(labels)) { - return 'registrar' - } - return 'nameWrapper' -} - -const encode = ( - client: ClientWithEns, - { name, contract }: GetExpiryParameters, -): SimpleTransactionRequest => { - const labels = name.split('.') - - const contractToUse = getContractToUse(contract, labels) - - const calls: SimpleTransactionRequest[] = [ - { - to: getChainContractAddress({ client, contract: 'multicall3' }), - data: encodeFunctionData({ - abi: multicallGetCurrentBlockTimestampSnippet, - functionName: 'getCurrentBlockTimestamp', - }), - }, - ] - - if (contractToUse === 'nameWrapper') { - calls.push({ - to: getChainContractAddress({ client, contract: 'ensNameWrapper' }), - data: encodeFunctionData({ - abi: nameWrapperGetDataSnippet, - functionName: 'getData', - args: [BigInt(namehash(labels.join('.')))], - }), - }) - } else { - const baseRegistrarImplementationAddress = getChainContractAddress({ - client, - contract: 'ensBaseRegistrarImplementation', - }) - calls.push({ - to: baseRegistrarImplementationAddress, - data: encodeFunctionData({ - abi: baseRegistrarNameExpiresSnippet, - functionName: 'nameExpires', - args: [BigInt(labelhash(labels[0]))], - }), - }) - calls.push({ - to: baseRegistrarImplementationAddress, - data: encodeFunctionData({ - abi: baseRegistrarGracePeriodSnippet, - functionName: 'GRACE_PERIOD', - }), - }) - } - - return multicallWrapper.encode(client, { transactions: calls }) -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - { name, contract }: GetExpiryParameters, -): Promise => { - if (typeof data === 'object') throw data - const labels = name.split('.') - const result = await multicallWrapper.decode(client, data, []) - - const blockTimestamp = decodeFunctionResult({ - abi: multicallGetCurrentBlockTimestampSnippet, - functionName: 'getCurrentBlockTimestamp', - data: result[0].returnData, - }) - - const contractToUse = getContractToUse(contract, labels) - - let expiry: bigint - let gracePeriod = 0n - - if (contractToUse === 'nameWrapper') { - ;[, , expiry] = decodeFunctionResult({ - abi: nameWrapperGetDataSnippet, - functionName: 'getData', - data: result[1].returnData, - }) - } else { - expiry = decodeFunctionResult({ - abi: baseRegistrarNameExpiresSnippet, - functionName: 'nameExpires', - data: result[1].returnData, - }) - gracePeriod = decodeFunctionResult({ - abi: baseRegistrarGracePeriodSnippet, - functionName: 'GRACE_PERIOD', - data: result[2].returnData, - }) - } - - if (expiry === 0n) { - return null - } - - let status: ExpiryStatus = 'active' - - if (blockTimestamp > expiry + gracePeriod) { - status = 'expired' - } else if (blockTimestamp > expiry) { - status = 'gracePeriod' - } - - return { - expiry: { - date: makeSafeSecondsDate(expiry), - value: expiry, - }, - gracePeriod: Number(gracePeriod), - status, - } -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the expiry for a name - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetExpiryParameters} - * @returns Expiry object, or `null` if no expiry. {@link GetExpiryReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getExpiry } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getExpiry(client, { name: 'ens.eth' }) - * // { expiry: { date: Date, value: 1913933217n }, gracePeriod: 7776000, status: 'active' } - */ -const getExpiry = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name, contract }: GetExpiryParameters, -) => Promise) & - BatchableFunctionObject - -export default getExpiry diff --git a/packages/ensjs/src/functions/public/getName.test.ts b/packages/ensjs/src/functions/public/getName.test.ts deleted file mode 100644 index 954f1ab79..000000000 --- a/packages/ensjs/src/functions/public/getName.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { type Address, type Hex, RawContractError } from 'viem' -import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import { - deploymentAddresses, - publicClient, - testClient, - waitForTransaction, - walletClient, -} from '../../test/addTestContracts.js' -import createSubname from '../wallet/createSubname.js' -import setAddressRecord from '../wallet/setAddressRecord.js' -import setPrimaryName from '../wallet/setPrimaryName.js' -import getName from './getName.js' - -let snapshot: Hex -let accounts: Address[] - -beforeAll(async () => { - accounts = await walletClient.getAddresses() -}) - -beforeEach(async () => { - snapshot = await testClient.snapshot() -}) - -afterEach(async () => { - await testClient.revert({ id: snapshot }) -}) - -describe('getName', () => { - it('should get a primary name from an address', async () => { - const result = await getName(publicClient, { - address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC', - }) - expect(result).toMatchInlineSnapshot(` - { - "match": true, - "name": "with-profile.eth", - "resolverAddress": "${deploymentAddresses.LegacyPublicResolver}", - "reverseResolverAddress": "${deploymentAddresses.PublicResolver}", - } - `) - }) - it('should return null for an address with no primary name', async () => { - const result = await getName(publicClient, { - address: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0', - }) - expect(result).toBeNull() - }) - it('should return null for a name with no forward resolution when allowMismatch is false', async () => { - const tx = await setPrimaryName(walletClient, { - name: 'with-profile.eth', - account: accounts[0], - }) - await waitForTransaction(tx) - - const result = await getName(publicClient, { - address: accounts[0], - }) - expect(result).toBeNull() - }) - it('should return with a false match for a name with no forward resolution when allowMismatch is true', async () => { - const tx = await setPrimaryName(walletClient, { - name: 'with-profile.eth', - account: accounts[0], - }) - await waitForTransaction(tx) - - const result = await getName(publicClient, { - address: accounts[0], - allowMismatch: true, - }) - expect(result).toMatchInlineSnapshot(` - { - "match": false, - "name": "with-profile.eth", - "resolverAddress": "${deploymentAddresses.LegacyPublicResolver}", - "reverseResolverAddress": "${deploymentAddresses.PublicResolver}", - } - `) - }) - it('should return null on error when strict is false', async () => { - await expect( - getName.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { address: accounts[0], allowMismatch: true, strict: false }, - ), - ).resolves.toBeNull() - }) - it('should throw on error when strict is true', async () => { - await expect( - getName.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x'], - }, - - { address: accounts[0], allowMismatch: true, strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "reverse" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: reverse(bytes reverseName) - args: (0x) - - Version: viem@2.30.6] - `) - }) - it('should not return unnormalised name', async () => { - const tx1 = await createSubname(walletClient, { - name: 'suB.with-profile.eth', - contract: 'registry', - owner: accounts[0], - resolverAddress: deploymentAddresses.PublicResolver, - account: accounts[0], - }) - await waitForTransaction(tx1) - const tx2 = await setAddressRecord(walletClient, { - name: 'suB.with-profile.eth', - coin: 'eth', - resolverAddress: deploymentAddresses.PublicResolver, - value: accounts[0], - account: accounts[0], - }) - await waitForTransaction(tx2) - const tx3 = await setPrimaryName(walletClient, { - name: 'suB.with-profile.eth', - account: accounts[0], - }) - await waitForTransaction(tx3) - - const result = await getName(publicClient, { - address: accounts[0], - }) - - expect(result).toBeNull() - }) -}) diff --git a/packages/ensjs/src/functions/public/getName.ts b/packages/ensjs/src/functions/public/getName.ts deleted file mode 100644 index dfdf8003d..000000000 --- a/packages/ensjs/src/functions/public/getName.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - getAddress, - toHex, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - universalResolverReverseSnippet, - universalResolverReverseWithGatewaysSnippet, -} from '../../contracts/universalResolver.js' -import type { - GenericPassthrough, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { checkSafeUniversalResolverData } from '../../utils/checkSafeUniversalResolverData.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' -import { normalise } from '../../utils/normalise.js' - -export type GetNameParameters = { - /** Address to get name for */ - address: Address - /** Whether or not to allow mismatched forward resolution */ - allowMismatch?: boolean - /** Whether or not to throw decoding errors */ - strict?: boolean - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] -} - -export type GetNameReturnType = { - /** Primary name for address */ - name: string - /** Indicates if forward resolution for name matches address */ - match: boolean - /** Resolver address for reverse node */ - reverseResolverAddress: Address - /** Resolver address for resolved name */ - resolverAddress: Address -} - -const encode = ( - client: ClientWithEns, - { address, gatewayUrls }: Omit, -): TransactionRequestWithPassthrough => { - const reverseNode = `${address.toLowerCase().substring(2)}.addr.reverse` - const to = getChainContractAddress({ - client, - contract: 'ensUniversalResolver', - }) - const args = [toHex(packetToBytes(reverseNode))] as const - - return { - to, - ...(gatewayUrls?.length - ? { - data: encodeFunctionData({ - abi: universalResolverReverseWithGatewaysSnippet, - functionName: 'reverse', - args: [...args, gatewayUrls] as const, - }), - passthrough: { - args: [...args, gatewayUrls], - address: to, - }, - } - : { - data: encodeFunctionData({ - abi: universalResolverReverseSnippet, - functionName: 'reverse', - args, - }), - passthrough: { - args, - address: to, - }, - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { address, allowMismatch, strict, gatewayUrls }: GetNameParameters, -): Promise => { - const isSafe = checkSafeUniversalResolverData(data, { - strict, - abi: gatewayUrls - ? universalResolverReverseWithGatewaysSnippet - : universalResolverReverseSnippet, - args: passthrough.args, - functionName: 'reverse', - address: passthrough.address, - }) - if (!isSafe) return null - - try { - const [ - unnormalisedName, - forwardResolvedAddress, - reverseResolverAddress, - resolverAddress, - ] = decodeFunctionResult({ - abi: universalResolverReverseSnippet, - functionName: 'reverse', - data, - }) - if (!unnormalisedName) return null - const match = getAddress(forwardResolvedAddress) === getAddress(address) - if (!match && !allowMismatch) return null - const normalisedName = normalise(unnormalisedName) - return { - name: normalisedName, - match, - reverseResolverAddress, - resolverAddress, - } - } catch (error) { - if (strict) throw error - return null - } -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the primary name for an address - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetNameParameters} - * @returns Name data object, or `null` if no primary name is set. {@link GetNameReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getName } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getName(client, { address: '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' }) - * // { name: 'nick.eth', match: true, reverseResolverAddress: '0xa2c122be93b0074270ebee7f6b7292c7deb45047', resolverAddress: '0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41' } - */ -const getName = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { address, allowMismatch, strict }: GetNameParameters, -) => Promise) & - BatchableFunctionObject - -export default getName diff --git a/packages/ensjs/src/functions/public/getOwner.ts b/packages/ensjs/src/functions/public/getOwner.ts deleted file mode 100644 index b3b572f4d..000000000 --- a/packages/ensjs/src/functions/public/getOwner.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeAbiParameters, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import type { SimpleTransactionRequest } from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { namehash as makeNamehash } from '../../utils/normalise.js' -import { - type OwnerContract, - ownerFromContract, -} from '../../utils/ownerFromContract.js' -import { checkIsDotEth } from '../../utils/validation.js' -import multicallWrapper from './multicallWrapper.js' - -export type GetOwnerParameters< - TContract extends OwnerContract | undefined = undefined, -> = { - /** Name to get owner for */ - name: string - /** Optional specific contract to get ownership value from */ - contract?: TContract -} - -type BaseGetOwnerReturnType = { - /** Owner of the name */ - owner?: Address | null - /** Registrant of the name (registrar owner) */ - registrant?: Address | null - /** The contract level that the ownership is on */ - ownershipLevel: 'registry' | 'registrar' | 'nameWrapper' -} - -type RegistrarOnlyOwnership = { - owner?: never - registrant: Address - ownershipLevel: 'registrar' -} - -type WrappedOwnership = { - owner: Address - registrant?: never - ownershipLevel: 'nameWrapper' -} - -type UnwrappedEth2ldOwnership = { - registrant: Address | null - owner: Address - ownershipLevel: 'registrar' -} - -type UnwrappedOwnership = { - owner: Address - registrant?: never - ownershipLevel: 'registry' -} - -export type GetOwnerReturnType< - TContract extends OwnerContract | undefined = undefined, -> = - | (BaseGetOwnerReturnType & - (TContract extends 'registrar' - ? RegistrarOnlyOwnership - : TContract extends 'nameWrapper' - ? WrappedOwnership - : TContract extends 'registry' - ? UnwrappedOwnership - : WrappedOwnership | UnwrappedEth2ldOwnership | UnwrappedOwnership)) - | null - -const encode = ( - client: ClientWithEns, - { name, contract }: GetOwnerParameters, -): SimpleTransactionRequest => { - const namehash = makeNamehash(name) - const labels = name.split('.') - - if (contract || labels.length === 1) { - return ownerFromContract({ - client, - contract: contract || 'registry', - namehash, - labels, - }) - } - - const registryData = ownerFromContract({ - client, - contract: 'registry', - namehash, - }) - const nameWrapperData = ownerFromContract({ - client, - contract: 'nameWrapper', - namehash, - }) - - const data: { to: Address; data: Hex }[] = [registryData, nameWrapperData] - - if (checkIsDotEth(labels)) { - data.push(ownerFromContract({ client, contract: 'registrar', labels })) - } - - return multicallWrapper.encode(client, { transactions: data }) -} - -const addressDecode = (data: Hex) => - decodeAbiParameters([{ type: 'address' }], data)[0] as Address - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - { name, contract }: GetOwnerParameters, -): Promise> => { - if (typeof data === 'object') throw data - const labels = name.split('.') - if (contract || labels.length === 1) { - const singleOwner = addressDecode(data) - if (contract === 'registrar') { - return { - ownershipLevel: 'registrar', - registrant: singleOwner, - } as GetOwnerReturnType - } - return { - ownershipLevel: contract || 'registry', - owner: singleOwner, - } as GetOwnerReturnType - } - - const result = await multicallWrapper.decode(client, data, []) - - const [registryOwner, nameWrapperOwner, registrarOwner] = [ - result[0].returnData, - result[1].returnData, - result[2]?.returnData, - ].map((ret) => (ret && ret !== '0x' ? addressDecode(ret) : undefined)) as [ - Address, - Address, - Address | undefined, - ] - - const nameWrapperAddress = getChainContractAddress({ - client, - contract: 'ensNameWrapper', - }) - - // check for only .eth names - if (labels[labels.length - 1] === 'eth') { - // if the owner on the registrar is the namewrapper, then the namewrapper owner is the owner - // there is no "registrant" for wrapped names - if (registrarOwner === nameWrapperAddress) { - return { - owner: nameWrapperOwner, - ownershipLevel: 'nameWrapper', - } as GetOwnerReturnType - } - // if there is a registrar owner, then it's not a subdomain but we have also passed the namewrapper clause - // this means that it's an unwrapped second-level name - // the registrant is the owner of the NFT - // the owner is the controller of the records - if (registrarOwner) { - return { - registrant: registrarOwner!, - owner: registryOwner!, - ownershipLevel: 'registrar', - } as GetOwnerReturnType - } - if (registryOwner !== EMPTY_ADDRESS) { - // if there is no registrar owner, but the label length is two, then the domain is an expired 2LD .eth - // so we still want to return the ownership values - if (labels.length === 2) { - return { - registrant: null, - owner: registryOwner, - ownershipLevel: 'registrar', - } as GetOwnerReturnType - } - // this means that the subname is wrapped - if ( - registryOwner === nameWrapperAddress && - nameWrapperOwner && - nameWrapperOwner !== EMPTY_ADDRESS - ) { - return { - owner: nameWrapperOwner, - ownershipLevel: 'nameWrapper', - } as GetOwnerReturnType - } - // unwrapped subnames do not have NFTs associated, so do not have a registrant - return { - owner: registryOwner, - ownershipLevel: 'registry', - } as GetOwnerReturnType - } - // .eth names with no registrar owner are either unregistered or expired - return null - } - - // non .eth names inherit the owner from the registry - // there will only ever be an owner for non .eth names, not a registrant - // this is because for unwrapped names, there is no associated NFT - // and for wrapped names, owner and registrant are the same thing - if ( - registryOwner === nameWrapperAddress && - nameWrapperOwner && - nameWrapperOwner !== EMPTY_ADDRESS - ) { - return { - owner: nameWrapperOwner, - ownershipLevel: 'nameWrapper', - } as GetOwnerReturnType - } - - // for unwrapped non .eth names, the owner is the registry owner - if (registryOwner && registryOwner !== EMPTY_ADDRESS) { - return { - owner: registryOwner, - ownershipLevel: 'registry', - } as GetOwnerReturnType - } - - // for anything else, return - return null -} - -type EncoderFunction = typeof encode -type DecoderFunction = typeof decode - -type BatchableFunctionObject = { - encode: EncoderFunction - decode: DecoderFunction - batch: < - TContract extends OwnerContract | undefined = undefined, - TParams extends - GetOwnerParameters = GetOwnerParameters, - >( - args: TParams, - ) => { - args: [TParams] - encode: EncoderFunction - decode: typeof decode - } -} - -/** - * Gets the owner(s) of a name. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetOwnerParameters} - * @returns Owner data object, or `null` if no owners exist. {@link GetOwnerReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getOwner } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getOwner(client, { name: 'ens.eth' }) - * // { owner: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', registrant: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', ownershipLevel: 'registrar } - */ -const getOwner = generateFunction({ encode, decode }) as (< - TContract extends OwnerContract | undefined = undefined, ->( - client: ClientWithEns, - { name, contract }: GetOwnerParameters, -) => Promise>) & - BatchableFunctionObject - -export default getOwner diff --git a/packages/ensjs/src/functions/public/getPrice.test.ts b/packages/ensjs/src/functions/public/getPrice.test.ts deleted file mode 100644 index 5c9acc0da..000000000 --- a/packages/ensjs/src/functions/public/getPrice.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { publicClient } from '../../test/addTestContracts.js' -import getPrice from './getPrice.js' - -const yearCost = BigInt('8561643835626') - -describe('getPrice', () => { - it('should return a base and premium price for a name', async () => { - const result = await getPrice(publicClient, { - nameOrNames: 'test123.eth', - duration: 86400, - }) - expect(result).toBeTruthy() - if (result) { - const { base, premium } = result - expect(base).toBe(yearCost) - expect(premium).toBe(0n) - } - }) - - it('should return a base and premium price for an array of names', async () => { - const result = await getPrice(publicClient, { - nameOrNames: ['test123.eth', 'to-be-renewed.eth'], - duration: 86400, - }) - expect(result).toBeTruthy() - if (result) { - const { base, premium } = result - expect(base).toBe(yearCost * 2n) - expect(premium).toBe(0n) - } - }) - - it('should allow labels as inputs', async () => { - const result = await getPrice(publicClient, { - nameOrNames: 'test123', - duration: 86400, - }) - expect(result).toBeTruthy() - if (result) { - const { base, premium } = result - expect(base).toBe(yearCost) - expect(premium).toBe(0n) - } - }) -}) diff --git a/packages/ensjs/src/functions/public/getPrice.ts b/packages/ensjs/src/functions/public/getPrice.ts deleted file mode 100644 index 4b6aaca0d..000000000 --- a/packages/ensjs/src/functions/public/getPrice.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, -} from 'viem' -import { bulkRenewalRentPriceSnippet } from '../../contracts/bulkRenewal.js' -import type { ClientWithEns } from '../../contracts/consts.js' -import { ethRegistrarControllerRentPriceSnippet } from '../../contracts/ethRegistrarController.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { UnsupportedNameTypeError } from '../../errors/general.js' -import type { SimpleTransactionRequest } from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { getNameType } from '../../utils/getNameType.js' -import multicallWrapper from './multicallWrapper.js' - -export type GetPriceParameters = { - /** Name, or array of names, to get price for */ - nameOrNames: string | string[] - /** Duration in seconds to get price for */ - duration: bigint | number -} - -export type GetPriceReturnType = { - /** Price base value */ - base: bigint - /** Price premium */ - premium: bigint -} - -const encode = ( - client: ClientWithEns, - { nameOrNames, duration }: GetPriceParameters, -): SimpleTransactionRequest => { - const names = (Array.isArray(nameOrNames) ? nameOrNames : [nameOrNames]).map( - (name) => { - const labels = name.split('.') - const nameType = getNameType(name) - if (nameType !== 'eth-2ld' && nameType !== 'tld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld', 'tld'], - details: 'Currently only the price of eth-2ld names can be fetched', - }) - return labels[0] - }, - ) - - if (names.length > 1) { - const bulkRenewalAddress = getChainContractAddress({ - client, - contract: 'ensBulkRenewal', - }) - return multicallWrapper.encode(client, { - transactions: [ - { - to: bulkRenewalAddress, - data: encodeFunctionData({ - abi: bulkRenewalRentPriceSnippet, - functionName: 'rentPrice', - args: [names, BigInt(duration)], - }), - }, - { - to: bulkRenewalAddress, - data: encodeFunctionData({ - abi: bulkRenewalRentPriceSnippet, - functionName: 'rentPrice', - args: [names, 0n], - }), - }, - ], - }) - } - return { - to: getChainContractAddress({ - client, - contract: 'ensEthRegistrarController', - }), - data: encodeFunctionData({ - abi: ethRegistrarControllerRentPriceSnippet, - functionName: 'rentPrice', - args: [names[0], BigInt(duration)], - }), - } -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - { nameOrNames }: GetPriceParameters, -): Promise => { - if (typeof data === 'object') throw data - const isBulkRenewal = Array.isArray(nameOrNames) && nameOrNames.length > 1 - if (isBulkRenewal) { - const result = await multicallWrapper.decode(client, data, []) - const price = decodeFunctionResult({ - abi: bulkRenewalRentPriceSnippet, - functionName: 'rentPrice', - data: result[0].returnData, - }) - const premium = decodeFunctionResult({ - abi: bulkRenewalRentPriceSnippet, - functionName: 'rentPrice', - data: result[1].returnData, - }) - const base = price - premium - return { base, premium } - } - - return decodeFunctionResult({ - abi: ethRegistrarControllerRentPriceSnippet, - functionName: 'rentPrice', - data, - }) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the price of a name, or array of names, for a given duration. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetPriceParameters} - * @returns Price data object. {@link GetPriceReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getPrice } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getPrice(client, { nameOrNames: 'ens.eth', duration: 31536000 }) - * // { base: 352828971668930335n, premium: 0n } - */ -const getPrice = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { nameOrNames, duration }: GetPriceParameters, -) => Promise) & - BatchableFunctionObject - -export default getPrice diff --git a/packages/ensjs/src/functions/public/getRecords.test.ts b/packages/ensjs/src/functions/public/getRecords.test.ts deleted file mode 100644 index 36dd23064..000000000 --- a/packages/ensjs/src/functions/public/getRecords.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { http, RawContractError, createPublicClient } from 'viem' -import { mainnet } from 'viem/chains' -import { describe, expect, it } from 'vitest' -import { addEnsContracts } from '../../index.js' -import { - deploymentAddresses, - publicClient, -} from '../../test/addTestContracts.js' -import getRecords from './getRecords.js' - -const mainnetPublicClient = createPublicClient({ - chain: addEnsContracts(mainnet), - transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), -}) - -describe('getRecords()', () => { - it('works', async () => { - const result = await getRecords(publicClient, { - name: 'with-profile.eth', - texts: ['description', 'url'], - coins: ['60', 'etcLegacy', '0'], - }) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 60, - "name": "eth", - "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - }, - { - "id": 61, - "name": "etcLegacy", - "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - }, - { - "id": 0, - "name": "btc", - "value": "bc1qjqg9slurvjukfl92wp58y94480fvh4uc2pwa6n", - }, - ], - "resolverAddress": "${deploymentAddresses.LegacyPublicResolver}", - "texts": [ - { - "key": "description", - "value": "Hello2", - }, - { - "key": "url", - "value": "twitter.com", - }, - ], - } - `) - }) - it('works with oldest resolver', async () => { - const result = await getRecords(publicClient, { - name: 'with-oldest-resolver.eth', - texts: ['description', 'url'], - coins: ['60', 'etcLegacy', '0'], - }) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 60, - "name": "eth", - "value": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", - }, - ], - "resolverAddress": "${deploymentAddresses.OldestResolver}", - "texts": [], - } - `) - }) - it('works with oldest resolver - jessesum.eth', async () => { - const result = await getRecords(mainnetPublicClient, { - name: 'jessesum.eth', - texts: ['description', 'url'], - coins: ['60', 'etcLegacy', '0'], - }) - expect(result).toMatchInlineSnapshot(` - { - "coins": [ - { - "id": 60, - "name": "eth", - "value": "0x8c4Eb6988A199DAbcae0Ce31052b3f3aC591787e", - }, - ], - "resolverAddress": "0x1da022710dF5002339274AaDEe8D58218e9D6AB5", - "texts": [], - } - `) - }) - - it('returns null results when known resolver error', async () => { - await expect( - getRecords.decode( - publicClient, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - calls: [ - { type: 'coin', key: 60, call: { to: '0x1234', data: '0x5678' } }, - ], - }, - { name: 'test.eth', coins: [60] }, - ), - ).resolves.toMatchInlineSnapshot(` - { - "coins": [], - "resolverAddress": "0x0000000000000000000000000000000000000000", - } - `) - }) - - it('throws when unknown resolver error', async () => { - await expect( - getRecords.decode( - publicClient, - new RawContractError({ - data: '0x4ced43fb', // SwagError() - }), - { - calls: [ - { type: 'coin', key: 60, call: { to: '0x1234', data: '0x5678' } }, - ], - address: '0x1234', - args: ['0x04746573740365746800', ['0x5678']], - }, - { name: 'test.eth', coins: [60] }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted with the following signature: - 0x4ced43fb - - Unable to decode signature "0x4ced43fb" as it was not found on the provided ABI. - Make sure you are using the correct ABI and that the error exists on it. - You can look up the decoded signature here: https://openchain.xyz/signatures?query=0x4ced43fb. - - Contract Call: - address: 0x1234 - function: resolve(bytes name, bytes[] data) - args: (0x04746573740365746800, ["0x5678"]) - - Docs: https://viem.sh/docs/contract/decodeErrorResult - Version: viem@2.30.6] - `) - }) -}) diff --git a/packages/ensjs/src/functions/public/getRecords.ts b/packages/ensjs/src/functions/public/getRecords.ts deleted file mode 100644 index d9706b2eb..000000000 --- a/packages/ensjs/src/functions/public/getRecords.ts +++ /dev/null @@ -1,497 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeAbiParameters, - decodeFunctionResult, - encodeFunctionData, - hexToBigInt, - toHex, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - universalResolverResolveArraySnippet, - universalResolverResolveArrayWithGatewaysSnippet, -} from '../../contracts/universalResolver.js' -import type { - DecodedAddr, - DecodedText, - Prettify, - SimpleTransactionRequest, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { checkSafeUniversalResolverData } from '../../utils/checkSafeUniversalResolverData.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' -import _getAbi, { type InternalGetAbiReturnType } from './_getAbi.js' -import _getAddr from './_getAddr.js' -import _getContentHash, { - type InternalGetContentHashReturnType, -} from './_getContentHash.js' -import _getText from './_getText.js' -import multicallWrapper from './multicallWrapper.js' - -export type GetRecordsParameters< - TTexts extends readonly string[] | undefined = readonly string[], - TCoins extends readonly (string | number)[] | undefined = readonly ( - | string - | number - )[], - TContentHash extends boolean | undefined = true, - TAbi extends boolean | undefined = true, -> = { - /** Name to get records for */ - name: string - /** Text record key array */ - texts?: TTexts - /** Coin record id/symbol array */ - coins?: TCoins - /** If true, will fetch content hash */ - contentHash?: TContentHash - /** If true, will fetch ABI */ - abi?: TAbi - /** Optional specific resolver address, for fallback or for all results */ - resolver?: { - /** Resolver address */ - address: Address - /** If true, will only use resolver if main fetch fails */ - fallbackOnly?: boolean - } - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] -} - -type WithContentHashResult = { - /** Retrieved content hash record for name */ - contentHash: InternalGetContentHashReturnType -} - -type WithAbiResult = { - /** Retrieved ABI record for name */ - abi: InternalGetAbiReturnType -} - -type WithTextsResult = { - /** Retrieved text records for name */ - texts: DecodedText[] -} - -type WithCoinsResult = { - /** Retrieved coins for name */ - coins: DecodedAddr[] -} - -export type GetRecordsReturnType< - TTexts extends readonly string[] | undefined = readonly string[], - TCoins extends readonly (string | number)[] | undefined = readonly ( - | string - | number - )[], - TContentHash extends boolean | undefined = true, - TAbi extends boolean | undefined = true, -> = Prettify< - (TContentHash extends true ? WithContentHashResult : {}) & - (TAbi extends true ? WithAbiResult : {}) & - (TTexts extends readonly string[] ? WithTextsResult : {}) & - (TCoins extends readonly (string | number)[] ? WithCoinsResult : {}) & { - /** Resolver address used for fetch */ - resolverAddress: Address - } -> - -type CallObj = - | { - key: string - call: SimpleTransactionRequest - type: 'text' - } - | { - key: string | number - call: SimpleTransactionRequest - type: 'coin' - } - | { - key: 'contentHash' - call: SimpleTransactionRequest - type: 'contentHash' - } - | { - key: 'abi' - call: SimpleTransactionRequest - type: 'abi' - } - -type EncodeReturnType = Required< - TransactionRequestWithPassthrough<{ - calls: (CallObj | null)[] - address?: Address - args?: any - }> -> - -const createCalls = ( - client: ClientWithEns, - { - name, - texts, - coins, - abi, - contentHash, - }: Pick< - GetRecordsParameters, - 'name' | 'texts' | 'coins' | 'abi' | 'contentHash' - >, -) => [ - ...(texts ?? []).map( - (text) => - ({ - key: text, - call: _getText.encode(client, { name, key: text }), - type: 'text', - }) as const, - ), - ...(coins ?? []).map( - (coin) => - ({ - key: coin, - call: _getAddr.encode(client, { name, coin }), - type: 'coin', - }) as const, - ), - ...(contentHash - ? ([ - { - key: 'contentHash', - call: _getContentHash.encode(client, { name }), - type: 'contentHash', - }, - ] as const) - : []), - ...(abi - ? ([ - { key: 'abi', call: _getAbi.encode(client, { name }), type: 'abi' }, - ] as const) - : []), -] - -const encode = ( - client: ClientWithEns, - { - name, - resolver, - texts, - coins, - contentHash, - abi, - gatewayUrls, - }: GetRecordsParameters, -): EncodeReturnType => { - const calls = createCalls(client, { - name, - texts, - coins, - contentHash, - abi, - }) - - if (resolver?.address && !resolver.fallbackOnly) { - const encoded = multicallWrapper.encode(client, { - transactions: calls.map((c) => ({ - to: resolver.address, - data: c.call.data, - })), - }) - return { - ...encoded, - passthrough: { calls }, - } - } - - const to = getChainContractAddress({ - client, - contract: 'ensUniversalResolver', - }) - const args = [ - toHex(packetToBytes(name)), - calls.map((c) => c.call.data), - ] as const - - return { - to, - ...(gatewayUrls - ? { - data: encodeFunctionData({ - abi: universalResolverResolveArrayWithGatewaysSnippet, - functionName: 'resolve', - args: [...args, gatewayUrls] as const, - }), - passthrough: { - calls, - args: [...args, gatewayUrls], - address: to, - }, - } - : { - data: encodeFunctionData({ - abi: universalResolverResolveArraySnippet, - functionName: 'resolve', - args, - }), - passthrough: { - calls, - args, - address: to, - }, - }), - } -} - -const createEmptyResult = < - TTexts extends readonly string[] | undefined, - TCoins extends readonly (string | number)[] | undefined, - TContentHash extends boolean | undefined, - TAbi extends boolean | undefined, ->({ - texts, - coins, - abi, - contentHash, -}: Pick< - GetRecordsParameters, - 'texts' | 'coins' | 'abi' | 'contentHash' ->) => ({ - ...(texts ? { texts: [] as DecodedText[] } : {}), - ...(coins ? { coins: [] as DecodedAddr[] } : {}), - ...(contentHash ? { contentHash: null } : {}), - ...(abi ? { abi: null } : {}), -}) - -const decodeRecord = async ( - client: ClientWithEns, - { item, call }: { item: Hex; call: CallObj }, -) => { - const { key, type } = call - const baseItem = { key, type } - if (type === 'contentHash') { - try { - const decodedFromAbi = decodeAbiParameters( - [{ type: 'bytes' }] as const, - item, - )[0] - if (decodedFromAbi === '0x' || hexToBigInt(decodedFromAbi) === 0n) { - return { ...baseItem, value: null } - } - } catch { - // ignore - } - } - if (type === 'text') { - const decodedFromAbi = await _getText.decode(client, item, { - strict: false, - }) - return { ...baseItem, value: decodedFromAbi } - } - if (type === 'coin') { - const decodedFromAbi = await _getAddr.decode(client, item, { - coin: key, - strict: false, - }) - return { ...baseItem, value: decodedFromAbi } - } - if (type === 'contentHash') { - const decodedFromAbi = await _getContentHash.decode(client, item, { - strict: false, - }) - return { ...baseItem, value: decodedFromAbi } - } - // abi - const decodedFromAbi = await _getAbi.decode(client, item, { - strict: false, - }) - return { ...baseItem, value: decodedFromAbi } -} - -const createRecordResult = ( - prev: GetRecordsReturnType, - curr: Awaited>, -) => { - if (curr.type === 'text' || curr.type === 'coin') { - if (!curr.value) { - return prev - } - } - if (curr.type === 'text') { - return { - ...prev, - texts: [ - ...(prev.texts || []), - { key: curr.key, value: curr.value } as DecodedText, - ], - } - } - if (curr.type === 'coin') { - return { - ...prev, - coins: [...(prev.coins || []), curr.value as DecodedAddr], - } - } - if (curr.type === 'contentHash') { - return { - ...prev, - contentHash: curr.value as InternalGetContentHashReturnType, - } - } - // abi - return { ...prev, abi: curr.value as InternalGetAbiReturnType } -} - -const decode = async < - const TTexts extends readonly string[] | undefined = readonly string[], - const TCoins extends readonly (string | number)[] | undefined = readonly ( - | string - | number - )[], - const TContentHash extends boolean | undefined = undefined, - const TAbi extends boolean | undefined = undefined, ->( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: EncodeReturnType['passthrough'], - { - resolver, - texts, - coins, - contentHash, - abi, - gatewayUrls, - }: GetRecordsParameters, -): Promise> => { - const { calls } = passthrough - let recordData: (Hex | null)[] = [] - let resolverAddress: Address - - const emptyResult = createEmptyResult({ texts, coins, contentHash, abi }) - - if (resolver?.address && !resolver.fallbackOnly) { - const result = await multicallWrapper.decode( - client, - data, - passthrough.calls.filter((c) => c).map((c) => c!.call), - ) - resolverAddress = resolver.address - recordData = result.map((r) => r.returnData) - } else { - const isSafe = checkSafeUniversalResolverData(data, { - strict: false, - abi: gatewayUrls - ? universalResolverResolveArrayWithGatewaysSnippet - : universalResolverResolveArraySnippet, - args: passthrough.args, - functionName: 'resolve', - address: passthrough.address, - }) - - if (!isSafe) - return { - ...emptyResult, - resolverAddress: EMPTY_ADDRESS, - } as GetRecordsReturnType - - const result = decodeFunctionResult({ - abi: universalResolverResolveArraySnippet, - functionName: 'resolve', - data, - }) - ;[, resolverAddress] = result - recordData = result[0].map((item, i) => { - if (!item.success) { - calls[i] = null - return null - } - return item.returnData - }) - } - - const filteredCalls = calls.filter((x) => x) as CallObj[] - const filteredRecordData = recordData.filter((x) => x) as Hex[] - - const decodedRecords = await Promise.all( - filteredRecordData.map(async (item, i) => - decodeRecord(client, { item, call: filteredCalls[i] }), - ), - ) - - const records = decodedRecords.reduce(createRecordResult, { - ...emptyResult, - resolverAddress, - } as GetRecordsReturnType) - - return records as GetRecordsReturnType -} - -type EncoderFunction = typeof encode -type DecoderFunction = typeof decode - -type BatchableFunctionObject = { - encode: EncoderFunction - decode: DecoderFunction - batch: < - const TTexts extends readonly string[] | undefined = undefined, - const TCoins extends readonly (string | number)[] | undefined = undefined, - const TContentHash extends boolean | undefined = undefined, - const TAbi extends boolean | undefined = undefined, - >( - args: GetRecordsParameters, - ) => { - args: [GetRecordsParameters] - encode: EncoderFunction - decode: typeof decode - } -} - -/** - * Gets arbitrary records for a name - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetRecordsParameters} - * @returns Records data object. {@link GetRecordsReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getRecords } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getRecords(client, { - * name: 'ens.eth', - * texts: ['com.twitter', 'com.github'], - * coins: ['ETH'], - * contentHash: true, - * }) - * // { texts: [{ key: 'com.twitter', value: 'ensdomains' }, { key: 'com.github', value: 'ensdomains' }], coins: [{ id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }], contentHash: { protocolType: 'ipns', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } } - */ -const getRecords = generateFunction({ encode, decode }) as (< - const TTexts extends readonly string[] | undefined = undefined, - const TCoins extends readonly (string | number)[] | undefined = undefined, - const TContentHash extends boolean | undefined = undefined, - const TAbi extends boolean | undefined = undefined, ->( - client: ClientWithEns, - { - name, - texts, - coins, - contentHash, - abi, - resolver, - gatewayUrls, - }: GetRecordsParameters, -) => Promise>) & - BatchableFunctionObject - -export default getRecords diff --git a/packages/ensjs/src/functions/public/getResolver.test.ts b/packages/ensjs/src/functions/public/getResolver.test.ts deleted file mode 100644 index de19dd48a..000000000 --- a/packages/ensjs/src/functions/public/getResolver.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - deploymentAddresses, - publicClient, -} from '../../test/addTestContracts.js' -import getResolver from './getResolver.js' - -describe('getResolver', () => { - it('should find the resolver for a name with a resolver', async () => { - const result = await getResolver(publicClient, { name: 'with-profile.eth' }) - expect(result).toBe(deploymentAddresses.LegacyPublicResolver) - }) -}) diff --git a/packages/ensjs/src/functions/public/getResolver.ts b/packages/ensjs/src/functions/public/getResolver.ts deleted file mode 100644 index 05ec00d61..000000000 --- a/packages/ensjs/src/functions/public/getResolver.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - getContractError, - toHex, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { universalResolverFindResolverSnippet } from '../../contracts/universalResolver.js' -import type { - GenericPassthrough, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' - -export type GetResolverParameters = { - /** Name to get resolver for */ - name: string -} - -export type GetResolverReturnType = Address | null - -const encode = ( - client: ClientWithEns, - { name }: GetResolverParameters, -): TransactionRequestWithPassthrough => { - const address = getChainContractAddress({ - client, - contract: 'ensUniversalResolver', - }) - const args = [toHex(packetToBytes(name))] as const - return { - to: address, - data: encodeFunctionData({ - abi: universalResolverFindResolverSnippet, - functionName: 'findResolver', - args, - }), - passthrough: { address, args }, - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, -): Promise => { - if (typeof data === 'object') - throw getContractError(data, { - abi: universalResolverFindResolverSnippet, - functionName: 'findResolver', - args: passthrough.args, - address: passthrough.address, - }) as BaseError - const response = decodeFunctionResult({ - abi: universalResolverFindResolverSnippet, - functionName: 'findResolver', - data, - }) - - if (response[0] === EMPTY_ADDRESS) return null - - return response[0] -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the resolver address for a name. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetResolverParameters} - * @returns Resolver address, or null if none is found. {@link GetResolverReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getResolver } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getResolver(client, { name: 'ens.eth' }) - * // 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 - */ -const getResolver = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name }: GetResolverParameters, -) => Promise) & - BatchableFunctionObject - -export default getResolver diff --git a/packages/ensjs/src/functions/public/getSupportedInterfaces.ts b/packages/ensjs/src/functions/public/getSupportedInterfaces.ts deleted file mode 100644 index fbbef1ed6..000000000 --- a/packages/ensjs/src/functions/public/getSupportedInterfaces.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - encodeFunctionData, - padHex, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { erc165SupportsInterfaceSnippet } from '../../contracts/erc165.js' -import type { - SimpleTransactionRequest, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { generateFunction } from '../../utils/generateFunction.js' -import multicallWrapper from './multicallWrapper.js' - -export type GetSupportedInterfacesParameters< - TInterfaces extends readonly Hex[], -> = { - address: Address - interfaces: TInterfaces -} - -export type GetSupportedInterfacesReturnType< - TInterfaces extends readonly Hex[], -> = { - -readonly [K in keyof TInterfaces]: boolean -} - -const encodeInterface = (interfaceId: Hex): Hex => - encodeFunctionData({ - abi: erc165SupportsInterfaceSnippet, - functionName: 'supportsInterface', - args: [interfaceId], - }) - -const encode = ( - client: ClientWithEns, - { address, interfaces }: GetSupportedInterfacesParameters, -): TransactionRequestWithPassthrough => { - const calls = interfaces.map((interfaceId) => ({ - to: address, - data: encodeInterface(interfaceId), - })) - const encoded = multicallWrapper.encode(client, { - transactions: calls, - }) - return { - ...encoded, - passthrough: calls, - } -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: SimpleTransactionRequest[], -): Promise> => { - if (typeof data === 'object') throw data - const result = await multicallWrapper.decode(client, data, passthrough) - return result.map( - (r) => r.success && r.returnData === padHex('0x01'), - ) as GetSupportedInterfacesReturnType -} - -type EncoderFunction = typeof encode -type DecoderFunction = typeof decode - -type BatchableFunctionObject = { - encode: EncoderFunction - decode: DecoderFunction - batch: < - const TInterfaces extends readonly Hex[], - TParams extends GetSupportedInterfacesParameters, - >( - args: TParams, - ) => { - args: [TParams] - encode: EncoderFunction - decode: typeof decode - } -} - -/** - * Gets the supported interfaces for any contract address. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetSupportedInterfacesParameters} - * @returns Array of booleans matching the input array {@link GetSupportedInterfacesReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getSupportedInterfaces } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getSupportedInterfaces(client, { - * address: '0x58774Bb8acD458A640aF0B88238369A167546ef2', - * interfaces: ['0x2f435428', '0x23b872dd'], - * }) - * // [true, false] - */ -const getSupportedInterfaces = generateFunction({ encode, decode }) as (< - const TInterfaces extends readonly Hex[], ->( - client: ClientWithEns, - { address, interfaces }: GetSupportedInterfacesParameters, -) => Promise>) & - BatchableFunctionObject - -export default getSupportedInterfaces diff --git a/packages/ensjs/src/functions/public/getTextRecord.test.ts b/packages/ensjs/src/functions/public/getTextRecord.test.ts deleted file mode 100644 index efd656567..000000000 --- a/packages/ensjs/src/functions/public/getTextRecord.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { RawContractError } from 'viem' -import { describe, expect, it } from 'vitest' -import type { ClientWithEns } from '../../contracts/consts.js' -import { publicClient } from '../../test/addTestContracts.js' -import getTextRecord from './getTextRecord.js' - -describe('getTextRecord()', () => { - it('should return a record from a key', async () => { - const result = await getTextRecord(publicClient, { - name: 'with-profile.eth', - key: 'description', - }) - expect(result).toBe('Hello2') - }) - - it('should return null for a non-existent key', async () => { - const result = await getTextRecord(publicClient, { - name: 'with-profile.eth', - key: 'thiskeydoesntexist', - }) - expect(result).toBeNull() - }) - it('should return null on error when strict is false', async () => { - await expect( - getTextRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() - }) - it('should throw on error when strict is true', async () => { - await expect( - getTextRecord.decode( - {} as ClientWithEns, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] - `) - }) -}) diff --git a/packages/ensjs/src/functions/public/getTextRecord.ts b/packages/ensjs/src/functions/public/getTextRecord.ts deleted file mode 100644 index 21e020661..000000000 --- a/packages/ensjs/src/functions/public/getTextRecord.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { BaseError, Hex } from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import type { - GenericPassthrough, - Prettify, - SimpleTransactionRequest, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import _getText, { - type InternalGetTextParameters, - type InternalGetTextReturnType, -} from './_getText.js' -import universalWrapper from './universalWrapper.js' - -export type GetTextRecordParameters = Prettify< - InternalGetTextParameters & { - /** Batch gateway URLs to use for resolving CCIP-read requests. */ - gatewayUrls?: string[] - } -> - -export type GetTextRecordReturnType = Prettify - -const encode = ( - client: ClientWithEns, - { name, key, gatewayUrls }: Omit, -): SimpleTransactionRequest => { - const prData = _getText.encode(client, { name, key }) - return universalWrapper.encode(client, { - name, - data: prData.data, - gatewayUrls, - }) -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { - strict, - gatewayUrls, - }: Pick, -): Promise => { - const urData = await universalWrapper.decode(client, data, passthrough, { - strict, - gatewayUrls, - }) - if (!urData) return null - return _getText.decode(client, urData.data, { strict }) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets a text record for a name. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetTextRecordParameters} - * @returns Text record string, or null if none is found. {@link GetTextRecordReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getTextRecord } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getTextRecord(client, { name: 'ens.eth', key: 'com.twitter' }) - * // ensdomains - */ -const getTextRecord = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name, key, strict, gatewayUrls }: GetTextRecordParameters, -) => Promise) & - BatchableFunctionObject - -export default getTextRecord diff --git a/packages/ensjs/src/functions/public/getWrapperData.ts b/packages/ensjs/src/functions/public/getWrapperData.ts deleted file mode 100644 index 7ce6c0048..000000000 --- a/packages/ensjs/src/functions/public/getWrapperData.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - getContractError, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperGetDataSnippet } from '../../contracts/nameWrapper.js' -import type { - DateWithValue, - GenericPassthrough, - Prettify, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { type DecodedFuses, decodeFuses } from '../../utils/fuses.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { makeSafeSecondsDate } from '../../utils/makeSafeSecondsDate.js' -import { namehash } from '../../utils/normalise.js' - -export type GetWrapperDataParameters = { - /** Name to get wrapper data for */ - name: string -} - -export type GetWrapperDataReturnType = Prettify<{ - /** Fuse object */ - fuses: DecodedFuses & { - value: number - } - /** Expiry of the name */ - expiry: DateWithValue | null - /** Owner of the name */ - owner: Address -} | null> - -const encode = ( - client: ClientWithEns, - { name }: GetWrapperDataParameters, -): TransactionRequestWithPassthrough => { - const address = getChainContractAddress({ - client, - contract: 'ensNameWrapper', - }) - const args = [BigInt(namehash(name))] as const - return { - to: address, - data: encodeFunctionData({ - abi: nameWrapperGetDataSnippet, - functionName: 'getData', - args, - }), - passthrough: { address, args }, - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, -): Promise => { - if (typeof data === 'object') - throw getContractError(data, { - abi: nameWrapperGetDataSnippet, - functionName: 'getData', - args: passthrough.args, - address: passthrough.address, - }) as BaseError - const [owner, fuses, expiry] = decodeFunctionResult({ - abi: nameWrapperGetDataSnippet, - functionName: 'getData', - data, - }) - - if (owner === EMPTY_ADDRESS) { - return null - } - - const fuseObj = decodeFuses(fuses) - const expiryDate = expiry > 0 ? makeSafeSecondsDate(expiry) : null - - return { - fuses: { - ...fuseObj, - value: fuses, - }, - expiry: expiryDate - ? { - date: expiryDate, - value: expiry, - } - : null, - owner, - } -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the wrapper data for a name. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetWrapperDataParameters} - * @returns Wrapper data object, or null if name is not wrapped. {@link GetWrapperDataReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getWrapperData } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getWrapperData(client, { name: 'ilikelasagna.eth' }) - */ -const getWrapperData = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name }: GetWrapperDataParameters, -) => Promise) & - BatchableFunctionObject - -export default getWrapperData diff --git a/packages/ensjs/src/functions/public/getWrapperName.ts b/packages/ensjs/src/functions/public/getWrapperName.ts deleted file mode 100644 index 9117269e6..000000000 --- a/packages/ensjs/src/functions/public/getWrapperName.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - getContractError, - hexToBytes, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperNamesSnippet } from '../../contracts/nameWrapper.js' -import type { - GenericPassthrough, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { - type GeneratedFunction, - generateFunction, -} from '../../utils/generateFunction.js' -import { bytesToPacket } from '../../utils/hexEncodedName.js' -import { namehash } from '../../utils/normalise.js' - -export type GetWrapperNameParameters = { - /** Name with unknown labels, e.g. "[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth" */ - name: string -} - -export type GetWrapperNameReturnType = string | null - -const encode = ( - client: ClientWithEns, - { name }: GetWrapperNameParameters, -): TransactionRequestWithPassthrough => { - const address = getChainContractAddress({ - client, - contract: 'ensNameWrapper', - }) - const args = [namehash(name)] as const - return { - to: address, - data: encodeFunctionData({ - abi: nameWrapperNamesSnippet, - functionName: 'names', - args, - }), - passthrough: { address, args }, - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, -): Promise => { - if (typeof data === 'object') - throw getContractError(data, { - abi: nameWrapperNamesSnippet, - functionName: 'names', - args: passthrough.args, - address: passthrough.address, - }) as BaseError - const result = decodeFunctionResult({ - abi: nameWrapperNamesSnippet, - functionName: 'names', - data, - }) - if (!result || result === '0x' || BigInt(result) === 0n) return null - return bytesToPacket(hexToBytes(result)) -} - -type BatchableFunctionObject = GeneratedFunction - -/** - * Gets the full name for a name with unknown labels from the NameWrapper. - * @param client - {@link ClientWithEns} - * @param parameters - {@link GetWrapperNameParameters} - * @returns Full name, or null if name was not found. {@link GetWrapperNameReturnType} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getWrapperName } from '@ensdomains/ensjs/public' - * - * const client = createPublicClient({ - * chain: addEnsContracts(mainnet), - * transport: http(), - * }) - * const result = await getWrapperName(client, { name: '[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth' }) - * // wrapped.eth - */ -const getWrapperName = generateFunction({ encode, decode }) as (( - client: ClientWithEns, - { name }: GetWrapperNameParameters, -) => Promise) & - BatchableFunctionObject - -export default getWrapperName diff --git a/packages/ensjs/src/functions/public/multicallWrapper.ts b/packages/ensjs/src/functions/public/multicallWrapper.ts deleted file mode 100644 index acefd1b6b..000000000 --- a/packages/ensjs/src/functions/public/multicallWrapper.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - getContractError, - offchainLookup, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { multicallTryAggregateSnippet } from '../../contracts/multicall.js' -import type { - SimpleTransactionRequest, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { generateFunction } from '../../utils/generateFunction.js' - -export type MulticallWrapperParameters = { - transactions: SimpleTransactionRequest[] - requireSuccess?: boolean -} - -export type MulticallWrapperReturnType = { - success: boolean - returnData: Hex -}[] - -const encode = ( - client: ClientWithEns, - { transactions, requireSuccess = false }: MulticallWrapperParameters, -): SimpleTransactionRequest => { - return { - to: getChainContractAddress({ client, contract: 'multicall3' }), - data: encodeFunctionData({ - abi: multicallTryAggregateSnippet, - functionName: 'tryAggregate', - args: [ - requireSuccess, - transactions.map((tx) => ({ target: tx.to!, callData: tx.data! })), - ], - }), - } -} - -const decode = async ( - client: ClientWithEns, - data: Hex | BaseError, - transactions: TransactionRequestWithPassthrough[], -): Promise => { - if (typeof data === 'object') { - throw getContractError(data, { - abi: multicallTryAggregateSnippet, - functionName: 'tryAggregate', - args: [], - }) as BaseError - } - const result = decodeFunctionResult({ - abi: multicallTryAggregateSnippet, - functionName: 'tryAggregate', - data, - }) - const ccipChecked = await Promise.all( - result.map(async ({ success, returnData }, i) => { - let newObj: { success: boolean; returnData: Hex } = { - success, - returnData, - } - // OffchainLookup(address,string[],bytes,bytes4,bytes) - if (!success && returnData.startsWith('0x556f1830')) { - try { - const newData = await offchainLookup(client, { - to: transactions[i].to!, - data: returnData, - }) - if (newData) { - newObj = { success: true, returnData: newData } - } - } catch {} - } - return newObj - }), - ) - return ccipChecked -} - -const multicallWrapper = generateFunction({ encode, decode }) - -export default multicallWrapper diff --git a/packages/ensjs/src/functions/public/universalWrapper.test.ts b/packages/ensjs/src/functions/public/universalWrapper.test.ts deleted file mode 100644 index 16ee4936e..000000000 --- a/packages/ensjs/src/functions/public/universalWrapper.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { RawContractError, decodeFunctionData, hexToBytes } from 'viem' -import { expect, it } from 'vitest' -import { universalResolverResolveSnippet } from '../../contracts/universalResolver.js' -import { - deploymentAddresses, - publicClient, -} from '../../test/addTestContracts.js' -import { bytesToPacket } from '../../utils/hexEncodedName.js' -import universalWrapper from './universalWrapper.js' - -it('returns with passthrough containing args and address on encode', () => { - const result = universalWrapper.encode(publicClient, { - name: 'test.eth', - data: '0x1234', - }) - expect(result.passthrough).toMatchInlineSnapshot(` - { - "address": "${deploymentAddresses.UniversalResolver}", - "args": [ - "0x04746573740365746800", - "0x1234", - ], - } - `) -}) - -it('encodes labels larger than 255 bytes', () => { - const result = universalWrapper.encode(publicClient, { - name: '696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969.eth', - data: '0x', - }) - const { - args: [hexEncodedName], - } = decodeFunctionData({ - abi: universalResolverResolveSnippet, - data: result.data, - }) - const name = bytesToPacket(hexToBytes(hexEncodedName)) - expect(name).toMatchInlineSnapshot( - `"[5af85b2a63602b9af41f41d514011c4a19d643827fb73d85526283d764d80ede].eth"`, - ) -}) - -it('does not throw on result decode error when strict is false', async () => { - await expect( - universalWrapper.decode( - publicClient, - '0x1234', - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() -}) - -it('throws on result decode error when strict is true', async () => { - await expect( - universalWrapper.decode( - publicClient, - '0x1234', - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. - - Params: (bytes data, address resolver) - Data: 0x1234 (2 bytes) - - Version: viem@2.30.6] - `) -}) - -it('does not throw known contract error when strict is false', async () => { - await expect( - universalWrapper.decode( - publicClient, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).resolves.toBeNull() -}) - -it('throws on known contract error when strict is true', async () => { - await expect( - universalWrapper.decode( - publicClient, - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: true }, - ), - ).rejects.toMatchInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] -`) -}) - -it('throws on unknown contract error when strict is false', async () => { - await expect( - universalWrapper.decode( - publicClient, - new RawContractError({ - data: '0x4ced43fb', // SwagError() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: false }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted with the following signature: - 0x4ced43fb - - Unable to decode signature "0x4ced43fb" as it was not found on the provided ABI. - Make sure you are using the correct ABI and that the error exists on it. - You can look up the decoded signature here: https://openchain.xyz/signatures?query=0x4ced43fb. - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Docs: https://viem.sh/docs/contract/decodeErrorResult - Version: viem@2.30.6] - `) -}) - -it('throws on unknown contract error when strict is true', async () => { - await expect( - universalWrapper.decode( - publicClient, - new RawContractError({ - data: '0x4ced43fb', // SwagError() - }), - { - address: '0x1234567890abcdef', - args: ['0x', '0x'], - }, - { strict: true }, - ), - ).rejects.toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted with the following signature: - 0x4ced43fb - - Unable to decode signature "0x4ced43fb" as it was not found on the provided ABI. - Make sure you are using the correct ABI and that the error exists on it. - You can look up the decoded signature here: https://openchain.xyz/signatures?query=0x4ced43fb. - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Docs: https://viem.sh/docs/contract/decodeErrorResult - Version: viem@2.30.6] - `) -}) diff --git a/packages/ensjs/src/functions/public/universalWrapper.ts b/packages/ensjs/src/functions/public/universalWrapper.ts deleted file mode 100644 index 8cb43bc0a..000000000 --- a/packages/ensjs/src/functions/public/universalWrapper.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { - type Address, - type BaseError, - type Hex, - decodeFunctionResult, - encodeFunctionData, - labelhash, - toBytes, - toHex, -} from 'viem' -import type { ClientWithEns } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - universalResolverResolveSnippet, - universalResolverResolveWithGatewaysSnippet, -} from '../../contracts/universalResolver.js' -import type { - GenericPassthrough, - TransactionRequestWithPassthrough, -} from '../../types.js' -import { checkSafeUniversalResolverData } from '../../utils/checkSafeUniversalResolverData.js' -import { generateFunction } from '../../utils/generateFunction.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' -import { encodeLabelhash } from '../../utils/labels.js' - -export type UniversalWrapperParameters = { - name: string - data: Hex - strict?: boolean - gatewayUrls?: string[] -} - -export type UniversalWrapperReturnType = { - data: Hex - resolver: Address -} | null - -const encode = ( - client: ClientWithEns, - { name, data, gatewayUrls }: Omit, -): TransactionRequestWithPassthrough => { - const nameWithSizedLabels = name - .split('.') - .map((label) => { - const labelLength = toBytes(label).byteLength - if (labelLength > 255) { - return encodeLabelhash(labelhash(label)) - } - return label - }) - .join('.') - const to = getChainContractAddress({ - client, - contract: 'ensUniversalResolver', - }) - const args = [toHex(packetToBytes(nameWithSizedLabels)), data] as const - - return { - to, - ...(gatewayUrls?.length - ? { - data: encodeFunctionData({ - abi: universalResolverResolveWithGatewaysSnippet, - functionName: 'resolve', - args: [...args, gatewayUrls] as const, - }), - passthrough: { - args: [...args, gatewayUrls], - address: to, - }, - } - : { - data: encodeFunctionData({ - abi: universalResolverResolveSnippet, - functionName: 'resolve', - args, - }), - passthrough: { - args, - address: to, - }, - }), - } -} - -const decode = async ( - _client: ClientWithEns, - data: Hex | BaseError, - passthrough: GenericPassthrough, - { - strict, - gatewayUrls, - }: Pick, -): Promise => { - const isSafe = checkSafeUniversalResolverData(data, { - strict, - abi: gatewayUrls - ? universalResolverResolveWithGatewaysSnippet - : universalResolverResolveSnippet, - args: passthrough.args, - functionName: 'resolve', - address: passthrough.address, - }) - - if (!isSafe) return null - - try { - const result = decodeFunctionResult({ - abi: universalResolverResolveSnippet, - functionName: 'resolve', - data, - }) - return { data: result[0], resolver: result[1] } - } catch (error) { - if (strict) throw error - return null - } -} - -const universalWrapper = generateFunction({ encode, decode }) - -export default universalWrapper diff --git a/packages/ensjs/src/functions/wallet/clearRecords.ts b/packages/ensjs/src/functions/wallet/clearRecords.ts deleted file mode 100644 index 14ba6e06e..000000000 --- a/packages/ensjs/src/functions/wallet/clearRecords.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { - Account, - Address, - Hash, - SendTransactionParameters, - Transport, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { encodeClearRecords } from '../../utils/encoders/encodeClearRecords.js' -import { namehash } from '../../utils/normalise.js' - -export type ClearRecordsDataParameters = { - /** The name to clear records for */ - name: string - /** The resolver address to use */ - resolverAddress: Address -} - -export type ClearRecordsDataReturnType = SimpleTransactionRequest - -export type ClearRecordsParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - ClearRecordsDataParameters & - WriteTransactionParameters -> - -export type ClearRecordsReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, resolverAddress }: ClearRecordsDataParameters, -): ClearRecordsDataReturnType => { - return { - to: resolverAddress, - data: encodeClearRecords(namehash(name)), - } -} - -/** - * Clears the records for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link ClearRecordsParameters} - * @returns Transaction hash. {@link ClearRecordsReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { clearRecords } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await clearRecords(wallet, { - * name: 'ens.eth', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function clearRecords< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - resolverAddress, - ...txArgs - }: ClearRecordsParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - resolverAddress, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -clearRecords.makeFunctionData = makeFunctionData - -export default clearRecords diff --git a/packages/ensjs/src/functions/wallet/commitName.ts b/packages/ensjs/src/functions/wallet/commitName.ts deleted file mode 100644 index da9c47aa6..000000000 --- a/packages/ensjs/src/functions/wallet/commitName.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { ethRegistrarControllerCommitSnippet } from '../../contracts/ethRegistrarController.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { UnsupportedNameTypeError } from '../../errors/general.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { getNameType } from '../../utils/getNameType.js' -import { - type RegistrationParameters, - makeCommitment, -} from '../../utils/registerHelpers.js' -import { wrappedLabelLengthCheck } from '../../utils/wrapper.js' - -export type CommitNameDataParameters = RegistrationParameters - -export type CommitNameDataReturnType = SimpleTransactionRequest - -export type CommitNameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - CommitNameDataParameters & - WriteTransactionParameters -> - -export type CommitNameReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - args: CommitNameDataParameters, -): CommitNameDataReturnType => { - const labels = args.name.split('.') - const nameType = getNameType(args.name) - if (nameType !== 'eth-2ld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld'], - details: 'Only 2ld-eth name registration is supported', - }) - wrappedLabelLengthCheck(labels[0]) - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensEthRegistrarController', - }), - data: encodeFunctionData({ - abi: ethRegistrarControllerCommitSnippet, - functionName: 'commit', - args: [makeCommitment(args)], - }), - } -} - -/** - * Commits a name to be registered - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link CommitNameParameters} - * @returns Transaction hash. {@link CommitNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { commitName } from '@ensdomains/ensjs/wallet' - * import { randomSecret } from '@ensdomains/ensjs/utils' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const secret = randomSecret() - * const hash = await commitName(wallet, { - * name: 'example.eth', - * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * duration: 31536000, // 1 year - * secret, - * }) - * // 0x... - */ -async function commitName< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - ...txArgs - }: CommitNameParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -commitName.makeFunctionData = makeFunctionData - -export default commitName diff --git a/packages/ensjs/src/functions/wallet/deleteSubname.ts b/packages/ensjs/src/functions/wallet/deleteSubname.ts deleted file mode 100644 index 3978e9ef9..000000000 --- a/packages/ensjs/src/functions/wallet/deleteSubname.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - nameWrapperSetRecordSnippet, - nameWrapperSetSubnodeRecordSnippet, -} from '../../contracts/nameWrapper.js' -import { - registrySetRecordSnippet, - registrySetSubnodeRecordSnippet, -} from '../../contracts/registry.js' -import { - InvalidContractTypeError, - UnsupportedNameTypeError, -} from '../../errors/general.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { EMPTY_ADDRESS } from '../../utils/consts.js' -import { getNameType } from '../../utils/getNameType.js' -import { makeLabelNodeAndParent } from '../../utils/makeLabelNodeAndParent.js' -import { namehash } from '../../utils/normalise.js' - -export type DeleteSubnameDataParameters = { - /** Subname to delete */ - name: string - /** Contract to delete subname on */ - contract: 'registry' | 'nameWrapper' - /** If true, deletes via owner methods, otherwise will delete via parent owner methods */ - asOwner?: boolean -} - -export type DeleteSubnameDataReturnType = SimpleTransactionRequest - -export type DeleteSubnameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - DeleteSubnameDataParameters & - WriteTransactionParameters -> - -export type DeleteSubnameReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { name, contract, asOwner }: DeleteSubnameDataParameters, -): DeleteSubnameDataReturnType => { - const nameType = getNameType(name) - if (nameType !== 'eth-subname' && nameType !== 'other-subname') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-subname', 'other-subname'], - details: 'Cannot delete a name that is not a subname', - }) - - switch (contract) { - case 'registry': { - const registryAddress = getChainContractAddress({ - client: wallet, - contract: 'ensRegistry', - }) - if (asOwner) - return { - to: registryAddress, - data: encodeFunctionData({ - abi: registrySetRecordSnippet, - functionName: 'setRecord', - args: [namehash(name), EMPTY_ADDRESS, EMPTY_ADDRESS, BigInt(0)], - }), - } - - const { labelhash, parentNode } = makeLabelNodeAndParent(name) - return { - to: registryAddress, - data: encodeFunctionData({ - abi: registrySetSubnodeRecordSnippet, - functionName: 'setSubnodeRecord', - args: [ - parentNode, - labelhash, - EMPTY_ADDRESS, - EMPTY_ADDRESS, - BigInt(0), - ], - }), - } - } - case 'nameWrapper': { - const nameWrapperAddress = getChainContractAddress({ - client: wallet, - contract: 'ensNameWrapper', - }) - if (asOwner) - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperSetRecordSnippet, - functionName: 'setRecord', - args: [namehash(name), EMPTY_ADDRESS, EMPTY_ADDRESS, BigInt(0)], - }), - } - - const { label, parentNode } = makeLabelNodeAndParent(name) - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperSetSubnodeRecordSnippet, - functionName: 'setSubnodeRecord', - args: [ - parentNode, - label, - EMPTY_ADDRESS, - EMPTY_ADDRESS, - BigInt(0), - 0, - BigInt(0), - ], - }), - } - } - default: - throw new InvalidContractTypeError({ - contractType: contract, - supportedContractTypes: ['registry', 'nameWrapper'], - }) - } -} - -/** - * Deletes a subname - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link DeleteSubnameParameters} - * @returns Transaction hash. {@link DeleteSubnameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { deleteSubname } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: mainnetWithEns, - * transport: custom(window.ethereum), - * }) - * const hash = await deleteSubname(wallet, { - * name: 'sub.ens.eth', - * contract: 'registry', - * }) - * // 0x... - */ -async function deleteSubname< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - contract, - asOwner, - ...txArgs - }: DeleteSubnameParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - contract, - asOwner, - } as DeleteSubnameDataParameters) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -deleteSubname.makeFunctionData = makeFunctionData - -export default deleteSubname diff --git a/packages/ensjs/src/functions/wallet/registerName.ts b/packages/ensjs/src/functions/wallet/registerName.ts deleted file mode 100644 index 38a28f508..000000000 --- a/packages/ensjs/src/functions/wallet/registerName.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { ethRegistrarControllerRegisterSnippet } from '../../contracts/ethRegistrarController.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { UnsupportedNameTypeError } from '../../errors/general.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { getNameType } from '../../utils/getNameType.js' -import { - type RegistrationParameters, - makeRegistrationTuple, -} from '../../utils/registerHelpers.js' -import { wrappedLabelLengthCheck } from '../../utils/wrapper.js' - -export type RegisterNameDataParameters = RegistrationParameters & { - /** Value of registration */ - value: bigint -} - -export type RegisterNameDataReturnType = SimpleTransactionRequest & { - value: bigint -} - -export type RegisterNameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - RegisterNameDataParameters & - WriteTransactionParameters -> - -export type RegisterNameReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { value, ...args }: RegisterNameDataParameters, -): RegisterNameDataReturnType => { - const nameType = getNameType(args.name) - if (nameType !== 'eth-2ld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld'], - details: 'Only 2ld-eth name registration is supported', - }) - - const labels = args.name.split('.') - wrappedLabelLengthCheck(labels[0]) - - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensEthRegistrarController', - }), - data: encodeFunctionData({ - abi: ethRegistrarControllerRegisterSnippet, - functionName: 'register', - args: makeRegistrationTuple(args), - }), - value, - } -} - -/** - * Registers a name on ENS - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link RegisterNameParameters} - * @returns Transaction hash. {@link RegisterNameReturnType} - * - * @example - * import { createPublicClient, createWalletClient, http, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getPrice } from '@ensdomains/ensjs/public' - * import { randomSecret } from '@ensdomains/ensjs/utils' - * import { commitName, registerName } from '@ensdomains/ensjs/wallet' - * - * const mainnetWithEns = addEnsContracts(mainnet) - * const client = createPublicClient({ - * chain: mainnetWithEns, - * transport: http(), - * }) - * const wallet = createWalletClient({ - * chain: mainnetWithEns, - * transport: custom(window.ethereum), - * }) - * const secret = randomSecret() - * const params = { - * name: 'example.eth', - * owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * duration: 31536000, // 1 year - * secret, - * } - * - * const commitmentHash = await commitName(wallet, params) - * await client.waitForTransactionReceipt({ hash: commitmentHash }) // wait for commitment to finalise - * await new Promise((resolve) => setTimeout(resolve, 60 * 1_000)) // wait for commitment to be valid - * - * const { base, premium } = await getPrice(client, { nameOrNames: params.name, duration: params.duration }) - * const value = (base + premium) * 110n / 100n // add 10% to the price for buffer - * const hash = await registerName(wallet, { ...params, value }) - * // 0x... - */ -async function registerName< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - value, - ...txArgs - }: RegisterNameParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - owner, - duration, - secret, - resolverAddress, - records, - reverseRecord, - fuses, - value, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -registerName.makeFunctionData = makeFunctionData - -export default registerName diff --git a/packages/ensjs/src/functions/wallet/renewNames.test.ts b/packages/ensjs/src/functions/wallet/renewNames.test.ts deleted file mode 100644 index 8834c3694..000000000 --- a/packages/ensjs/src/functions/wallet/renewNames.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { type Address, type Hex, labelhash } from 'viem' -import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' -import { baseRegistrarNameExpiresSnippet } from '../../contracts/baseRegistrar.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - publicClient, - testClient, - waitForTransaction, - walletClient, -} from '../../test/addTestContracts.js' -import getPrice from '../public/getPrice.js' -import renewNames from './renewNames.js' - -let snapshot: Hex -let accounts: Address[] - -beforeAll(async () => { - accounts = await walletClient.getAddresses() -}) - -beforeEach(async () => { - snapshot = await testClient.snapshot() -}) - -afterEach(async () => { - await testClient.revert({ id: snapshot }) -}) - -const getExpiry = async (name: string) => { - return publicClient.readContract({ - abi: baseRegistrarNameExpiresSnippet, - functionName: 'nameExpires', - address: getChainContractAddress({ - client: publicClient, - contract: 'ensBaseRegistrarImplementation', - }), - args: [BigInt(labelhash(name.split('.')[0]))], - }) -} - -it('should return a renew transaction for a single name and succeed', async () => { - const name = 'to-be-renewed.eth' - const duration = 31536000n - - const oldExpiry = await getExpiry(name) - - const price = await getPrice(publicClient, { - nameOrNames: name, - duration, - }) - const total = price!.base + price!.premium - - const tx = await renewNames(walletClient, { - nameOrNames: name, - duration, - value: total, - account: accounts[0], - }) - expect(tx).toBeTruthy() - const receipt = await waitForTransaction(tx) - expect(receipt.status).toBe('success') - - const newExpiry = await getExpiry(name) - expect(newExpiry).toBe(oldExpiry + duration) -}) - -it('should return a renewAll transaction for multiple names and succeed', async () => { - const names = ['to-be-renewed.eth', 'test123.eth'] - const duration = 31536000n - - const oldExpiries = await Promise.all(names.map(getExpiry)) - - const price = await getPrice(publicClient, { - nameOrNames: names, - duration, - }) - const total = price!.base + price!.premium - - const tx = await renewNames(walletClient, { - nameOrNames: names, - duration, - value: total, - account: accounts[1], - }) - expect(tx).toBeTruthy() - const receipt = await waitForTransaction(tx) - expect(receipt.status).toBe('success') - - const newExpiries = await Promise.all(names.map(getExpiry)) - for (let i = 0; i < names.length; i += 1) { - expect(newExpiries[i]).toBe(oldExpiries[i] + duration) - } -}) diff --git a/packages/ensjs/src/functions/wallet/renewNames.ts b/packages/ensjs/src/functions/wallet/renewNames.ts deleted file mode 100644 index 0c7916c3b..000000000 --- a/packages/ensjs/src/functions/wallet/renewNames.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import { bulkRenewalRenewAllSnippet } from '../../contracts/bulkRenewal.js' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { ethRegistrarControllerRenewSnippet } from '../../contracts/ethRegistrarController.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { UnsupportedNameTypeError } from '../../errors/general.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { getNameType } from '../../utils/getNameType.js' - -export type RenewNamesDataParameters = { - /** Name or names to renew */ - nameOrNames: string | string[] - /** Duration to renew name(s) for */ - duration: bigint | number - /** Value of all renewals */ - value: bigint -} - -export type RenewNamesDataReturnType = SimpleTransactionRequest & { - value: bigint -} - -export type RenewNamesParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - RenewNamesDataParameters & - WriteTransactionParameters -> - -export type RenewNamesReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { nameOrNames, duration, value }: RenewNamesDataParameters, -): RenewNamesDataReturnType => { - const names = Array.isArray(nameOrNames) ? nameOrNames : [nameOrNames] - const labels = names.map((name) => { - const label = name.split('.') - const nameType = getNameType(name) - if (nameType !== 'eth-2ld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld'], - details: 'Only 2ld-eth renewals are currently supported', - }) - return label[0] - }) - - if (labels.length === 1) { - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensEthRegistrarController', - }), - data: encodeFunctionData({ - abi: ethRegistrarControllerRenewSnippet, - functionName: 'renew', - args: [labels[0], BigInt(duration)], - }), - value, - } - } - - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensBulkRenewal', - }), - data: encodeFunctionData({ - abi: bulkRenewalRenewAllSnippet, - functionName: 'renewAll', - args: [labels, BigInt(duration)], - }), - value, - } -} - -/** - * Renews a name or names for a specified duration. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link RenewNamesParameters} - * @returns Transaction hash. {@link RenewNamesReturnType} - * - * @example - * import { createPublicClient, createWalletClient, http, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { getPrice } from '@ensdomains/ensjs/public' - * import { renewNames } from '@ensdomains/ensjs/wallet' - * - * const mainnetWithEns = addEnsContracts(mainnet) - * const client = createPublicClient({ - * chain: mainnetWithEns, - * transport: http(), - * }) - * const wallet = createWalletClient({ - * chain: mainnetWithEns, - * transport: custom(window.ethereum), - * }) - * - * const duration = 31536000 // 1 year - * const { base, premium } = await getPrice(wallet, { - * nameOrNames: 'example.eth', - * duration, - * }) - * const value = (base + premium) * 110n / 100n // add 10% to the price for buffer - * const hash = await renewNames(wallet, { - * nameOrNames: 'example.eth', - * duration, - * value, - * }) - * // 0x... - */ -async function renewNames< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - nameOrNames, - duration, - value, - ...txArgs - }: RenewNamesParameters, -): Promise { - const data = makeFunctionData(wallet, { nameOrNames, duration, value }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -renewNames.makeFunctionData = makeFunctionData - -export default renewNames diff --git a/packages/ensjs/src/functions/wallet/setAbiRecord.ts b/packages/ensjs/src/functions/wallet/setAbiRecord.ts deleted file mode 100644 index 41825ec75..000000000 --- a/packages/ensjs/src/functions/wallet/setAbiRecord.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { - Account, - Address, - Hash, - SendTransactionParameters, - Transport, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import type { EncodedAbi } from '../../utils/encoders/encodeAbi.js' -import { - type EncodeSetAbiParameters, - encodeSetAbi, -} from '../../utils/encoders/encodeSetAbi.js' -import { namehash } from '../../utils/normalise.js' - -export type SetAbiRecordDataParameters = { - /** Name to set ABI for */ - name: string - /** Encoded ABI data to set */ - encodedAbi: EncodedAbi - /** Resolver address to set ABI on */ - resolverAddress: Address -} - -export type SetAbiRecordDataReturnType = SimpleTransactionRequest - -export type SetAbiRecordParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetAbiRecordDataParameters & - WriteTransactionParameters -> - -export type SetAbiRecordReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, encodedAbi, resolverAddress }: SetAbiRecordDataParameters, -): SetAbiRecordDataReturnType => { - return { - to: resolverAddress, - data: encodeSetAbi({ - namehash: namehash(name), - ...encodedAbi, - } as EncodeSetAbiParameters), - } -} - -/** - * Sets the ABI for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetAbiRecordParameters} - * @returns Transaction hash. {@link SetAbiRecordReturnType} - * - * @example - * import abi from './abi.json' - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { encodeAbi } from '@ensdomains/ensjs/utils' - * import { setAbiRecord } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * - * const encodedAbi = await encodeAbi({ encodeAs: 'json', abi }) - * const hash = await setAbiRecord(wallet, { - * name: 'ens.eth', - * encodedAbi, - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setAbiRecord< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - encodedAbi, - resolverAddress, - ...txArgs - }: SetAbiRecordParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - encodedAbi, - resolverAddress, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setAbiRecord.makeFunctionData = makeFunctionData - -export default setAbiRecord diff --git a/packages/ensjs/src/functions/wallet/setAddressRecord.ts b/packages/ensjs/src/functions/wallet/setAddressRecord.ts deleted file mode 100644 index 740d1b247..000000000 --- a/packages/ensjs/src/functions/wallet/setAddressRecord.ts +++ /dev/null @@ -1,108 +0,0 @@ -import type { - Account, - Address, - Hash, - SendTransactionParameters, - Transport, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { encodeSetAddr } from '../../utils/encoders/encodeSetAddr.js' -import { namehash } from '../../utils/normalise.js' - -export type SetAddressRecordDataParameters = { - /** Name to set address record for */ - name: string - /** Coin ticker or ID to set */ - coin: string | number - /** Value to set, null if deleting */ - value: Address | string | null - /** Resolver address to set address record on */ - resolverAddress: Address -} - -export type SetAddressRecordDataReturnType = SimpleTransactionRequest - -export type SetAddressRecordParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetAddressRecordDataParameters & - WriteTransactionParameters -> - -export type SetAddressRecordReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, coin, value, resolverAddress }: SetAddressRecordDataParameters, -): SetAddressRecordDataReturnType => { - return { - to: resolverAddress, - data: encodeSetAddr({ namehash: namehash(name), coin, value }), - } -} - -/** - * Sets an address record for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetAddressRecordParameters} - * @returns Transaction hash. {@link SetAddressRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setAddressRecord } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setAddressRecord(wallet, { - * name: 'ens.eth', - * coin: 'ETH', - * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setAddressRecord< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - coin, - value, - resolverAddress, - ...txArgs - }: SetAddressRecordParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - coin, - value, - resolverAddress, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setAddressRecord.makeFunctionData = makeFunctionData - -export default setAddressRecord diff --git a/packages/ensjs/src/functions/wallet/setChildFuses.ts b/packages/ensjs/src/functions/wallet/setChildFuses.ts deleted file mode 100644 index c89496ca2..000000000 --- a/packages/ensjs/src/functions/wallet/setChildFuses.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, - labelhash, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperSetChildFusesSnippet } from '../../contracts/nameWrapper.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { type EncodeFusesInputObject, encodeFuses } from '../../utils/fuses.js' -import { namehash } from '../../utils/normalise.js' - -export type SetChildFusesDataParameters = { - /** Name to set child fuses for */ - name: string - /** Fuse object or number value to set to */ - fuses: EncodeFusesInputObject - /** Expiry to set for fuses */ - expiry?: number | bigint -} - -export type SetChildFusesDataReturnType = SimpleTransactionRequest - -export type SetChildFusesParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetChildFusesDataParameters & - WriteTransactionParameters -> - -export type SetChildFusesReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { name, fuses, expiry }: SetChildFusesDataParameters, -): SetChildFusesDataReturnType => { - const encodedFuses = encodeFuses({ input: fuses }) - const labels = name.split('.') - const labelHash = labelhash(labels.shift()!) - const parentNode = namehash(labels.join('.')) - return { - to: getChainContractAddress({ client: wallet, contract: 'ensNameWrapper' }), - data: encodeFunctionData({ - abi: nameWrapperSetChildFusesSnippet, - functionName: 'setChildFuses', - args: [parentNode, labelHash, encodedFuses, BigInt(expiry ?? 0)], - }), - } -} - -/** - * Sets the fuses for a name as the parent. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetChildFusesParameters} - * @returns Transaction hash. {@link SetChildFusesReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setChildFuses } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setChildFuses(wallet, { - * name: 'sub.ens.eth', - * fuses: { - * parent: { - * named: ['PARENT_CANNOT_CONTROl'], - * }, - * }, - * }) - * // 0x... - */ -async function setChildFuses< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - fuses, - expiry, - ...txArgs - }: SetChildFusesParameters, -): Promise { - const data = makeFunctionData(wallet, { name, fuses, expiry }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setChildFuses.makeFunctionData = makeFunctionData - -export default setChildFuses diff --git a/packages/ensjs/src/functions/wallet/setContentHashRecord.ts b/packages/ensjs/src/functions/wallet/setContentHashRecord.ts deleted file mode 100644 index ea248a50a..000000000 --- a/packages/ensjs/src/functions/wallet/setContentHashRecord.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { - Account, - Address, - Hash, - SendTransactionParameters, - Transport, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { encodeSetContentHash } from '../../utils/encoders/encodeSetContentHash.js' -import { namehash } from '../../utils/normalise.js' - -export type SetContentHashRecordDataParameters = { - /** Name to set content hash for */ - name: string - /** Content hash value */ - contentHash: string | null - /** Resolver address to set content hash on */ - resolverAddress: Address -} - -export type SetContentHashRecordDataReturnType = SimpleTransactionRequest - -export type SetContentHashRecordParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetContentHashRecordDataParameters & - WriteTransactionParameters -> - -export type SetContentHashRecordReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, contentHash, resolverAddress }: SetContentHashRecordDataParameters, -): SetContentHashRecordDataReturnType => { - return { - to: resolverAddress, - data: encodeSetContentHash({ namehash: namehash(name), contentHash }), - } -} - -/** - * Sets the content hash record for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetContentHashRecordParameters} - * @returns Transaction hash. {@link SetContentHashRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setContentHashRecord } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setContentHashRecord(wallet, { - * name: 'ens.eth', - * value: 'ipns://k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setContentHashRecord< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - contentHash, - resolverAddress, - ...txArgs - }: SetContentHashRecordParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - contentHash, - resolverAddress, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setContentHashRecord.makeFunctionData = makeFunctionData - -export default setContentHashRecord diff --git a/packages/ensjs/src/functions/wallet/setFuses.ts b/packages/ensjs/src/functions/wallet/setFuses.ts deleted file mode 100644 index 7c62211ff..000000000 --- a/packages/ensjs/src/functions/wallet/setFuses.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { - type Account, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperSetFusesSnippet } from '../../contracts/nameWrapper.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { - type EncodeChildFusesInputObject, - encodeFuses, -} from '../../utils/fuses.js' -import { namehash } from '../../utils/normalise.js' - -export type SetFusesDataParameters = { - /** Name to set fuses for */ - name: string - /** Fuse object to set to */ - fuses: EncodeChildFusesInputObject -} - -export type SetFusesDataReturnType = SimpleTransactionRequest - -export type SetFusesParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetFusesDataParameters & - WriteTransactionParameters -> - -export type SetFusesReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { name, fuses }: SetFusesDataParameters, -): SetFusesDataReturnType => { - const encodedFuses = encodeFuses({ restriction: 'child', input: fuses }) - return { - to: getChainContractAddress({ client: wallet, contract: 'ensNameWrapper' }), - data: encodeFunctionData({ - abi: nameWrapperSetFusesSnippet, - functionName: 'setFuses', - args: [namehash(name), encodedFuses], - }), - } -} - -/** - * Sets the fuses for a name. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetFusesParameters} - * @returns Transaction hash. {@link SetFusesReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setFuses } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setFuses(wallet, { - * name: 'sub.ens.eth', - * fuses: { - * named: ['CANNOT_TRANSFER'], - * }, - * }) - * // 0x... - */ -async function setFuses< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - fuses, - ...txArgs - }: SetFusesParameters, -): Promise { - const data = makeFunctionData(wallet, { name, fuses }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setFuses.makeFunctionData = makeFunctionData - -export default setFuses diff --git a/packages/ensjs/src/functions/wallet/setPrimaryName.ts b/packages/ensjs/src/functions/wallet/setPrimaryName.ts deleted file mode 100644 index 9baab402d..000000000 --- a/packages/ensjs/src/functions/wallet/setPrimaryName.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import { parseAccount } from 'viem/utils' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - reverseRegistrarSetNameForAddrSnippet, - reverseRegistrarSetNameSnippet, -} from '../../contracts/reverseRegistrar.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' - -type BaseSetPrimaryNameDataParameters = { - /** The name to set as primary */ - name: string - /** The address to set the primary name for */ - address?: Address - /** The resolver address to use */ - resolverAddress?: Address -} - -type SelfSetPrimaryNameDataParameters = { - address?: never - resolverAddress?: never -} - -type OtherSetPrimaryNameDataParameters = { - address: Address - resolverAddress?: Address -} - -export type SetPrimaryNameDataParameters = BaseSetPrimaryNameDataParameters & - (SelfSetPrimaryNameDataParameters | OtherSetPrimaryNameDataParameters) - -export type SetPrimaryNameDataReturnType = SimpleTransactionRequest - -export type SetPrimaryNameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetPrimaryNameDataParameters & - WriteTransactionParameters -> - -export type SetPrimaryNameReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account, ->( - wallet: ClientWithAccount, - { - name, - address, - resolverAddress = getChainContractAddress({ - client: wallet, - contract: 'ensPublicResolver', - }), - }: SetPrimaryNameDataParameters, -): SetPrimaryNameDataReturnType => { - const reverseRegistrarAddress = getChainContractAddress({ - client: wallet, - contract: 'ensReverseRegistrar', - }) - if (address) { - return { - to: reverseRegistrarAddress, - data: encodeFunctionData({ - abi: reverseRegistrarSetNameForAddrSnippet, - functionName: 'setNameForAddr', - args: [ - address, - wallet.account.address, - resolverAddress || - getChainContractAddress({ - client: wallet, - contract: 'ensPublicResolver', - }), - name, - ], - }), - } - } - - return { - to: reverseRegistrarAddress, - data: encodeFunctionData({ - abi: reverseRegistrarSetNameSnippet, - functionName: 'setName', - args: [name], - }), - } -} - -/** - * Sets a primary name for an address. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetPrimaryNameParameters} - * @returns Transaction hash. {@link SetPrimaryNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setPrimaryName } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setPrimaryName(wallet, { - * name: 'ens.eth', - * }) - * // 0x... - */ -async function setPrimaryName< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - address, - resolverAddress, - ...txArgs - }: SetPrimaryNameParameters, -): Promise { - const data = makeFunctionData( - { - ...wallet, - account: parseAccount((txArgs.account || wallet.account)!), - } as ClientWithAccount, - { name, address, resolverAddress } as SetPrimaryNameDataParameters, - ) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setPrimaryName.makeFunctionData = makeFunctionData - -export default setPrimaryName diff --git a/packages/ensjs/src/functions/wallet/setRecords.ts b/packages/ensjs/src/functions/wallet/setRecords.ts deleted file mode 100644 index 4dad31b90..000000000 --- a/packages/ensjs/src/functions/wallet/setRecords.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { publicResolverMulticallSnippet } from '../../contracts/publicResolver.js' -import { NoRecordsSpecifiedError } from '../../errors/public.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { - type RecordOptions, - generateRecordCallArray, -} from '../../utils/generateRecordCallArray.js' -import { namehash } from '../../utils/normalise.js' - -export type SetRecordsDataParameters = { - /** The name to set records for */ - name: string - /** The resolver address to set records on */ - resolverAddress: Address -} & RecordOptions - -export type SetRecordsDataReturnType = SimpleTransactionRequest - -export type SetRecordsParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetRecordsDataParameters & - WriteTransactionParameters -> - -export type SetRecordsReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, resolverAddress, ...records }: SetRecordsDataParameters, -): SetRecordsDataReturnType => { - const callArray = generateRecordCallArray({ - namehash: namehash(name), - ...records, - }) - if (callArray.length === 0) throw new NoRecordsSpecifiedError() - if (callArray.length === 1) - return { - to: resolverAddress, - data: callArray[0], - } - return { - to: resolverAddress, - data: encodeFunctionData({ - abi: publicResolverMulticallSnippet, - functionName: 'multicall', - args: [callArray], - }), - } -} - -/** - * Sets multiple records for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetRecordsParameters} - * @returns Transaction hash. {@link SetRecordsReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setRecords } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setRecords(wallet, { - * name: 'ens.eth', - * coins: [ - * { - * coin: 'ETH', - * value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }, - * ], - * texts: [{ key: 'foo', value: 'bar' }], - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setRecords< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - resolverAddress, - clearRecords, - contentHash, - texts, - coins, - abi, - ...txArgs - }: SetRecordsParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - resolverAddress, - clearRecords, - contentHash, - texts, - coins, - abi, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setRecords.makeFunctionData = makeFunctionData - -export default setRecords diff --git a/packages/ensjs/src/functions/wallet/setResolver.ts b/packages/ensjs/src/functions/wallet/setResolver.ts deleted file mode 100644 index ace5ad104..000000000 --- a/packages/ensjs/src/functions/wallet/setResolver.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperSetResolverSnippet } from '../../contracts/nameWrapper.js' -import { registrySetResolverSnippet } from '../../contracts/registry.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { namehash } from '../../utils/normalise.js' - -export type SetResolverDataParameters = { - /** Name to set resolver for */ - name: string - /** Contract to set resolver on */ - contract: 'registry' | 'nameWrapper' - /** Resolver address to set */ - resolverAddress: Address -} - -export type SetResolverDataReturnType = SimpleTransactionRequest - -export type SetResolverParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetResolverDataParameters & - WriteTransactionParameters -> - -export type SetResolverReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { name, contract, resolverAddress }: SetResolverDataParameters, -): SetResolverDataReturnType => { - if (contract !== 'registry' && contract !== 'nameWrapper') - throw new Error(`Unknown contract: ${contract}`) - - const to = getChainContractAddress({ - client: wallet, - contract: contract === 'nameWrapper' ? 'ensNameWrapper' : 'ensRegistry', - }) - - const args = [namehash(name), resolverAddress] as const - const functionName = 'setResolver' - - if (contract === 'nameWrapper') - return { - to, - data: encodeFunctionData({ - abi: nameWrapperSetResolverSnippet, - functionName, - args, - }), - } - - return { - to, - data: encodeFunctionData({ - abi: registrySetResolverSnippet, - functionName, - args, - }), - } -} - -/** - * Sets a resolver for a name. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetResolverParameters} - * @returns Transaction hash. {@link SetResolverReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setResolver } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setResolver(wallet, { - * name: 'ens.eth', - * contract: 'registry', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setResolver< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - contract, - resolverAddress, - ...txArgs - }: SetResolverParameters, -): Promise { - const data = makeFunctionData(wallet, { name, contract, resolverAddress }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setResolver.makeFunctionData = makeFunctionData - -export default setResolver diff --git a/packages/ensjs/src/functions/wallet/setTextRecord.ts b/packages/ensjs/src/functions/wallet/setTextRecord.ts deleted file mode 100644 index 879c23c0b..000000000 --- a/packages/ensjs/src/functions/wallet/setTextRecord.ts +++ /dev/null @@ -1,108 +0,0 @@ -import type { - Account, - Address, - Hash, - SendTransactionParameters, - Transport, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { encodeSetText } from '../../utils/encoders/encodeSetText.js' -import { namehash } from '../../utils/normalise.js' - -export type SetTextRecordDataParameters = { - /** The name to set a text record for */ - name: string - /** The text record key to set */ - key: string - /** The text record value to set */ - value: string | null - /** The resolver address to use */ - resolverAddress: Address -} - -export type SetTextRecordDataReturnType = SimpleTransactionRequest - -export type SetTextRecordParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - SetTextRecordDataParameters & - WriteTransactionParameters -> - -export type SetTextRecordReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - _wallet: ClientWithAccount, - { name, key, value, resolverAddress }: SetTextRecordDataParameters, -): SetTextRecordDataReturnType => { - return { - to: resolverAddress, - data: encodeSetText({ namehash: namehash(name), key, value }), - } -} - -/** - * Sets a text record for a name on a resolver. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link SetTextRecordParameters} - * @returns Transaction hash. {@link SetTextRecordReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { setTextRecord } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await setTextRecord(wallet, { - * name: 'ens.eth', - * key: 'foo', - * value: 'bar', - * resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', - * }) - * // 0x... - */ -async function setTextRecord< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - key, - value, - resolverAddress, - ...txArgs - }: SetTextRecordParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - key, - value, - resolverAddress, - }) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -setTextRecord.makeFunctionData = makeFunctionData - -export default setTextRecord diff --git a/packages/ensjs/src/functions/wallet/transferName.ts b/packages/ensjs/src/functions/wallet/transferName.ts deleted file mode 100644 index c45093534..000000000 --- a/packages/ensjs/src/functions/wallet/transferName.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, - labelhash, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import { parseAccount } from 'viem/utils' -import { - baseRegistrarReclaimSnippet, - baseRegistrarSafeTransferFromSnippet, -} from '../../contracts/baseRegistrar.js' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - nameWrapperSafeTransferFromSnippet, - nameWrapperSetSubnodeOwnerSnippet, -} from '../../contracts/nameWrapper.js' -import { - registrySetOwnerSnippet, - registrySetSubnodeOwnerSnippet, -} from '../../contracts/registry.js' -import { - AdditionalParameterSpecifiedError, - InvalidContractTypeError, - UnsupportedNameTypeError, -} from '../../errors/general.js' -import type { - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { getNameType } from '../../utils/getNameType.js' -import { makeLabelNodeAndParent } from '../../utils/makeLabelNodeAndParent.js' -import { namehash } from '../../utils/normalise.js' - -type BaseTransferNameDataParameters = { - /** Name to transfer */ - name: string - /** Transfer recipient */ - newOwnerAddress: Address - /** Contract to use for transfer */ - contract: 'registry' | 'nameWrapper' | 'registrar' - /** Reclaim ownership as registrant (registrar only) */ - reclaim?: boolean - /** Transfer name as the parent owner */ - asParent?: boolean -} - -type RegistryOrNameWrapperTransferNameDataParameters = { - contract: 'registry' | 'nameWrapper' - reclaim?: never -} - -type BaseRegistrarTransferNameDataParameters = { - contract: 'registrar' - reclaim?: boolean - asParent?: never -} - -export type TransferNameDataParameters = BaseTransferNameDataParameters & - ( - | RegistryOrNameWrapperTransferNameDataParameters - | BaseRegistrarTransferNameDataParameters - ) - -export type TransferNameDataReturnType = SimpleTransactionRequest - -export type TransferNameParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - TransferNameDataParameters & - WriteTransactionParameters -> - -export type TransferNameReturnType = Hash - -export const makeFunctionData = < - TChain extends ChainWithEns, - TAccount extends Account, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - contract, - reclaim, - asParent, - }: TransferNameDataParameters, -): TransferNameDataReturnType => { - if (reclaim && contract !== 'registrar') - throw new AdditionalParameterSpecifiedError({ - parameter: 'reclaim', - allowedParameters: ['name', 'newOwnerAddress', 'contract'], - details: - "Can't reclaim a name from any contract other than the registrar", - }) - switch (contract) { - case 'registry': { - const registryAddress = getChainContractAddress({ - client: wallet, - contract: 'ensRegistry', - }) - if (asParent) { - const { labelhash: labelhashId, parentNode } = - makeLabelNodeAndParent(name) - return { - to: registryAddress, - data: encodeFunctionData({ - abi: registrySetSubnodeOwnerSnippet, - functionName: 'setSubnodeOwner', - args: [parentNode, labelhashId, newOwnerAddress], - }), - } - } - return { - to: registryAddress, - data: encodeFunctionData({ - abi: registrySetOwnerSnippet, - functionName: 'setOwner', - args: [namehash(name), newOwnerAddress], - }), - } - } - case 'registrar': { - if (asParent) - throw new AdditionalParameterSpecifiedError({ - parameter: 'asParent', - allowedParameters: ['name', 'newOwnerAddress', 'contract', 'reclaim'], - details: "Can't transfer a name as the parent owner on the registrar", - }) - const nameType = getNameType(name) - if (nameType !== 'eth-2ld') - throw new UnsupportedNameTypeError({ - nameType, - supportedNameTypes: ['eth-2ld'], - details: - 'Only eth-2ld names can be transferred on the registrar contract', - }) - const labels = name.split('.') - const tokenId = BigInt(labelhash(labels[0])) - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensBaseRegistrarImplementation', - }), - data: reclaim - ? encodeFunctionData({ - abi: baseRegistrarReclaimSnippet, - functionName: 'reclaim', - args: [tokenId, newOwnerAddress], - }) - : encodeFunctionData({ - abi: baseRegistrarSafeTransferFromSnippet, - functionName: 'safeTransferFrom', - args: [wallet.account.address, newOwnerAddress, tokenId], - }), - } - } - case 'nameWrapper': { - const nameWrapperAddress = getChainContractAddress({ - client: wallet, - contract: 'ensNameWrapper', - }) - if (asParent) { - const { label, parentNode } = makeLabelNodeAndParent(name) - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperSetSubnodeOwnerSnippet, - functionName: 'setSubnodeOwner', - args: [parentNode, label, newOwnerAddress, 0, BigInt(0)], - }), - } - } - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperSafeTransferFromSnippet, - functionName: 'safeTransferFrom', - args: [ - wallet.account.address, - newOwnerAddress, - BigInt(namehash(name)), - BigInt(1), - '0x', - ], - }), - } - } - default: - throw new InvalidContractTypeError({ - contractType: contract, - supportedContractTypes: ['registry', 'registrar', 'nameWrapper'], - }) - } -} - -/** - * Transfers a name to a new owner. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link TransferNameParameters} - * @returns Transaction hash. {@link TransferNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { transferName } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await transferName(wallet, { - * name: 'ens.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * contract: 'registry', - * }) - * // 0x... - */ -async function transferName< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - contract, - reclaim, - asParent, - ...txArgs - }: TransferNameParameters, -): Promise { - const data = makeFunctionData( - { - ...wallet, - account: parseAccount((txArgs.account || wallet.account)!), - } as ClientWithAccount, - { - name, - newOwnerAddress, - contract, - reclaim, - asParent, - } as TransferNameDataParameters, - ) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -transferName.makeFunctionData = makeFunctionData - -export default transferName diff --git a/packages/ensjs/src/functions/wallet/unwrapName.ts b/packages/ensjs/src/functions/wallet/unwrapName.ts deleted file mode 100644 index 06422f5ad..000000000 --- a/packages/ensjs/src/functions/wallet/unwrapName.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeFunctionData, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { - nameWrapperUnwrapEth2ldSnippet, - nameWrapperUnwrapSnippet, -} from '../../contracts/nameWrapper.js' -import { - AdditionalParameterSpecifiedError, - RequiredParameterNotSpecifiedError, -} from '../../errors/general.js' -import type { - Eth2ldName, - Eth2ldNameSpecifier, - GetNameType, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { getNameType } from '../../utils/getNameType.js' -import { makeLabelNodeAndParent } from '../../utils/makeLabelNodeAndParent.js' - -type BaseUnwrapNameDataParameters = { - /** The name to unwrap */ - name: TName - /** The recipient of the unwrapped name */ - newOwnerAddress: Address - /** The registrant of the unwrapped name (eth-2ld only) */ - newRegistrantAddress?: Address -} - -type Eth2ldUnwrapNameDataParameters = { - name: Eth2ldName - newRegistrantAddress: Address -} - -type OtherUnwrapNameDataParameters = { - name: string - newRegistrantAddress?: never -} - -export type UnwrapNameDataParameters< - TName extends string, - TNameType extends GetNameType = GetNameType, -> = BaseUnwrapNameDataParameters & - (TNameType extends Eth2ldNameSpecifier - ? Eth2ldUnwrapNameDataParameters - : OtherUnwrapNameDataParameters) - -export type UnwrapNameDataReturnType = SimpleTransactionRequest - -export type UnwrapNameParameters< - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = UnwrapNameDataParameters & - WriteTransactionParameters - -export type UnwrapNameReturnType = Hash - -export const makeFunctionData = < - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account | undefined, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - newRegistrantAddress, - }: UnwrapNameDataParameters, -): UnwrapNameDataReturnType => { - const { labelhash, parentNode } = makeLabelNodeAndParent(name) - const nameWrapperAddress = getChainContractAddress({ - client: wallet, - contract: 'ensNameWrapper', - }) - const nameType = getNameType(name) - - if (nameType === 'eth-2ld') { - if (!newRegistrantAddress) - throw new RequiredParameterNotSpecifiedError({ - parameter: 'newRegistrantAddress', - details: 'Must provide newRegistrantAddress for eth-2ld names', - }) - - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperUnwrapEth2ldSnippet, - functionName: 'unwrapETH2LD', - args: [labelhash, newRegistrantAddress, newOwnerAddress], - }), - } - } - - if (newRegistrantAddress) - throw new AdditionalParameterSpecifiedError({ - parameter: 'newRegistrantAddress', - allowedParameters: ['name', 'newOwnerAddress'], - details: 'newRegistrantAddress can only be specified for eth-2ld names', - }) - - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperUnwrapSnippet, - functionName: 'unwrap', - args: [parentNode, labelhash, newOwnerAddress], - }), - } -} - -/** - * Unwraps a name. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link UnwrapNameParameters} - * @returns Transaction hash. {@link UnwrapNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { unwrapName } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await unwrapName(wallet, { - * name: 'example.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * newRegistrantAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }) - * // 0x... - */ -async function unwrapName< - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - newRegistrantAddress, - ...txArgs - }: UnwrapNameParameters, -): Promise { - const data = makeFunctionData(wallet, { - name, - newOwnerAddress, - newRegistrantAddress, - } as UnwrapNameDataParameters) - const writeArgs = { - ...data, - ...(txArgs as WriteTransactionParameters), - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -unwrapName.makeFunctionData = makeFunctionData - -export default unwrapName diff --git a/packages/ensjs/src/functions/wallet/wrapName.ts b/packages/ensjs/src/functions/wallet/wrapName.ts deleted file mode 100644 index 5cae33a13..000000000 --- a/packages/ensjs/src/functions/wallet/wrapName.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { - type Account, - type Address, - type Hash, - type SendTransactionParameters, - type Transport, - encodeAbiParameters, - encodeFunctionData, - labelhash, - toHex, -} from 'viem' -import { sendTransaction } from 'viem/actions' -import { parseAccount } from 'viem/utils' -import { baseRegistrarSafeTransferFromWithDataSnippet } from '../../contracts/baseRegistrar.js' -import type { ChainWithEns, ClientWithAccount } from '../../contracts/consts.js' -import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' -import { nameWrapperWrapSnippet } from '../../contracts/nameWrapper.js' -import { AdditionalParameterSpecifiedError } from '../../errors/general.js' -import type { - Eth2ldNameSpecifier, - GetNameType, - Prettify, - SimpleTransactionRequest, - WriteTransactionParameters, -} from '../../types.js' -import { - type EncodeChildFusesInputObject, - encodeFuses, -} from '../../utils/fuses.js' -import { packetToBytes } from '../../utils/hexEncodedName.js' -import { checkIsDotEth } from '../../utils/validation.js' -import { wrappedLabelLengthCheck } from '../../utils/wrapper.js' - -export type WrapNameDataParameters< - TName extends string, - TNameOption extends GetNameType = GetNameType, -> = { - /** The name to wrap */ - name: TName - /** The recipient of the wrapped name */ - newOwnerAddress: Address - /** Fuses to set on wrap (eth-2ld only) */ - fuses?: TNameOption extends Eth2ldNameSpecifier - ? EncodeChildFusesInputObject - : never - /** The resolver address to set on wrap */ - resolverAddress?: Address -} - -export type WrapNameDataReturnType = SimpleTransactionRequest - -export type WrapNameParameters< - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined, -> = Prettify< - WrapNameDataParameters & - WriteTransactionParameters -> - -export type WrapNameReturnType = Hash - -export const makeFunctionData = < - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - fuses, - resolverAddress = getChainContractAddress({ - client: wallet, - contract: 'ensPublicResolver', - }), - }: WrapNameDataParameters, -): WrapNameDataReturnType => { - const labels = name.split('.') - const isEth2ld = checkIsDotEth(labels) - - const nameWrapperAddress = getChainContractAddress({ - client: wallet, - contract: 'ensNameWrapper', - }) - - if (isEth2ld) { - wrappedLabelLengthCheck(labels[0]) - const encodedFuses = fuses - ? encodeFuses({ restriction: 'child', input: fuses }) - : 0 - const tokenId = BigInt(labelhash(labels[0])) - - const data = encodeAbiParameters( - [ - { name: 'label', type: 'string' }, - { name: 'wrappedOwner', type: 'address' }, - { name: 'ownerControlledFuses', type: 'uint16' }, - { name: 'resolverAddress', type: 'address' }, - ], - [labels[0], newOwnerAddress, encodedFuses, resolverAddress], - ) - - return { - to: getChainContractAddress({ - client: wallet, - contract: 'ensBaseRegistrarImplementation', - }), - data: encodeFunctionData({ - abi: baseRegistrarSafeTransferFromWithDataSnippet, - functionName: 'safeTransferFrom', - args: [wallet.account.address, nameWrapperAddress, tokenId, data], - }), - } - } - - if (fuses) - throw new AdditionalParameterSpecifiedError({ - parameter: 'fuses', - allowedParameters: ['name', 'wrappedOwner', 'resolverAddress'], - details: 'Fuses cannot be initially set when wrapping non eth-2ld names', - }) - - labels.forEach((label) => wrappedLabelLengthCheck(label)) - return { - to: nameWrapperAddress, - data: encodeFunctionData({ - abi: nameWrapperWrapSnippet, - functionName: 'wrap', - args: [toHex(packetToBytes(name)), newOwnerAddress, resolverAddress], - }), - } -} - -/** - * Wraps a name. - * @param wallet - {@link ClientWithAccount} - * @param parameters - {@link WrapNameParameters} - * @returns Transaction hash. {@link WrapNameReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { addEnsContracts } from '@ensdomains/ensjs' - * import { wrapName } from '@ensdomains/ensjs/wallet' - * - * const wallet = createWalletClient({ - * chain: addEnsContracts(mainnet), - * transport: custom(window.ethereum), - * }) - * const hash = await wrapName(wallet, { - * name: 'ens.eth', - * newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', - * }) - * // 0x... - */ -async function wrapName< - TName extends string, - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, ->( - wallet: ClientWithAccount, - { - name, - newOwnerAddress, - fuses, - resolverAddress, - ...txArgs - }: WrapNameParameters, -): Promise { - const data = makeFunctionData( - { - ...wallet, - account: parseAccount((txArgs.account || wallet.account)!), - } as ClientWithAccount, - { name, newOwnerAddress, fuses, resolverAddress }, - ) - const writeArgs = { - ...data, - ...txArgs, - } as SendTransactionParameters - return sendTransaction(wallet, writeArgs) -} - -wrapName.makeFunctionData = makeFunctionData - -export default wrapName diff --git a/packages/ensjs/src/index.ts b/packages/ensjs/src/index.ts index 0dbfeb5d1..8f0fe1a1f 100644 --- a/packages/ensjs/src/index.ts +++ b/packages/ensjs/src/index.ts @@ -1,32 +1,4 @@ -export { - ensPublicActions, - type EnsPublicActions, -} from './clients/decorators/public.js' -export { - ensSubgraphActions, - type EnsSubgraphActions, -} from './clients/decorators/subgraph.js' -export { - ensWalletActions, - type EnsWalletActions, -} from './clients/decorators/wallet.js' -export { - createEnsPublicClient, - type EnsPublicClient, - type EnsPublicClientConfig, -} from './clients/public.js' -export { - createEnsSubgraphClient, - type EnsSubgraphClient, - type EnsSubgraphClientConfig, -} from './clients/subgraph.js' -export { - createEnsWalletClient, - type EnsWalletClient, - type EnsWalletClientConfig, -} from './clients/wallet.js' - -export { addEnsContracts } from './contracts/addEnsContracts.js' +export { addEnsL1Contracts } from './contracts/addEnsL1Contracts.js' export { BaseError } from './errors/base.js' export { NoChainError, UnsupportedChainError } from './errors/contracts.js' export { diff --git a/packages/ensjs/src/public.ts b/packages/ensjs/src/public.ts deleted file mode 100644 index cc00b9fc2..000000000 --- a/packages/ensjs/src/public.ts +++ /dev/null @@ -1,100 +0,0 @@ -export { - default as _getAbi, - type InternalGetAbiParameters, - type InternalGetAbiReturnType, -} from './functions/public/_getAbi.js' -export { - default as _getAddr, - type InternalGetAddrParameters, - type InternalGetAddrReturnType, -} from './functions/public/_getAddr.js' -export { - default as _getContentHash, - type InternalGetContentHashParameters, - type InternalGetContentHashReturnType, -} from './functions/public/_getContentHash.js' -export { - default as _getText, - type InternalGetTextParameters, - type InternalGetTextReturnType, -} from './functions/public/_getText.js' -export { - default as batch, - type BatchParameters, - type BatchReturnType, -} from './functions/public/batch.js' -export { - default as getAbiRecord, - type GetAbiRecordParameters, - type GetAbiRecordReturnType, -} from './functions/public/getAbiRecord.js' -export { - default as getAddressRecord, - type GetAddressRecordParameters, - type GetAddressRecordReturnType, -} from './functions/public/getAddressRecord.js' -export { - default as getAvailable, - type GetAvailableParameters, - type GetAvailableReturnType, -} from './functions/public/getAvailable.js' -export { - default as getContentHashRecord, - type GetContentHashRecordParameters, - type GetContentHashRecordReturnType, -} from './functions/public/getContentHashRecord.js' -export { - default as getExpiry, - type GetExpiryParameters, - type GetExpiryReturnType, -} from './functions/public/getExpiry.js' -export { - default as getName, - type GetNameParameters, - type GetNameReturnType, -} from './functions/public/getName.js' -export { - default as getOwner, - type GetOwnerParameters, - type GetOwnerReturnType, -} from './functions/public/getOwner.js' -export { - default as getPrice, - type GetPriceParameters, - type GetPriceReturnType, -} from './functions/public/getPrice.js' -export { - default as getRecords, - type GetRecordsParameters, - type GetRecordsReturnType, -} from './functions/public/getRecords.js' -export { - default as getResolver, - type GetResolverParameters, - type GetResolverReturnType, -} from './functions/public/getResolver.js' -export { - default as getSupportedInterfaces, - type GetSupportedInterfacesParameters, - type GetSupportedInterfacesReturnType, -} from './functions/public/getSupportedInterfaces.js' -export { - default as getTextRecord, - type GetTextRecordParameters, - type GetTextRecordReturnType, -} from './functions/public/getTextRecord.js' -export { - default as getWrapperData, - type GetWrapperDataParameters, - type GetWrapperDataReturnType, -} from './functions/public/getWrapperData.js' -export { - default as multicallWrapper, - type MulticallWrapperParameters, - type MulticallWrapperReturnType, -} from './functions/public/multicallWrapper.js' -export { - default as universalWrapper, - type UniversalWrapperParameters, - type UniversalWrapperReturnType, -} from './functions/public/universalWrapper.js' diff --git a/packages/ensjs/src/subgraph.ts b/packages/ensjs/src/subgraph.ts index 2a8739d5f..a2bd1b5b1 100644 --- a/packages/ensjs/src/subgraph.ts +++ b/packages/ensjs/src/subgraph.ts @@ -1,4 +1,4 @@ -export { createSubgraphClient } from './functions/subgraph/client.js' +export { createSubgraphClient } from './actions/subgraph/client.js' export type { AbiChanged, AddrChanged, @@ -31,40 +31,59 @@ export type { Transfer, VersionChanged, WrappedTransfer, -} from './functions/subgraph/events.js' +} from './actions/subgraph/events.js' export { default as getDecodedName, + type GetDecodedNameErrorType, type GetDecodedNameParameters, type GetDecodedNameReturnType, -} from './functions/subgraph/getDecodedName.js' +} from './actions/subgraph/getDecodedName.js' export { default as getNameHistory, + type GetNameHistoryErrorType, type GetNameHistoryParameters, type GetNameHistoryReturnType, -} from './functions/subgraph/getNameHistory.js' + type ReturnResolverEvent, +} from './actions/subgraph/getNameHistory.js' export { default as getNamesForAddress, + type GetNamesForAddressErrorType, type GetNamesForAddressParameters, type GetNamesForAddressReturnType, type NameWithRelation, -} from './functions/subgraph/getNamesForAddress.js' +} from './actions/subgraph/getNamesForAddress.js' +export { + type GetRecordHistoryErrorType, + type GetRecordHistoryParameters, + type GetRecordHistoryReturnType, + getRecordHistory, +} from './actions/subgraph/getRecordHistory.js' +export { + type GetResolvedNamesForAddressErrorType, + type GetResolvedNamesForAddressParameters, + type GetResolvedNamesForAddressReturnType, + getResolvedNamesForAddress, +} from './actions/subgraph/getResolvedNamesForAddress.js' export { default as getSubgraphRecords, + type GetSubgraphRecordsErrorType, type GetSubgraphRecordsParameters, type GetSubgraphRecordsReturnType, -} from './functions/subgraph/getSubgraphRecords.js' +} from './actions/subgraph/getSubgraphRecords.js' export { default as getSubgraphRegistrant, + type GetSubgraphRegistrantErrorType, type GetSubgraphRegistrantParameters, type GetSubgraphRegistrantReturnType, -} from './functions/subgraph/getSubgraphRegistrant.js' +} from './actions/subgraph/getSubgraphRegistrant.js' export { default as getSubnames, + type GetSubnamesErrorType, type GetSubnamesParameters, type GetSubnamesReturnType, -} from './functions/subgraph/getSubnames.js' +} from './actions/subgraph/getSubnames.js' export { getChecksumAddressOrNull, makeNameObject, type Name, -} from './functions/subgraph/utils.js' +} from './actions/subgraph/utils.js' diff --git a/packages/ensjs/src/test/addTestContracts.ts b/packages/ensjs/src/test/addTestContracts.ts index 52e41d7e5..af816585d 100644 --- a/packages/ensjs/src/test/addTestContracts.ts +++ b/packages/ensjs/src/test/addTestContracts.ts @@ -1,59 +1,82 @@ -import { resolve } from 'node:path' -import { config } from 'dotenv' import { - http, type Account, - type Address, + createPublicClient, + createTestClient, + createWalletClient, type Hash, + http, type PublicClient, type TestClient, type TransactionReceipt, TransactionReceiptNotFoundError, type WalletClient, - createPublicClient, - createTestClient, - createWalletClient, } from 'viem' import { localhost as _localhost } from 'viem/chains' -config({ - path: resolve(import.meta.dirname, '../../.env.local'), - override: true, -}) - -type ContractName = - | 'BaseRegistrarImplementation' - | 'ETHRegistrarController' - | 'Multicall' - | 'NameWrapper' - | 'DNSRegistrar' - | 'PublicResolver' - | 'ENSRegistry' - | 'ReverseRegistrar' - | 'UniversalResolver' - | 'StaticBulkRenewal' - | 'DNSSECImpl' - | 'Root' +/** + * Devnet contract addresses. + * Parsed from: docker logs ens-test-env-devnet-1 + */ +export const deploymentAddresses = { + // ENS v1 + LegacyENSRegistry: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707', + ENSRegistry: '0x0165878A594ca255338adfa4d48449f69242Eb8F', + BaseRegistrarImplementation: '0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0', + Root: '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82', + DNSSECImpl: '0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9', + DNSRegistrar: '0x7A9Ec1d04904907De0ED7b6839CcdD59c3716AC9', + ReverseRegistrar: '0x82e01223d51Eb87e16A03E24687EDF0F294da6f1', + DefaultReverseRegistrar: '0xB0D4afd8879eD9F52b28595d31B441D079B2Ca07', + NameWrapper: '0x7969c5eD335650692Bc04293B07F5BF2e7A673C0', + LegacyETHRegistrarController: '0xf953b3A269d80e3eB0F2947630Da976B896A8C5b', + WrappedETHRegistrarController: '0x253553366Da8546fC250F225fe3d25d0C782303b', + ETHRegistrarController: '0x922D6956C99E12DFeB3224DEA977D0939758A1Fe', + StaticBulkRenewal: '0xAA292E8611aDF267e563f334Ee42320aC96D0463', + LegacyPublicResolver: '0x5067457698Fd6Fa1C6964e416b3f42713513B3dD', + PublicResolver: '0x1fA02b2d6A771842690194Cf62D91bdd92BfE28d', + UniversalResolver: '0x4b6aB5F819A515382B0dEB6935D793817bB4af28', + Multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', -export const deploymentAddresses = JSON.parse( - process.env.DEPLOYMENT_ADDRESSES!, -) as Record< - | ContractName - | 'ENSRegistry' - | 'LegacyPublicResolver' - | 'NoMulticallResolver' - | 'OldestResolver', - Address -> + // ENS v2 + ETHRegistry: '0x4c5859f0F772848b2D91F1D83E2Fe57935348029', + RootRegistry: '0x610178dA211FEF7D417bC0e6FeD39F05609AD788', + // ENSv1 mirror resolver. Reserved v1 names point at this on v2 so the + // Universal Resolver can resolve unmigrated v1 names (matches sepolia + // pre-migration). + ENSV1Resolver: '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853', + UniversalResolverV2: '0x367761085BF3C12e5DA2Df99AC6E1a824612b8fb', + UpgradableUniversalResolverProxy: + '0x4C2F7092C2aE51D986bEFEe378e50BD4dB99C901', + ETHRegistrar: '0x0355B7B8cb128fA5692729Ab3AAa199C1753f726', + VerifiableFactory: '0x7a2088a1bFc9d81c55368AE168C2C02570cB814F', + PermissionedResolverImpl: '0xFD471836031dc5108809D173A067e8486B9047A3', + UserRegistryImpl: '0x2E2Ed0Cfd3AD2f1d34481277b3204d807Ca2F8c2', + OwnedResolver: '0x59b670e9fA9D0A427751Af201D676719a970857b', + BatchRegistrar: '0x2B0d36FACD61B71CC05ab8F3D2355ec3631C0dd5', + MigrationHelper: '0xCace1b78160AE76398F486c8a18044da0d66d86D', + StandardRentPriceOracle: '0x21dF544947ba3E8b3c32561399E88B52Dc8b2823', + USDC: '0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f', + DAI: '0xe915cebbc1570a74177b6c589fed1e8f53117559', + HCAFactory: '0x358680728dedb552adaa9f5eb5d4395b291cf943', +} as const export const localhost = { ..._localhost, + id: 31337, contracts: { + // v1 + ensLegacyRegistry: { + address: deploymentAddresses.ENSRegistry, + }, ensRegistry: { address: deploymentAddresses.ENSRegistry, }, ensUniversalResolver: { - address: deploymentAddresses.UniversalResolver, + // The devnet's Universal Resolver is the UpgradableUniversalResolverProxy, + // which fronts UniversalResolverV2. It resolves both v1 and v2 names and + // exposes the v2-only methods (e.g. findParentRegistry) that the bare v1 + // UniversalResolver lacks. + address: deploymentAddresses.UpgradableUniversalResolverProxy, }, multicall3: { address: deploymentAddresses.Multicall, @@ -82,6 +105,47 @@ export const localhost = { ensDnssecImpl: { address: deploymentAddresses.DNSSECImpl, }, + ensLegacyDnsRegistrar: { + address: deploymentAddresses.DNSRegistrar, + }, + ensLegacyDnssecImpl: { + address: deploymentAddresses.DNSSECImpl, + }, + ensEthRegistrar: { + address: deploymentAddresses.ETHRegistrar, + }, + ensEthRenewerV1: { + address: deploymentAddresses.ETHRegistrar, + }, + ensDefaultReverseRegistrar: { + address: deploymentAddresses.DefaultReverseRegistrar, + }, + legacyEthRegistrarController: { + address: deploymentAddresses.LegacyETHRegistrarController, + }, + legacyPublicResolver: { + address: deploymentAddresses.LegacyPublicResolver, + }, + + // v2 + ethRegistrar: { + address: deploymentAddresses.ETHRegistrar, + }, + usdc: { + address: deploymentAddresses.USDC, + }, + ensVerifiableFactory: { + address: deploymentAddresses.VerifiableFactory, + }, + ensPermissionedResolverImpl: { + address: deploymentAddresses.PermissionedResolverImpl, + }, + ensUserRegistryImpl: { + address: deploymentAddresses.UserRegistryImpl, + }, + ensStandardRentPriceOracle: { + address: deploymentAddresses.StandardRentPriceOracle, + }, }, subgraphs: { ens: { @@ -125,7 +189,11 @@ export const waitForTransaction = async (hash: Hash) => .catch((e) => { if (e instanceof TransactionReceiptNotFoundError) { setTimeout(() => { - waitForTransaction(hash).then(resolveFn) + waitForTransaction(hash).then((receipt) => { + if (receipt.status !== 'success') + reject(new Error('transaction unsuccessful')) + resolveFn(receipt) + }) }, 100) } else { reject(e) diff --git a/packages/ensjs/src/test/createHttpServer.ts b/packages/ensjs/src/test/createHttpServer.ts index 597404082..c9819d347 100644 --- a/packages/ensjs/src/test/createHttpServer.ts +++ b/packages/ensjs/src/test/createHttpServer.ts @@ -1,4 +1,4 @@ -import { type RequestListener, createServer } from 'node:http' +import { createServer, type RequestListener } from 'node:http' import type { AddressInfo } from 'node:net' export function createHttpServer(handler: RequestListener): Promise<{ diff --git a/packages/ensjs/src/test/dns.ts b/packages/ensjs/src/test/dns.ts deleted file mode 100644 index 9f4576b68..000000000 --- a/packages/ensjs/src/test/dns.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { RequestListener } from 'node:http' -import type { MockedFunction } from 'vitest' - -type Handler = MockedFunction - -export const createHandlerResponse = (handler: Handler, response: object) => { - handler.mockImplementation((_req, res) => { - res.writeHead(200, { 'Content-Type': 'application/dns-json' }) - res.end(JSON.stringify(response)) - res.destroy() - }) -} diff --git a/packages/ensjs/src/test/globalSetup.ts b/packages/ensjs/src/test/globalSetup.ts new file mode 100644 index 000000000..b99cbc834 --- /dev/null +++ b/packages/ensjs/src/test/globalSetup.ts @@ -0,0 +1,113 @@ +import { exec } from 'node:child_process' +import { promisify } from 'node:util' +import { seedTestNames } from './seedTestNames.js' + +const execAsync = promisify(exec) + +const DOCKER_COMPOSE_FILE = './compose.yml' +const STARTUP_TIMEOUT = 180000 // 3 minutes +const HEALTH_CHECK_INTERVAL = 2000 // 2 seconds + +let startedByUs = false + +async function isDevnetRunning(): Promise { + try { + const response = await fetch('http://localhost:8545', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'eth_blockNumber', + params: [], + id: 1, + }), + }) + if (response.ok) { + const data = await response.json() + return data.result !== undefined + } + } catch { + // ignore + } + return false +} + +async function waitForDevnetReady(timeout: number): Promise { + const startTime = Date.now() + // The devnet exposes a healthcheck endpoint on port 8000 that returns 200 + // only after all contracts are deployed and test names are seeded + const healthUrl = 'http://localhost:8000/health' + + while (Date.now() - startTime < timeout) { + try { + const response = await fetch(healthUrl) + if (response.ok) { + console.log('✓ Devnet is ready (contracts deployed, names seeded)') + return + } + } catch { + // not ready yet + } + + await new Promise((resolve) => setTimeout(resolve, HEALTH_CHECK_INTERVAL)) + } + + throw new Error(`Devnet did not become ready within ${timeout}ms`) +} + +export async function setup() { + if (await isDevnetRunning()) { + console.log('✓ Devnet already running, skipping docker compose') + + // Wait for devnet to be fully ready (contracts deployed + names seeded) + // even when devnet was started externally + console.log('Waiting for devnet to be ready...') + await waitForDevnetReady(STARTUP_TIMEOUT) + + // Devnet was started externally — seed test names as a fallback + // (idempotent, skips already registered names) + await seedTestNames() + } else { + console.log('Starting Docker Compose services...') + + try { + // Start only the devnet service (other services like ensindexer are not needed for unit tests) + // The devnet image runs with --testNames which seeds names automatically + await execAsync(`docker compose -f ${DOCKER_COMPOSE_FILE} up -d devnet`) + console.log('✓ Devnet service started') + startedByUs = true + + // Wait for devnet to be fully ready (contracts deployed + names seeded) + console.log('Waiting for devnet to be ready...') + await waitForDevnetReady(STARTUP_TIMEOUT) + + // Seed v1 test names (the devnet --testNames only seeds v2-style names) + await seedTestNames() + } catch (error) { + console.error('Failed to start Docker Compose services:', error) + // Attempt cleanup on failure + try { + await execAsync(`docker compose -f ${DOCKER_COMPOSE_FILE} down -v`) + } catch { + // Ignore cleanup errors + } + throw error + } + } + + console.log('✓ Test environment is ready') +} + +export async function teardown() { + if (startedByUs) { + console.log('Stopping Docker Compose services...') + + try { + await execAsync(`docker compose -f ${DOCKER_COMPOSE_FILE} down -v`) + console.log('✓ Docker Compose services stopped') + } catch (error) { + console.error('Failed to stop Docker Compose services:', error) + throw error + } + } +} diff --git a/packages/ensjs/src/test/seedTestNames.ts b/packages/ensjs/src/test/seedTestNames.ts new file mode 100644 index 000000000..0da2aa8ba --- /dev/null +++ b/packages/ensjs/src/test/seedTestNames.ts @@ -0,0 +1,1344 @@ +/** + * Seeds test names for integration tests + * This script registers the minimal set of names needed for tests to pass + */ + +import { cborEncode } from '@ensdomains/address-encoder/utils' +import { permissionedRegistryGetStatusSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry' +import pako from 'pako' +import { + type Address, + bytesToHex, + createPublicClient, + createWalletClient, + type Hash, + http, + keccak256, + namehash, + type PublicClient, + stringToHex, + toHex, + type WalletClient, + zeroAddress, + zeroHash, +} from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { deploymentAddresses } from './addTestContracts.js' + +// Test account from anvil (account #1 with known private key) +const account = privateKeyToAccount( + '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', +) + +// Anvil account #2: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC +const account2 = privateKeyToAccount( + '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a', +) + +// Anvil account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 — the devnet +// deployer, which holds the REGISTRAR role on the v2 ETHRegistry and so can +// create the reserved v2 entries used by pre-migration. +const deployer = privateKeyToAccount( + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', +) + +async function setupClients(l1Url: string) { + const localhost = { + id: 31337, + name: 'Localhost', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { http: [l1Url] }, + }, + contracts: { + ensRegistry: { address: deploymentAddresses.ENSRegistry }, + ensBaseRegistrarImplementation: { + address: deploymentAddresses.BaseRegistrarImplementation, + }, + ensEthRegistrarController: { + address: deploymentAddresses.LegacyETHRegistrarController, + }, + ensNameWrapper: { address: deploymentAddresses.NameWrapper }, + ensPublicResolver: { address: deploymentAddresses.PublicResolver }, + }, + } + + const transport = http(l1Url) + + const publicClient = createPublicClient({ + chain: localhost, + transport, + }) + + const walletClient = createWalletClient({ + account, + chain: localhost, + transport, + }) + + const walletClient2 = createWalletClient({ + account: account2, + chain: localhost, + transport, + }) + + const deployerClient = createWalletClient({ + account: deployer, + chain: localhost, + transport, + }) + + return { + publicClient, + walletClient, + walletClient2, + deployerClient, + addresses: deploymentAddresses, + account, + account2, + deployer, + } +} + +// Register names using LegacyETHRegistrarController (unwrapped names) +// Note: This may cause ensindexer to crash but unit tests don't need it +async function registerUnwrappedName( + walletClient: WalletClient, + publicClient: PublicClient, + registrarController: Address, + resolver: Address, + label: string, + owner: Address, + duration = 31536000, // 1 year +): Promise { + const secret = zeroHash + + // Check if name is available + const available = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [{ name: 'name', type: 'string' }], + name: 'available', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + ] as const, + functionName: 'available', + args: [label], + }) + + if (!available) { + console.log(` ⏭️ Name ${label}.eth already registered, skipping`) + return '0x0' as Hash // Return dummy hash to indicate skip + } + + // Prepare commitment using legacy makeCommitmentWithConfig + const commitment = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [ + { name: 'name', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'secret', type: 'bytes32' }, + { name: 'resolver', type: 'address' }, + { name: 'addr', type: 'address' }, + ], + name: 'makeCommitmentWithConfig', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'pure', + type: 'function', + }, + ] as const, + functionName: 'makeCommitmentWithConfig', + args: [label, owner, secret, resolver, owner], + }) + + // Commit + const commitTx = await walletClient.writeContract({ + chain: null, + address: registrarController, + abi: [ + { + inputs: [{ name: 'commitment', type: 'bytes32' }], + name: 'commit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'commit', + args: [commitment], + }) + + await waitForTransaction(publicClient, commitTx) + + // Wait for commitment age (min wait time) by advancing blockchain time + await increaseTime(120) + + // Get rental price + const price = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [ + { name: 'name', type: 'string' }, + { name: 'duration', type: 'uint256' }, + ], + name: 'rentPrice', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + ] as const, + functionName: 'rentPrice', + args: [label, BigInt(duration)], + }) + + console.log(` Price for ${label}.eth (unwrapped): ${price} wei`) + + // Register using legacy registerWithConfig + const registerTx = await walletClient.writeContract({ + chain: null, + address: registrarController, + abi: [ + { + inputs: [ + { name: 'name', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'duration', type: 'uint256' }, + { name: 'secret', type: 'bytes32' }, + { name: 'resolver', type: 'address' }, + { name: 'addr', type: 'address' }, + ], + name: 'registerWithConfig', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + ] as const, + functionName: 'registerWithConfig', + args: [label, owner, BigInt(duration), secret, resolver, owner], + value: price, + }) + + return registerTx +} + +// Register names using ETHRegistrarController (wrapped names) +// The devnet ETHRegistrarController uses a tuple-based interface: +// makeCommitment((string,address,uint256,bytes32,address,bytes[],uint8,bytes32)) +// register((string,address,uint256,bytes32,address,bytes[],uint8,bytes32)) +const registrationParamsTuple = { + name: 'params', + type: 'tuple', + components: [ + { name: 'name', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'duration', type: 'uint256' }, + { name: 'secret', type: 'bytes32' }, + { name: 'resolver', type: 'address' }, + { name: 'data', type: 'bytes[]' }, + { name: 'ownerControlledFuses', type: 'uint8' }, + { name: 'referralCode', type: 'bytes32' }, + ], +} as const + +async function registerWrappedName( + walletClient: WalletClient, + publicClient: PublicClient, + registrarController: Address, + resolver: Address, + label: string, + owner: Address, + baseRegistrar: Address, + nameWrapper: Address, + duration = 31536000, // 1 year +): Promise { + // Check if name is available + const available = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [{ name: 'name', type: 'string' }], + name: 'available', + outputs: [{ name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + ] as const, + functionName: 'available', + args: [label], + }) + + if (!available) { + console.log(` ⏭️ Name ${label}.eth already registered, skipping`) + return '0x0' as Hash // Return dummy hash to indicate skip + } + + const params = { + name: label, + owner, + duration: BigInt(duration), + secret: zeroHash, + resolver, + data: [] as `0x${string}`[], + ownerControlledFuses: 0, + referralCode: zeroHash, + } + + // Prepare commitment + const commitment = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [registrationParamsTuple], + name: 'makeCommitment', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'pure', + type: 'function', + }, + ] as const, + functionName: 'makeCommitment', + args: [params], + }) + + // Commit + const commitTx = await walletClient.writeContract({ + chain: null, + address: registrarController, + abi: [ + { + inputs: [{ name: 'commitment', type: 'bytes32' }], + name: 'commit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'commit', + args: [commitment], + }) + + await waitForTransaction(publicClient, commitTx) + + // Wait for commitment age by advancing blockchain time + await increaseTime(120) + + // Get rental price + const [base, premium] = await publicClient.readContract({ + address: registrarController, + abi: [ + { + inputs: [ + { name: 'name', type: 'string' }, + { name: 'duration', type: 'uint256' }, + ], + name: 'rentPrice', + outputs: [ + { name: 'base', type: 'uint256' }, + { name: 'premium', type: 'uint256' }, + ], + stateMutability: 'view', + type: 'function', + }, + ] as const, + functionName: 'rentPrice', + args: [label, BigInt(duration)], + }) + + const price = base + premium + console.log(` Price for ${label}.eth: ${price} wei`) + + // Register + const registerTx = await walletClient.writeContract({ + chain: null, + address: registrarController, + abi: [ + { + inputs: [registrationParamsTuple], + name: 'register', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + ] as const, + functionName: 'register', + args: [params], + value: price, + }) + + await waitForTransaction(publicClient, registerTx) + + // The devnet ETHRegistrarController does not auto-wrap, so we wrap manually + // 1. Approve NameWrapper to manage BaseRegistrar tokens + const approveTx = await approveNameWrapper( + walletClient, + baseRegistrar, + nameWrapper, + ) + await waitForTransaction(publicClient, approveTx) + + // 2. Wrap the name via NameWrapper.wrapETH2LD + const wrapTx = await wrapETH2LD( + walletClient, + nameWrapper, + label, + owner, + resolver, + ) + + return wrapTx +} + +async function mineBlock() { + await fetch('http://localhost:8545', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'evm_mine', + params: [], + id: 1, + }), + }) +} + +async function increaseTime(seconds: number) { + await fetch('http://localhost:8545', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'evm_increaseTime', + params: [seconds], + id: 1, + }), + }) + await mineBlock() // Mine a block to apply the time change +} + +async function setPrimaryName( + walletClient: WalletClient, + reverseRegistrar: Address, + name: string, +): Promise { + const tx = await walletClient.writeContract({ + chain: null, + address: reverseRegistrar, + abi: [ + { + inputs: [{ name: 'name', type: 'string' }], + name: 'setName', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setName', + args: [name], + }) + return tx +} + +async function setAddrRecord( + walletClient: WalletClient, + resolver: Address, + name: string, + addr: Address, +): Promise { + const node = namehash(name) + const tx = await walletClient.writeContract({ + chain: null, + address: resolver, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'a', type: 'address' }, + ], + name: 'setAddr', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setAddr', + args: [node, addr], + }) + return tx +} + +async function setAddrRecordWithCoin( + walletClient: WalletClient, + resolver: Address, + name: string, + coinType: bigint, + addrBytes: `0x${string}`, +): Promise { + const node = namehash(name) + const tx = await walletClient.writeContract({ + chain: null, + address: resolver, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'coinType', type: 'uint256' }, + { name: 'a', type: 'bytes' }, + ], + name: 'setAddr', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setAddr', + args: [node, coinType, addrBytes], + }) + return tx +} + +async function setTextRecord( + walletClient: WalletClient, + resolver: Address, + name: string, + key: string, + value: string, +): Promise { + const node = namehash(name) + const tx = await walletClient.writeContract({ + chain: null, + address: resolver, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'key', type: 'string' }, + { name: 'value', type: 'string' }, + ], + name: 'setText', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setText', + args: [node, key, value], + }) + return tx +} + +async function setContenthashRecord( + walletClient: WalletClient, + resolver: Address, + name: string, + contenthash: `0x${string}`, +): Promise { + const node = namehash(name) + const tx = await walletClient.writeContract({ + chain: null, + address: resolver, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'hash', type: 'bytes' }, + ], + name: 'setContenthash', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setContenthash', + args: [node, contenthash], + }) + return tx +} + +async function setAbiRecord( + walletClient: WalletClient, + resolver: Address, + name: string, + contentType: bigint, + data: `0x${string}`, +): Promise { + const node = namehash(name) + const tx = await walletClient.writeContract({ + chain: null, + address: resolver, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'contentType', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + ], + name: 'setABI', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setABI', + args: [node, contentType, data], + }) + return tx +} + +async function approveNameWrapper( + walletClient: WalletClient, + baseRegistrar: Address, + nameWrapper: Address, +): Promise { + const tx = await walletClient.writeContract({ + chain: null, + address: baseRegistrar, + abi: [ + { + inputs: [ + { name: 'operator', type: 'address' }, + { name: 'approved', type: 'bool' }, + ], + name: 'setApprovalForAll', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setApprovalForAll', + args: [nameWrapper, true], + }) + return tx +} + +async function wrapETH2LD( + walletClient: WalletClient, + nameWrapper: Address, + label: string, + owner: Address, + resolver: Address, +): Promise { + const tx = await walletClient.writeContract({ + chain: null, + address: nameWrapper, + abi: [ + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'wrappedOwner', type: 'address' }, + { name: 'fuses', type: 'uint16' }, + { name: 'resolver', type: 'address' }, + ], + name: 'wrapETH2LD', + outputs: [{ name: '', type: 'uint64' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'wrapETH2LD', + args: [label, owner, 0, resolver], + }) + return tx +} + +async function setSubnodeOwner( + walletClient: WalletClient, + nameWrapper: Address, + parentName: string, + label: string, + owner: Address, + fuses: number, + expiry: bigint, +): Promise { + const parentNode = namehash(parentName) + const tx = await walletClient.writeContract({ + chain: null, + address: nameWrapper, + abi: [ + { + inputs: [ + { name: 'parentNode', type: 'bytes32' }, + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'fuses', type: 'uint32' }, + { name: 'expiry', type: 'uint64' }, + ], + name: 'setSubnodeOwner', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setSubnodeOwner', + args: [parentNode, label, owner, fuses, expiry], + }) + return tx +} + +async function setSubnodeOwnerRegistry( + walletClient: WalletClient, + registry: Address, + parentName: string, + label: string, + owner: Address, +): Promise { + const parentNode = namehash(parentName) + const labelHash = keccak256(toHex(label)) + const tx = await walletClient.writeContract({ + chain: null, + address: registry, + abi: [ + { + inputs: [ + { name: 'node', type: 'bytes32' }, + { name: 'label', type: 'bytes32' }, + { name: 'owner', type: 'address' }, + ], + name: 'setSubnodeOwner', + outputs: [{ name: '', type: 'bytes32' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'setSubnodeOwner', + args: [parentNode, labelHash, owner], + }) + return tx +} + +async function waitForTransaction(publicClient: any, hash: Hash) { + // Mine a block to include the transaction + await mineBlock() + + let receipt: + | Awaited> + | undefined + for (let i = 0; i < 20; i++) { + try { + receipt = await publicClient.getTransactionReceipt({ hash }) + if (receipt) { + if (receipt.status !== 'success') { + throw new Error(`Transaction ${hash} reverted`) + } + return receipt + } + } catch (e) { + if (e instanceof Error && e.message.includes('reverted')) { + throw e + } + // Not found yet + } + await new Promise((resolve) => setTimeout(resolve, 200)) + } + throw new Error(`Transaction ${hash} not found after 4 seconds`) +} + +// Max uint64, used as the v2 reservation expiry (no expiry). +const MAX_EXPIRY = 18446744073709551615n + +// Mirror of the on-chain pre-migration: reserve a v1 `.eth` 2LD in the v2 +// ETHRegistry with its resolver pointed at the ENSv1 mirror resolver, so the +// Universal Resolver (URv2) can resolve the unmigrated v1 name. On sepolia the +// migration's pre-migration phase does this via BatchRegistrar; the devnet runs +// no migration, so we replicate it here for the v1 names seeded above. +// +// Idempotent: skips names already present in the v2 ETHRegistry. +async function preMigrateName( + deployerClient: WalletClient, + publicClient: PublicClient, + ethRegistry: Address, + ensV1Resolver: Address, + label: string, + owner: Address, +): Promise { + const status = await publicClient.readContract({ + address: ethRegistry, + abi: permissionedRegistryGetStatusSnippet, + functionName: 'getStatus', + args: [BigInt(keccak256(stringToHex(label)))], + }) + + // Non-zero status means the label already exists on v2 (registered or + // reserved); leave it untouched. + if (status !== 0) return '0x0' as Hash + + return deployerClient.writeContract({ + chain: null, + account: deployerClient.account!, + address: ethRegistry, + abi: [ + { + inputs: [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'registry', type: 'address' }, + { name: 'resolver', type: 'address' }, + { name: 'roleBitmap', type: 'uint256' }, + { name: 'expiry', type: 'uint64' }, + ], + name: 'register', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + ] as const, + functionName: 'register', + args: [label, owner, zeroAddress, ensV1Resolver, 0n, MAX_EXPIRY], + }) +} + +export async function seedTestNames(l1Url = 'http://localhost:8545') { + console.log('🌱 Seeding test names...') + + const { + walletClient, + walletClient2, + deployerClient, + publicClient, + addresses, + account, + account2, + } = await setupClients(l1Url) + + try { + // Helper to conditionally wait for transaction + const maybeWaitForTx = async (hash: Hash) => { + if (hash !== '0x0') { + await waitForTransaction(publicClient, hash) + return true + } + return false + } + + // Register "with-profile.eth" for account2 (0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC) + // This name is expected by many tests with primary name and address records + // Using Legacy controller = unwrapped name (for getWrapperData null test) + console.log(' 📝 Registering with-profile.eth (unwrapped)...') + const txProfile = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-profile', + account2.address, + ) + if (await maybeWaitForTx(txProfile)) { + console.log(' ✓ Registered with-profile.eth') + } + + // Set addr record for with-profile.eth pointing to account2 + console.log(' 📝 Setting addr record for with-profile.eth...') + try { + const txAddr = await setAddrRecord( + walletClient2, + addresses.LegacyPublicResolver, + 'with-profile.eth', + account2.address, + ) + await waitForTransaction(publicClient, txAddr) + console.log(' ✓ Set addr record for with-profile.eth') + } catch { + console.log(' ⏭️ Skipping addr record (may already exist)') + } + + // Set primary name for account2 to with-profile.eth + console.log(' 📝 Setting primary name for account2...') + try { + const txPrimary = await setPrimaryName( + walletClient2, + addresses.ReverseRegistrar, + 'with-profile.eth', + ) + await waitForTransaction(publicClient, txPrimary) + console.log(' ✓ Set primary name for account2') + } catch { + console.log(' ⏭️ Skipping primary name (may already exist)') + } + + // Set text record for with-profile.eth (description = "Hello2") + console.log(' 📝 Setting text record for with-profile.eth...') + try { + const txText = await setTextRecord( + walletClient2, + addresses.LegacyPublicResolver, + 'with-profile.eth', + 'description', + 'Hello2', + ) + await waitForTransaction(publicClient, txText) + console.log(' ✓ Set text record for with-profile.eth') + } catch { + console.log(' ⏭️ Skipping text record (may already exist)') + } + + // Set etcLegacy (coin 61) address for with-profile.eth + // Using the same address as ETH but for coin type 61 + console.log(' 📝 Setting etcLegacy address for with-profile.eth...') + try { + const txEtc = await setAddrRecordWithCoin( + walletClient2, + addresses.LegacyPublicResolver, + 'with-profile.eth', + 61n, + account2.address, + ) + await waitForTransaction(publicClient, txEtc) + console.log(' ✓ Set etcLegacy address for with-profile.eth') + } catch { + console.log(' ⏭️ Skipping etcLegacy record (may already exist)') + } + + // Register "with-contenthash.eth" and set contenthash (unwrapped) + console.log(' 📝 Registering with-contenthash.eth (unwrapped)...') + const txContenthash = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-contenthash', + account.address, + ) + await maybeWaitForTx(txContenthash) + console.log(' ✓ Registered with-contenthash.eth') + + // Set contenthash record (ipfs://bafybeico3uuyj3vphxpvbowchdwjlrlrh62awxscrnii7w7flu5z6fk77y) + // Encoded as: e3010170122002ee392573f8a323abde0c04710971f2cac7a8eb00b9234f7bd82f9e664ebffc + console.log(' 📝 Setting contenthash for with-contenthash.eth...') + try { + const txSetContenthash = await setContenthashRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-contenthash.eth', + '0xe3010170122002ee392573f8a323abde0c04710971f2cac7a8eb00b9234f7bd82f9e664ebffc', + ) + await waitForTransaction(publicClient, txSetContenthash) + console.log(' ✓ Set contenthash for with-contenthash.eth') + } catch { + console.log(' ⏭️ Skipping contenthash (may already exist)') + } + + // ABI test data + const dummyABI = [ + { + type: 'event', + anonymous: false, + name: 'ABIChanged', + inputs: [ + { type: 'bytes32', indexed: true }, + { type: 'uint256', indexed: true }, + ], + }, + { + type: 'event', + anonymous: false, + name: 'VersionChanged', + inputs: [{ type: 'bytes32', indexed: true }, { type: 'uint64' }], + }, + { + type: 'function', + name: 'ABI', + constant: true, + stateMutability: 'view', + payable: false, + inputs: [{ type: 'bytes32' }, { type: 'uint256' }], + outputs: [{ type: 'uint256' }, { type: 'bytes' }], + }, + { + type: 'function', + name: 'clearRecords', + constant: false, + payable: false, + inputs: [{ type: 'bytes32' }], + outputs: [], + }, + { + type: 'function', + name: 'recordVersions', + constant: true, + stateMutability: 'view', + payable: false, + inputs: [{ type: 'bytes32' }], + outputs: [{ type: 'uint64' }], + }, + { + type: 'function', + name: 'setABI', + constant: false, + payable: false, + inputs: [{ type: 'bytes32' }, { type: 'uint256' }, { type: 'bytes' }], + outputs: [], + }, + { + type: 'function', + name: 'supportsInterface', + constant: true, + stateMutability: 'view', + payable: false, + inputs: [{ type: 'bytes4' }], + outputs: [{ type: 'bool' }], + }, + ] + + // Encode ABI for different content types + const abiJsonStr = JSON.stringify(dummyABI) + const abiType1 = stringToHex(abiJsonStr) as `0x${string}` + const abiType2 = toHex(pako.deflate(abiJsonStr)) as `0x${string}` + const abiType4 = bytesToHex( + new Uint8Array(cborEncode(dummyABI)), + ) as `0x${string}` + const abiType8 = stringToHex('https://example.com') as `0x${string}` + + // Register and set ABI for with-type-1-abi.eth (unwrapped) + console.log(' 📝 Registering with-type-1-abi.eth (unwrapped)...') + const txAbi1 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-1-abi', + account.address, + ) + await maybeWaitForTx(txAbi1) + const txSetAbi1 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-1-abi.eth', + 1n, + abiType1, + ) + await waitForTransaction(publicClient, txSetAbi1) + console.log(' ✓ Registered with-type-1-abi.eth') + + // Register and set ABI for with-type-2-abi.eth (unwrapped) + console.log(' 📝 Registering with-type-2-abi.eth (unwrapped)...') + const txAbi2 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-2-abi', + account.address, + ) + await maybeWaitForTx(txAbi2) + const txSetAbi2 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-2-abi.eth', + 2n, + abiType2, + ) + await waitForTransaction(publicClient, txSetAbi2) + console.log(' ✓ Registered with-type-2-abi.eth') + + // Register and set ABI for with-type-4-abi.eth (unwrapped) + console.log(' 📝 Registering with-type-4-abi.eth (unwrapped)...') + const txAbi4 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-4-abi', + account.address, + ) + await maybeWaitForTx(txAbi4) + const txSetAbi4 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-4-abi.eth', + 4n, + abiType4, + ) + await waitForTransaction(publicClient, txSetAbi4) + console.log(' ✓ Registered with-type-4-abi.eth') + + // Register and set ABI for with-type-8-abi.eth (unwrapped) + console.log(' 📝 Registering with-type-8-abi.eth (unwrapped)...') + const txAbi8 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-8-abi', + account.address, + ) + await maybeWaitForTx(txAbi8) + const txSetAbi8 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-8-abi.eth', + 8n, + abiType8, + ) + await waitForTransaction(publicClient, txSetAbi8) + console.log(' ✓ Registered with-type-8-abi.eth') + + // Register and set ABI for with-type-256-abi.eth (unsupported type, unwrapped) + console.log(' 📝 Registering with-type-256-abi.eth (unwrapped)...') + const txAbi256 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-256-abi', + account.address, + ) + await maybeWaitForTx(txAbi256) + const txSetAbi256 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-256-abi.eth', + 256n, + abiType1, + ) + await waitForTransaction(publicClient, txSetAbi256) + console.log(' ✓ Registered with-type-256-abi.eth') + + // Register and set all ABI types for with-type-all-abi.eth (unwrapped) + console.log(' 📝 Registering with-type-all-abi.eth (unwrapped)...') + const txAbiAll = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-type-all-abi', + account.address, + ) + await maybeWaitForTx(txAbiAll) + // Set all ABI types + const txSetAbiAll1 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-all-abi.eth', + 1n, + abiType1, + ) + await waitForTransaction(publicClient, txSetAbiAll1) + const txSetAbiAll2 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-all-abi.eth', + 2n, + abiType2, + ) + await waitForTransaction(publicClient, txSetAbiAll2) + const txSetAbiAll4 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-all-abi.eth', + 4n, + abiType4, + ) + await waitForTransaction(publicClient, txSetAbiAll4) + const txSetAbiAll8 = await setAbiRecord( + walletClient, + addresses.LegacyPublicResolver, + 'with-type-all-abi.eth', + 8n, + abiType8, + ) + await waitForTransaction(publicClient, txSetAbiAll8) + console.log(' ✓ Registered with-type-all-abi.eth') + + // Register "test123.eth" for account (unwrapped - needed for getOwner test) + console.log(' 📝 Registering test123.eth (unwrapped)...') + const txTest123 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'test123', + account.address, + ) + await maybeWaitForTx(txTest123) + console.log(' ✓ Registered test123.eth') + + // Register "with-subnames.eth" (unwrapped) + console.log(' 📝 Registering with-subnames.eth (unwrapped)...') + const tx1 = await registerUnwrappedName( + walletClient, + publicClient, + addresses.LegacyETHRegistrarController, + addresses.LegacyPublicResolver, + 'with-subnames', + account.address, + ) + await maybeWaitForTx(tx1) + console.log(' ✓ Registered with-subnames.eth') + + // Create subname test.with-subnames.eth on the registry for account2 + // This is needed by wrapName (other) and transferName (registry) tests + console.log(' 📝 Creating test.with-subnames.eth on registry...') + try { + const txSubnameTest = await setSubnodeOwnerRegistry( + walletClient, + addresses.ENSRegistry, + 'with-subnames.eth', + 'test', + account2.address, + ) + await waitForTransaction(publicClient, txSubnameTest) + console.log(' ✓ Created test.with-subnames.eth') + } catch { + console.log(' ⏭️ Skipping test.with-subnames.eth (may already exist)') + } + + // Register "wrapped.eth" (wrapped via ETHRegistrarController + NameWrapper) + // Use PublicResolver (NameWrapper-aware) so record operations by NameWrapper owner work + console.log(' 📝 Registering wrapped.eth (wrapped)...') + const tx2 = await registerWrappedName( + walletClient, + publicClient, + addresses.ETHRegistrarController, + addresses.PublicResolver, + 'wrapped', + account.address, + addresses.BaseRegistrarImplementation, + addresses.NameWrapper, + ) + await maybeWaitForTx(tx2) + console.log(' ✓ Registered wrapped.eth') + + // Register "wrapped-with-subnames.eth" (wrapped) + console.log(' 📝 Registering wrapped-with-subnames.eth (wrapped)...') + const tx3 = await registerWrappedName( + walletClient, + publicClient, + addresses.ETHRegistrarController, + addresses.PublicResolver, + 'wrapped-with-subnames', + account.address, + addresses.BaseRegistrarImplementation, + addresses.NameWrapper, + ) + await maybeWaitForTx(tx3) + console.log(' ✓ Registered wrapped-with-subnames.eth') + + // Create subname test.wrapped-with-subnames.eth for account2 + console.log(' 📝 Creating test.wrapped-with-subnames.eth...') + try { + const futureExpiry = BigInt(Math.floor(Date.now() / 1000) + 31536000 * 2) // 2 years from now + const txSubname = await setSubnodeOwner( + walletClient, + addresses.NameWrapper, + 'wrapped-with-subnames.eth', + 'test', + account2.address, + 0, // no fuses + futureExpiry, + ) + await waitForTransaction(publicClient, txSubname) + console.log(' ✓ Created test.wrapped-with-subnames.eth') + } catch { + console.log(' ⏭️ Skipping subname creation (may already exist)') + } + + // Create subname addr.wrapped-with-subnames.eth for account2 + // This is needed by deleteSubname (nameWrapper, asOwner) test + console.log(' 📝 Creating addr.wrapped-with-subnames.eth...') + try { + const futureExpiry2 = BigInt(Math.floor(Date.now() / 1000) + 31536000 * 2) // 2 years from now + const txAddrSubname = await setSubnodeOwner( + walletClient, + addresses.NameWrapper, + 'wrapped-with-subnames.eth', + 'addr', + account2.address, + 0, // no fuses + futureExpiry2, + ) + await waitForTransaction(publicClient, txAddrSubname) + console.log(' ✓ Created addr.wrapped-with-subnames.eth') + } catch { + console.log(' ⏭️ Skipping addr subname creation (may already exist)') + } + + // Register "wrapped-with-expiring-subnames.eth" (wrapped) + console.log( + ' 📝 Registering wrapped-with-expiring-subnames.eth (wrapped)...', + ) + const tx4 = await registerWrappedName( + walletClient, + publicClient, + addresses.ETHRegistrarController, + addresses.PublicResolver, + 'wrapped-with-expiring-subnames', + account.address, + addresses.BaseRegistrarImplementation, + addresses.NameWrapper, + ) + await maybeWaitForTx(tx4) + console.log(' ✓ Registered wrapped-with-expiring-subnames.eth') + + // Register "wrapped-big-duration.eth" with a long duration (wrapped) + console.log(' 📝 Registering wrapped-big-duration.eth (wrapped)...') + const txBigDuration = await registerWrappedName( + walletClient, + publicClient, + addresses.ETHRegistrarController, + addresses.PublicResolver, + 'wrapped-big-duration', + account.address, + addresses.BaseRegistrarImplementation, + addresses.NameWrapper, + 315360000, // 10 years + ) + await maybeWaitForTx(txBigDuration) + console.log(' ✓ Registered wrapped-big-duration.eth') + + // Pre-migration: reserve the seeded v1 `.eth` 2LDs in the v2 ETHRegistry, + // pointed at the ENSv1 mirror resolver, so the Universal Resolver can + // resolve these unmigrated v1 names (mirrors sepolia pre-migration). Each + // reservation is owned by the name's v1 registrant. + console.log(' 📝 Pre-migrating v1 names onto v2...') + const namesToPreMigrate: { label: string; owner: Address }[] = [ + { label: 'with-profile', owner: account2.address }, + { label: 'with-contenthash', owner: account.address }, + { label: 'with-type-1-abi', owner: account.address }, + { label: 'with-type-2-abi', owner: account.address }, + { label: 'with-type-4-abi', owner: account.address }, + { label: 'with-type-8-abi', owner: account.address }, + { label: 'with-type-256-abi', owner: account.address }, + { label: 'with-type-all-abi', owner: account.address }, + // NB: `test123` is intentionally NOT pre-migrated. It stays a v1-only + // name so the v2 getRegisterPrice tests can treat it as available on v2. + { label: 'with-subnames', owner: account.address }, + { label: 'wrapped', owner: account.address }, + { label: 'wrapped-with-subnames', owner: account.address }, + { label: 'wrapped-with-expiring-subnames', owner: account.address }, + { label: 'wrapped-big-duration', owner: account.address }, + ] + for (const { label, owner } of namesToPreMigrate) { + const tx = await preMigrateName( + deployerClient, + publicClient, + addresses.ETHRegistry, + addresses.ENSV1Resolver, + label, + owner, + ) + if (await maybeWaitForTx(tx)) { + console.log(` ✓ Pre-migrated ${label}.eth`) + } + } + + console.log('✅ Test names seeded successfully') + return true + } catch (error) { + console.error('❌ Failed to seed test names:', error) + throw error + } +} + +// Allow running as standalone script +if (import.meta.url === `file://${process.argv[1]}`) { + seedTestNames() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +} diff --git a/packages/ensjs/src/test/setup.ts b/packages/ensjs/src/test/setup.ts index 3342ef1c4..ee09e45f2 100644 --- a/packages/ensjs/src/test/setup.ts +++ b/packages/ensjs/src/test/setup.ts @@ -1,5 +1,25 @@ import { beforeAll, vi } from 'vitest' +// Mock localStorage for tests +const localStorageMock = (() => { + let store: Record = {} + + return { + getItem: (key: string) => store[key] || null, + setItem: (key: string, value: string) => { + store[key] = value.toString() + }, + removeItem: (key: string) => { + delete store[key] + }, + clear: () => { + store = {} + }, + } +})() + +global.localStorage = localStorageMock as Storage + beforeAll(() => { vi.mock('../errors/error-utils.ts', () => ({ getVersion: vi.fn().mockReturnValue('@ensdomains/ensjs@1.0.0-mock.0'), diff --git a/packages/ensjs/src/types.ts b/packages/ensjs/src/types/index.ts similarity index 61% rename from packages/ensjs/src/types.ts rename to packages/ensjs/src/types/index.ts index beecc044b..031e82a54 100644 --- a/packages/ensjs/src/types.ts +++ b/packages/ensjs/src/types/index.ts @@ -1,25 +1,25 @@ import type { + Abi, Account, - Address, + Chain, Client, - SendTransactionParameters, TransactionRequest, + WriteContractParameters, } from 'viem' -import type { ChainWithEns } from './contracts/consts.js' -export type Prettify = { - [K in keyof T]: T[K] +export type Prettify = { + [key in keyof type]: type[key] } & {} -type AssignI = { - [K in keyof T as K extends keyof U - ? U[K] extends void +type AssignI = { + [key in keyof type as key extends keyof union + ? union[key] extends void ? never - : K - : K]: K extends keyof U ? U[K] : T[K] + : key + : key]: key extends keyof union ? union[key] : type[key] } -export type Assign = AssignI & U +export type Assign = AssignI & union export type SimpleTransactionRequest = { [P in keyof Pick]-?: Exclude< @@ -28,13 +28,6 @@ export type SimpleTransactionRequest = { > } -export type TransactionRequestWithPassthrough = - SimpleTransactionRequest & { - passthrough?: TPassthrough - } - -export type GenericPassthrough = { args: any; address: Address } - export type Extended = { [K in keyof Client]?: undefined } & { [key: string]: unknown } @@ -46,21 +39,29 @@ type AllowedWriteParameters = | 'maxPriorityFeePerGas' | 'nonce' | 'account' - + | 'chain' + +export type BasicWriteContractParameters< + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined = Chain | undefined, +> = WriteContractParameters< + Abi, + string, + readonly unknown[], + chain, + account, + chainOverride +> export type WriteTransactionParameters< - TChain extends ChainWithEns, - TAccount extends Account | undefined, - TChainOverride extends ChainWithEns | undefined = ChainWithEns, + chain extends Chain | undefined, + account extends Account | undefined, + chainOverride extends Chain | undefined = Chain | undefined, > = Pick< - SendTransactionParameters, + BasicWriteContractParameters, AllowedWriteParameters > -export type DateWithValue = { - date: Date - value: T -} - export type DecodedAbi = { contentType: 1 | 2 | 4 | 8 | number decoded: boolean @@ -68,8 +69,8 @@ export type DecodedAbi = { } export type DecodedAddr = { - id: number - name: string + coinType: number + symbol: string value: string } @@ -113,19 +114,24 @@ export type NameType = | RootNameSpecifier | EthTldNameSpecifier -type GetEthNameType = TString extends EthTldName +type GetEthNameType = name extends EthTldName ? EthTldNameSpecifier - : TString extends EthSubname + : name extends EthSubname ? EthSubnameSpecifier : Eth2ldNameSpecifier -type GetOtherNameType = TString extends OtherSubname +type GetOtherNameType = name extends OtherSubname ? OtherSubnameSpecifier - : TString extends Other2ldName + : name extends Other2ldName ? Other2ldNameSpecifier : TldNameSpecifier -export type GetNameType = TString extends RootName +export type GetNameType = name extends RootName ? RootNameSpecifier - : TString extends EthAnyName - ? GetEthNameType - : GetOtherNameType + : name extends EthAnyName + ? GetEthNameType + : GetOtherNameType + +export type DateWithValue = { + date: Date + value: T +} diff --git a/packages/ensjs/src/types/internal.ts b/packages/ensjs/src/types/internal.ts new file mode 100644 index 000000000..d9eb69297 --- /dev/null +++ b/packages/ensjs/src/types/internal.ts @@ -0,0 +1,62 @@ +// ================================ +// Helper types for making custom typescript errors +// ================================ + +const _errorSymbol = Symbol() + +export type TypeError = TMessage & { + _: typeof _errorSymbol +} + +// Helper type to remove TypeErrors from unions +/** + * Remove TypeErrors from a union to allow the actual type to be used. + */ +export type ExcludeTE = Exclude> + +/** + * Type assertion helper that removes TypeErrors from a union type. + * This is a no-op function that only exists to help TypeScript's type system. + * Use this when ExcludeTE type helper alone isn't sufficient to narrow the type. + */ +export function ASSERT_NO_TYPE_ERROR(_: T): asserts _ is ExcludeTE {} + +/** + * Type assertion helper that removes TypeErrors from a union type. + * This is a unit function that returns it's input and only exists to help TypeScript's type system. + */ +export function EXCLUDE_TYPE_ERROR(_: T): ExcludeTE { + return _ as ExcludeTE +} + +// ================================ +// String type manipulation +// ================================ + +export declare type UnionToIntersection = ( + U extends any + ? (k: U) => void + : never +) extends (k: infer I) => void + ? I + : never + +export type UnionToOvlds = UnionToIntersection< + U extends any ? (f: U) => void : never +> +export type PopUnion = UnionToOvlds extends (a: infer A) => void + ? A + : never + +export type StringConcatenationOrder< + S extends string, + SEPARATOR extends string, +> = PopUnion extends infer SELF + ? // + SELF extends string + ? Exclude extends never + ? `${SELF}` + : // This works because the values of S are always interpreted in ascending lexiographical order + `${StringConcatenationOrder, SEPARATOR>}${SEPARATOR}${SELF}` + : never + : never diff --git a/packages/ensjs/src/utils/ccipBatchRequest.test.ts b/packages/ensjs/src/utils/ccipBatchRequest.test.ts index 2a847949d..6a51dfe40 100644 --- a/packages/ensjs/src/utils/ccipBatchRequest.test.ts +++ b/packages/ensjs/src/utils/ccipBatchRequest.test.ts @@ -4,14 +4,12 @@ import { createHttpServer } from '../test/createHttpServer.js' import { ccipBatchRequest } from './ccipBatchRequest.js' it('returns array of responses', async () => { - const handler = vi - .fn, ReturnType>() - .mockImplementation((_, res) => { - res.writeHead(200, { - 'Content-Type': 'application/json', - }) - res.end(JSON.stringify({ data: '0xdeadbeef' })) + const handler = vi.fn().mockImplementation((_, res) => { + res.writeHead(200, { + 'Content-Type': 'application/json', }) + res.end(JSON.stringify({ data: '0xdeadbeef' })) + }) const { url } = await createHttpServer(handler) const items = [ ['0x8464135c8F25Da09e49BC8782676a84730C318bC', [url], '0xdeadbeef01'], @@ -37,14 +35,12 @@ it('returns array of responses', async () => { // await close() }) it('removes duplicate requests', async () => { - const handler = vi - .fn, ReturnType>() - .mockImplementation((_, res) => { - res.writeHead(200, { - 'Content-Type': 'application/json', - }) - res.end(JSON.stringify({ data: '0xdeadbeef' })) + const handler = vi.fn().mockImplementation((_, res) => { + res.writeHead(200, { + 'Content-Type': 'application/json', }) + res.end(JSON.stringify({ data: '0xdeadbeef' })) + }) const { url } = await createHttpServer(handler) const items = [ ['0x8464135c8F25Da09e49BC8782676a84730C318bC', [url], '0xdeadbeef'], @@ -70,12 +66,10 @@ it('removes duplicate requests', async () => { // await close() }) it('handles and correctly returns HttpRequestError', async () => { - const handler = vi - .fn, ReturnType>() - .mockImplementation((_, res) => { - res.writeHead(404) - res.end() - }) + const handler = vi.fn().mockImplementation((_, res) => { + res.writeHead(404) + res.end() + }) const { url } = await createHttpServer(handler) const items = [ ['0x8464135c8F25Da09e49BC8782676a84730C318bC', [url], '0xdeadbeef'], @@ -94,14 +88,12 @@ it('handles and correctly returns HttpRequestError', async () => { // await close() }) it('handles and correctly returns misc. error', async () => { - const handler = vi - .fn, ReturnType>() - .mockImplementation((_, res) => { - res.writeHead(200, { - 'Content-Type': 'application/json', - }) - res.end('invalid json') + const handler = vi.fn().mockImplementation((_, res) => { + res.writeHead(200, { + 'Content-Type': 'application/json', }) + res.end('invalid json') + }) const { url } = await createHttpServer(handler) diff --git a/packages/ensjs/src/utils/ccipBatchRequest.ts b/packages/ensjs/src/utils/ccipBatchRequest.ts index 816f0824d..24f324441 100644 --- a/packages/ensjs/src/utils/ccipBatchRequest.ts +++ b/packages/ensjs/src/utils/ccipBatchRequest.ts @@ -1,12 +1,13 @@ import { type Address, BaseError, - type Hex, - HttpRequestError, ccipRequest, encodeErrorResult, + type Hex, + HttpRequestError, parseAbi, } from 'viem' +import type { ErrorType } from '../errors/utils.js' type ReadonlyDeep = { readonly [P in keyof T]: ReadonlyDeep @@ -16,6 +17,10 @@ const errorAbi = parseAbi(['error HttpError((uint16, string)[])']) type CcipRequestItem = [success: boolean, result: Hex] +// ================================ +// Ccip request item handler +// ================================ + const ccipRequestItemHandler = async ([ wrappedSender, wrappedUrls, @@ -50,6 +55,13 @@ const ccipRequestItemHandler = async ([ } } +// ================================ +// Ccip batch request +// ================================ + +// Could possibly be error free, but not sure +export type CcipBatchRequestErrorType = ErrorType + export const ccipBatchRequest = async ( callDatas: ReadonlyDeep<[Address, string[], Hex][]>, ) => { diff --git a/packages/ensjs/src/utils/ccipRequest.test.ts b/packages/ensjs/src/utils/ccipRequest.test.ts index 0fd32c307..0910689fa 100644 --- a/packages/ensjs/src/utils/ccipRequest.test.ts +++ b/packages/ensjs/src/utils/ccipRequest.test.ts @@ -2,23 +2,21 @@ import type { RequestListener } from 'node:http' import { encodeFunctionData, parseAbi } from 'viem' import { mainnet } from 'viem/chains' import { expect, it, vi } from 'vitest' -import { getChainContractAddress } from '../contracts/getChainContractAddress.js' -import { addEnsContracts } from '../index.js' +import { getChainContractAddress } from '../clients/shared.js' +import { addEnsL1Contracts } from '../index.js' import { createHttpServer } from '../test/createHttpServer.js' import { ccipRequest } from './ccipRequest.js' -const handler = vi - .fn, ReturnType>() - .mockImplementation((_, res) => { - res.writeHead(200, { - 'Content-Type': 'application/json', - }) - res.end(JSON.stringify({ data: '0xdeadbeef' })) +const handler = vi.fn().mockImplementation((_, res) => { + res.writeHead(200, { + 'Content-Type': 'application/json', }) + res.end(JSON.stringify({ data: '0xdeadbeef' })) +}) it('uses viemCcipRequest for standard request', async () => { const { url } = await createHttpServer(handler) - const chain = addEnsContracts(mainnet) + const chain = addEnsL1Contracts(mainnet) const result = await ccipRequest(chain)({ sender: '0x8464135c8F25Da09e49BC8782676a84730C318bC', urls: [url], @@ -30,10 +28,10 @@ it('uses viemCcipRequest for standard request', async () => { }) it('uses viemCcipRequest for universalresolver request that is not matching signature', async () => { const { url } = await createHttpServer(handler) - const chain = addEnsContracts(mainnet) + const chain = addEnsL1Contracts(mainnet) const result = await ccipRequest(chain)({ sender: getChainContractAddress({ - client: { chain }, + chain, contract: 'ensUniversalResolver', }), urls: [url], @@ -48,7 +46,7 @@ it('uses ccipBatchRequest for universalresolver request that is matching signatu 'function query((address,string[],bytes)[]) returns (bool[],bytes[])', ]) const { url } = await createHttpServer(handler) - const chain = addEnsContracts(mainnet) + const chain = addEnsL1Contracts(mainnet) const data = encodeFunctionData({ abi, args: [ @@ -57,7 +55,7 @@ it('uses ccipBatchRequest for universalresolver request that is matching signatu }) const result = await ccipRequest(chain)({ sender: getChainContractAddress({ - client: { chain }, + chain, contract: 'ensUniversalResolver', }), urls: ['https://example.com'], diff --git a/packages/ensjs/src/utils/ccipRequest.ts b/packages/ensjs/src/utils/ccipRequest.ts index 3e7437abc..7e2194091 100644 --- a/packages/ensjs/src/utils/ccipRequest.ts +++ b/packages/ensjs/src/utils/ccipRequest.ts @@ -1,14 +1,22 @@ import { type CcipRequestParameters, type Chain, + type DecodeFunctionDataErrorType, decodeFunctionData, + type EncodeFunctionResultErrorType, encodeFunctionResult, + type GetChainContractAddressErrorType, + type IsAddressEqualErrorType, isAddressEqual, parseAbi, + type CcipRequestErrorType as viem_CcipRequestErrorType, ccipRequest as viemCcipRequest, } from 'viem' -import { getChainContractAddress } from '../contracts/getChainContractAddress.js' -import { ccipBatchRequest } from './ccipBatchRequest.js' +import { getChainContractAddress } from '../clients/shared.js' +import { + type CcipBatchRequestErrorType, + ccipBatchRequest, +} from './ccipBatchRequest.js' const abi = parseAbi([ 'function query((address,string[],bytes)[]) returns (bool[],bytes[])', @@ -16,6 +24,18 @@ const abi = parseAbi([ const universalResolverQuerySig = '0xa780bab6' +// ================================ +// Ccip request +// ================================ + +export type CcipRequestErrorType = + | GetChainContractAddressErrorType + | IsAddressEqualErrorType + | DecodeFunctionDataErrorType + | CcipBatchRequestErrorType + | EncodeFunctionResultErrorType + | viem_CcipRequestErrorType + export const ccipRequest = (chain: TChain) => async ({ @@ -23,8 +43,9 @@ export const ccipRequest = sender, urls, }: CcipRequestParameters): ReturnType => { + // TODO: Improve const universalResolverAddress = getChainContractAddress({ - client: { chain }, + chain, contract: 'ensUniversalResolver', }) const isUniversalResolverRequest = isAddressEqual( diff --git a/packages/ensjs/src/utils/checkSafeUniversalResolverData.test.ts b/packages/ensjs/src/utils/checkSafeUniversalResolverData.test.ts deleted file mode 100644 index fd50b7c21..000000000 --- a/packages/ensjs/src/utils/checkSafeUniversalResolverData.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { RawContractError } from 'viem' -import { describe, expect, it } from 'vitest' -import { universalResolverResolveSnippet } from '../contracts/universalResolver.js' -import { checkSafeUniversalResolverData } from './checkSafeUniversalResolverData.js' - -describe('checkSafeUniversalResolverData', () => { - it('returns true when the data is safe to use and strict is false', () => { - expect( - checkSafeUniversalResolverData('0x1234567890abcdef', { - strict: false, - abi: [], - args: [], - functionName: 'resolve', - address: '0x1234567890abcdef', - }), - ).toBe(true) - }) - - it('returns true when the data is safe to use and strict is true', () => { - expect( - checkSafeUniversalResolverData('0x1234567890abcdef', { - strict: true, - abi: [], - args: [], - functionName: 'resolve', - address: '0x1234567890abcdef', - }), - ).toBe(true) - }) - - it('returns false when the data is a known revert error and strict is false', () => { - expect( - checkSafeUniversalResolverData( - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - strict: false, - abi: universalResolverResolveSnippet, - args: [[], []], - functionName: 'resolve', - address: '0x1234567890abcdef', - }, - ), - ).toBe(false) - }) - - it('throws error when the data is a known revert error and strict is true', () => { - expect(() => { - checkSafeUniversalResolverData( - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - strict: true, - abi: universalResolverResolveSnippet, - args: ['0x', '0x'], - functionName: 'resolve', - address: '0x1234567890abcdef', - }, - ) - }).toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Version: viem@2.30.6] - `) - }) - - it('throws error with args as a function', () => { - expect(() => { - checkSafeUniversalResolverData( - new RawContractError({ - data: '0x7199966d', // ResolverNotFound() - }), - { - strict: true, - abi: universalResolverResolveSnippet, - args: () => ['ab', 'cd'], - functionName: 'resolve', - address: '0x1234567890abcdef', - }, - ) - }).toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted. - - Error: ResolverNotFound() - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (ab, cd) - - Version: viem@2.30.6] - `) - }) - - it('throws error when the data is an unknown error and strict is false', () => { - expect(() => { - checkSafeUniversalResolverData( - new RawContractError({ - data: '0x4ced43fb', // SwagError() - }), - { - strict: false, - abi: universalResolverResolveSnippet, - args: ['0x', '0x'], - functionName: 'resolve', - address: '0x1234567890abcdef', - }, - ) - }).toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted with the following signature: - 0x4ced43fb - - Unable to decode signature "0x4ced43fb" as it was not found on the provided ABI. - Make sure you are using the correct ABI and that the error exists on it. - You can look up the decoded signature here: https://openchain.xyz/signatures?query=0x4ced43fb. - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Docs: https://viem.sh/docs/contract/decodeErrorResult - Version: viem@2.30.6] - `) - }) - - it('throws error when the data is an unknown error and strict is true', () => { - expect(() => { - checkSafeUniversalResolverData( - new RawContractError({ - data: '0x4ced43fb', // SwagError() - }), - { - strict: true, - abi: universalResolverResolveSnippet, - args: ['0x', '0x'], - functionName: 'resolve', - address: '0x1234567890abcdef', - }, - ) - }).toThrowErrorMatchingInlineSnapshot(` - [ContractFunctionExecutionError: The contract function "resolve" reverted with the following signature: - 0x4ced43fb - - Unable to decode signature "0x4ced43fb" as it was not found on the provided ABI. - Make sure you are using the correct ABI and that the error exists on it. - You can look up the decoded signature here: https://openchain.xyz/signatures?query=0x4ced43fb. - - Contract Call: - address: 0x1234567890abcdef - function: resolve(bytes name, bytes data) - args: (0x, 0x) - - Docs: https://viem.sh/docs/contract/decodeErrorResult - Version: viem@2.30.6] - `) - }) -}) diff --git a/packages/ensjs/src/utils/checkSafeUniversalResolverData.ts b/packages/ensjs/src/utils/checkSafeUniversalResolverData.ts deleted file mode 100644 index d5c025407..000000000 --- a/packages/ensjs/src/utils/checkSafeUniversalResolverData.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - type BaseError, - CallExecutionError, - type Hex, - decodeErrorResult, - getContractError, -} from 'viem' -import type { Prettify } from '../types.js' -import { getRevertErrorData } from './getRevertErrorData.js' - -type CheckSafeUniversalResolverDataParameters = Prettify< - { - strict: boolean | undefined - args: any[] | (() => any[]) - } & Omit[1], 'args'> -> - -/** - * Checks if the data returned from a universal resolver is safe to use, or if it is a known revert error - * @param parameters - {@link CheckSafeUniversalResolverDataParameters} - * @returns `true` if the data is safe to use, `false` if it is a known revert error, or throws if it is an unknown error - */ -export const checkSafeUniversalResolverData = ( - data: Hex | BaseError, - { - strict, - abi, - args, - functionName, - address, - docsPath, - sender, - }: CheckSafeUniversalResolverDataParameters, -): data is Hex => { - if (typeof data === 'object') { - // if not strict, attempt to check if the error is a known revert error before throwing - // if it is a known revert error, return false instead of throwing - // even if strict is false, we want to throw unknown contract errors - if (!strict) { - const errorData = getRevertErrorData(data) - if (errorData) { - try { - decodeErrorResult({ - abi, - data: errorData, - }) - return false - } catch (_error) {} - } - } - if (data instanceof CallExecutionError) throw data - throw getContractError(data, { - abi, - args: typeof args === 'function' ? args() : args, - functionName, - address, - docsPath, - sender, - }) as BaseError - } - return true -} diff --git a/packages/ensjs/src/utils/clientWithOverrides.ts b/packages/ensjs/src/utils/clientWithOverrides.ts new file mode 100644 index 000000000..100f36e5d --- /dev/null +++ b/packages/ensjs/src/utils/clientWithOverrides.ts @@ -0,0 +1,27 @@ +import type { Account, Address, Chain, Client, Transport } from 'viem' +import { type ParseAccountErrorType, parseAccount } from 'viem/utils' + +// ================================ +// Client with overrides +// ================================ + +export type ClientWithOverridesErrorType = ParseAccountErrorType + +export const clientWithOverrides = < + chain extends Chain | undefined, + account extends Account | undefined, +>( + client: Client, + overrides: { + account?: unknown + chain?: unknown + }, +) => { + return { + ...client, + account: parseAccount( + (overrides.account as Account | Address | undefined) || client.account!, + ), + chain: overrides.chain || client.chain, + } as Client, NonNullable> +} diff --git a/packages/ensjs/src/utils/coders/clearRecords.ts b/packages/ensjs/src/utils/coders/clearRecords.ts new file mode 100644 index 000000000..d6f82f7b9 --- /dev/null +++ b/packages/ensjs/src/utils/coders/clearRecords.ts @@ -0,0 +1,25 @@ +import { publicResolverClearRecordsSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type EncodeFunctionDataParameters, + type NamehashErrorType, + namehash, +} from 'viem' + +// ================================ +// Clear records parameters +// ================================ + +export type ClearRecordsParametersErrorType = NamehashErrorType + +export const clearRecordsParameters = (name: string) => + ({ + abi: publicResolverClearRecordsSnippet, + functionName: 'clearRecords', + args: [namehash(name)], + }) as const satisfies EncodeFunctionDataParameters< + typeof publicResolverClearRecordsSnippet + > + +export type ClearRecordsParametersReturnType = ReturnType< + typeof clearRecordsParameters +> diff --git a/packages/ensjs/src/utils/encoders/encodeAbi.test.ts b/packages/ensjs/src/utils/coders/encodeAbi.test.ts similarity index 100% rename from packages/ensjs/src/utils/encoders/encodeAbi.test.ts rename to packages/ensjs/src/utils/coders/encodeAbi.test.ts diff --git a/packages/ensjs/src/utils/coders/encodeAbi.ts b/packages/ensjs/src/utils/coders/encodeAbi.ts new file mode 100644 index 000000000..b7a90b45e --- /dev/null +++ b/packages/ensjs/src/utils/coders/encodeAbi.ts @@ -0,0 +1,143 @@ +import { + type BytesToHexErrorType, + bytesToHex, + type Hex, + type StringToHexErrorType, + stringToHex, +} from 'viem' +import { type ErrorType, UnknownContentTypeError } from '../../errors/utils.js' +import type { Prettify } from '../../types/index.js' +import { deflateToHex } from '../deflate.js' + +export type AbiEncodeAs = 'json' | 'zlib' | 'cbor' | 'uri' + +type AbiContentType = 1 | 2 | 4 | 8 + +const abiEncodeMap = { + json: 1, + zlib: 2, + cbor: 4, + uri: 8, +} as const +type AbiEncodeMap = typeof abiEncodeMap + +type GetAbiContentType = AbiEncodeMap[encodeAs] + +export type EncodeAbiParameters = + encodeAs extends 'uri' + ? { + encodeAs: encodeAs + data: string | null + } + : { + encodeAs: encodeAs + data: Record | null + } + +export type EncodedAbi = { + contentType: contentType + encodedData: Hex +} + +export type EncodeAbiReturnType = + EncodedAbi + +// ================================ +// Content Type To Encode As +// ================================ + +export type ContentTypeToEncodeAsErrorType = UnknownContentTypeError + +/** + * Converts a content type to an encode as + * + * @throws {ContentTypeToEncodeAsErrorType} + * @param contentType - The content type to convert + */ +export const contentTypeToEncodeAs = ( + contentType: AbiContentType, +): AbiEncodeAs => { + switch (contentType) { + case 1: + return 'json' + case 2: + return 'zlib' + case 4: + return 'cbor' + case 8: + return 'uri' + default: + throw new UnknownContentTypeError({ contentType }) + } +} + +// ================================ +// Encode As To Content Type +// ================================ + +export type EncodeAsToContentTypeErrorType = UnknownContentTypeError + +export const encodeAsToContentType = ( + encodeAs: AbiEncodeAs, +): AbiContentType => { + const contentType = abiEncodeMap[encodeAs] + if (contentType === undefined) { + throw new UnknownContentTypeError({ contentType: encodeAs }) + } + return contentType +} + +// ================================ +// Encode Abi +// ================================ + +export type EncodeAbiErrorType = + | UnknownContentTypeError + | StringToHexErrorType + | BytesToHexErrorType + | ErrorType + +export const encodeAbi = async < + encodeAs extends AbiEncodeAs, + contentType extends GetAbiContentType, +>({ + encodeAs, + data, +}: EncodeAbiParameters): Promise< + Prettify> +> => { + let contentType: AbiContentType + let encodedData: Hex = '0x' + switch (encodeAs) { + case 'json': + contentType = 1 + // may throw StringToHexErrorType + if (data) encodedData = stringToHex(JSON.stringify(data)) + break + case 'zlib': { + contentType = 2 + if (data) { + encodedData = await deflateToHex(data) + } + break + } + case 'cbor': { + contentType = 4 + if (data) { + const { cborEncode } = await import('@ensdomains/address-encoder/utils') + // may throw BytesToHexErrorType + encodedData = bytesToHex(new Uint8Array(cborEncode(data))) + } + break + } + default: { + contentType = 8 + if (data) { + // may throw StringToHexErrorType + encodedData = stringToHex(data as string) + } + break + } + } + return { contentType: contentType as contentType, encodedData } +} diff --git a/packages/ensjs/src/utils/coders/getAbi.test.ts b/packages/ensjs/src/utils/coders/getAbi.test.ts new file mode 100644 index 000000000..0bb0b217e --- /dev/null +++ b/packages/ensjs/src/utils/coders/getAbi.test.ts @@ -0,0 +1,154 @@ +import { publicResolverAbiSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { encodeFunctionResult, stringToHex } from 'viem' +import { describe, expect, it } from 'vitest' +import { + decodeAbiResult, + decodeAbiResultFromPrimitiveTypes, + getAbiParameters, +} from './getAbi.js' + +describe('getAbiParameters', () => { + it('returns parameters', () => { + expect(getAbiParameters({ name: 'test.eth' })).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "contentTypes", + "type": "uint256", + }, + ], + "name": "ABI", + "outputs": [ + { + "name": "", + "type": "uint256", + }, + { + "name": "", + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 15n, + ], + "functionName": "ABI", + } + `) + }) + it('allows content type override', () => { + expect( + getAbiParameters({ name: 'test.eth', supportedContentTypes: 0x1n }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "contentTypes", + "type": "uint256", + }, + ], + "name": "ABI", + "outputs": [ + { + "name": "", + "type": "uint256", + }, + { + "name": "", + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 1n, + ], + "functionName": "ABI", + } + `) + }) +}) +describe('decodeAbiResultFromPrimitiveTypes', () => { + it('returns object', async () => { + await expect( + decodeAbiResultFromPrimitiveTypes({ + decodedData: [8n, stringToHex('https://example.com')], + }), + ).resolves.toMatchInlineSnapshot(` + { + "abi": "https://example.com", + "contentType": 8, + "decoded": true, + } + `) + }) + it('returns null data when content type is 0', async () => { + await expect( + decodeAbiResultFromPrimitiveTypes({ decodedData: [0n, '0x1234'] }), + ).resolves.toBeNull() + }) + it('returns null data when abi data is 0x', async () => { + await expect( + decodeAbiResultFromPrimitiveTypes({ decodedData: [1n, '0x'] }), + ).resolves.toBeNull() + }) +}) +describe('decodeAbiResult', () => { + it('decodes abi result', async () => { + const encoded = encodeFunctionResult({ + abi: publicResolverAbiSnippet, + functionName: 'ABI', + result: [8n, stringToHex('https://example.com')], + }) + await expect( + decodeAbiResult(encoded, { strict: false }), + ).resolves.toMatchInlineSnapshot(` + { + "abi": "https://example.com", + "contentType": 8, + "decoded": true, + } + `) + }) + it('returns null when strict is false and decoding fails', async () => { + const encoded = encodeFunctionResult({ + abi: publicResolverAbiSnippet, + functionName: 'ABI', + result: [1n, '0x1234'], + }) + await expect( + decodeAbiResult(encoded, { strict: false }), + ).resolves.toBeNull() + }) + it('throws when strict is true and decoding fails', async () => { + const encoded = encodeFunctionResult({ + abi: publicResolverAbiSnippet, + functionName: 'ABI', + result: [1n, '0x1234'], + }) + await expect( + decodeAbiResult(encoded, { strict: true }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[SyntaxError: Unexpected token '', "4" is not valid JSON]`, + ) + }) +}) diff --git a/packages/ensjs/src/utils/coders/getAbi.ts b/packages/ensjs/src/utils/coders/getAbi.ts new file mode 100644 index 000000000..ed8dd13f9 --- /dev/null +++ b/packages/ensjs/src/utils/coders/getAbi.ts @@ -0,0 +1,204 @@ +import { publicResolverAbiSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type DecodeFunctionResultErrorType, + decodeFunctionResult, + type Hex, + type HexToBytesErrorType, + type HexToStringErrorType, + hexToBytes, + hexToString, + type NamehashErrorType, + namehash, + type TrimErrorType, + trim, +} from 'viem' +import type { ErrorType } from '../../errors/utils.js' +import type { DecodedAbi, Prettify } from '../../types/index.js' +import { inflateFromHex } from '../deflate.js' + +/** @deprecated */ +export type GetAbiParameters = { + /** Name to get ABI record for */ + name: string + /** Supported content types as bitwise + * ID 1: JSON + * ID 2: zlib compressed JSON + * ID 4: CBOR + * ID 8: URI + */ + supportedContentTypes?: bigint | undefined + /** Whether or not to throw decoding errors */ + strict?: boolean | undefined +} + +/** + * @deprecated + */ +export type GetAbiReturnType = Prettify + +/** + * @deprecated + */ +export type GetAbiErrorType = Error + +// ================================ +// Get ABI parameters +// ================================ + +export type GetAbiParametersParameters = { + /** Name to get ABI record for */ + name: string + /** Supported content types as bitwise + * ID 1: JSON + * ID 2: zlib compressed JSON + * ID 4: CBOR + * ID 8: URI + */ + supportedContentTypes?: bigint | undefined +} + +export type GetAbiParametersReturnType = { + abi: typeof publicResolverAbiSnippet + functionName: 'ABI' + args: readonly [Hex, bigint] +} + +export type GetAbiParametersErrorType = NamehashErrorType + +export function getAbiParameters({ + name, + supportedContentTypes = 0xfn, +}: GetAbiParametersParameters): GetAbiParametersReturnType { + return { + abi: publicResolverAbiSnippet, + functionName: 'ABI', + args: [namehash(name), supportedContentTypes], + } as const +} + +// ================================ +// Decode ABI result from primitive types +// ================================ + +export type DecodeAbiResultFromPrimitiveTypesParameters = { + decodedData: readonly [bigint, Hex] +} + +export type DecodeAbiResultFromPrimitiveTypesReturnType = + Prettify + +export type DecodeAbiResultFromPrimitiveTypesErrorType = + | TrimErrorType + | HexToStringErrorType + | HexToBytesErrorType + | ErrorType + +export async function decodeAbiResultFromPrimitiveTypes({ + decodedData: [bigintContentType, encodedAbiData], +}: DecodeAbiResultFromPrimitiveTypesParameters): Promise { + if (!bigintContentType || !encodedAbiData) return null + + const contentType = Number(bigintContentType) + if (!contentType) return null + + if ( + encodedAbiData === '0x' || + encodedAbiData === '0x0' || + // may throw TrimErrorType + trim(encodedAbiData) === '0x00' + ) + return null + + let abiData: string | object + let decoded = false + switch (contentType) { + // JSON + case 1: + // may throw Error + abiData = JSON.parse( + // may throw HexToStringErrorType + hexToString(encodedAbiData), + ) + decoded = true + break + // zlib compressed JSON + case 2: { + abiData = await inflateFromHex(encodedAbiData) + decoded = true + break + } + // CBOR + case 4: { + const { cborDecode } = await import('@ensdomains/address-encoder/utils') + // may throw Error + abiData = await cborDecode( + // may throw HexToBytesErrorType + hexToBytes(encodedAbiData).buffer, + ) + decoded = true + break + } + // URI + case 8: + // may throw HexToStringErrorType + abiData = hexToString(encodedAbiData) + decoded = true + break + default: + try { + // may throw HexToStringErrorType + abiData = hexToString(encodedAbiData) + decoded = true + } catch { + abiData = encodedAbiData + } + } + + return { + contentType, + decoded, + abi: abiData, + } +} + +// ================================ +// Decode ABI result +// ================================ + +export type DecodeAbiResultParameters = { + /** Whether or not to throw decoding errors */ + strict?: boolean | undefined +} + +export type DecodeAbiResultReturnType = Prettify + +export type DecodeAbiResultErrorType = DecodeFunctionResultErrorType +/** + * Decodes an ABI result from a hex string. + * @param data - The hex string to decode. + * @param strict - Whether or not to throw decoding errors. + * @throws {DecodeAbiResultErrorType} + * @returns The decoded ABI result. + */ +export async function decodeAbiResult( + data: Hex, + { strict }: DecodeAbiResultParameters, +): Promise { + if (data === '0x') return null + + try { + // may throw DecodeFunctionResultErrorType + const decodedData = decodeFunctionResult({ + abi: publicResolverAbiSnippet, + functionName: 'ABI', + data, + }) + + return await decodeAbiResultFromPrimitiveTypes({ + decodedData, + }) + } catch (error) { + if (strict) throw error + return null + } +} diff --git a/packages/ensjs/src/utils/coders/getAddress.test.ts b/packages/ensjs/src/utils/coders/getAddress.test.ts new file mode 100644 index 000000000..af2286feb --- /dev/null +++ b/packages/ensjs/src/utils/coders/getAddress.test.ts @@ -0,0 +1,207 @@ +import { + publicResolverMultiAddrSnippet, + publicResolverSingleAddrSnippet, +} from '@ensdomains/ensjs-abi/v1/publicResolver' +import { encodeFunctionResult } from 'viem' +import { describe, expect, it } from 'vitest' +import { + decodeAddressResult, + decodeAddressResultFromPrimitiveTypes, + getAddressParameters, +} from './getAddress.js' + +describe('getAddressParameters', () => { + it('returns parameters for ETH address', () => { + expect(getAddressParameters({ name: 'test.eth' })).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + ], + "functionName": "addr", + } + `) + }) + + it('returns parameters for non-ETH address', () => { + expect( + getAddressParameters({ name: 'test.eth', coin: 'btc' }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + ], + "name": "addr", + "outputs": [ + { + "name": "", + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 0n, + ], + "functionName": "addr", + } + `) + }) +}) + +describe('decodeAddressResultFromPrimitiveTypes', () => { + it('returns ETH address', () => { + const result = decodeAddressResultFromPrimitiveTypes({ + decodedData: '0x1234567890123456789012345678901234567890', + coin: 60, + }) + expect(result).toEqual({ + coinType: 60, + symbol: 'eth', + value: '0x1234567890123456789012345678901234567890', + }) + }) + + it('returns BTC address', () => { + const result = decodeAddressResultFromPrimitiveTypes({ + decodedData: '0x76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac', + coin: 'btc', + }) + expect(result).toEqual({ + coinType: 0, + symbol: 'btc', + value: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', + }) + }) + + it('returns null for empty data', () => { + const result = decodeAddressResultFromPrimitiveTypes({ + decodedData: '0x', + coin: 60, + }) + expect(result).toBeNull() + }) +}) + +describe('decodeAddressResult', () => { + it('decodes address result', () => { + const encoded = encodeFunctionResult({ + abi: publicResolverSingleAddrSnippet, + functionName: 'addr', + result: '0x1234567890123456789012345678901234567890', + }) + expect( + decodeAddressResult(encoded, { coin: 60, strict: false }), + ).toMatchInlineSnapshot(` + { + "coinType": 60, + "symbol": "eth", + "value": "0x1234567890123456789012345678901234567890", + } + `) + }) + + it('returns null when strict is false and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(decodeAddressResult(encoded, { coin: 60, strict: false })).toBeNull() + }) + + it('throws when strict is true and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(() => + decodeAddressResult(encoded, { coin: 60, strict: true }), + ).toThrowErrorMatchingInlineSnapshot(` + [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. + + Params: (address) + Data: 0x1234 (2 bytes) + + Version: viem@2.47.10] + `) + }) + + it('decodes non-ETH address', async () => { + const encoded = encodeFunctionResult({ + abi: publicResolverMultiAddrSnippet, + functionName: 'addr', + result: '0x76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac', + }) + expect( + decodeAddressResult(encoded, { coin: 'btc', strict: false }), + ).toMatchInlineSnapshot(` + { + "coinType": 0, + "symbol": "btc", + "value": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", + } + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/getAddress.ts b/packages/ensjs/src/utils/coders/getAddress.ts new file mode 100644 index 000000000..01c295818 --- /dev/null +++ b/packages/ensjs/src/utils/coders/getAddress.ts @@ -0,0 +1,254 @@ +import { + publicResolverMultiAddrSnippet, + publicResolverSingleAddrSnippet, +} from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type Address, + type DecodeFunctionResultErrorType, + decodeFunctionResult, + type Hex, + type HexToBytesErrorType, + hexToBytes, + type NamehashErrorType, + namehash, + type TrimErrorType, + trim, +} from 'viem' +import { type NormalizeErrorType, normalize } from 'viem/ens' +import type { ErrorType } from '../../errors/utils.js' +import type { DecodedAddr, Prettify } from '../../types/index.js' +import { + type GetCoderFromCoinErrorType, + getCoderFromCoin, +} from '../normalizeCoinId.js' + +const defaultCoinValue = 60 as const + +/** @deprecated */ +export type GetAddressParameters< + coin extends number | string | undefined = undefined, +> = { + /** Name to get the address record for */ + name: string + /** Coin to get the address record for, can be either symbol (string) or coinId (number) (default: `60`) */ + coin?: coin + /** Optionally return raw bytes value of address record (default: false) */ + bypassFormat?: boolean + /** Whether or not to throw decoding errors */ + strict?: boolean +} + +/** @deprecated */ +export type GetAddressReturnType< + coin extends number | string | undefined = undefined, +> = Prettify | null> + +/** @deprecated */ +export type GetAddressErrorType = Error + +type ResolveCoinReturnType = coin extends + | 60 + | 'eth' + ? { + coinType: 60 + symbol: 'eth' + value: Address + } + : DecodedAddr + +const abi = [ + ...publicResolverSingleAddrSnippet, + ...publicResolverMultiAddrSnippet, +] as const + +// ================================ +// Get address parameters +// ================================ + +export type GetAddressParametersParameters< + coin extends number | string | undefined = undefined, +> = { + /** Name to get the address record for */ + name: string + /** Coin to get the address record for, can be either symbol (string) or coinId (number) (default: `60`) */ + coin?: coin + /** Optionally return raw bytes value of address record (default: false) */ + bypassFormat?: boolean + /** Ignore invalid coinTypes */ + ignoreInvalidCoinTypes?: boolean +} + +export type GetAddressParametersErrorType = + | GetCoderFromCoinErrorType + | NamehashErrorType + | NormalizeErrorType + +export function getAddressParameters< + coin extends number | string | undefined = undefined, +>({ + name, + coin = defaultCoinValue, + bypassFormat, + ignoreInvalidCoinTypes, +}: GetAddressParametersParameters) { + // may throw GetCoderFromCoinErrorType + let coinType: number + + if (ignoreInvalidCoinTypes) { + try { + coinType = getCoderFromCoin(coin).coinType + } catch (e) { + const n = typeof coin === 'string' ? Number.parseInt(coin) : coin + if (Number.isNaN(n)) throw e + coinType = n + } + } else { + coinType = getCoderFromCoin(coin).coinType + } + + if (coinType === 60) + return { + abi, + functionName: 'addr', + args: [ + // may throw NamehashErrorType + namehash( + // may throw NormalizeErrorType + normalize(name), + ), + ], + } as const + + return { + abi, + functionName: 'addr', + args: [ + // may throw NamehashErrorType + namehash( + // may throw NormalizeErrorType + normalize(name), + ), + BigInt(bypassFormat ? coin : coinType), + ], + } as const +} + +// ================================ +// Decode address result from primitive types +// ================================ + +export type DecodeAddressResultFromPrimitiveTypesParameters< + coin extends number | string | undefined = undefined, +> = { + decodedData: Hex + coin: coin +} + +export type DecodeAddressResultFromPrimitiveTypesReturnType< + coin extends number | string | undefined = undefined, +> = Prettify | null> + +export type DecodeAddressResultFromPrimitiveTypesErrorType = + | GetCoderFromCoinErrorType + | TrimErrorType + | HexToBytesErrorType + | ErrorType + +export function decodeAddressResultFromPrimitiveTypes< + coin extends number | string | undefined = undefined, +>({ + decodedData, + coin = defaultCoinValue, +}: DecodeAddressResultFromPrimitiveTypesParameters): DecodeAddressResultFromPrimitiveTypesReturnType { + // may throw GetCoderFromCoinErrorType + const coder = getCoderFromCoin(coin) + + // may throw TrimErrorType + const trimmed = trim(decodedData) + if (trimmed === '0x' || trimmed === '0x0' || trimmed === '0x00') return null + + // could throw Error + const decodedAddr = coder.encode( + // may throw HexToBytesErrorType + hexToBytes(decodedData), + ) + + if (!decodedAddr) return null + + return { + coinType: coder.coinType, + symbol: coder.name, + value: decodedAddr, + } as DecodeAddressResultFromPrimitiveTypesReturnType +} + +// ================================ +// Decode address result +// ================================ + +export type DecodeAddressResultParameters< + coin extends number | string | undefined = undefined, +> = { + /** Coin to get the address record for, can be either symbol (string) or coinId (number) (default: `60`) */ + coin?: coin + /** Whether or not to throw decoding errors */ + strict?: boolean +} + +export type DecodeAddressResultReturnType< + coin extends number | string | undefined = undefined, +> = DecodeAddressResultFromPrimitiveTypesReturnType + +export type DecodeAddressResultErrorType = + | GetCoderFromCoinErrorType + | DecodeFunctionResultErrorType + | ErrorType + +export function decodeAddressResult< + coin extends number | string | undefined = undefined, +>( + data: Hex, + { coin = defaultCoinValue, strict }: DecodeAddressResultParameters, +): DecodeAddressResultReturnType { + if (data === '0x') return null + + // may throw GetCoderFromCoinErrorType + const coder = getCoderFromCoin(coin) + + try { + const decodedData = + coder.coinType === 60 + ? // may throw DecodeFunctionResultErrorType + decodeFunctionResult({ + abi: publicResolverSingleAddrSnippet, + functionName: 'addr', + data, + }) + : // may throw DecodeFunctionResultErrorType + decodeFunctionResult({ + abi: publicResolverMultiAddrSnippet, + functionName: 'addr', + data, + }) + + return decodeAddressResultFromPrimitiveTypes({ + decodedData, + coin: coin as coin, + }) + } catch (error) { + if (strict) throw error + return null + } +} diff --git a/packages/ensjs/src/utils/coders/getContentHash.test.ts b/packages/ensjs/src/utils/coders/getContentHash.test.ts new file mode 100644 index 000000000..9af425b93 --- /dev/null +++ b/packages/ensjs/src/utils/coders/getContentHash.test.ts @@ -0,0 +1,100 @@ +import { publicResolverContenthashSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { encodeFunctionResult } from 'viem' +import { describe, expect, it } from 'vitest' +import { + decodeContentHashResult, + decodeContentHashResultFromPrimitiveTypes, + getContentHashParameters, +} from './getContentHash.js' + +describe('getContenthashParameters', () => { + it('returns parameters', () => { + expect( + getContentHashParameters({ name: 'test.eth' }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + ], + "name": "contenthash", + "outputs": [ + { + "name": "", + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + ], + "functionName": "contenthash", + } + `) + }) +}) + +describe('decodeContenthashResultFromPrimitiveTypes', () => { + it('returns decoded IPFS hash', () => { + const result = decodeContentHashResultFromPrimitiveTypes({ + decodedData: + '0xe3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f', + }) + expect(result).toEqual({ + protocolType: 'ipfs', + decoded: 'bafybeibj6lixxzqtsb45ysdjnupvqkufgdvzqbnvmhw2kf7cfkesy7r7d4', + }) + }) + + it('returns null for empty data', () => { + const result = decodeContentHashResultFromPrimitiveTypes({ + decodedData: '0x', + }) + expect(result).toBeNull() + }) +}) + +describe('decodeContenthashResult', () => { + it('decodes contenthash result', () => { + const encoded = encodeFunctionResult({ + abi: publicResolverContenthashSnippet, + functionName: 'contenthash', + result: + '0xe3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f', + }) + expect( + decodeContentHashResult(encoded, { strict: false }), + ).toMatchInlineSnapshot(` + { + "decoded": "bafybeibj6lixxzqtsb45ysdjnupvqkufgdvzqbnvmhw2kf7cfkesy7r7d4", + "protocolType": "ipfs", + } + `) + }) + + it('returns null when strict is false and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(decodeContentHashResult(encoded, { strict: false })).toBeNull() + }) + + it('throws when strict is true and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(() => + decodeContentHashResult(encoded, { strict: true }), + ).toThrowErrorMatchingInlineSnapshot(` + [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. + + Params: (bytes) + Data: 0x1234 (2 bytes) + + Version: viem@2.47.10] + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/getContentHash.ts b/packages/ensjs/src/utils/coders/getContentHash.ts new file mode 100644 index 000000000..297543963 --- /dev/null +++ b/packages/ensjs/src/utils/coders/getContentHash.ts @@ -0,0 +1,81 @@ +import { publicResolverContenthashSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type DecodeFunctionResultErrorType, + decodeFunctionResult, + type Hex, + type NamehashErrorType, + namehash, +} from 'viem' +import type { Prettify } from '../../types/index.js' +import type { + DecodeContentHashErrorType, + DecodedContentHash, +} from '../contentHash.js' +import { decodeContentHash } from '../contentHash.js' + +// ================================ +// Get content hash parameters +// ================================ + +export type GetContentHashParameters = { + /** Name to get content hash record for */ + name: string + /** Whether or not to throw decoding errors */ + strict?: boolean +} + +export type GetContentHashReturnType = Prettify + +export type GetContentHashErrorType = NamehashErrorType + +export function getContentHashParameters({ + name, +}: Omit) { + return { + abi: publicResolverContenthashSnippet, + functionName: 'contenthash', + args: [namehash(name)], + } as const +} + +// ================================ +// Decode content hash result from primitive types +// ================================ +export type DecodeContentHashResultFromPrimitiveTypesErrorType = + DecodeContentHashErrorType + +export function decodeContentHashResultFromPrimitiveTypes({ + decodedData, +}: { + decodedData: Hex +}): GetContentHashReturnType { + return decodeContentHash(decodedData) +} + +// ================================ +// Decode content hash result +// ================================ + +export type DecodeContentHashResultErrorType = + | DecodeFunctionResultErrorType + | DecodeContentHashResultFromPrimitiveTypesErrorType + +export function decodeContentHashResult( + data: Hex, + { strict }: Pick, +): GetContentHashReturnType { + if (data === '0x') return null + + try { + const decodedData = decodeFunctionResult({ + abi: publicResolverContenthashSnippet, + functionName: 'contenthash', + data, + }) + + return decodeContentHashResultFromPrimitiveTypes({ decodedData }) + } catch (error) { + if (strict) throw error + return null + } +} diff --git a/packages/ensjs/src/utils/coders/getText.test.ts b/packages/ensjs/src/utils/coders/getText.test.ts new file mode 100644 index 000000000..27e5c14e3 --- /dev/null +++ b/packages/ensjs/src/utils/coders/getText.test.ts @@ -0,0 +1,95 @@ +import { publicResolverTextSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { encodeFunctionResult } from 'viem' +import { describe, expect, it } from 'vitest' +import { + decodeTextResult, + decodeTextResultFromPrimitiveTypes, + getTextParameters, +} from './getText.js' + +describe('getTextParameters', () => { + it('returns parameters', () => { + expect( + getTextParameters({ name: 'test.eth', key: 'email' }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "key", + "type": "string", + }, + ], + "name": "text", + "outputs": [ + { + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + "email", + ], + "functionName": "text", + } + `) + }) +}) + +describe('decodeTextResultFromPrimitiveTypes', () => { + it('returns decoded text', () => { + const result = decodeTextResultFromPrimitiveTypes({ + decodedData: 'example@example.com', + }) + expect(result).toBe('example@example.com') + }) + + it('returns null for empty data', () => { + const result = decodeTextResultFromPrimitiveTypes({ + decodedData: '', + }) + expect(result).toBeNull() + }) +}) + +describe('decodeTextResult', () => { + it('decodes text result', () => { + const encoded = encodeFunctionResult({ + abi: publicResolverTextSnippet, + functionName: 'text', + result: 'example@example.com', + }) + expect(decodeTextResult(encoded, { strict: false })).toBe( + 'example@example.com', + ) + }) + + it('returns null when strict is false and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(decodeTextResult(encoded, { strict: false })).toBeNull() + }) + + it('throws when strict is true and decoding fails', () => { + const encoded = '0x1234' // Invalid data + expect(() => + decodeTextResult(encoded, { strict: true }), + ).toThrowErrorMatchingInlineSnapshot(` + [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. + + Params: (string) + Data: 0x1234 (2 bytes) + + Version: viem@2.47.10] + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/getText.ts b/packages/ensjs/src/utils/coders/getText.ts new file mode 100644 index 000000000..e39f6c62c --- /dev/null +++ b/packages/ensjs/src/utils/coders/getText.ts @@ -0,0 +1,104 @@ +import { publicResolverTextSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type DecodeFunctionResultErrorType, + decodeFunctionResult, + type Hex, + type NamehashErrorType, + namehash, +} from 'viem' + +/** @deprecated */ +export type GetTextParameters = { + /** Name to get text record for */ + name: string + /** Text record key to get */ + key: string + /** Whether or not to throw decoding errors */ + strict?: boolean +} + +/** @deprecated */ +export type GetTextReturnType = string | null + +/** @deprecated */ +export type GetTextErrorType = Error + +// ================================ +// Get text parameters +// ================================ + +export type GetTextParametersParameters = { + /** Name to get text record for */ + name: string + /** Text record key to get */ + key: string +} + +export type GetTextParametersReturnType = { + abi: typeof publicResolverTextSnippet + functionName: 'text' + args: readonly [Hex, string] +} + +export type GetTextParametersErrorType = NamehashErrorType + +export function getTextParameters({ name, key }: GetTextParametersParameters) { + return { + abi: publicResolverTextSnippet, + functionName: 'text', + args: [namehash(name), key], + } as const +} + +// ================================ +// Decode text result from primitive types +// ================================ + +export type DecodeTextResultFromPrimitiveTypesErrorType = never + +export function decodeTextResultFromPrimitiveTypes({ + decodedData, +}: { + decodedData: string +}): string | null { + if (decodedData === '') return null + return decodedData +} + +// ================================ +// Decode text result +// ================================ + +export type DecodeTextResultParameters = { + strict?: boolean +} + +export type DecodeTextResultReturnType = string | null + +export type DecodeTextResultErrorType = + | DecodeFunctionResultErrorType + | DecodeTextResultFromPrimitiveTypesErrorType + +export function decodeTextResult( + data: Hex, + { + strict, + }: { + strict?: boolean + }, +): DecodeTextResultReturnType { + if (data === '0x') return null + + try { + const decodedData = decodeFunctionResult({ + abi: publicResolverTextSnippet, + functionName: 'text', + data, + }) + + return decodeTextResultFromPrimitiveTypes({ decodedData }) + } catch (error) { + if (strict) throw error + return null + } +} diff --git a/packages/ensjs/src/utils/generateRecordCallArray.test.ts b/packages/ensjs/src/utils/coders/resolverMulticallParameters.test.ts similarity index 65% rename from packages/ensjs/src/utils/generateRecordCallArray.test.ts rename to packages/ensjs/src/utils/coders/resolverMulticallParameters.test.ts index faab8afe9..5690da694 100644 --- a/packages/ensjs/src/utils/generateRecordCallArray.test.ts +++ b/packages/ensjs/src/utils/coders/resolverMulticallParameters.test.ts @@ -1,19 +1,28 @@ +import { encodeFunctionData } from 'viem' import { expect, it } from 'vitest' -import { generateRecordCallArray } from './generateRecordCallArray.js' -import { encodeAbi } from './index.js' -import { namehash } from './normalise.js' +import { + type ResolverMulticallItem, + resolverMulticallParameters, +} from './resolverMulticallParameters.js' -it('generates a record call array', () => { +const encodeResult = (items: readonly ResolverMulticallItem[]) => { + // @ts-expect-error ABI overlap type issues + return items.map((item) => encodeFunctionData(item)) +} + +it('generates a record call array', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - clearRecords: true, - coins: [ - { coin: 'ETH', value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' }, - ], + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + clearRecords: true, + coins: [ + { coin: 'ETH', value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' }, + ], - texts: [{ key: 'email', value: 'test@example.com' }], - }), + texts: [{ key: 'email', value: 'test@example.com' }], + }), + ), ).toMatchInlineSnapshot(` [ "0x3603d758eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", @@ -22,58 +31,69 @@ it('generates a record call array', () => { ] `) }) -it('adds clearRecords call when clearRecords is true', () => { +it('adds clearRecords call when clearRecords is true', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - clearRecords: true, - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + clearRecords: true, + }), + ), ).toMatchInlineSnapshot(` [ "0x3603d758eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", ] `) }) -it('adds contentHash call when contentHash is defined', () => { +it('adds contentHash call when contentHash is defined', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - contentHash: 'ipfs://Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio', - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + contentHash: 'ipfs://Qma8mnp6xV3J2cRNf3mTth5C8nV11CAnceVinc3y8jSbio', + }), + ), ).toMatchInlineSnapshot(` [ "0x304e6adeeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000026e30101701220af4147b366bee9eb9a21a9c2d5ccdde35fa4b8d2e0d44854fc81aeb894e8ce280000000000000000000000000000000000000000000000000000", ] `) }) -it('does not add abi call when abi is undefined', () => { +it('does not add abi call when abi is undefined', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), + await resolverMulticallParameters({ + name: 'test.eth', abi: undefined, }), ).toMatchInlineSnapshot('[]') }) -it('adds text calls when texts array is defined and not empty', () => { +it('adds text calls when texts array is defined and not empty', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - texts: [{ key: 'email', value: 'cool' }], - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + texts: [{ key: 'email', value: 'cool' }], + }), + ), ).toMatchInlineSnapshot(` [ "0x10f13a8ceb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005656d61696c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004636f6f6c00000000000000000000000000000000000000000000000000000000", ] `) }) -it('adds coin calls when coins array is defined and not empty', () => { +it('adds coin calls when coins array is defined and not empty', async () => { expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - coins: [ - { coin: 'ETH', value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' }, - ], - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + coins: [ + { + coin: 'ETH', + value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + }, + ], + }), + ), ).toMatchInlineSnapshot(` [ "0x8b95dd71eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", @@ -81,12 +101,13 @@ it('adds coin calls when coins array is defined and not empty', () => { `) }) it('adds abi call when data is null', async () => { - const result = await encodeAbi({ encodeAs: 'uri', data: null }) expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - abi: result, - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + abi: { encodeAs: 'uri', data: null }, + }), + ), ).toMatchInlineSnapshot(` [ "0x623195b0eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", @@ -94,12 +115,13 @@ it('adds abi call when data is null', async () => { `) }) it('adds abi call when data is not empty', async () => { - const result = await encodeAbi({ encodeAs: 'json', data: { foo: 'bar' } }) expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - abi: result, - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + abi: { encodeAs: 'json', data: { foo: 'bar' } }, + }), + ), ).toMatchInlineSnapshot(` [ "0x623195b0eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d7b22666f6f223a22626172227d00000000000000000000000000000000000000", @@ -107,15 +129,16 @@ it('adds abi call when data is not empty', async () => { `) }) it('adds multiple abi calls when multiple abis are added', async () => { - const result = [ - await encodeAbi({ encodeAs: 'json', data: { foo: 'bar' } }), - await encodeAbi({ encodeAs: 'uri', data: null }), - ] expect( - generateRecordCallArray({ - namehash: namehash('test.eth'), - abi: result, - }), + encodeResult( + await resolverMulticallParameters({ + name: 'test.eth', + abi: [ + { encodeAs: 'json', data: { foo: 'bar' } }, + { encodeAs: 'uri', data: null }, + ], + }), + ), ).toMatchInlineSnapshot(` [ "0x623195b0eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d7b22666f6f223a22626172227d00000000000000000000000000000000000000", diff --git a/packages/ensjs/src/utils/coders/resolverMulticallParameters.ts b/packages/ensjs/src/utils/coders/resolverMulticallParameters.ts new file mode 100644 index 000000000..da6e96d42 --- /dev/null +++ b/packages/ensjs/src/utils/coders/resolverMulticallParameters.ts @@ -0,0 +1,112 @@ +import type { Prettify } from '../../types/index.js' +import { + type ClearRecordsParametersErrorType, + type ClearRecordsParametersReturnType, + clearRecordsParameters, +} from './clearRecords.js' +import type { EncodeAbiParameters } from './encodeAbi.js' +import { + type SetAbiParametersErrorType, + type SetAbiParametersReturnType, + setAbiParameters, +} from './setAbi.js' +import { + type SetAddrParametersErrorType, + type SetAddrParametersParameters, + type SetAddrParametersReturnType, + setAddrParameters, +} from './setAddr.js' +import { + type SetContentHashParametersErrorType, + type SetContentHashParametersReturnType, + setContentHashParameters, +} from './setContentHash.js' +import { + type SetTextParameters, + type SetTextParametersErrorType, + type SetTextParametersReturnType, + setTextParameters, +} from './setText.js' + +export type RecordOptions = Prettify<{ + /** Clears all current records */ + clearRecords?: boolean + /** ContentHash value */ + contentHash?: string | null + /** Array of text records */ + texts?: Omit[] + /** Array of coin records */ + coins?: Omit[] + /** ABI value */ + abi?: EncodeAbiParameters | EncodeAbiParameters[] +}> + +export type ResolverMulticallItem = + | ClearRecordsParametersReturnType + | SetContentHashParametersReturnType + | SetAbiParametersReturnType + | SetTextParametersReturnType + | SetAddrParametersReturnType + +// ================================ +// Resolver multicall item +// ================================ + +export type ResolverMulticallParametersReturnType = ResolverMulticallItem[] + +export type ResolverMulticallItemErrorType = + | ClearRecordsParametersErrorType + | SetContentHashParametersErrorType + | SetAbiParametersErrorType + | SetTextParametersErrorType + | SetAddrParametersErrorType + +/** + * Generates multicall parameters for setting records. + * All individual calls compute the namehash internally. + */ +export const resolverMulticallParameters = async ({ + name, + clearRecords, + contentHash, + texts, + coins, + abi, +}: { + name: string +} & RecordOptions): Promise => { + const calls: ResolverMulticallParametersReturnType = [] + + if (clearRecords) { + calls.push(clearRecordsParameters(name)) + } + + if (contentHash !== undefined) { + const data = setContentHashParameters({ name, contentHash }) + if (data) calls.push(data) + } + + if (abi !== undefined) { + const abis = Array.isArray(abi) ? abi : [abi] + const data = await Promise.all( + abis.map(async (abiItem) => setAbiParameters({ name, ...abiItem })), + ) + if (data) calls.push(...data) + } + + if (texts && texts.length > 0) { + const data = texts.map((textItem) => + setTextParameters({ name, ...textItem }), + ) + if (data) calls.push(...data) + } + + if (coins && coins.length > 0) { + const data = coins.map((coinItem) => + setAddrParameters({ name, ...coinItem }), + ) + if (data) calls.push(...data) + } + + return calls +} diff --git a/packages/ensjs/src/utils/coders/setAbi.test.ts b/packages/ensjs/src/utils/coders/setAbi.test.ts new file mode 100644 index 000000000..21f9345e0 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setAbi.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from 'vitest' +import { setAbiParameters } from './setAbi.js' + +describe('setAbiParameters', () => { + const name = 'test.eth' + + it('returns correct parameters with null data', async () => { + await expect( + setAbiParameters({ name, data: null, encodeAs: 'json' }), + ).resolves.toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "contentType", + "type": "uint256", + }, + { + "name": "data", + "type": "bytes", + }, + ], + "name": "setABI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 1n, + "0x", + ], + "functionName": "setABI", + } + `) + }) + + it('returns correct parameters ', async () => { + await expect( + setAbiParameters({ name, data: { foo: 'bar' }, encodeAs: 'json' }), + ).resolves.toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "contentType", + "type": "uint256", + }, + { + "name": "data", + "type": "bytes", + }, + ], + "name": "setABI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 1n, + "0x7b22666f6f223a22626172227d", + ], + "functionName": "setABI", + } + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/setAbi.ts b/packages/ensjs/src/utils/coders/setAbi.ts new file mode 100644 index 000000000..8deba00ca --- /dev/null +++ b/packages/ensjs/src/utils/coders/setAbi.ts @@ -0,0 +1,54 @@ +import { publicResolverSetAbiSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type EncodeFunctionDataParameters, + type NamehashErrorType, + namehash, +} from 'viem' +import type { Prettify } from '../../types/index.js' +import { + type AbiEncodeAs, + type EncodeAbiErrorType, + type EncodeAbiParameters, + encodeAbi, +} from './encodeAbi.js' + +// ================================ +// Set ABI parameters +// ================================ + +export type SetAbiParameters = + Prettify< + { + /** Name to set ABI for (namehash is computed internally) */ + name: string + } & EncodeAbiParameters + > + +export type SetAbiParametersErrorType = EncodeAbiErrorType | NamehashErrorType + +/** + * Sets ABI parameters for encoding. + * Uses Public Resolver ABI `setABI(node, contentType, data)` + */ +export const setAbiParameters = async ({ + name, + data, + encodeAs, +}: SetAbiParameters) => { + const { contentType, encodedData } = await encodeAbi({ + data, + encodeAs, + } as EncodeAbiParameters) + + return { + abi: publicResolverSetAbiSnippet, + functionName: 'setABI', + args: [namehash(name), BigInt(contentType), encodedData], + } as const satisfies EncodeFunctionDataParameters< + typeof publicResolverSetAbiSnippet + > +} + +export type SetAbiParametersReturnType = Awaited< + ReturnType +> diff --git a/packages/ensjs/src/utils/coders/setAddr.test.ts b/packages/ensjs/src/utils/coders/setAddr.test.ts new file mode 100644 index 000000000..7b4855326 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setAddr.test.ts @@ -0,0 +1,210 @@ +import { describe, expect, suite, test } from 'vitest' +import { setAddrParameters } from './setAddr.js' + +describe('setAddrParameters', () => { + suite('returns the correct parameters', () => { + test('with eth address', () => { + expect( + setAddrParameters({ + name: 'test.eth', + coin: 'eth', + value: '0x1234567890123456789012345678901234567890', + }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + { + "name": "a", + "type": "bytes", + }, + ], + "name": "setAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 60n, + "0x1234567890123456789012345678901234567890", + ], + "functionName": "setAddr", + } + `) + }) + test('with btc address', () => { + expect( + setAddrParameters({ + name: 'test.eth', + coin: 'btc', + value: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', + }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + { + "name": "a", + "type": "bytes", + }, + ], + "name": "setAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 0n, + "0x0014e8df018c7e326cc253faac7e46cdc51e68542c42", + ], + "functionName": "setAddr", + } + `) + }) + + test('with coin type as number', () => { + expect( + setAddrParameters({ + name: 'test.eth', + coin: 60, + value: '0x1234567890123456789012345678901234567890', + }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + { + "name": "a", + "type": "bytes", + }, + ], + "name": "setAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 60n, + "0x1234567890123456789012345678901234567890", + ], + "functionName": "setAddr", + } + `) + }) + + test('with coin type as string', () => { + expect( + setAddrParameters({ + name: 'test.eth', + coin: '60', + value: '0x1234567890123456789012345678901234567890', + }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + { + "name": "a", + "type": "bytes", + }, + ], + "name": "setAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 60n, + "0x1234567890123456789012345678901234567890", + ], + "functionName": "setAddr", + } + `) + }) + + test('with null value', () => { + expect( + setAddrParameters({ + name: 'test.eth', + coin: 'ETH', + value: null, + }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "coinType", + "type": "uint256", + }, + { + "name": "a", + "type": "bytes", + }, + ], + "name": "setAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + 60n, + "0x0000000000000000000000000000000000000000", + ], + "functionName": "setAddr", + } + `) + }) + }) +}) diff --git a/packages/ensjs/src/utils/coders/setAddr.ts b/packages/ensjs/src/utils/coders/setAddr.ts new file mode 100644 index 000000000..032cd0c81 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setAddr.ts @@ -0,0 +1,58 @@ +import { publicResolverSetAddrSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type Address, + type BytesToHexErrorType, + bytesToHex, + type EncodeFunctionDataParameters, + type Hex, + type NamehashErrorType, + namehash, +} from 'viem' +import type { ErrorType } from '../../errors/utils.js' +import { + type GetCoderFromCoinErrorType, + getCoderFromCoin, +} from '../normalizeCoinId.js' + +// ================================ +// Set address parameters +// ================================ + +export type SetAddrParametersParameters = { + /** Name to set address record for (namehash is computed internally) */ + name: string + coin: string | number + value: Address | string | null +} + +export type SetAddrParametersErrorType = + | GetCoderFromCoinErrorType + | BytesToHexErrorType + | ErrorType + | NamehashErrorType + +export const setAddrParameters = ({ + name, + coin, + value, +}: SetAddrParametersParameters) => { + const coder = getCoderFromCoin(coin) + const inputCoinType = coder.coinType + let encodedAddress: Hex | Uint8Array = value ? coder.decode(value) : '0x' + + if (inputCoinType === 60 && encodedAddress === '0x') + encodedAddress = coder.decode('0x0000000000000000000000000000000000000000') + if (typeof encodedAddress !== 'string') { + encodedAddress = bytesToHex(encodedAddress) + } + + return { + abi: publicResolverSetAddrSnippet, + functionName: 'setAddr', + args: [namehash(name), BigInt(inputCoinType), encodedAddress], + } as const satisfies EncodeFunctionDataParameters< + typeof publicResolverSetAddrSnippet + > +} + +export type SetAddrParametersReturnType = ReturnType diff --git a/packages/ensjs/src/utils/coders/setContentHash.test.ts b/packages/ensjs/src/utils/coders/setContentHash.test.ts new file mode 100644 index 000000000..f22f81f46 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setContentHash.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from 'vitest' +import { setContentHashParameters } from './setContentHash.js' + +describe('setContentHashParameters', () => { + const name = 'test.eth' + const contentHash = 'ipfs://QmXwMFNjzjRvZuPvzJfYJZ1QqX2QJjzj1YJZ1QqX2QJjzj' + + it('returns the correct parameters when contentHash is not null', () => { + expect( + setContentHashParameters({ name, contentHash }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "hash", + "type": "bytes", + }, + ], + "name": "setContenthash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + "0xe301017012208e9cc47fde7ff64028480ec671a4ddb8f767a71ff71a73247f51a495a6f29634", + ], + "functionName": "setContenthash", + } + `) + }) + + it('returns the correct parameters when contentHash is null', () => { + expect( + setContentHashParameters({ name, contentHash: null }), + ).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "hash", + "type": "bytes", + }, + ], + "name": "setContenthash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + "0x", + ], + "functionName": "setContenthash", + } + `) + }) + + it('throws an error when contentHash is invalid', () => { + expect(() => + setContentHashParameters({ + name, + contentHash: 'invalid-content-hash', + }), + ).toThrowErrorMatchingInlineSnapshot(` + [InvalidContentHashError: Invalid content hash + + Version: @ensdomains/ensjs@1.0.0-mock.0] + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/setContentHash.ts b/packages/ensjs/src/utils/coders/setContentHash.ts new file mode 100644 index 000000000..ece82bb8b --- /dev/null +++ b/packages/ensjs/src/utils/coders/setContentHash.ts @@ -0,0 +1,42 @@ +import { publicResolverSetContenthashSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type EncodeFunctionDataParameters, + type NamehashErrorType, + namehash, +} from 'viem' +import { + type EncodeContentHashErrorType, + encodeContentHash, +} from '../contentHash.js' + +// ================================ +// Set content hash parameters +// ================================ + +export type SetContentHashParameters = { + /** Name to set content hash for (namehash is computed internally) */ + name: string + contentHash: string | null +} + +export type SetContentHashParametersErrorType = + | EncodeContentHashErrorType + | NamehashErrorType + +export const setContentHashParameters = ({ + name, + contentHash, +}: SetContentHashParameters) => { + const encodedHash = contentHash ? encodeContentHash(contentHash) : '0x' + return { + abi: publicResolverSetContenthashSnippet, + functionName: 'setContenthash', + args: [namehash(name), encodedHash], + } as const satisfies EncodeFunctionDataParameters< + typeof publicResolverSetContenthashSnippet + > +} + +export type SetContentHashParametersReturnType = ReturnType< + typeof setContentHashParameters +> diff --git a/packages/ensjs/src/utils/coders/setText.test.ts b/packages/ensjs/src/utils/coders/setText.test.ts new file mode 100644 index 000000000..63ae8cac9 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setText.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest' +import { setTextParameters } from './setText.js' + +describe('setTextParameters', () => { + const name = 'test.eth' + const key = 'email' + const value = 'test@example.com' + + const parameters = { + name, + key, + value, + } as const + + it('returns the correct parameters', () => { + const result = setTextParameters(parameters) + expect(result).toMatchInlineSnapshot(` + { + "abi": [ + { + "inputs": [ + { + "name": "node", + "type": "bytes32", + }, + { + "name": "key", + "type": "string", + }, + { + "name": "value", + "type": "string", + }, + ], + "name": "setText", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "args": [ + "0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", + "email", + "test@example.com", + ], + "functionName": "setText", + } + `) + }) +}) diff --git a/packages/ensjs/src/utils/coders/setText.ts b/packages/ensjs/src/utils/coders/setText.ts new file mode 100644 index 000000000..332674429 --- /dev/null +++ b/packages/ensjs/src/utils/coders/setText.ts @@ -0,0 +1,31 @@ +import { publicResolverSetTextSnippet } from '@ensdomains/ensjs-abi/v1/publicResolver' +import { + type EncodeFunctionDataParameters, + type NamehashErrorType, + namehash, +} from 'viem' + +// ================================ +// Set text parameters +// ================================ + +export type SetTextParameters = { + /** Name to set text record for (namehash is computed internally) */ + name: string + key: string + value: string | null +} + +export type SetTextParametersErrorType = NamehashErrorType + +export const setTextParameters = ({ name, key, value }: SetTextParameters) => { + return { + abi: publicResolverSetTextSnippet, + functionName: 'setText', + args: [namehash(name), key, value ?? ''], + } as const satisfies EncodeFunctionDataParameters< + typeof publicResolverSetTextSnippet + > +} + +export type SetTextParametersReturnType = ReturnType diff --git a/packages/ensjs/src/utils/contentHash.ts b/packages/ensjs/src/utils/contentHash.ts index dc02d5f78..e7dd9ccd5 100644 --- a/packages/ensjs/src/utils/contentHash.ts +++ b/packages/ensjs/src/utils/contentHash.ts @@ -1,11 +1,11 @@ import { - type Codec as InternalCodec, decode, encode, getCodec, + type Codec as InternalCodec, } from '@ensdomains/content-hash' import { type Hex, isHex } from 'viem' -import { InvalidContentHashError } from '../errors/utils.js' +import { type ErrorType, InvalidContentHashError } from '../errors/utils.js' export type ProtocolType = | 'ipfs' @@ -30,7 +30,14 @@ function matchProtocol(text: string) { ) } +// ================================ +// Get display codec +// ================================ + +export type GetDisplayCodecErrorType = ErrorType + export const getDisplayCodec = (encoded: string): ProtocolType => { + // could throw const codec = getCodec(encoded) switch (codec) { case 'ipfs': @@ -49,6 +56,12 @@ export const getDisplayCodec = (encoded: string): ProtocolType => { } } +// ================================ +// Get internal codec +// ================================ + +export type GetInternalCodecErrorType = ErrorType + export const getInternalCodec = ( displayCodec: NonNullable, ): InternalCodec => { @@ -64,6 +77,12 @@ export const getInternalCodec = ( } } +// ================================ +// Decode content hash +// ================================ + +export type DecodeContentHashErrorType = ErrorType | GetDisplayCodecErrorType + export function decodeContentHash(encoded: Hex): DecodedContentHash | null { if (!encoded || encoded === '0x') { return null @@ -73,12 +92,24 @@ export function decodeContentHash(encoded: Hex): DecodedContentHash | null { return { protocolType, decoded } } +// ================================ +// Is valid content hash +// ================================ + +export type IsValidContentHashErrorType = ErrorType | GetDisplayCodecErrorType + export function isValidContentHash(encoded: unknown) { if (typeof encoded !== 'string') return false const codec = getCodec(encoded) return Boolean(codec && isHex(encoded)) } +// ================================ +// Get protocol type +// ================================ + +export type GetProtocolTypeErrorType = ErrorType + export function getProtocolType(encoded: string) { const matched = matchProtocol(encoded) if (!matched) return null @@ -86,6 +117,12 @@ export function getProtocolType(encoded: string) { return { protocolType: protocolType as NonNullable, decoded } } +// ================================ +// Encode content hash +// ================================ + +export type EncodeContentHashErrorType = ErrorType | GetProtocolTypeErrorType + export function encodeContentHash(text: string): Hex { const typeData = getProtocolType(text) if (!typeData) throw new InvalidContentHashError() diff --git a/packages/ensjs/src/utils/deflate.ts b/packages/ensjs/src/utils/deflate.ts new file mode 100644 index 000000000..10590ddbc --- /dev/null +++ b/packages/ensjs/src/utils/deflate.ts @@ -0,0 +1,38 @@ +import { bytesToHex, type Hex, hexToBytes } from 'viem' + +export async function deflateToHex(data: unknown): Promise { + const json = JSON.stringify(data) + const input = new TextEncoder().encode(json) + + // Stream through CompressionStream("deflate") + const cs = new CompressionStream('deflate') + const writer = cs.writable.getWriter() + writer.write(input) + writer.close() + + const compressed = new Uint8Array( + await new Response(cs.readable).arrayBuffer(), + ) + + return bytesToHex(compressed) +} + +/** + * Inflate a deflate-compressed hex string back to its original JSON value. + */ +export async function inflateFromHex(encoded: Hex): Promise { + const compressed = hexToBytes(encoded) + + const ds = new DecompressionStream('deflate') + const source = new ReadableStream({ + start(c) { + c.enqueue(compressed) + c.close() + }, + }) + const inflatedStream = source.pipeThrough(ds) + + const text = await new Response(inflatedStream).text() + + return JSON.parse(text) as T +} diff --git a/packages/ensjs/src/utils/dns/getDnsTxtRecords.ts b/packages/ensjs/src/utils/dns/getDnsTxtRecords.ts index 99bfdd34c..b35487e09 100644 --- a/packages/ensjs/src/utils/dns/getDnsTxtRecords.ts +++ b/packages/ensjs/src/utils/dns/getDnsTxtRecords.ts @@ -1,4 +1,5 @@ -import type { Endpoint } from '../../functions/dns/types.js' +import type { Endpoint } from '../../actions/dns/types.js' +import type { ErrorType } from '../../errors/utils.js' import type { DnsResponse } from './misc.js' export type GetDnsTxtRecordsParameters = { @@ -10,6 +11,8 @@ export type GetDnsTxtRecordsParameters = { export type GetDnsTxtRecordsReturnType = DnsResponse +export type GetDnsTxtRecordsErrorType = ErrorType + /** * Gets the DNS record response of a name, via DNS record lookup * @param parameters - {@link GetDnsTxtRecordsParameters} diff --git a/packages/ensjs/src/utils/encoders/encodeAbi.ts b/packages/ensjs/src/utils/encoders/encodeAbi.ts deleted file mode 100644 index 162eb7bea..000000000 --- a/packages/ensjs/src/utils/encoders/encodeAbi.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { type Hex, bytesToHex, stringToHex } from 'viem' -import { UnknownContentTypeError } from '../../errors/utils.js' -import type { Prettify } from '../../types.js' - -export type AbiEncodeAs = 'json' | 'zlib' | 'cbor' | 'uri' - -type AbiContentType = 1 | 2 | 4 | 8 - -const abiEncodeMap = { - json: 1, - zlib: 2, - cbor: 4, - uri: 8, -} as const -type AbiEncodeMap = typeof abiEncodeMap - -type GetAbiContentType = AbiEncodeMap[TEncodeAs] - -export type EncodeAbiParameters = - TEncodeAs extends 'uri' - ? { - encodeAs: TEncodeAs - data: string | null - } - : { - encodeAs: TEncodeAs - data: Record | null - } - -export type EncodedAbi = { - contentType: TContentType - encodedData: Hex -} - -export type EncodeAbiReturnType = - EncodedAbi - -export const contentTypeToEncodeAs = ( - contentType: AbiContentType, -): AbiEncodeAs => { - switch (contentType) { - case 1: - return 'json' - case 2: - return 'zlib' - case 4: - return 'cbor' - case 8: - return 'uri' - default: - throw new UnknownContentTypeError({ contentType }) - } -} - -export const encodeAsToContentType = ( - encodeAs: AbiEncodeAs, -): AbiContentType => { - const contentType = abiEncodeMap[encodeAs] - if (contentType === undefined) { - throw new UnknownContentTypeError({ contentType: encodeAs }) - } - return contentType -} - -export const encodeAbi = async < - TEncodeAs extends AbiEncodeAs, - TContentType extends GetAbiContentType, ->({ - encodeAs, - data, -}: EncodeAbiParameters): Promise< - Prettify> -> => { - let contentType: AbiContentType - let encodedData: Hex = '0x' - switch (encodeAs) { - case 'json': - contentType = 1 - if (data) encodedData = stringToHex(JSON.stringify(data)) - break - case 'zlib': { - contentType = 2 - if (data) { - const { deflate } = await import('pako/dist/pako_deflate.min.js') - encodedData = bytesToHex(deflate(JSON.stringify(data))) - } - break - } - case 'cbor': { - contentType = 4 - if (data) { - const { cborEncode } = await import('@ensdomains/address-encoder/utils') - encodedData = bytesToHex(new Uint8Array(cborEncode(data))) - } - break - } - default: { - contentType = 8 - if (data) encodedData = stringToHex(data as string) - break - } - } - return { contentType: contentType as TContentType, encodedData } -} diff --git a/packages/ensjs/src/utils/encoders/encodeClearRecords.ts b/packages/ensjs/src/utils/encoders/encodeClearRecords.ts deleted file mode 100644 index 5359af9e6..000000000 --- a/packages/ensjs/src/utils/encoders/encodeClearRecords.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { type Hex, encodeFunctionData } from 'viem' -import { publicResolverClearRecordsSnippet } from '../../contracts/publicResolver.js' - -export const encodeClearRecords = (namehash: Hex) => - encodeFunctionData({ - abi: publicResolverClearRecordsSnippet, - functionName: 'clearRecords', - args: [namehash], - }) diff --git a/packages/ensjs/src/utils/encoders/encodeSetAbi.test.ts b/packages/ensjs/src/utils/encoders/encodeSetAbi.test.ts deleted file mode 100644 index bb7e82e8b..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetAbi.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { encodeAbi } from './encodeAbi.js' -import { type EncodeSetAbiParameters, encodeSetAbi } from './encodeSetAbi.js' - -describe('encodeSetAbi', () => { - const namehash = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const contentType = 1 - const encodedData = '0x' - - const parameters: EncodeSetAbiParameters = { - namehash, - contentType, - encodedData, - } - - it('encodes the setAbi function data correctly with null encodedData', async () => { - const expected = - '0x623195b01234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000' - const result = encodeSetAbi(parameters) - expect(result).toEqual(expected) - }) - - it('encodes the setAbi function data correctly with encodedData', async () => { - const result = encodeSetAbi({ - namehash, - ...(await encodeAbi({ encodeAs: 'json', data: { foo: 'bar' } })), - }) - expect(result).toEqual( - '0x623195b01234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d7b22666f6f223a22626172227d00000000000000000000000000000000000000', - ) - }) -}) diff --git a/packages/ensjs/src/utils/encoders/encodeSetAbi.ts b/packages/ensjs/src/utils/encoders/encodeSetAbi.ts deleted file mode 100644 index 5b1c56841..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetAbi.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type Hex, encodeFunctionData } from 'viem' -import { publicResolverSetAbiSnippet } from '../../contracts/publicResolver.js' -import type { EncodedAbi } from './encodeAbi.js' - -export type EncodeSetAbiParameters = { - namehash: Hex -} & EncodedAbi - -export type EncodeSetAbiReturnType = Hex - -export const encodeSetAbi = ({ - namehash, - contentType, - encodedData, -}: EncodeSetAbiParameters): EncodeSetAbiReturnType => { - return encodeFunctionData({ - abi: publicResolverSetAbiSnippet, - functionName: 'setABI', - args: [namehash, BigInt(contentType), encodedData], - }) -} diff --git a/packages/ensjs/src/utils/encoders/encodeSetAddr.test.ts b/packages/ensjs/src/utils/encoders/encodeSetAddr.test.ts deleted file mode 100644 index 126cab1a3..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetAddr.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { type EncodeSetAddrParameters, encodeSetAddr } from './encodeSetAddr.js' - -const namehash = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - -describe('encodeSetAddr', () => { - const testCases: Array<[string, EncodeSetAddrParameters, string]> = [ - [ - 'should encode setAddr with ETH address', - { - namehash, - coin: 'ETH', - value: '0x1234567890123456789012345678901234567890', - }, - '0x8b95dd711234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890000000000000000000000000', - ], - [ - 'should encode setAddr with BTC address', - { - namehash, - coin: 'BTC', - value: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', - }, - '0x8b95dd711234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000160014e8df018c7e326cc253faac7e46cdc51e68542c4200000000000000000000', - ], - [ - 'should encode setAddr with coin type as number', - { - namehash, - coin: 60, - value: '0x1234567890123456789012345678901234567890', - }, - '0x8b95dd711234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890000000000000000000000000', - ], - [ - 'should encode setAddr with coin type as string', - { - namehash, - coin: '60', - value: '0x1234567890123456789012345678901234567890', - }, - '0x8b95dd711234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890000000000000000000000000', - ], - [ - 'should encode setAddr with null value', - { - namehash, - coin: 'ETH', - value: null, - }, - '0x8b95dd711234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000', - ], - ] - - testCases.forEach(([description, parameters, expectedOutput]) => { - it(description, () => { - expect(encodeSetAddr(parameters)).toEqual(expectedOutput) - }) - }) -}) diff --git a/packages/ensjs/src/utils/encoders/encodeSetAddr.ts b/packages/ensjs/src/utils/encoders/encodeSetAddr.ts deleted file mode 100644 index 100364e61..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetAddr.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { type Address, type Hex, bytesToHex, encodeFunctionData } from 'viem' -import { publicResolverSetAddrSnippet } from '../../contracts/publicResolver.js' -import { getCoderFromCoin } from '../normaliseCoinId.js' - -export type EncodeSetAddrParameters = { - namehash: Hex - coin: string | number - value: Address | string | null -} - -export type EncodeSetAddrReturnType = Hex - -export const encodeSetAddr = ({ - namehash, - coin, - value, -}: EncodeSetAddrParameters): EncodeSetAddrReturnType => { - const coder = getCoderFromCoin(coin) - const inputCoinType = coder.coinType - let encodedAddress: Hex | Uint8Array = value ? coder.decode(value) : '0x' - if (inputCoinType === 60 && encodedAddress === '0x') - encodedAddress = coder.decode('0x0000000000000000000000000000000000000000') - if (typeof encodedAddress !== 'string') { - encodedAddress = bytesToHex(encodedAddress) - } - - return encodeFunctionData({ - abi: publicResolverSetAddrSnippet, - functionName: 'setAddr', - args: [namehash, BigInt(inputCoinType), encodedAddress], - }) -} diff --git a/packages/ensjs/src/utils/encoders/encodeSetContentHash.test.ts b/packages/ensjs/src/utils/encoders/encodeSetContentHash.test.ts deleted file mode 100644 index 5143d3765..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetContentHash.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - type EncodeSetContentHashParameters, - encodeSetContentHash, -} from './encodeSetContentHash.js' - -describe('encodeSetContentHash', () => { - const namehash = - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const contentHash = 'ipfs://QmXwMFNjzjRvZuPvzJfYJZ1QqX2QJjzj1YJZ1QqX2QJjzj' - - it('encodes the function data correctly when contentHash is not null', () => { - const expectedEncodedData = - '0x304e6ade1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000026e301017012208e9cc47fde7ff64028480ec671a4ddb8f767a71ff71a73247f51a495a6f296340000000000000000000000000000000000000000000000000000' - const params: EncodeSetContentHashParameters = { namehash, contentHash } - expect(encodeSetContentHash(params)).toEqual(expectedEncodedData) - }) - - it('encodes the function data correctly when contentHash is null', () => { - const expectedEncodedData = - '0x304e6ade1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000' - const params: EncodeSetContentHashParameters = { - namehash, - contentHash: null, - } - expect(encodeSetContentHash(params)).toEqual(expectedEncodedData) - }) - - it('throws an error when contentHash is invalid', () => { - const params: EncodeSetContentHashParameters = { - namehash, - contentHash: 'invalid-content-hash', - } - expect(() => - encodeSetContentHash(params), - ).toThrowErrorMatchingInlineSnapshot(` - [InvalidContentHashError: Invalid content hash - - Version: @ensdomains/ensjs@1.0.0-mock.0] - `) - }) -}) diff --git a/packages/ensjs/src/utils/encoders/encodeSetContentHash.ts b/packages/ensjs/src/utils/encoders/encodeSetContentHash.ts deleted file mode 100644 index 0fe6e5aa0..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetContentHash.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { type Hex, encodeFunctionData } from 'viem' -import { publicResolverSetContenthashSnippet } from '../../contracts/publicResolver.js' -import { encodeContentHash } from '../contentHash.js' - -export type EncodeSetContentHashParameters = { - namehash: Hex - contentHash: string | null -} - -export type EncodeSetContentHashReturnType = Hex - -export const encodeSetContentHash = ({ - namehash, - contentHash, -}: EncodeSetContentHashParameters): EncodeSetContentHashReturnType => { - let encodedHash: Hex = '0x' - if (contentHash) { - encodedHash = encodeContentHash(contentHash) - } - return encodeFunctionData({ - abi: publicResolverSetContenthashSnippet, - functionName: 'setContenthash', - args: [namehash, encodedHash], - }) -} diff --git a/packages/ensjs/src/utils/encoders/encodeSetText.test.ts b/packages/ensjs/src/utils/encoders/encodeSetText.test.ts deleted file mode 100644 index b90e5be09..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetText.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { type EncodeSetTextParameters, encodeSetText } from './encodeSetText.js' - -describe('encodeSetText', () => { - const namehash = - '0x1234567890123456789012345678901234567890123456789012345678901234' - const key = 'email' - const value = 'test@example.com' - - const parameters: EncodeSetTextParameters = { - namehash, - key, - value, - } - - it('encodes the function data correctly', () => { - const expected = - '0x10f13a8c1234567890123456789012345678901234567890123456789012345678901234000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005656d61696c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001074657374406578616d706c652e636f6d00000000000000000000000000000000' - const result = encodeSetText(parameters) - expect(result).toEqual(expected) - }) -}) diff --git a/packages/ensjs/src/utils/encoders/encodeSetText.ts b/packages/ensjs/src/utils/encoders/encodeSetText.ts deleted file mode 100644 index 04e22281a..000000000 --- a/packages/ensjs/src/utils/encoders/encodeSetText.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type Hex, encodeFunctionData } from 'viem' -import { publicResolverSetTextSnippet } from '../../contracts/publicResolver.js' - -export type EncodeSetTextParameters = { - namehash: Hex - key: string - value: string | null -} - -export type EncodeSetTextReturnType = Hex - -export const encodeSetText = ({ - namehash, - key, - value, -}: EncodeSetTextParameters): EncodeSetTextReturnType => { - return encodeFunctionData({ - abi: publicResolverSetTextSnippet, - functionName: 'setText', - args: [namehash, key, value ?? ''], - }) -} diff --git a/packages/ensjs/src/utils/errors/isNullRegistrarOwnerOfError.ts b/packages/ensjs/src/utils/errors/isNullRegistrarOwnerOfError.ts new file mode 100644 index 000000000..5fe6e22da --- /dev/null +++ b/packages/ensjs/src/utils/errors/isNullRegistrarOwnerOfError.ts @@ -0,0 +1,12 @@ +import { BaseError, ContractFunctionRevertedError } from 'viem' + +/** + * Checks if the error is a known revert error for the registrar ownerOf function + * @returns `true` if the error is a known revert error, `false` if it is not, or throws if it is an unknown error + */ +export const isNullRegistrarOwnerOfError = (err: unknown) => { + if (!(err instanceof BaseError)) return false + const cause = err.walk((e) => e instanceof ContractFunctionRevertedError) + if (!(cause instanceof ContractFunctionRevertedError)) return false + return true +} diff --git a/packages/ensjs/src/utils/errors/isNullUniversalResolverError.ts b/packages/ensjs/src/utils/errors/isNullUniversalResolverError.ts new file mode 100644 index 000000000..9b71e1e14 --- /dev/null +++ b/packages/ensjs/src/utils/errors/isNullUniversalResolverError.ts @@ -0,0 +1,19 @@ +import { BaseError, ContractFunctionRevertedError } from 'viem' + +/** + * Checks if the data returned from a universal resolver is safe to use, or if it is a known revert error + * @returns `true` if the data is safe to use, `false` if it is a known revert error, or throws if it is an unknown error + */ +export const isNullUniversalResolverError = (err: unknown) => { + if (!(err instanceof BaseError)) return false + const cause = err.walk((e) => e instanceof ContractFunctionRevertedError) + if (!(cause instanceof ContractFunctionRevertedError)) return false + + if (cause.data?.errorName === 'ResolverNotFound') return true + if (cause.data?.errorName === 'ResolverWildcardNotSupported') return true + if (cause.data?.errorName === 'ResolverNotContract') return true + if (cause.data?.errorName === 'ResolverError') return true + if (cause.data?.errorName === 'HttpError') return true + + return false +} diff --git a/packages/ensjs/src/utils/errors/parseReverseAddressMismatchError.ts b/packages/ensjs/src/utils/errors/parseReverseAddressMismatchError.ts new file mode 100644 index 000000000..d4375c158 --- /dev/null +++ b/packages/ensjs/src/utils/errors/parseReverseAddressMismatchError.ts @@ -0,0 +1,14 @@ +import { BaseError, ContractFunctionRevertedError } from 'viem' + +/** + * Determines whether the error is a `ReverseAddressMismatch` and returns its arguments if so. + */ +export const parseReverseAddressMismatchError = (err: unknown) => { + if (!(err instanceof BaseError)) return false + const cause = err.walk((e) => e instanceof ContractFunctionRevertedError) + if (!(cause instanceof ContractFunctionRevertedError)) return false + + if (cause.data?.errorName === 'ReverseAddressMismatch') return cause.data.args + + return false +} diff --git a/packages/ensjs/src/utils/fuses.test.ts b/packages/ensjs/src/utils/fuses.test.ts index cbe9e309e..9e3ace0b6 100644 --- a/packages/ensjs/src/utils/fuses.test.ts +++ b/packages/ensjs/src/utils/fuses.test.ts @@ -2,6 +2,8 @@ import { describe, expect, it } from 'vitest' import { ChildFuseKeys, ChildFuses, + decodeFuses, + encodeFuses, FullParentFuseKeys, FullParentFuses, FuseRanges, @@ -11,8 +13,6 @@ import { UnnamedParentFuses, UserSettableFuseKeys, UserSettableFuses, - decodeFuses, - encodeFuses, } from './fuses.js' describe('correctly exports', () => { diff --git a/packages/ensjs/src/utils/fuses.ts b/packages/ensjs/src/utils/fuses.ts index 82b3b9750..4529f8670 100644 --- a/packages/ensjs/src/utils/fuses.ts +++ b/packages/ensjs/src/utils/fuses.ts @@ -202,6 +202,12 @@ export type EncodeFusesInputObject = number: bigint } +// ================================ +// Validate fuse number +// ================================ + +type ValidateFuseNumberErrorType = FusesOutOfRangeError + const validateFuseNumber = (fuses: bigint) => { if (fuses > 2n ** 32n || fuses < 0n) throw new FusesOutOfRangeError({ @@ -218,6 +224,17 @@ const validateFuseNumber = (fuses: bigint) => { }) } +// ================================ +// Check fuse object +// ================================ + +type CheckFuseObjectErrorType = + | FusesInvalidFuseObjectError + | ValidateFuseNumberErrorType + | FusesOutOfRangeError + | FusesInvalidNamedFuseError + | FusesInvalidUnnamedFuseError + const checkFuseObject = ({ reference, object, @@ -267,7 +284,11 @@ const checkFuseObject = ({ return Number(fuseNumber) } -type EncodeFusesParameters = +// ================================ +// Encode fuses +// ================================ + +export type EncodeFusesParameters = | { restriction: 'child' input: EncodeChildFusesInputObject @@ -281,6 +302,10 @@ type EncodeFusesParameters = input: EncodeFusesInputObject } +export type EncodeFusesErrorType = + | CheckFuseObjectErrorType + | FusesRestrictionNotAllowedError + export const encodeFuses = ({ restriction, input, @@ -325,6 +350,10 @@ export const encodeFuses = ({ return Number(childFuses | parentFuses) } +// ================================ +// Decode fuses +// ================================ + type DecodedFuseGroup = { [key in TFuseReference['Keys'][number]]: boolean } & { diff --git a/packages/ensjs/src/utils/generateFunction.ts b/packages/ensjs/src/utils/generateFunction.ts deleted file mode 100644 index 595945625..000000000 --- a/packages/ensjs/src/utils/generateFunction.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { BaseError } from 'viem' -import { call } from 'viem/actions' -import type { ClientWithEns } from '../contracts/consts.js' -import type { TransactionRequestWithPassthrough } from '../types.js' - -export type EncoderFunction = ( - ...args: any[] -) => TransactionRequestWithPassthrough -export type DecoderFunction = (...args: any[]) => Promise - -type OmitFirstArg = F extends (x: any, ...args: infer P) => infer R - ? (...args: P) => R - : never - -export type CoderObject< - TEncoderFn extends EncoderFunction = EncoderFunction, - TDecoderFn extends DecoderFunction = DecoderFunction, -> = { - encode: TEncoderFn - decode: TDecoderFn -} - -export type BatchFunctionResult< - TEncoderFn extends EncoderFunction = EncoderFunction, - TDecoderFn extends DecoderFunction = DecoderFunction, -> = { - args: Parameters> -} & CoderObject - -export type ExtractResult = TFunction extends ( - ...args: any[] -) => Promise - ? U - : never - -export interface GeneratedFunction< - TEncoderFn extends EncoderFunction, - TDecoderFn extends DecoderFunction, -> extends Function, - CoderObject { - ( - client: ClientWithEns, - ...args: Parameters> - ): Promise | null> - batch: ( - ...args: Parameters> - ) => BatchFunctionResult -} - -export const generateFunction = < - TEncoderFn extends EncoderFunction, - TDecoderFn extends DecoderFunction, - TFunction extends GeneratedFunction< - TEncoderFn, - TDecoderFn - > = GeneratedFunction, ->({ - encode, - decode, -}: { - encode: TEncoderFn - decode: TDecoderFn -}) => { - const single = (async (client, ...args) => { - const { passthrough, ...encodedData } = encode(client, ...args) - const result = await call(client, encodedData) - .then((ret) => ret.data) - .catch((e) => { - if (!(e instanceof BaseError)) throw e - return e - }) - if (passthrough) return decode(client, result, passthrough, ...args) - return decode(client, result, ...args) - }) as TFunction - single.batch = (...args) => ({ - args, - encode, - decode, - }) - single.encode = encode - single.decode = decode - return single -} diff --git a/packages/ensjs/src/utils/generateRecordCallArray.ts b/packages/ensjs/src/utils/generateRecordCallArray.ts deleted file mode 100644 index 598bd8f6b..000000000 --- a/packages/ensjs/src/utils/generateRecordCallArray.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { Hex } from 'viem' -import type { Prettify } from '../types.js' -import type { EncodedAbi } from './encoders/encodeAbi.js' -import { encodeClearRecords } from './encoders/encodeClearRecords.js' -import { - type EncodeSetAbiParameters, - encodeSetAbi, -} from './encoders/encodeSetAbi.js' -import { - type EncodeSetAddrParameters, - encodeSetAddr, -} from './encoders/encodeSetAddr.js' -import { encodeSetContentHash } from './encoders/encodeSetContentHash.js' -import { - type EncodeSetTextParameters, - encodeSetText, -} from './encoders/encodeSetText.js' - -export type RecordOptions = Prettify<{ - /** Clears all current records */ - clearRecords?: boolean - /** ContentHash value */ - contentHash?: string | null - /** Array of text records */ - texts?: Omit[] - /** Array of coin records */ - coins?: Omit[] - /** ABI value */ - abi?: EncodedAbi | EncodedAbi[] -}> - -export const generateRecordCallArray = ({ - namehash, - clearRecords, - contentHash, - texts, - coins, - abi, -}: { namehash: Hex } & RecordOptions): Hex[] => { - const calls: Hex[] = [] - - if (clearRecords) { - calls.push(encodeClearRecords(namehash)) - } - - if (contentHash !== undefined) { - const data = encodeSetContentHash({ namehash, contentHash }) - if (data) calls.push(data) - } - - if (abi !== undefined) { - const abis = Array.isArray(abi) ? abi : [abi] - for (const abi_ of abis) { - const data = encodeSetAbi({ namehash, ...abi_ } as EncodeSetAbiParameters) - if (data) calls.push(data) - } - } - - if (texts && texts.length > 0) { - const data = texts.map((textItem) => - encodeSetText({ namehash, ...textItem }), - ) - if (data) calls.push(...data) - } - - if (coins && coins.length > 0) { - const data = coins.map((coinItem) => - encodeSetAddr({ namehash, ...coinItem }), - ) - if (data) calls.push(...data) - } - - return calls -} diff --git a/packages/ensjs/src/utils/generateSupportedContentTypes.ts b/packages/ensjs/src/utils/generateSupportedContentTypes.ts index c21f1ad4c..196d86ef8 100644 --- a/packages/ensjs/src/utils/generateSupportedContentTypes.ts +++ b/packages/ensjs/src/utils/generateSupportedContentTypes.ts @@ -1,4 +1,4 @@ -import type { AbiEncodeAs } from './encoders/encodeAbi.js' +import type { AbiEncodeAs } from './coders/encodeAbi.js' const abiEncodeAsMap: { [key in AbiEncodeAs]: bigint } = { json: 1n, diff --git a/packages/ensjs/src/utils/getRevertErrorData.ts b/packages/ensjs/src/utils/getRevertErrorData.ts deleted file mode 100644 index ed7957d66..000000000 --- a/packages/ensjs/src/utils/getRevertErrorData.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BaseError, type RawContractError } from 'viem' - -export const getRevertErrorData = (err: unknown) => { - if (!(err instanceof BaseError)) return undefined - const error = err.walk() as RawContractError - const hex = typeof error.data === 'object' ? error.data.data : error.data - if (hex === '0x') return undefined - return hex -} diff --git a/packages/ensjs/src/utils/hexEncodedName.ts b/packages/ensjs/src/utils/hexEncodedName.ts deleted file mode 100644 index 609d41dcb..000000000 --- a/packages/ensjs/src/utils/hexEncodedName.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Adapted from https://github.com/mafintosh/dns-packet -import { type ByteArray, bytesToString, labelhash, stringToBytes } from 'viem' -import { encodeLabelhash } from './labels.js' - -/* - * @description Encodes a DNS packet into a ByteArray containing a UDP payload. - */ -export function packetToBytes(packet: string): ByteArray { - // strip leading and trailing `.` - const value = packet.replace(/^\.|\.$/gm, '') - if (value.length === 0) return new Uint8Array(1) - - const bytes = new Uint8Array(stringToBytes(value).byteLength + 2) - - let offset = 0 - const list = value.split('.') - for (let i = 0; i < list.length; i += 1) { - let encoded = stringToBytes(list[i]) - // if the length is > 255, make the encoded label value a labelhash - // this is compatible with the universal resolver - if (encoded.byteLength > 255) - encoded = stringToBytes(encodeLabelhash(labelhash(list[i]))) - bytes[offset] = encoded.length - bytes.set(encoded, offset + 1) - offset += encoded.length + 1 - } - - if (bytes.byteLength !== offset + 1) return bytes.slice(0, offset + 1) - - return bytes -} - -export function bytesToPacket(bytes: ByteArray): string { - let offset = 0 - let result = '' - - while (offset < bytes.length) { - const len = bytes[offset] - if (len === 0) { - offset += 1 - break - } - - result += `${bytesToString(bytes.subarray(offset + 1, offset + len + 1))}.` - offset += len + 1 - } - - return result.replace(/\.$/, '') -} diff --git a/packages/ensjs/src/utils/index.ts b/packages/ensjs/src/utils/index.ts index 1132520bd..f2e9cca9e 100644 --- a/packages/ensjs/src/utils/index.ts +++ b/packages/ensjs/src/utils/index.ts @@ -1,149 +1,35 @@ -export { ccipRequest } from './ccipRequest.js' -export { - getDnsTxtRecords, - type GetDnsTxtRecordsParameters, - type GetDnsTxtRecordsReturnType, -} from './dns/getDnsTxtRecords.js' -export { - DnsRecordType, - DnsResponseStatus, - type DnsQuestionItem, - type DnsResponse, - type DnsResponseItem, -} from './dns/misc.js' -export { - contentTypeToEncodeAs, - encodeAbi, - encodeAsToContentType, - type EncodeAbiParameters, - type EncodeAbiReturnType, - type EncodedAbi, -} from './encoders/encodeAbi.js' -export { encodeClearRecords } from './encoders/encodeClearRecords.js' -export { - encodeSetAbi, - type EncodeSetAbiParameters, - type EncodeSetAbiReturnType, -} from './encoders/encodeSetAbi.js' -export { - encodeSetAddr, - type EncodeSetAddrParameters, - type EncodeSetAddrReturnType, -} from './encoders/encodeSetAddr.js' -export { - encodeSetContentHash, - type EncodeSetContentHashParameters, - type EncodeSetContentHashReturnType, -} from './encoders/encodeSetContentHash.js' -export { - encodeSetText, - type EncodeSetTextParameters, - type EncodeSetTextReturnType, -} from './encoders/encodeSetText.js' - -export { - EMPTY_ADDRESS, - GRACE_PERIOD_SECONDS, - MAX_DATE_INT, - MAX_INT_64, - MINIMUM_DOT_ETH_CHARS, -} from './consts.js' -export { - ChildFuseKeys, - ChildFuseReference, - ChildFuses, - FullParentFuseKeys, - FullParentFuseReference, - FullParentFuses, - FuseRanges, - ParentFuseKeys, - ParentFuseReference, - ParentFuses, - UnnamedChildFuseKeys, - UnnamedChildFuses, - UnnamedParentFuseKeys, - UnnamedParentFuses, - UserSettableFuseKeys, - UserSettableFuses, - checkPccBurned, - decodeFuses, - encodeFuses, - type ChildFuseReferenceType, - type DecodedFuses, - type EncodeChildFusesInputObject, - type EncodeFusesInputObject, - type EncodeParentFusesInputObject, - type FullParentFuseReferenceType, - type ParentFuseReferenceType, -} from './fuses.js' - -export { - decodeContentHash, - encodeContentHash, - getDisplayCodec, - getInternalCodec, - getProtocolType, - isValidContentHash, - type DecodedContentHash, - type ProtocolType, -} from './contentHash.js' -export { truncateFormat } from './format.js' -export { - generateRecordCallArray, - type RecordOptions, -} from './generateRecordCallArray.js' -export { generateSupportedContentTypes } from './generateSupportedContentTypes.js' -export { bytesToPacket, packetToBytes } from './hexEncodedName.js' -export { - checkIsDecrypted, - checkLabel, - decodeLabelhash, - decryptName, - encodeLabelhash, - isEncodedLabelhash, - saveLabel, - saveName, -} from './labels.js' -export { makeSafeSecondsDate } from './makeSafeSecondsDate.js' -export { - beautify, - emoji, - isCombiningMark, - namehash, - normalise, - normaliseFragment, - shouldEscape, - split, - tokenise, - type DisallowedToken, - type EmojiToken, - type IgnoredToken, - type Label, - type MappedToken, - type NFCToken, - type StopToken, - type TextToken, - type Token, - type ValidToken, -} from './normalise.js' -export { - makeCommitment, - makeCommitmentFromTuple, - makeCommitmentTuple, - makeRegistrationTuple, - randomSecret, - type CommitmentTuple, - type RegistrationParameters, - type RegistrationTuple, -} from './registerHelpers.js' -export { - checkIsDotEth, - parseInput, - validateName, - type ParsedInputResult, -} from './validation.js' -export { - MAX_EXPIRY, - expiryToBigInt, - wrappedLabelLengthCheck, -} from './wrapper.js' +export * from './ccipBatchRequest.js' +export * from './ccipRequest.js' +export * from './clientWithOverrides.js' +export * from './coders/clearRecords.js' +export * from './coders/encodeAbi.js' +export * from './coders/getAbi.js' +export * from './coders/getAddress.js' +export * from './coders/getContentHash.js' +export * from './coders/getText.js' +export * from './coders/resolverMulticallParameters.js' +export * from './coders/setAbi.js' +export * from './coders/setAddr.js' +export * from './coders/setContentHash.js' +export * from './coders/setText.js' +export * from './consts.js' +export * from './contentHash.js' +export * from './dns/getDnsTxtRecords.js' +export * from './dns/misc.js' +export * from './errors/isNullRegistrarOwnerOfError.js' +export * from './errors/isNullUniversalResolverError.js' +export * from './format.js' +export * from './fuses.js' +export * from './generateSupportedContentTypes.js' +export * from './name/getNameType.js' +export * from './name/getNameWithSizedLabels.js' +export * from './name/hexEncodedName.js' +export * from './name/labels.js' +export * from './name/makeLabelNodeAndParent.js' +export * from './name/normalize.js' +export * from './name/validation.js' +export * from './normalizeCoinId.js' +export * from './nullableAddress.js' +export * from './ownerFromContract.js' +export * from './registerHelpers.js' +export * from './wrapper.js' diff --git a/packages/ensjs/src/utils/l2RegisterHelpers.ts b/packages/ensjs/src/utils/l2RegisterHelpers.ts new file mode 100644 index 000000000..3add17179 --- /dev/null +++ b/packages/ensjs/src/utils/l2RegisterHelpers.ts @@ -0,0 +1,156 @@ +import { + type Address, + type EncodeAbiParametersErrorType, + encodeAbiParameters, + type Hex, + type Keccak256ErrorType, + keccak256, + type PadErrorType, + pad, + padHex, + type ToBytesErrorType, + type ToHexErrorType, + toBytes, + toHex, + zeroAddress, +} from 'viem' +import { type NamehashErrorType, namehash } from 'viem/ens' +import { + CampaignReferenceTooLargeError, + type ErrorType, +} from '../errors/utils.js' + +export type L2RegistrationParameters = { + /** Name's label (the thing before .eth) to register */ + label: string + /** Address to set owner to */ + owner: Address + /** Duration of registration */ + duration: number + /** Random 32 bytes to use for registration */ + secret: Hex + /** Subregistry address to use for registration */ + subregistryAddress?: Address + /** Custom resolver address, defaults to current public resolver deployment */ + resolverAddress?: Address + /** Referrer address. Optional */ + referrer?: Hex + /** Payment token. USDC is used by default */ + paymentToken?: Address +} + +export type L2CommitmentTuple = [ + label: string, + owner: Address, + secret: Hex, + subregistry: Address, + resolver: Address, + duration: bigint, + referrer: Hex, +] + +const cryptoRef = + (typeof crypto !== 'undefined' && crypto) || + (typeof window !== 'undefined' && + typeof window.crypto !== 'undefined' && + window.crypto) || + undefined + +// ================================ +// Random secret +// ================================ + +export type RandomSecretErrorType = + | ToBytesErrorType + | NamehashErrorType + | CampaignReferenceTooLargeError + | PadErrorType + | ToHexErrorType + | ErrorType + +export const randomSecret = ({ + platformDomain, + campaign, +}: { + platformDomain?: string + campaign?: number +} = {}) => { + const bytes = cryptoRef?.getRandomValues(new Uint8Array(32))! + if (platformDomain) { + const hash = toBytes(namehash(platformDomain)) + for (let i = 0; i < 4; i += 1) { + bytes[i] = hash[i] + } + } + if (campaign) { + if (campaign > 0xffffffff) + throw new CampaignReferenceTooLargeError({ campaign }) + const campaignBytes = pad(toBytes(campaign), { size: 4 }) + for (let i = 0; i < 4; i += 1) { + bytes[i + 4] = campaignBytes[i] + } + } + return toHex(bytes) +} + +// ================================ +// Make L2 commitment tuple +// ================================ + +export type MakeL2CommitmentTupleErrorType = ErrorType + +export const makeL2CommitmentTuple = ({ + label, + owner, + duration, + secret, + subregistryAddress = zeroAddress, + resolverAddress = zeroAddress, + referrer, +}: L2RegistrationParameters): L2CommitmentTuple => { + return [ + label, + owner, + secret, + subregistryAddress, + resolverAddress, + BigInt(duration), + referrer || padHex('0x', { size: 32 }), + ] +} + +// ================================ +// Make L2 commitment from tuple +// ================================ + +export type MakeL2CommitmentFromTupleErrorType = + | Keccak256ErrorType + | EncodeAbiParametersErrorType + +export const makeL2CommitmentFromTuple = (params: L2CommitmentTuple): Hex => { + return keccak256( + encodeAbiParameters( + [ + { name: 'label', type: 'string' }, + { name: 'owner', type: 'address' }, + { name: 'secret', type: 'bytes32' }, + { name: 'subregistry', type: 'address' }, + { name: 'resolver', type: 'address' }, + { name: 'duration', type: 'uint64' }, + { name: 'referrer', type: 'bytes32' }, + ], + params, + ), + ) +} + +// ================================ +// Make L2 commitment +// ================================ + +export type MakeL2CommitmentErrorType = + | MakeL2CommitmentTupleErrorType + | MakeL2CommitmentFromTupleErrorType + +export const makeL2Commitment = (params: L2RegistrationParameters): Hex => + makeL2CommitmentFromTuple(makeL2CommitmentTuple(params)) diff --git a/packages/ensjs/src/utils/makeLabelNodeAndParent.ts b/packages/ensjs/src/utils/makeLabelNodeAndParent.ts deleted file mode 100644 index abda8869d..000000000 --- a/packages/ensjs/src/utils/makeLabelNodeAndParent.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { labelhash } from 'viem' -import { namehash } from './normalise.js' - -export const makeLabelNodeAndParent = (name: string) => { - const labels = name.split('.') - const label = labels.shift() as string - const parentNode = namehash(labels.join('.')) - return { - label, - labelhash: labelhash(label), - parentNode, - } -} diff --git a/packages/ensjs/src/utils/makeSafeSecondsDate.test.ts b/packages/ensjs/src/utils/makeSafeSecondsDate.test.ts deleted file mode 100644 index abcadd8c0..000000000 --- a/packages/ensjs/src/utils/makeSafeSecondsDate.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { expect, it } from 'vitest' -import { MAX_DATE_INT } from './consts.js' -import { makeSafeSecondsDate } from './makeSafeSecondsDate.js' - -it('makes date from seconds', () => { - expect(makeSafeSecondsDate(21000)).toEqual(new Date(21000000)) -}) -it('makes date from seconds with big int', () => { - expect(makeSafeSecondsDate(21000n)).toEqual(new Date(21000000)) -}) -it('makes date from seconds with big int larger than max date int', () => { - expect(makeSafeSecondsDate(MAX_DATE_INT + 28930402)).toEqual( - new Date(MAX_DATE_INT), - ) -}) diff --git a/packages/ensjs/src/utils/makeSafeSecondsDate.ts b/packages/ensjs/src/utils/makeSafeSecondsDate.ts deleted file mode 100644 index 239a13ecb..000000000 --- a/packages/ensjs/src/utils/makeSafeSecondsDate.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MAX_DATE_INT } from './consts.js' - -export const makeSafeSecondsDate = (seconds: number | bigint) => { - const milliseconds = BigInt(seconds) * 1000n - if (milliseconds > BigInt(MAX_DATE_INT)) return new Date(MAX_DATE_INT) - return new Date(Number(milliseconds)) -} diff --git a/packages/ensjs/src/utils/getNameType.ts b/packages/ensjs/src/utils/name/getNameType.ts similarity index 88% rename from packages/ensjs/src/utils/getNameType.ts rename to packages/ensjs/src/utils/name/getNameType.ts index ae761486e..6b8fd09fe 100644 --- a/packages/ensjs/src/utils/getNameType.ts +++ b/packages/ensjs/src/utils/name/getNameType.ts @@ -1,4 +1,4 @@ -import type { NameType } from '../types.js' +import type { NameType } from '../../types/index.js' export const getNameType = (name: string): NameType => { const labels = name.split('.') diff --git a/packages/ensjs/src/utils/name/getNameWithSizedLabels.ts b/packages/ensjs/src/utils/name/getNameWithSizedLabels.ts new file mode 100644 index 000000000..2a54ac608 --- /dev/null +++ b/packages/ensjs/src/utils/name/getNameWithSizedLabels.ts @@ -0,0 +1,28 @@ +import { + type LabelhashErrorType, + labelhash, + type ToBytesErrorType, + toBytes, +} from 'viem' +import { type EncodeLabelhashErrorType, encodeLabelhash } from './labels.js' + +export type GetNameWithSizedLabelsErrorType = + | ToBytesErrorType + | EncodeLabelhashErrorType + | LabelhashErrorType + +/** + * Ensures a name has labels with a max length of 255 bytes (the max length of labels supported by the universal resolver). + */ +export function getNameWithSizedLabels(name: string) { + return name + .split('.') + .map((label) => { + const labelLength = toBytes(label).byteLength + if (labelLength > 255) { + return encodeLabelhash(labelhash(label)) + } + return label + }) + .join('.') +} diff --git a/packages/ensjs/src/utils/name/hexEncodedName.ts b/packages/ensjs/src/utils/name/hexEncodedName.ts new file mode 100644 index 000000000..ddcba4ce0 --- /dev/null +++ b/packages/ensjs/src/utils/name/hexEncodedName.ts @@ -0,0 +1,33 @@ +// Adapted from https://github.com/mafintosh/dns-packet +import { + type ByteArray, + type BytesToStringErrorType, + bytesToString, +} from 'viem' + +// ================================ +// Bytes To Packet +// ================================ + +export type BytesToPacketErrorType = BytesToStringErrorType + +/** + * @throws {BytesToPacketErrorType} + */ +export function bytesToPacket(bytes: ByteArray): string { + let offset = 0 + let result = '' + + while (offset < bytes.length) { + const len = bytes[offset] + if (len === 0) { + offset += 1 + break + } + + result += `${bytesToString(bytes.subarray(offset + 1, offset + len + 1))}.` + offset += len + 1 + } + + return result.replace(/\.$/, '') +} diff --git a/packages/ensjs/src/utils/labels.test.ts b/packages/ensjs/src/utils/name/labels.test.ts similarity index 100% rename from packages/ensjs/src/utils/labels.test.ts rename to packages/ensjs/src/utils/name/labels.test.ts diff --git a/packages/ensjs/src/utils/labels.ts b/packages/ensjs/src/utils/name/labels.ts similarity index 51% rename from packages/ensjs/src/utils/labels.ts rename to packages/ensjs/src/utils/name/labels.ts index be29b6cee..93ce9c999 100644 --- a/packages/ensjs/src/utils/labels.ts +++ b/packages/ensjs/src/utils/name/labels.ts @@ -1,11 +1,24 @@ -import { type Hex, labelhash } from 'viem' +import { type Hex, type LabelhashErrorType, labelhash } from 'viem' import { InvalidEncodedLabelError, InvalidLabelhashError, -} from '../errors/utils.js' +} from '../../errors/utils.js' const hasLocalStorage = typeof localStorage !== 'undefined' +// ================================ +// Decode Labelhash +// ================================ + +export type DecodeLabelhashErrorType = InvalidEncodedLabelError + +/** + * Decodes an encoded labelhash + * + * @throws {DecodeLabelhashErrorType} + * @param hash - Labelhash to decode + * @returns {Hex} + */ export function decodeLabelhash(hash: string): Hex { if (!(hash.startsWith('[') && hash.endsWith(']'))) throw new InvalidEncodedLabelError({ @@ -23,6 +36,19 @@ export function decodeLabelhash(hash: string): Hex { return `0x${hash.slice(1, -1)}` } +// ================================ +// Encode Labelhash +// ================================ + +export type EncodeLabelhashErrorType = InvalidLabelhashError + +/** + * Encodes a labelhash + * + * @throws {EncodeLabelhashErrorType} + * @param hash - Labelhash to encode + * @throws {EncodeLabelhashErrorType} + */ export function encodeLabelhash(hash: string) { if (!hash.startsWith('0x')) throw new InvalidLabelhashError({ @@ -43,13 +69,11 @@ export function isEncodedLabelhash(hash: string) { return hash.startsWith('[') && hash.endsWith(']') && hash.length === 66 } -function getLabels() { - return hasLocalStorage - ? JSON.parse(localStorage.getItem('ensjs:labels') as string) || {} - : {} -} +// ================================ +// Save Label +// ================================ -function _saveLabel(hash: string, label: any) { +function _saveLabel(hash: string, label: string) { if (!hasLocalStorage) return hash const labels = getLabels() localStorage.setItem( @@ -62,11 +86,31 @@ function _saveLabel(hash: string, label: any) { return hash } +export type SaveLabelErrorType = LabelhashErrorType + +/** + * Saves a label to local storage (if available) + * + * @throws {SaveLabelErrorType} + * @param label - Label to save + */ export function saveLabel(label: string) { const hash = `${labelhash(label.toLowerCase())}` return _saveLabel(hash, label) } +// ================================ +// Save Name +// ================================ + +export type SaveNameErrorType = SaveLabelErrorType + +/** + * Saves a name to local storage (if available) + * + * @throws {SaveNameErrorType} + * @param name - Name to save + */ export function saveName(name: string) { const nameArray = name.split('.') for (const label of nameArray) { @@ -76,6 +120,18 @@ export function saveName(name: string) { } } +// ================================ +// Check Label +// ================================ + +export type CheckLabelErrorType = DecodeLabelhashErrorType + +/** + * Checks a label + * + * @throws {CheckLabelErrorType} + * @param hash - Label to check + */ export function checkLabel(hash: string): string { const labels = getLabels() if (isEncodedLabelhash(hash)) { @@ -84,13 +140,39 @@ export function checkLabel(hash: string): string { return hash } +// ================================ +// Check Is Decrypted +// ================================ + export function checkIsDecrypted(string: string | string[]) { return !string?.includes('[') } +// ================================ +// Decrypt Name +// ================================ + +export type DecryptNameErrorType = CheckLabelErrorType + +/** + * Decrypts a name + * + * @throws {DecryptNameErrorType} + * @param name - Name to decrypt + */ export function decryptName(name: string) { return name .split('.') - .map((label: any) => checkLabel(label)) + .map((label: string) => checkLabel(label)) .join('.') } + +// ================================ +// Internal Utils +// ================================ + +function getLabels() { + return hasLocalStorage + ? JSON.parse(localStorage.getItem('ensjs:labels') as string) || {} + : {} +} diff --git a/packages/ensjs/src/utils/makeLabelNodeAndParent.test.ts b/packages/ensjs/src/utils/name/makeLabelNodeAndParent.test.ts similarity index 100% rename from packages/ensjs/src/utils/makeLabelNodeAndParent.test.ts rename to packages/ensjs/src/utils/name/makeLabelNodeAndParent.test.ts diff --git a/packages/ensjs/src/utils/name/makeLabelNodeAndParent.ts b/packages/ensjs/src/utils/name/makeLabelNodeAndParent.ts new file mode 100644 index 000000000..6930eeb4c --- /dev/null +++ b/packages/ensjs/src/utils/name/makeLabelNodeAndParent.ts @@ -0,0 +1,27 @@ +import { + type LabelhashErrorType, + labelhash, + type NamehashErrorType, + namehash, +} from 'viem' + +export type MakeLabelNodeAndParentErrorType = + | NamehashErrorType + | LabelhashErrorType + +/** + * Makes a label node and parent + * + * @throws {MakeLabelNodeAndParentErrorType} + * @param name - Name to make label node and parent + */ +export const makeLabelNodeAndParent = (name: string) => { + const labels = name.split('.') + const label = labels.shift() as string + const parentNode = namehash(labels.join('.')) + return { + label, + labelhash: labelhash(label), + parentNode, + } +} diff --git a/packages/ensjs/src/utils/name/normalize.test.ts b/packages/ensjs/src/utils/name/normalize.test.ts new file mode 100644 index 000000000..ee402341b --- /dev/null +++ b/packages/ensjs/src/utils/name/normalize.test.ts @@ -0,0 +1,20 @@ +import { expect, it } from 'vitest' + +it('exports functions from ens-normalize', async () => { + const modules = await import('./normalize.js') + expect(modules).toMatchInlineSnapshot(` + { + "beautify": [Function], + "emoji": [Function], + "isCombiningMark": [Function], + "normalise": [Function], + "normaliseFragment": [Function], + "normalize": [Function], + "normalizeFragment": [Function], + "shouldEscape": [Function], + "split": [Function], + "tokenise": [Function], + "tokenize": [Function], + } + `) +}) diff --git a/packages/ensjs/src/utils/name/normalize.ts b/packages/ensjs/src/utils/name/normalize.ts new file mode 100644 index 000000000..93065e6f7 --- /dev/null +++ b/packages/ensjs/src/utils/name/normalize.ts @@ -0,0 +1,70 @@ +import { + type DisallowedToken, + type EmojiToken, + ens_beautify, + ens_emoji, + ens_normalize, + ens_normalize_fragment, + ens_split, + ens_tokenize, + type IgnoredToken, + is_combining_mark, + type Label, + type MappedToken, + type NFCToken, + type StopToken, + should_escape, + type TextToken, + type Token, + type ValidToken, +} from '@adraffy/ens-normalize' +import type { ErrorType } from '../../errors/utils.js' + +const zeros = new Uint8Array(32) +zeros.fill(0) + +// ================================ +// Normalize +// ================================ + +export type NormalizeErrorType = ErrorType + +/** + * Normalizes a name + * + * @throws {NormalizeErrorType} + * @param name - Name to normalize + */ +export const normalize = (name: string) => (name ? ens_normalize(name) : name) + +/** @deprecated use `normalize` instead */ +export const normalise = normalize + +// ================================ +// Extra Exports +// ================================ + +export const beautify = ens_beautify +export const emoji = ens_emoji +export const normalizeFragment = ens_normalize_fragment +/** @deprecated use `normalizeFragment` instead */ +export const normaliseFragment = ens_normalize_fragment +export const split = ens_split +export const tokenize = ens_tokenize +/** @deprecated use `tokenize` instead */ +export const tokenise = tokenize +export const isCombiningMark = is_combining_mark +export const shouldEscape = should_escape + +export type { + DisallowedToken, + EmojiToken, + IgnoredToken, + Label, + MappedToken, + NFCToken, + StopToken, + TextToken, + Token, + ValidToken, +} diff --git a/packages/ensjs/src/utils/validation.test.ts b/packages/ensjs/src/utils/name/validation.test.ts similarity index 85% rename from packages/ensjs/src/utils/validation.test.ts rename to packages/ensjs/src/utils/name/validation.test.ts index af5a59347..0b2e94fea 100644 --- a/packages/ensjs/src/utils/validation.test.ts +++ b/packages/ensjs/src/utils/name/validation.test.ts @@ -1,20 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { - type MockedFunction, - beforeEach, - describe, - expect, - it, - vi, -} from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vitest' import { parseInput, validateName } from './validation.js' -declare namespace localStorage { - const getItem: MockedFunction - const setItem: MockedFunction - const removeItem: MockedFunction -} - const labelsMock = { '0x68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87': 'something', @@ -25,9 +12,9 @@ const labelsMockJSON = JSON.stringify(labelsMock) describe('validateName()', () => { beforeEach(() => { - localStorage.setItem('ensjs:labels', labelsMockJSON) - vi.spyOn(localStorage, 'getItem') - vi.spyOn(localStorage, 'setItem') + global.localStorage.setItem('ensjs:labels', labelsMockJSON) + vi.spyOn(global.localStorage, 'getItem') + vi.spyOn(global.localStorage, 'setItem') }) it('should throw if the name has an empty label', () => { expect(() => validateName('foo..bar')).toThrowError( @@ -61,7 +48,7 @@ describe('validateName()', () => { 'thing.[68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87].eth', ), ).toEqual('thing.something.eth') - expect(localStorage.getItem).toHaveBeenCalled() + expect(global.localStorage.getItem).toHaveBeenCalled() }) it('should fallback to encoded label hash if the decoded label hash is not in local storage', () => { expect( @@ -71,7 +58,7 @@ describe('validateName()', () => { ).toEqual( 'something.[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17].eth', ) - expect(localStorage.getItem).toHaveBeenCalled() + expect(global.localStorage.getItem).toHaveBeenCalled() expect( validateName( '[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17]', @@ -80,12 +67,12 @@ describe('validateName()', () => { '[8c6c947d200f53fa1127b152f95b118f9e1d0eeb06fc678b6fc8a6d5c6fc5e17]', ) }) - it('should normalise the name', () => { + it('should normalize the name', () => { expect(validateName('aAaaA.eth')).toEqual('aaaaa.eth') }) - it('should save the normalised name to local storage', () => { + it('should save the normalized name to local storage', () => { validateName('swAgCity.eth') - expect(localStorage.setItem).toHaveBeenCalledWith( + expect(global.localStorage.setItem).toHaveBeenCalledWith( 'ensjs:labels', JSON.stringify({ ...labelsMock, @@ -94,7 +81,7 @@ describe('validateName()', () => { }), ) }) - it('should return the normalised name', () => { + it('should return the normalized name', () => { expect(validateName('swAgCity.eth')).toEqual('swagcity.eth') }) }) @@ -103,7 +90,7 @@ describe('parseInput()', () => { it('should parse the input', () => { expect(parseInput('bar.eth')).toEqual({ type: 'name', - normalised: 'bar.eth', + normalized: 'bar.eth', isShort: false, isValid: true, is2LD: true, @@ -111,13 +98,13 @@ describe('parseInput()', () => { labelDataArray: expect.any(Array), }) }) - it('should return a normalised name', () => { - expect(parseInput('bAr.etH').normalised).toEqual('bar.eth') + it('should return a normalized name', () => { + expect(parseInput('bAr.etH').normalized).toEqual('bar.eth') }) it('should parse the input if it is invalid', () => { expect(parseInput('bar..eth')).toEqual({ type: 'name', - normalised: undefined, + normalized: undefined, isShort: false, isValid: false, is2LD: false, diff --git a/packages/ensjs/src/utils/validation.ts b/packages/ensjs/src/utils/name/validation.ts similarity index 50% rename from packages/ensjs/src/utils/validation.ts rename to packages/ensjs/src/utils/name/validation.ts index e1e75d900..3682b43d5 100644 --- a/packages/ensjs/src/utils/validation.ts +++ b/packages/ensjs/src/utils/name/validation.ts @@ -1,31 +1,63 @@ import { NameWithEmptyLabelsError, RootNameIncludesOtherLabelsError, -} from '../errors/utils.js' -import { MINIMUM_DOT_ETH_CHARS } from './consts.js' -import { checkLabel, isEncodedLabelhash, saveName } from './labels.js' -import { type Label, normalise, split } from './normalise.js' +} from '../../errors/utils.js' +import { MINIMUM_DOT_ETH_CHARS } from '../consts.js' +import { + type CheckLabelErrorType, + checkLabel, + isEncodedLabelhash, + type SaveNameErrorType, + saveName, +} from './labels.js' +import { + type Label, + type NormalizeErrorType, + normalize, + split, +} from './normalize.js' + +// ================================ +// Validate Name +// ================================ + +export type ValidateNameErrorType = + | NameWithEmptyLabelsError + | RootNameIncludesOtherLabelsError + | CheckLabelErrorType + | NormalizeErrorType + | SaveNameErrorType export const validateName = (name: string) => { const nameArray = name.split('.') - const normalisedArray = nameArray.map((label) => { + const normalizedArray = nameArray.map((label) => { if (label.length === 0) throw new NameWithEmptyLabelsError({ name }) if (label === '[root]') { if (name !== label) throw new RootNameIncludesOtherLabelsError({ name }) return label } return isEncodedLabelhash(label) - ? checkLabel(label) || label - : normalise(label) + ? // may throw CheckLabelErrorType + checkLabel(label) || label + : // may throw NormalizeErrorType + normalize(label) }) - const normalisedName = normalisedArray.join('.') - saveName(normalisedName) - return normalisedName + + const normalizedName = normalizedArray.join('.') + + // may throw SaveNameErrorType + saveName(normalizedName) + + return normalizedName } +// ================================ +// Parse Input +// ================================ + export type ParsedInputResult = { type: 'name' | 'label' - normalised: string | undefined + normalized: string | undefined isValid: boolean isShort: boolean is2LD: boolean @@ -37,12 +69,13 @@ export const parseInput = (input: string): ParsedInputResult => { let nameReference = input let isValid = false + // TODO: Should this only be catching the explicitly thrown errors or should it be catching all errors? try { nameReference = validateName(input) isValid = true } catch {} - const normalisedName = isValid ? nameReference : undefined + const normalizedName = isValid ? nameReference : undefined const labels = nameReference.split('.') const tld = labels[labels.length - 1] @@ -54,7 +87,7 @@ export const parseInput = (input: string): ParsedInputResult => { if (labels.length === 1) { return { type: 'label', - normalised: normalisedName, + normalized: normalizedName, isShort, isValid, is2LD: false, @@ -66,7 +99,7 @@ export const parseInput = (input: string): ParsedInputResult => { const is2LD = labels.length === 2 return { type: 'name', - normalised: normalisedName, + normalized: normalizedName, isShort: isETH && is2LD ? isShort : false, isValid, is2LD, @@ -75,5 +108,12 @@ export const parseInput = (input: string): ParsedInputResult => { } } +// ================================ +// Check Is Dot Eth +// ================================ + +/** + * Checks if a label is a 2LD and is the .eth TLD + */ export const checkIsDotEth = (labels: string[]) => labels.length === 2 && labels[1] === 'eth' diff --git a/packages/ensjs/src/utils/normalise.test.ts b/packages/ensjs/src/utils/normalise.test.ts deleted file mode 100644 index 390cc53d8..000000000 --- a/packages/ensjs/src/utils/normalise.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { namehash } from './normalise.js' - -describe('namehash()', () => { - it('returns namehash for name', () => { - expect(namehash('test.eth')).toEqual( - '0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1', - ) - }) - it('returns namehash for name with encoded labelhash', () => { - expect( - namehash( - '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].eth', - ), - ).toEqual( - '0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1', - ) - }) -}) - -it('exports functions from ens-normalize', async () => { - const modules = await import('./normalise.js') - expect(modules).toMatchInlineSnapshot(` - { - "beautify": [Function], - "emoji": [Function], - "isCombiningMark": [Function], - "namehash": [Function], - "normalise": [Function], - "normaliseFragment": [Function], - "shouldEscape": [Function], - "split": [Function], - "tokenise": [Function], - } - `) -}) diff --git a/packages/ensjs/src/utils/normalise.ts b/packages/ensjs/src/utils/normalise.ts deleted file mode 100644 index 9ce381fe0..000000000 --- a/packages/ensjs/src/utils/normalise.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - type DisallowedToken, - type EmojiToken, - type IgnoredToken, - type Label, - type MappedToken, - type NFCToken, - type StopToken, - type TextToken, - type Token, - type ValidToken, - ens_beautify, - ens_emoji, - ens_normalize, - ens_normalize_fragment, - ens_split, - ens_tokenize, - is_combining_mark, - should_escape, -} from '@adraffy/ens-normalize' -import { - type ByteArray, - bytesToHex, - concat, - hexToBytes, - keccak256, - stringToBytes, -} from 'viem' -import { decodeLabelhash, isEncodedLabelhash } from './labels.js' - -const zeros = new Uint8Array(32) -zeros.fill(0) - -export const normalise = (name: string) => (name ? ens_normalize(name) : name) - -export function namehash(name: string) { - let result: ByteArray = new Uint8Array(32).fill(0) - if (!name) return bytesToHex(result) - - const labels = name.split('.') - // Iterate in reverse order building up hash - for (let i = labels.length - 1; i >= 0; i -= 1) { - let labelSha: Uint8Array - if (isEncodedLabelhash(labels[i])) { - labelSha = hexToBytes(decodeLabelhash(labels[i])) - } else { - const normalised = normalise(labels[i]) - labelSha = keccak256(stringToBytes(normalised), 'bytes') - } - result = keccak256(concat([result, labelSha]), 'bytes') - } - - return bytesToHex(result) -} - -export const beautify = ens_beautify -export const emoji = ens_emoji -export const normaliseFragment = ens_normalize_fragment -export const split = ens_split -export const tokenise = ens_tokenize -export const isCombiningMark = is_combining_mark -export const shouldEscape = should_escape - -export type { - DisallowedToken, - EmojiToken, - IgnoredToken, - Label, - MappedToken, - NFCToken, - StopToken, - TextToken, - Token, - ValidToken, -} diff --git a/packages/ensjs/src/utils/normaliseCoinId.ts b/packages/ensjs/src/utils/normaliseCoinId.ts deleted file mode 100644 index 662a68748..000000000 --- a/packages/ensjs/src/utils/normaliseCoinId.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - type Coin, - getCoderByCoinName, - getCoderByCoinType, -} from '@ensdomains/address-encoder' -import { CoinFormatterNotFoundError } from '../errors/public.js' - -export const normaliseCoinId = (coinId: string | number) => { - const isString = typeof coinId === 'string' - - if (isString && Number.isNaN(Number.parseInt(coinId))) { - return { - type: 'name', - value: coinId.toLowerCase().replace(/legacy$/, 'Legacy'), - } as const - } - return { - type: 'id', - value: isString ? Number.parseInt(coinId as string) : (coinId as number), - } as const -} - -export const getCoderFromCoin = (coinId: string | number): Coin => { - const normalisedCoin = normaliseCoinId(coinId) - let coder: Coin - try { - coder = - normalisedCoin.type === 'id' - ? getCoderByCoinType(normalisedCoin.value) - : getCoderByCoinName(normalisedCoin.value) - } catch { - throw new CoinFormatterNotFoundError({ coinType: coinId }) - } - - return coder -} diff --git a/packages/ensjs/src/utils/normalizeCoinId.ts b/packages/ensjs/src/utils/normalizeCoinId.ts new file mode 100644 index 000000000..af10b3fb0 --- /dev/null +++ b/packages/ensjs/src/utils/normalizeCoinId.ts @@ -0,0 +1,65 @@ +import { + type Coin, + getCoderByCoinName, + getCoderByCoinType, +} from '@ensdomains/address-encoder' +import { CoinFormatterNotFoundError } from '../errors/public.js' +import type { ErrorType } from '../errors/utils.js' + +// ================================ +// Normalize coin id +// ================================ + +export type NormalizeCoinIdReturnType = + | { + type: 'id' + value: number + } + | { + type: 'name' + value: string + } + +export const normalizeCoinId = ( + coinId: string | number, +): NormalizeCoinIdReturnType => { + const isString = typeof coinId === 'string' + + if (isString && Number.isNaN(Number.parseInt(coinId))) { + return { + type: 'name', + value: coinId.toLowerCase().replace(/legacy$/, 'Legacy'), + } as const + } + return { + type: 'id', + value: isString ? Number.parseInt(coinId as string) : (coinId as number), + } as const +} + +// ================================ +// Get coder from coin +// ================================ + +export type GetCoderFromCoinReturnType = Coin + +export type GetCoderFromCoinErrorType = CoinFormatterNotFoundError | ErrorType + +export const getCoderFromCoin = ( + coinId: string | number, +): GetCoderFromCoinReturnType => { + const normalizedCoin = normalizeCoinId(coinId) + let coder: Coin + try { + coder = + normalizedCoin.type === 'id' + ? // may throw Error + getCoderByCoinType(normalizedCoin.value) + : // may throw Error + getCoderByCoinName(normalizedCoin.value) + } catch { + throw new CoinFormatterNotFoundError({ coinType: coinId }) + } + + return coder +} diff --git a/packages/ensjs/src/utils/nullableAddress.ts b/packages/ensjs/src/utils/nullableAddress.ts new file mode 100644 index 000000000..dd8872d7e --- /dev/null +++ b/packages/ensjs/src/utils/nullableAddress.ts @@ -0,0 +1,4 @@ +import { type Address, zeroAddress } from 'viem' + +export const nullableAddress = (address: Address) => + address === zeroAddress ? null : address diff --git a/packages/ensjs/src/utils/ownerFromContract.test.ts b/packages/ensjs/src/utils/ownerFromContract.test.ts index 4ddcff234..f3911336a 100644 --- a/packages/ensjs/src/utils/ownerFromContract.test.ts +++ b/packages/ensjs/src/utils/ownerFromContract.test.ts @@ -1,16 +1,42 @@ +import { + type Abi, + type Address, + encodeFunctionData, + type Hex, + namehash, + type ReadContractParameters, +} from 'viem' import { expect, it } from 'vitest' import { deploymentAddresses, publicClient } from '../test/addTestContracts.js' -import { namehash } from './normalise.js' -import { ownerFromContract } from './ownerFromContract.js' +import { getContractSpecificOwnerParameters } from './ownerFromContract.js' const baseParams = { - client: publicClient, - namehash: namehash('test.eth'), + node: namehash('test.eth'), +} + +const toSnapshot = ({ + abi, + functionName, + args, + address, +}: ReadContractParameters): { data: Hex; to?: Address } => { + // @ts-expect-error requires viem ABI TS magic + const data = encodeFunctionData({ abi, functionName, args }) + + return { + data, + to: address, + } } it('uses nameWrapper contract when contract is nameWrapper', () => { expect( - ownerFromContract({ ...baseParams, contract: 'nameWrapper' }), + toSnapshot( + getContractSpecificOwnerParameters(publicClient.chain, { + ...baseParams, + contract: 'nameWrapper', + }), + ), ).toMatchInlineSnapshot(` { "data": "0x6352211eeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", @@ -20,7 +46,12 @@ it('uses nameWrapper contract when contract is nameWrapper', () => { }) it('uses registry contract when contract is registry', () => { expect( - ownerFromContract({ ...baseParams, contract: 'registry' }), + toSnapshot( + getContractSpecificOwnerParameters(publicClient.chain, { + ...baseParams, + contract: 'registry', + }), + ), ).toMatchInlineSnapshot(` { "data": "0x02571be3eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", @@ -30,11 +61,13 @@ it('uses registry contract when contract is registry', () => { }) it('uses registrar contract when contract is registrar', () => { expect( - ownerFromContract({ - ...baseParams, - contract: 'registrar', - labels: ['test'], - }), + toSnapshot( + getContractSpecificOwnerParameters(publicClient.chain, { + ...baseParams, + contract: 'registrar', + labels: ['test'], + }), + ), ).toMatchInlineSnapshot(` { "data": "0x6352211e9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658", @@ -44,7 +77,11 @@ it('uses registrar contract when contract is registrar', () => { }) it('throws when contract is not nameWrapper, registry, or registrar', () => { expect(() => - ownerFromContract({ ...baseParams, contract: 'invalid' as any }), + getContractSpecificOwnerParameters(publicClient.chain, { + ...baseParams, + // biome-ignore lint/suspicious/noExplicitAny: Intentionally invalid contract type + contract: 'invalid' as any, + }), ).toThrowErrorMatchingInlineSnapshot(` [InvalidContractTypeError: Invalid contract type: invalid diff --git a/packages/ensjs/src/utils/ownerFromContract.ts b/packages/ensjs/src/utils/ownerFromContract.ts index df7429695..104b0e07d 100644 --- a/packages/ensjs/src/utils/ownerFromContract.ts +++ b/packages/ensjs/src/utils/ownerFromContract.ts @@ -1,22 +1,27 @@ -import { type Hex, encodeFunctionData, labelhash } from 'viem' -import { baseRegistrarOwnerOfSnippet } from '../contracts/baseRegistrar.js' -import type { ClientWithEns } from '../contracts/consts.js' -import { getChainContractAddress } from '../contracts/getChainContractAddress.js' -import { nameWrapperOwnerOfSnippet } from '../contracts/nameWrapper.js' -import { registryOwnerSnippet } from '../contracts/registry.js' +import { registryOwnerSnippet } from '@ensdomains/ensjs-abi/registry' +import { nameWrapperOwnerOfSnippet } from '@ensdomains/ensjs-abi/v1/nameWrapper' +import { + type Chain, + getChainContractAddress, + type Hex, + type LabelhashErrorType, + labelhash, + type ReadContractParameters, +} from 'viem' +import type { RequireChainContracts } from '../clients/l1.js' import { InvalidContractTypeError } from '../errors/general.js' +import { ASSERT_NO_TYPE_ERROR } from '../types/internal.js' export type OwnerContract = 'nameWrapper' | 'registry' | 'registrar' export type OwnerFromContractArgs = { - client: ClientWithEns contract: OwnerContract - namehash?: Hex + node?: Hex labels?: string[] } & ( | { contract: Exclude - namehash: Hex + node: Hex } | { contract: 'registrar' @@ -24,43 +29,56 @@ export type OwnerFromContractArgs = { } ) -export const ownerFromContract = ({ - client, - contract, - namehash, - labels, -}: OwnerFromContractArgs) => { +const abi = [...nameWrapperOwnerOfSnippet, ...registryOwnerSnippet] as const + +// ================================ +// Get contract specific owner parameters +// ================================ + +export type GetContractSpecificOwnerParametersErrorType = + // | GetChainContractAddressErrorType + InvalidContractTypeError | LabelhashErrorType + +export const getContractSpecificOwnerParameters = <_chain extends Chain>( + chain: RequireChainContracts< + _chain, + 'ensNameWrapper' | 'ensLegacyRegistry' | 'ensBaseRegistrarImplementation' + >, + { contract, node, labels }: OwnerFromContractArgs, +) => { + ASSERT_NO_TYPE_ERROR(chain) + switch (contract) { case 'nameWrapper': return { - to: getChainContractAddress({ client, contract: 'ensNameWrapper' }), - data: encodeFunctionData({ - abi: nameWrapperOwnerOfSnippet, - functionName: 'ownerOf', - args: [BigInt(namehash)], + address: getChainContractAddress({ + chain, + contract: 'ensNameWrapper', }), - } + abi, + functionName: 'ownerOf', + args: [BigInt(node)], + } as const satisfies ReadContractParameters case 'registry': return { - to: getChainContractAddress({ client, contract: 'ensRegistry' }), - data: encodeFunctionData({ - abi: registryOwnerSnippet, - functionName: 'owner', - args: [namehash], + address: getChainContractAddress({ + chain, + contract: 'ensLegacyRegistry', }), - } + abi, + functionName: 'owner', + args: [node], + } as const satisfies ReadContractParameters case 'registrar': return { - to: getChainContractAddress({ - client, + address: getChainContractAddress({ + chain, contract: 'ensBaseRegistrarImplementation', }), - data: encodeFunctionData({ - abi: baseRegistrarOwnerOfSnippet, - functionName: 'ownerOf', - args: [BigInt(labelhash(labels[0]))], - }), - } + abi, + functionName: 'ownerOf', + args: [BigInt(labelhash(labels[0]))], + } as const satisfies ReadContractParameters default: throw new InvalidContractTypeError({ contractType: contract, diff --git a/packages/ensjs/src/utils/registerHelpers.test.ts b/packages/ensjs/src/utils/registerHelpers.test.ts index fdd8574b9..9d57adbe3 100644 --- a/packages/ensjs/src/utils/registerHelpers.test.ts +++ b/packages/ensjs/src/utils/registerHelpers.test.ts @@ -1,14 +1,6 @@ -import { labelhash } from 'viem' +import { namehash } from 'viem' import { describe, expect, it } from 'vitest' -import { namehash } from './normalise.js' -import { - type RegistrationParameters, - makeCommitment, - makeCommitmentFromTuple, - makeCommitmentTuple, - makeRegistrationTuple, - randomSecret, -} from './registerHelpers.js' +import { randomSecret } from './registerHelpers.js' describe('randomSecret()', () => { it('generates a random secret with no args', () => { @@ -39,156 +31,157 @@ describe('randomSecret()', () => { }) }) -describe('makeCommitmentTuple()', () => { - it('generates a commitment tuple', () => { - const tuple = makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: '0xsecret', - }) - // labelhash - expect(tuple[0]).toBe(labelhash('test')) - // owner - expect(tuple[1]).toBe('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266') - // duration - expect(tuple[2]).toBe(31536000n) - // secret - expect(tuple[3]).toBe('0xsecret') - // resolver address - expect(tuple[4]).toBe('0x0000000000000000000000000000000000000000') - // records - expect(tuple[5]).toStrictEqual([]) - // reverse record - expect(tuple[6]).toBe(false) - // owner controlled fuses - expect(tuple[7]).toBe(0) - }) - it('encodes fuses when supplied', () => { - const tuple = makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: '0xsecret', - fuses: { - named: ['CANNOT_UNWRAP', 'CANNOT_BURN_FUSES'], - }, - }) - expect(tuple[7]).toBe(3) - }) - it('adds ETH coin when reverse record is supplied and no ETH coin is supplied', () => { - const tuple = makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: '0xsecret', - reverseRecord: true, - resolverAddress: '0xresolverAddress', - }) - expect(tuple[5]).toMatchInlineSnapshot(` - [ - "0x8b95dd71eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", - ] - `) - expect(tuple[6]).toBe(true) - }) - it('does not add ETH coin when reverse record is supplied and ETH coin is supplied', () => { - const tuple = makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - records: { - coins: [ - { coin: 'ETH', value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' }, - ], - }, - resolverAddress: '0xresolverAddress', - secret: '0xsecret', - reverseRecord: true, - }) - expect(tuple[5]).toMatchInlineSnapshot(` - [ - "0x8b95dd71eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", - ] - `) - expect(tuple[6]).toBe(true) - }) - it('throws when records are supplied without a resolver address', () => { - expect(() => - makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - records: { - coins: [ - { - coin: 'ETH', - value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - }, - ], - }, - secret: '0xsecret', - reverseRecord: true, - }), - ).toThrowErrorMatchingInlineSnapshot(` - [ResolverAddressRequiredError: Resolver address is required when data is supplied +// TODO: makeCommitmentTuple() depends on resolverMulticallParameters (previously called generateRecordCallArray) but it needs fixing +// describe('makeCommitmentTuple()', () => { +// it('generates a commitment tuple', () => { +// const tuple = makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: '0xsecret', +// }) +// // labelhash +// expect(tuple[0]).toBe(labelhash('test')) +// // owner +// expect(tuple[1]).toBe('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266') +// // duration +// expect(tuple[2]).toBe(31536000n) +// // secret +// expect(tuple[3]).toBe('0xsecret') +// // resolver address +// expect(tuple[4]).toBe('0x0000000000000000000000000000000000000000') +// // records +// expect(tuple[5]).toStrictEqual([]) +// // reverse record +// expect(tuple[6]).toBe(false) +// // owner controlled fuses +// expect(tuple[7]).toBe(0) +// }) +// it('encodes fuses when supplied', () => { +// const tuple = makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: '0xsecret', +// fuses: { +// named: ['CANNOT_UNWRAP', 'CANNOT_BURN_FUSES'], +// }, +// }) +// expect(tuple[7]).toBe(3) +// }) +// it('adds ETH coin when reverse record is supplied and no ETH coin is supplied', () => { +// const tuple = makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: '0xsecret', +// reverseRecord: true, +// resolverAddress: '0xresolverAddress', +// }) +// expect(tuple[5]).toMatchInlineSnapshot(` +// [ +// "0x8b95dd71eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", +// ] +// `) +// expect(tuple[6]).toBe(true) +// }) +// it('does not add ETH coin when reverse record is supplied and ETH coin is supplied', () => { +// const tuple = makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// records: { +// coins: [ +// { coin: 'ETH', value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' }, +// ], +// }, +// resolverAddress: '0xresolverAddress', +// secret: '0xsecret', +// reverseRecord: true, +// }) +// expect(tuple[5]).toMatchInlineSnapshot(` +// [ +// "0x8b95dd71eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000", +// ] +// `) +// expect(tuple[6]).toBe(true) +// }) +// it('throws when records are supplied without a resolver address', () => { +// expect(() => +// makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// records: { +// coins: [ +// { +// coin: 'ETH', +// value: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// }, +// ], +// }, +// secret: '0xsecret', +// reverseRecord: true, +// }), +// ).toThrowErrorMatchingInlineSnapshot(` +// [ResolverAddressRequiredError: Resolver address is required when data is supplied - Supplied data: - - name: test.eth - - owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - - duration: 31536000 - - resolverAddress: 0x0000000000000000000000000000000000000000 - - records: [object Object] - - reverseRecord: true - - fuses: undefined +// Supplied data: +// - name: test.eth +// - owner: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +// - duration: 31536000 +// - resolverAddress: 0x0000000000000000000000000000000000000000 +// - records: [object Object] +// - reverseRecord: true +// - fuses: undefined - Version: @ensdomains/ensjs@1.0.0-mock.0] - `) - }) -}) +// Version: @ensdomains/ensjs@1.0.0-mock.0] +// `) +// }) +// }) -describe('makeRegistrationTuple()', () => { - it('replaces labelhash from commitment tuple with label', () => { - const data: RegistrationParameters = { - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: '0xsecret', - } - const commitmentTuple = makeCommitmentTuple(data) - const registrationTuple = makeRegistrationTuple(data) - expect(registrationTuple[0]).toBe('test') - expect(registrationTuple.slice(1)).toEqual(commitmentTuple.slice(1)) - }) -}) +// describe('makeRegistrationTuple()', () => { +// it('replaces labelhash from commitment tuple with label', () => { +// const data: RegistrationParameters = { +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: '0xsecret', +// } +// const commitmentTuple = makeCommitmentTuple(data) +// const registrationTuple = makeRegistrationTuple(data) +// expect(registrationTuple[0]).toBe('test') +// expect(registrationTuple.slice(1)).toEqual(commitmentTuple.slice(1)) +// }) +// }) -describe('makeCommitmentFromTuple()', () => { - it('generates a commitment from a tuple', () => { - const tuple = makeCommitmentTuple({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: - '0xde99acb8241826c5b3012b2c7a05dc28043428744a9c39445b4707c92b3fc054', - }) - const commitment = makeCommitmentFromTuple(tuple) - expect(commitment).toMatchInlineSnapshot( - `"0x0d7fe28313600187945700f6c6374cc0ba4a360df039b3e62d435506e69dbe63"`, - ) - }) -}) +// describe('makeCommitmentFromTuple()', () => { +// it('generates a commitment from a tuple', () => { +// const tuple = makeCommitmentTuple({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: +// '0xde99acb8241826c5b3012b2c7a05dc28043428744a9c39445b4707c92b3fc054', +// }) +// const commitment = makeCommitmentFromTuple(tuple) +// expect(commitment).toMatchInlineSnapshot( +// `"0x0d7fe28313600187945700f6c6374cc0ba4a360df039b3e62d435506e69dbe63"`, +// ) +// }) +// }) -describe('makeCommitment()', () => { - it('generates a commitment from a RegistrationParameters', () => { - const commitment = makeCommitment({ - name: 'test.eth', - owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - duration: 31536000, - secret: - '0xde99acb8241826c5b3012b2c7a05dc28043428744a9c39445b4707c92b3fc054', - }) - expect(commitment).toMatchInlineSnapshot( - `"0x0d7fe28313600187945700f6c6374cc0ba4a360df039b3e62d435506e69dbe63"`, - ) - }) -}) +// describe('makeCommitment()', () => { +// it('generates a commitment from a RegistrationParameters', () => { +// const commitment = makeCommitment({ +// name: 'test.eth', +// owner: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +// duration: 31536000, +// secret: +// '0xde99acb8241826c5b3012b2c7a05dc28043428744a9c39445b4707c92b3fc054', +// }) +// expect(commitment).toMatchInlineSnapshot( +// `"0x0d7fe28313600187945700f6c6374cc0ba4a360df039b3e62d435506e69dbe63"`, +// ) +// }) +// }) diff --git a/packages/ensjs/src/utils/registerHelpers.ts b/packages/ensjs/src/utils/registerHelpers.ts index c4d712eba..6d7eb838a 100644 --- a/packages/ensjs/src/utils/registerHelpers.ts +++ b/packages/ensjs/src/utils/registerHelpers.ts @@ -1,24 +1,36 @@ import { type Address, - type Hex, + type EncodeAbiParametersErrorType, encodeAbiParameters, + encodeFunctionData, + type Hex, + type Keccak256ErrorType, keccak256, + type LabelhashErrorType, labelhash, + type PadErrorType, pad, + type ToBytesErrorType, + type ToHexErrorType, toBytes, toHex, + zeroAddress, } from 'viem' +import { type NamehashErrorType, namehash } from 'viem/ens' import { CampaignReferenceTooLargeError, + type ErrorType, ResolverAddressRequiredError, } from '../errors/utils.js' -import { EMPTY_ADDRESS } from './consts.js' -import { type EncodeChildFusesInputObject, encodeFuses } from './fuses.js' import { type RecordOptions, - generateRecordCallArray, -} from './generateRecordCallArray.js' -import { namehash } from './normalise.js' + resolverMulticallParameters, +} from './coders/resolverMulticallParameters.js' +import { + type EncodeChildFusesInputObject, + type EncodeFusesErrorType, + encodeFuses, +} from './fuses.js' export type RegistrationParameters = { /** Name to register */ @@ -68,6 +80,18 @@ const cryptoRef = window.crypto) || undefined +// ================================ +// Random secret +// ================================ + +export type RandomSecretErrorType = + | ToBytesErrorType + | NamehashErrorType + | CampaignReferenceTooLargeError + | PadErrorType + | ToHexErrorType + | ErrorType + export const randomSecret = ({ platformDomain, campaign, @@ -75,7 +99,7 @@ export const randomSecret = ({ platformDomain?: string campaign?: number } = {}) => { - const bytes = cryptoRef!.getRandomValues(new Uint8Array(32)) + const bytes = cryptoRef?.getRandomValues(new Uint8Array(32))! if (platformDomain) { const hash = toBytes(namehash(platformDomain)) for (let i = 0; i < 4; i += 1) { @@ -93,18 +117,27 @@ export const randomSecret = ({ return toHex(bytes) } -export const makeCommitmentTuple = ({ +// ================================ +// Make commitment tuple +// ================================ + +export type MakeCommitmentTupleErrorType = + | LabelhashErrorType + | NamehashErrorType + | EncodeFusesErrorType + | ResolverAddressRequiredError + +export const makeCommitmentTuple = async ({ name, owner, duration, - resolverAddress = EMPTY_ADDRESS, + resolverAddress = zeroAddress, records: { coins = [], ...records } = { texts: [], coins: [] }, reverseRecord, fuses, secret, -}: RegistrationParameters): CommitmentTuple => { +}: RegistrationParameters): Promise => { const labelHash = labelhash(name.split('.')[0]) - const hash = namehash(name) const fuseData = fuses ? encodeFuses({ restriction: 'child', input: fuses }) : 0 @@ -123,10 +156,12 @@ export const makeCommitmentTuple = ({ } const data = records - ? generateRecordCallArray({ namehash: hash, coins, ...records }) + ? (await resolverMulticallParameters({ name, coins, ...records })) + // @ts-expect-error needs TS magic + .map((item) => encodeFunctionData(item)) : [] - if (data.length > 0 && resolverAddress === EMPTY_ADDRESS) + if (data.length > 0 && resolverAddress === zeroAddress) throw new ResolverAddressRequiredError({ data: { name, @@ -151,15 +186,29 @@ export const makeCommitmentTuple = ({ ] } -export const makeRegistrationTuple = ( +// ================================ +// Make registration tuple +// ================================ + +export type MakeRegistrationTupleErrorType = MakeCommitmentTupleErrorType + +export const makeRegistrationTuple = async ( params: RegistrationParameters, -): RegistrationTuple => { +): Promise => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_labelhash, ...commitmentData] = makeCommitmentTuple(params) + const [_labelhash, ...commitmentData] = await makeCommitmentTuple(params) const label = params.name.split('.')[0] return [label, ...commitmentData] } +// ================================ +// Make commitment from tuple +// ================================ + +export type MakeCommitmentFromTupleErrorType = + | Keccak256ErrorType + | EncodeAbiParametersErrorType + export const makeCommitmentFromTuple = (params: CommitmentTuple): Hex => { return keccak256( encodeAbiParameters( @@ -178,5 +227,14 @@ export const makeCommitmentFromTuple = (params: CommitmentTuple): Hex => { ) } -export const makeCommitment = (params: RegistrationParameters): Hex => - makeCommitmentFromTuple(makeCommitmentTuple(params)) +// ================================ +// Make commitment +// ================================ + +export type MakeCommitmentErrorType = + | MakeCommitmentTupleErrorType + | MakeCommitmentFromTupleErrorType + +export const makeCommitment = async ( + params: RegistrationParameters, +): Promise => makeCommitmentFromTuple(await makeCommitmentTuple(params)) diff --git a/packages/ensjs/src/utils/v2/index.ts b/packages/ensjs/src/utils/v2/index.ts new file mode 100644 index 000000000..60344625c --- /dev/null +++ b/packages/ensjs/src/utils/v2/index.ts @@ -0,0 +1,4 @@ +export * from './registry/labelToCanonicalId.js' +export * from './roles/decodeRoleCounts.js' +export * from './roles/encodeRoleBitmap.js' +export * as registryRoles from './roles/registryRoles.js' diff --git a/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.test.ts b/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.test.ts new file mode 100644 index 000000000..6066a9dd7 --- /dev/null +++ b/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.test.ts @@ -0,0 +1,24 @@ +import { labelhash } from 'viem' +import { describe, expect, it } from 'vitest' +import { labelToCanonicalId } from './labelToCanonicalId.js' + +describe('labelToCanonicalId', () => { + it('zeroes out the low 32 bits of the labelhash', () => { + const id = labelToCanonicalId('test') + // low 32 bits should be zero + expect(id & 0xffffffffn).toBe(0n) + }) + + it('preserves the upper bits of the labelhash', () => { + const id = labelToCanonicalId('test') + const full = BigInt(labelhash('test')) + // XOR with low32 zeroes them, so id should equal full with low 32 bits cleared + expect(id).toBe(full ^ (full & 0xffffffffn)) + }) + + it('returns different ids for different labels', () => { + const a = labelToCanonicalId('foo') + const b = labelToCanonicalId('bar') + expect(a).not.toBe(b) + }) +}) diff --git a/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.ts b/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.ts new file mode 100644 index 000000000..1cf56459b --- /dev/null +++ b/packages/ensjs/src/utils/v2/registry/labelToCanonicalId.ts @@ -0,0 +1,7 @@ +import { hexToBigInt, labelhash } from 'viem' + +export const labelToCanonicalId = (label: string) => { + const id = hexToBigInt(labelhash(label)) + const low32 = id & 0xffffffffn + return id ^ low32 +} diff --git a/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.test.ts b/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.test.ts new file mode 100644 index 000000000..d72efb3c1 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.test.ts @@ -0,0 +1,33 @@ +import { expect, it } from 'vitest' +import { decodeRoleCounts } from './decodeRoleCounts.js' +import * as registryRoles from './registryRoles.js' + +it('decodes a role bitmap into a mapping of role counts', () => { + expect( + decodeRoleCounts( + 97409655027181761882228017414928043058174885888n, + registryRoles, + ), + ).toEqual({ + ROLE_CAN_TRANSFER_ADMIN: 1, + ROLE_REGISTER_RESERVED: 0, + ROLE_REGISTER_RESERVED_ADMIN: 0, + ROLE_REGISTRAR: 0, + ROLE_REGISTRAR_ADMIN: 0, + ROLE_RENEW: 0, + ROLE_RENEW_ADMIN: 0, + ROLE_SET_PARENT: 0, + ROLE_SET_PARENT_ADMIN: 0, + ROLE_SET_RESOLVER: 3, + ROLE_SET_RESOLVER_ADMIN: 1, + ROLE_SET_SUBREGISTRY: 2, + ROLE_SET_SUBREGISTRY_ADMIN: 1, + ROLE_SET_URI: 0, + ROLE_SET_URI_ADMIN: 0, + ROLE_UNREGISTER: 0, + ROLE_UNREGISTER_ADMIN: 0, + ROLE_UPGRADE: 0, + ROLE_UPGRADE_ADMIN: 0, + ROLE_WAS_RESERVED: 0, + }) +}) diff --git a/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.ts b/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.ts new file mode 100644 index 000000000..3f353d673 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/decodeRoleCounts.ts @@ -0,0 +1,26 @@ +export type RoleNames = readonly string[] +export type RoleName = N[number] | `${N[number]}_ADMIN` + +export type DecodeRoleCountsReturnType> = + Record + +/** + * Decode a role count bitmap into a mapping of a role and how many accounts have that role + * @param keys set of role keys + * @param encodedRoleCounts role bitmap + * @returns mapping of a role and how many accounts have that role + */ +export function decodeRoleCounts>( + bitmap: bigint, + roles: T, +): Record { + return Object.fromEntries( + Object.entries(roles).map(([k, x]) => { + x |= x << 1n + x |= x << 2n + x &= bitmap + while (x > 15n) x >>= 4n + return [k, Number(x)] as const + }), + ) as Record +} diff --git a/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.test.ts b/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.test.ts new file mode 100644 index 000000000..fd100d32c --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest' +import { encodeRoleBitmap } from './encodeRoleBitmap.js' +import * as registryRoles from './registryRoles.js' + +describe('encodeRoleBitmap', () => { + it('returns 0 for an empty role list', () => { + expect(encodeRoleBitmap([])).toBe(0n) + }) + + it('encodes a single role', () => { + expect(encodeRoleBitmap(['ROLE_REGISTRAR'])).toBe( + registryRoles.ROLE_REGISTRAR, + ) + }) + + it('encodes multiple roles as a bitwise OR', () => { + const bitmap = encodeRoleBitmap([ + 'ROLE_SET_RESOLVER', + 'ROLE_SET_SUBREGISTRY', + ]) + expect(bitmap).toBe( + registryRoles.ROLE_SET_RESOLVER | registryRoles.ROLE_SET_SUBREGISTRY, + ) + }) + + it('is idempotent for duplicate roles', () => { + const single = encodeRoleBitmap(['ROLE_RENEW']) + const duplicate = encodeRoleBitmap(['ROLE_RENEW', 'ROLE_RENEW']) + expect(single).toBe(duplicate) + }) +}) diff --git a/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.ts b/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.ts new file mode 100644 index 000000000..e8206351f --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/encodeRoleBitmap.ts @@ -0,0 +1,7 @@ +import * as registryRoles from './registryRoles.js' + +export type Role = keyof typeof registryRoles + +export const encodeRoleBitmap = (roleList: Role[]): bigint => + // biome-ignore lint: all registry roles are statically defined + roleList.reduce((a, x) => a | registryRoles[x], 0n) diff --git a/packages/ensjs/src/utils/v2/roles/registryRoles.test.ts b/packages/ensjs/src/utils/v2/roles/registryRoles.test.ts new file mode 100644 index 000000000..26cb1bd23 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/registryRoles.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest' +import * as roles from './registryRoles.js' + +describe('registryRoles', () => { + it('each role occupies a distinct nybble', () => { + const baseRoles = [ + roles.ROLE_REGISTRAR, + roles.ROLE_REGISTER_RESERVED, + roles.ROLE_SET_PARENT, + roles.ROLE_UNREGISTER, + roles.ROLE_RENEW, + roles.ROLE_SET_SUBREGISTRY, + roles.ROLE_SET_RESOLVER, + roles.ROLE_UPGRADE, + ] + + // no two base roles should share any bits + for (let i = 0; i < baseRoles.length; i++) { + for (let j = i + 1; j < baseRoles.length; j++) { + expect(baseRoles[i] & baseRoles[j]).toBe(0n) + } + } + }) + + it('admin roles are shifted 128 bits from their base', () => { + expect(roles.ROLE_REGISTRAR_ADMIN).toBe(roles.ROLE_REGISTRAR << 128n) + expect(roles.ROLE_RENEW_ADMIN).toBe(roles.ROLE_RENEW << 128n) + expect(roles.ROLE_SET_RESOLVER_ADMIN).toBe(roles.ROLE_SET_RESOLVER << 128n) + expect(roles.ROLE_UPGRADE_ADMIN).toBe(roles.ROLE_UPGRADE << 128n) + }) + + it('ROLE_CAN_TRANSFER_ADMIN has no base role', () => { + // CAN_TRANSFER is admin-only: (1n << 28n) << 128n + expect(roles.ROLE_CAN_TRANSFER_ADMIN).toBe((1n << 28n) << 128n) + }) + + it('exports the expected number of roles', () => { + expect(Object.keys(roles)).toHaveLength(20) + }) +}) diff --git a/packages/ensjs/src/utils/v2/roles/registryRoles.ts b/packages/ensjs/src/utils/v2/roles/registryRoles.ts new file mode 100644 index 000000000..1168a377b --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/registryRoles.ts @@ -0,0 +1,67 @@ +/** Nybble 0 — authorizes registering and reserving new names. Root only. */ +const ROLE_REGISTRAR = 1n << 0n +const ROLE_REGISTRAR_ADMIN = ROLE_REGISTRAR << 128n + +/** Nybble 1 — authorizes registering a reserved name. Root only. */ +const ROLE_REGISTER_RESERVED = 1n << 4n +const ROLE_REGISTER_RESERVED_ADMIN = ROLE_REGISTER_RESERVED << 128n + +/** Nybble 2 — authorizes setting the parent registry. Root only. */ +const ROLE_SET_PARENT = 1n << 8n +const ROLE_SET_PARENT_ADMIN = ROLE_SET_PARENT << 128n + +/** Nybble 3 — authorizes unregistering names. Root or token. */ +const ROLE_UNREGISTER = 1n << 12n +const ROLE_UNREGISTER_ADMIN = ROLE_UNREGISTER << 128n + +/** Nybble 4 — authorizes extending name expiry. Root or token. */ +const ROLE_RENEW = 1n << 16n +const ROLE_RENEW_ADMIN = ROLE_RENEW << 128n + +/** Nybble 5 — authorizes changing a name's child registry. Root or token. */ +const ROLE_SET_SUBREGISTRY = 1n << 20n +const ROLE_SET_SUBREGISTRY_ADMIN = ROLE_SET_SUBREGISTRY << 128n + +/** Nybble 6 — authorizes changing a name's resolver. Root or token. */ +const ROLE_SET_RESOLVER = 1n << 24n +const ROLE_SET_RESOLVER_ADMIN = ROLE_SET_RESOLVER << 128n + +/** Nybble 7 — authorizes ERC1155 token transfers. Admin-only (checked on token owner). */ +const ROLE_CAN_TRANSFER_ADMIN = (1n << 28n) << 128n + +/** + * Nybble 8 — tags a name that was registered via `ROLE_REGISTER_RESERVED`. Token-only. + * Not revocable. Useful for indexers to identify pre-migrated names. + */ +const ROLE_WAS_RESERVED = 1n << 32n + +/** Nybble 9 — authorizes setting the registry metadata URI. Root only. */ +const ROLE_SET_URI = 1n << 36n +const ROLE_SET_URI_ADMIN = ROLE_SET_URI << 128n + +/** Nybble 31 — authorizes UUPS proxy upgrades. Root only. */ +const ROLE_UPGRADE = 1n << 124n +const ROLE_UPGRADE_ADMIN = ROLE_UPGRADE << 128n + +export { + ROLE_CAN_TRANSFER_ADMIN, + ROLE_REGISTER_RESERVED, + ROLE_REGISTER_RESERVED_ADMIN, + ROLE_REGISTRAR, + ROLE_REGISTRAR_ADMIN, + ROLE_RENEW, + ROLE_RENEW_ADMIN, + ROLE_SET_PARENT, + ROLE_SET_PARENT_ADMIN, + ROLE_SET_RESOLVER, + ROLE_SET_RESOLVER_ADMIN, + ROLE_SET_SUBREGISTRY, + ROLE_SET_SUBREGISTRY_ADMIN, + ROLE_SET_URI, + ROLE_SET_URI_ADMIN, + ROLE_UNREGISTER, + ROLE_UNREGISTER_ADMIN, + ROLE_UPGRADE, + ROLE_UPGRADE_ADMIN, + ROLE_WAS_RESERVED, +} diff --git a/packages/ensjs/src/utils/v2/roles/resolverResource.ts b/packages/ensjs/src/utils/v2/roles/resolverResource.ts new file mode 100644 index 000000000..93de87522 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/resolverResource.ts @@ -0,0 +1,42 @@ +import type { Hex } from 'viem' +import { encodePacked, keccak256, toBytes } from 'viem' + +/** + * Compute a PermissionedResolver EAC resource ID from a node and part. + * Mirrors `PermissionedResolverLib.resource(node, part)` in Solidity: + * - Returns `0` when both `node` and `part` are `bytes32(0)`. + * - Otherwise returns `keccak256(abi.encode(node, part))` (32-byte node ‖ + * 32-byte part). + * + * @param node - The namehash of the name (bytes32). Use '0x00...00' for "any name". + * @param part - The record-type part (bytes32). Use '0x00...00' for "any record type". + * @returns The resource ID as a bigint. + */ +export function computeResolverResource(node: Hex, part: Hex): bigint { + if (/^0x0+$/.test(node) && /^0x0+$/.test(part)) return 0n + return BigInt(keccak256(encodePacked(['bytes32', 'bytes32'], [node, part]))) +} + +/** + * Compute the record-type identifier for text records, namespaced by key. + * Mirrors `PermissionedResolverLib.partHash(string)` in Solidity: + * `keccak256(bytes(key))`. + * + * @param key - The text record key (e.g. "avatar"). + * @returns The part as a bytes32 hex string. + */ +export function textPart(key: string): Hex { + return keccak256(toBytes(key)) +} + +/** + * Compute the record-type identifier for address records, namespaced by coin type. + * Mirrors `PermissionedResolverLib.partHash(uint256)` in Solidity: + * `keccak256(abi.encode(coinType))` — the 32-byte big-endian word. + * + * @param coinType - The SLIP-44 coin type (e.g. 60n for ETH). + * @returns The part as a bytes32 hex string. + */ +export function addrPart(coinType: bigint): Hex { + return keccak256(encodePacked(['uint256'], [coinType])) +} diff --git a/packages/ensjs/src/utils/v2/roles/resolverRoles.test.ts b/packages/ensjs/src/utils/v2/roles/resolverRoles.test.ts new file mode 100644 index 000000000..5a8b111f5 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/resolverRoles.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, it } from 'vitest' +import { + encodeResolverRoleBitmap, + RESOLVER_ROLE_CLEAR, + RESOLVER_ROLE_CLEAR_ADMIN, + RESOLVER_ROLE_SET_ABI, + RESOLVER_ROLE_SET_ADDR, + RESOLVER_ROLE_SET_ADDR_ADMIN, + RESOLVER_ROLE_SET_ALIAS, + RESOLVER_ROLE_SET_CONTENTHASH, + RESOLVER_ROLE_SET_INTERFACE, + RESOLVER_ROLE_SET_NAME, + RESOLVER_ROLE_SET_PUBKEY, + RESOLVER_ROLE_SET_TEXT, + RESOLVER_ROLE_UPGRADE, + RESOLVER_ROLE_UPGRADE_ADMIN, +} from './resolverRoles.js' + +describe('resolver role constants', () => { + it('each role occupies a distinct nybble', () => { + const baseRoles = [ + RESOLVER_ROLE_SET_ADDR, + RESOLVER_ROLE_SET_TEXT, + RESOLVER_ROLE_SET_CONTENTHASH, + RESOLVER_ROLE_SET_PUBKEY, + RESOLVER_ROLE_SET_ABI, + RESOLVER_ROLE_SET_INTERFACE, + RESOLVER_ROLE_SET_NAME, + RESOLVER_ROLE_SET_ALIAS, + RESOLVER_ROLE_CLEAR, + RESOLVER_ROLE_UPGRADE, + ] + + for (let i = 0; i < baseRoles.length; i++) { + for (let j = i + 1; j < baseRoles.length; j++) { + expect(baseRoles[i] & baseRoles[j]).toBe(0n) + } + } + }) + + it('admin roles are shifted 128 bits from their base', () => { + expect(RESOLVER_ROLE_SET_ADDR_ADMIN).toBe(RESOLVER_ROLE_SET_ADDR << 128n) + expect(RESOLVER_ROLE_CLEAR_ADMIN).toBe(RESOLVER_ROLE_CLEAR << 128n) + expect(RESOLVER_ROLE_UPGRADE_ADMIN).toBe(RESOLVER_ROLE_UPGRADE << 128n) + }) +}) + +describe('encodeResolverRoleBitmap', () => { + it('returns 0 for an empty role list', () => { + expect(encodeResolverRoleBitmap([])).toBe(0n) + }) + + it('encodes a single role', () => { + expect(encodeResolverRoleBitmap(['ROLE_SET_TEXT'])).toBe( + RESOLVER_ROLE_SET_TEXT, + ) + }) + + it('encodes multiple roles as a bitwise OR', () => { + const bitmap = encodeResolverRoleBitmap(['ROLE_SET_TEXT', 'ROLE_CLEAR']) + expect(bitmap).toBe(RESOLVER_ROLE_SET_TEXT | RESOLVER_ROLE_CLEAR) + }) + + it('is idempotent for duplicate roles', () => { + const single = encodeResolverRoleBitmap(['ROLE_SET_ADDR']) + const duplicate = encodeResolverRoleBitmap([ + 'ROLE_SET_ADDR', + 'ROLE_SET_ADDR', + ]) + expect(single).toBe(duplicate) + }) +}) diff --git a/packages/ensjs/src/utils/v2/roles/resolverRoles.ts b/packages/ensjs/src/utils/v2/roles/resolverRoles.ts new file mode 100644 index 000000000..d3a0605e7 --- /dev/null +++ b/packages/ensjs/src/utils/v2/roles/resolverRoles.ts @@ -0,0 +1,71 @@ +/** + * PermissionedResolver role constants. + * + * These are the bit positions for roles defined in PermissionedResolverLib. + * They are different from registry roles (RegistryRolesLib) even though they + * occupy the same bit positions. + */ + +export const RESOLVER_ROLE_SET_ADDR = 1n << 0n +export const RESOLVER_ROLE_SET_TEXT = 1n << 4n +export const RESOLVER_ROLE_SET_CONTENTHASH = 1n << 8n +export const RESOLVER_ROLE_SET_PUBKEY = 1n << 12n +export const RESOLVER_ROLE_SET_ABI = 1n << 16n +export const RESOLVER_ROLE_SET_INTERFACE = 1n << 20n +export const RESOLVER_ROLE_SET_NAME = 1n << 24n +export const RESOLVER_ROLE_SET_ALIAS = 1n << 28n +export const RESOLVER_ROLE_CLEAR = 1n << 32n +export const RESOLVER_ROLE_SET_DATA = 1n << 36n +export const RESOLVER_ROLE_UPGRADE = 1n << 124n + +export const RESOLVER_ROLE_SET_ADDR_ADMIN = RESOLVER_ROLE_SET_ADDR << 128n +export const RESOLVER_ROLE_SET_TEXT_ADMIN = RESOLVER_ROLE_SET_TEXT << 128n +export const RESOLVER_ROLE_SET_CONTENTHASH_ADMIN = + RESOLVER_ROLE_SET_CONTENTHASH << 128n +export const RESOLVER_ROLE_SET_PUBKEY_ADMIN = RESOLVER_ROLE_SET_PUBKEY << 128n +export const RESOLVER_ROLE_SET_ABI_ADMIN = RESOLVER_ROLE_SET_ABI << 128n +export const RESOLVER_ROLE_SET_INTERFACE_ADMIN = + RESOLVER_ROLE_SET_INTERFACE << 128n +export const RESOLVER_ROLE_SET_NAME_ADMIN = RESOLVER_ROLE_SET_NAME << 128n +export const RESOLVER_ROLE_SET_ALIAS_ADMIN = RESOLVER_ROLE_SET_ALIAS << 128n +export const RESOLVER_ROLE_CLEAR_ADMIN = RESOLVER_ROLE_CLEAR << 128n +export const RESOLVER_ROLE_SET_DATA_ADMIN = RESOLVER_ROLE_SET_DATA << 128n +export const RESOLVER_ROLE_UPGRADE_ADMIN = RESOLVER_ROLE_UPGRADE << 128n + +export type ResolverRole = + | 'ROLE_SET_ADDR' + | 'ROLE_SET_TEXT' + | 'ROLE_SET_CONTENTHASH' + | 'ROLE_SET_PUBKEY' + | 'ROLE_SET_ABI' + | 'ROLE_SET_INTERFACE' + | 'ROLE_SET_NAME' + | 'ROLE_SET_ALIAS' + | 'ROLE_CLEAR' + | 'ROLE_SET_DATA' + | 'ROLE_UPGRADE' + +const resolverRoleMap: Record = { + ROLE_SET_ADDR: RESOLVER_ROLE_SET_ADDR, + ROLE_SET_TEXT: RESOLVER_ROLE_SET_TEXT, + ROLE_SET_CONTENTHASH: RESOLVER_ROLE_SET_CONTENTHASH, + ROLE_SET_PUBKEY: RESOLVER_ROLE_SET_PUBKEY, + ROLE_SET_ABI: RESOLVER_ROLE_SET_ABI, + ROLE_SET_INTERFACE: RESOLVER_ROLE_SET_INTERFACE, + ROLE_SET_NAME: RESOLVER_ROLE_SET_NAME, + ROLE_SET_ALIAS: RESOLVER_ROLE_SET_ALIAS, + ROLE_CLEAR: RESOLVER_ROLE_CLEAR, + ROLE_SET_DATA: RESOLVER_ROLE_SET_DATA, + ROLE_UPGRADE: RESOLVER_ROLE_UPGRADE, +} + +/** + * Encode an array of resolver role names into a single bitmap. + */ +export function encodeResolverRoleBitmap(roles: ResolverRole[]): bigint { + let bitmap = 0n + for (const role of roles) { + bitmap |= resolverRoleMap[role] + } + return bitmap +} diff --git a/packages/ensjs/src/utils/wrapper.ts b/packages/ensjs/src/utils/wrapper.ts index c4e8d39f4..d37468a98 100644 --- a/packages/ensjs/src/utils/wrapper.ts +++ b/packages/ensjs/src/utils/wrapper.ts @@ -1,10 +1,16 @@ -import { stringToBytes } from 'viem' -import { WrappedLabelTooLargeError } from '../errors/utils.js' -import type { AnyDate } from '../types.js' +import { type StringToBytesErrorType, stringToBytes } from 'viem' +import { type ErrorType, WrappedLabelTooLargeError } from '../errors/utils.js' +import type { AnyDate } from '../types/index.js' import { ParentFuses } from './fuses.js' export const MAX_EXPIRY = 2n ** 64n - 1n +// ================================ +// Expiry to bigint +// ================================ + +export type ExpiryToBigIntErrorType = TypeError | ErrorType + export const expiryToBigInt = (expiry?: AnyDate, defaultValue = 0n): bigint => { if (!expiry) return defaultValue if (typeof expiry === 'bigint') return expiry @@ -14,12 +20,29 @@ export const expiryToBigInt = (expiry?: AnyDate, defaultValue = 0n): bigint => { throw new TypeError('Expiry must be a bigint, string, number or Date') } +// ================================ +// Wrapped label length check +// ================================ + +export type WrappedLabelLengthCheckErrorType = + | WrappedLabelTooLargeError + | StringToBytesErrorType + export const wrappedLabelLengthCheck = (label: string) => { const bytes = stringToBytes(label) if (bytes.byteLength > 255) - throw new WrappedLabelTooLargeError({ label, byteLength: bytes.byteLength }) + throw new WrappedLabelTooLargeError({ + label, + byteLength: bytes.byteLength, + }) } +// ================================ +// Make default expiry +// ================================ + +export type MakeDefaultExpiryErrorType = ErrorType + export const makeDefaultExpiry = (fuses?: number): bigint => { if (fuses && BigInt(fuses) & ParentFuses.PARENT_CANNOT_CONTROL) return MAX_EXPIRY diff --git a/packages/ensjs/src/wallet.ts b/packages/ensjs/src/wallet.ts deleted file mode 100644 index b4ae03820..000000000 --- a/packages/ensjs/src/wallet.ts +++ /dev/null @@ -1,126 +0,0 @@ -export { - default as clearRecords, - type ClearRecordsDataParameters, - type ClearRecordsDataReturnType, - type ClearRecordsParameters, - type ClearRecordsReturnType, -} from './functions/wallet/clearRecords.js' -export { - default as commitName, - type CommitNameDataParameters, - type CommitNameDataReturnType, - type CommitNameParameters, - type CommitNameReturnType, -} from './functions/wallet/commitName.js' -export { - default as createSubname, - type CreateSubnameDataParameters, - type CreateSubnameDataReturnType, - type CreateSubnameParameters, - type CreateSubnameReturnType, -} from './functions/wallet/createSubname.js' -export { - default as deleteSubname, - type DeleteSubnameDataParameters, - type DeleteSubnameDataReturnType, - type DeleteSubnameParameters, - type DeleteSubnameReturnType, -} from './functions/wallet/deleteSubname.js' -export { - default as registerName, - type RegisterNameDataParameters, - type RegisterNameDataReturnType, - type RegisterNameParameters, - type RegisterNameReturnType, -} from './functions/wallet/registerName.js' -export { - default as renewNames, - type RenewNamesDataParameters, - type RenewNamesDataReturnType, - type RenewNamesParameters, - type RenewNamesReturnType, -} from './functions/wallet/renewNames.js' -export { - default as setAbiRecord, - type SetAbiRecordDataParameters, - type SetAbiRecordDataReturnType, - type SetAbiRecordParameters, - type SetAbiRecordReturnType, -} from './functions/wallet/setAbiRecord.js' -export { - default as setAddressRecord, - type SetAddressRecordDataParameters, - type SetAddressRecordDataReturnType, - type SetAddressRecordParameters, - type SetAddressRecordReturnType, -} from './functions/wallet/setAddressRecord.js' -export { - default as setChildFuses, - type SetChildFusesDataParameters, - type SetChildFusesDataReturnType, - type SetChildFusesParameters, - type SetChildFusesReturnType, -} from './functions/wallet/setChildFuses.js' -export { - default as setContentHashRecord, - type SetContentHashRecordDataParameters, - type SetContentHashRecordDataReturnType, - type SetContentHashRecordParameters, - type SetContentHashRecordReturnType, -} from './functions/wallet/setContentHashRecord.js' -export { - default as setFuses, - type SetFusesDataParameters, - type SetFusesDataReturnType, - type SetFusesParameters, - type SetFusesReturnType, -} from './functions/wallet/setFuses.js' -export { - default as setPrimaryName, - type SetPrimaryNameDataParameters, - type SetPrimaryNameDataReturnType, - type SetPrimaryNameParameters, - type SetPrimaryNameReturnType, -} from './functions/wallet/setPrimaryName.js' -export { - default as setRecords, - type SetRecordsDataParameters, - type SetRecordsDataReturnType, - type SetRecordsParameters, - type SetRecordsReturnType, -} from './functions/wallet/setRecords.js' -export { - default as setResolver, - type SetResolverDataParameters, - type SetResolverDataReturnType, - type SetResolverParameters, - type SetResolverReturnType, -} from './functions/wallet/setResolver.js' -export { - default as setTextRecord, - type SetTextRecordDataParameters, - type SetTextRecordDataReturnType, - type SetTextRecordParameters, - type SetTextRecordReturnType, -} from './functions/wallet/setTextRecord.js' -export { - default as transferName, - type TransferNameDataParameters, - type TransferNameDataReturnType, - type TransferNameParameters, - type TransferNameReturnType, -} from './functions/wallet/transferName.js' -export { - default as unwrapName, - type UnwrapNameDataParameters, - type UnwrapNameDataReturnType, - type UnwrapNameParameters, - type UnwrapNameReturnType, -} from './functions/wallet/unwrapName.js' -export { - default as wrapName, - type WrapNameDataParameters, - type WrapNameDataReturnType, - type WrapNameParameters, - type WrapNameReturnType, -} from './functions/wallet/wrapName.js' diff --git a/packages/ensjs/tsconfig.build.json b/packages/ensjs/tsconfig.build.json index f51c77b76..f95878c34 100644 --- a/packages/ensjs/tsconfig.build.json +++ b/packages/ensjs/tsconfig.build.json @@ -1,15 +1,19 @@ { - "include": ["./src"], - "exclude": ["src/**/*.test.ts", "src/test/**/*"], + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": [ + "src/**/*.test.ts", + "src/**/*.test-d.ts", + "src/test", + "src/utils/name/namehash.test-d.ts" + ], "compilerOptions": { "sourceMap": true, - "rootDir": "./src", + // TODO: The following options are also not enabled by default in `strict` mode and would be nice to have but would require some adjustments to the codebase. + "exactOptionalPropertyTypes": false, + "noUncheckedIndexedAccess": false, "outDir": "./dist", "declaration": true, - "module":"nodenext", - "target": "esnext", - "moduleResolution": "nodenext", - "skipLibCheck": true, - "strict": true + "declarationMap": true } -} \ No newline at end of file +} diff --git a/packages/ensjs/tsconfig.json b/packages/ensjs/tsconfig.json index 8a07f9833..b7719a4a4 100644 --- a/packages/ensjs/tsconfig.json +++ b/packages/ensjs/tsconfig.json @@ -1,19 +1,5 @@ { - "compilerOptions": { - "target": "es2020", - "module": "nodenext", - "strict": true, - "esModuleInterop": true, - "moduleResolution": "nodenext", - "forceConsistentCasingInFileNames": true - }, - "include": [ - "hardhat.config.cts", - "./scripts", - "./deploy", - "./test", - "typechain/**/*", - "./utils" - ], - "exclude": ["node_modules"] + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": ["src/test/seedTestNames.ts", "src/test/globalSetup.ts"] } diff --git a/packages/ensjs/utils/legacyNameGenerator.ts b/packages/ensjs/utils/legacyNameGenerator.ts deleted file mode 100644 index 8f1041b85..000000000 --- a/packages/ensjs/utils/legacyNameGenerator.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { Address } from 'abitype' -import type { HardhatRuntimeEnvironment } from 'hardhat/types/runtime.js' -import { labelhash, namehash } from 'viem/ens' - -const makeNameGenerator = async ( - hre: HardhatRuntimeEnvironment, - optionalNonceManager?: { getNonce: (acct?: string) => number | undefined }, -) => { - const { getNamedAccounts, viem } = hre - const allNamedAccts = await getNamedAccounts() - const controller = await viem.getContract('LegacyETHRegistrarController') - const publicResolver = await viem.getContract('LegacyPublicResolver') - const nonceManager = optionalNonceManager ?? { getNonce: () => undefined } - - return { - commit: async ({ - label, - namedOwner, - namedAddr, - }: { label: string; namedOwner: string; namedAddr: Address }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const registrant = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const addr = allNamedAccts[namedAddr] - - const commitment = await controller.read.makeCommitmentWithConfig([ - label, - registrant, - secret, - resolver, - addr, - ]) - - return controller.write.commit([commitment], { - nonce: nonceManager.getNonce(namedOwner), - account: registrant, - }) - }, - register: async ({ - label, - namedOwner, - namedAddr, - duration = 31536000, - }: { - label: string - namedOwner: string - namedAddr: string - duration?: number | bigint - }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const registrant = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const addr = allNamedAccts[namedAddr] - const price = await controller.read.rentPrice([label, duration]) - - return controller.write.registerWithConfig( - [label, registrant, duration, secret, resolver, addr], - { - value: price, - nonce: nonceManager.getNonce(namedOwner), - account: registrant, - }, - ) - }, - subname: async ({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - }: { - label: string - namedOwner: string - subnameLabel: string - namedSubnameOwner: string - }) => { - console.log(`Setting subnames for ${label}.eth...`) - const resolver = publicResolver.address - const registrant = allNamedAccts[namedOwner] - const owner = allNamedAccts[namedSubnameOwner] - - const _registry = await viem.getContract('ENSRegistry') - - return _registry.write.setSubnodeRecord( - [ - namehash(`${label}.eth`), - labelhash(subnameLabel), - owner, - resolver, - '0', - ], - { account: registrant }, - ) - }, - setSubnameRecords: async () => {}, - configure: async () => {}, - } -} - -export { makeNameGenerator } diff --git a/packages/ensjs/utils/nonceManager.ts b/packages/ensjs/utils/nonceManager.ts deleted file mode 100644 index cc2ec5025..000000000 --- a/packages/ensjs/utils/nonceManager.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types/runtime.js' -import { hexToNumber } from 'viem/utils' - -const makeNonceManager = async (href: HardhatRuntimeEnvironment) => { - const { getNamedAccounts, network } = href - const names = ['owner', 'owner2', 'owner3', 'owner4'] - const allNamedAccts = await getNamedAccounts() - const startingNonces = await Promise.all( - names.map((name) => - network.provider - .send('eth_getTransactionCount', [allNamedAccts[name], 'latest']) - .then(hexToNumber), - ), - ) - const nonceMap = Object.fromEntries( - names.map((name, i) => [name, startingNonces[i]]), - ) - - console.log('Nonce manager initialized', nonceMap) - - return { - getNonce: (name: string) => { - const nonce = nonceMap[name] - nonceMap[name]++ - return nonce - }, - } -} - -export { makeNonceManager } diff --git a/packages/ensjs/utils/wrappedNameGenerator.ts b/packages/ensjs/utils/wrappedNameGenerator.ts deleted file mode 100644 index eeb301a95..000000000 --- a/packages/ensjs/utils/wrappedNameGenerator.ts +++ /dev/null @@ -1,118 +0,0 @@ -import type { HardhatRuntimeEnvironment } from 'hardhat/types/runtime.js' -import { namehash } from 'viem/ens' - -const makeNameGenerator = async ( - hre: HardhatRuntimeEnvironment, - optionalNonceManager?: { getNonce: (acct?: string) => number | undefined }, -) => { - const { getNamedAccounts, viem } = hre - const allNamedAccts = await getNamedAccounts() - const controller = await viem.getContract('ETHRegistrarController') - const publicResolver = await viem.getContract('PublicResolver') - const nameWrapper = await viem.getContract('NameWrapper') - const nonceManager = optionalNonceManager ?? { getNonce: () => undefined } - - return { - commit: async ({ - label, - namedOwner, - data = [], - reverseRecord = false, - fuses = 0, - duration = 31536000, - }: { - label: string - namedOwner: string - data?: any[] - reverseRecord?: boolean - fuses?: number - duration?: number | bigint - }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const owner = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const commitment = await controller.read.makeCommitment([ - label, - owner, - duration, - secret, - resolver, - data, - reverseRecord, - fuses, - ]) - - return controller.write.commit([commitment], { - nonce: nonceManager.getNonce(namedOwner), - account: owner, - }) - }, - register: async ({ - label, - namedOwner, - data = [], - reverseRecord = false, - fuses = 0, - duration = 31536000, - }: { - label: string - namedOwner: string - data?: any[] - reverseRecord?: boolean - fuses?: number - duration?: number | bigint - }) => { - const secret = - '0x0000000000000000000000000000000000000000000000000000000000000000' - const owner = allNamedAccts[namedOwner] - const resolver = publicResolver.address - const price = await controller.read.rentPrice([label, duration]) - - const priceWithBuffer = (price.base * 105n) / 100n - return controller.write.register( - [label, owner, duration, secret, resolver, data, reverseRecord, fuses], - { - value: priceWithBuffer, - nonce: nonceManager.getNonce(namedOwner), - account: owner, - }, - ) - }, - subname: async ({ - label, - namedOwner, - subnameLabel, - namedSubnameOwner, - subnameFuses = 0, - subnameExpiry = BigInt(2) ** BigInt(64) - BigInt(1), - }: { - label: string - namedOwner: string - subnameLabel: string - namedSubnameOwner: string - subnameFuses?: number - subnameExpiry?: number | bigint - }) => { - const resolver = publicResolver.address - const owner = allNamedAccts[namedOwner] - const subnameOwner = allNamedAccts[namedSubnameOwner] - return nameWrapper.write.setSubnodeRecord( - [ - namehash(`${label}.eth`), - subnameLabel, - subnameOwner, - resolver, - '0', - subnameFuses, - subnameExpiry, - ], - { - account: owner, - }, - ) - }, - } -} - -export { makeNameGenerator } diff --git a/packages/ensjs/vitest.config.ts b/packages/ensjs/vitest.config.ts index 9ca54c28f..1adad131f 100644 --- a/packages/ensjs/vitest.config.ts +++ b/packages/ensjs/vitest.config.ts @@ -1,15 +1,66 @@ import { defineConfig } from 'vitest/config' +// Shared excludes for all projects. +const baseExclude = ['data/**/*', 'src/actions/subgraph/**/*.test.ts'] + +// Pure-logic tests: encoders/coders, name utils, fuses/roles, content hash, +// formatting, CCIP helpers, and a couple of actions that only exercise encoding +// against a mocked client. None of these touch the devnet (no publicClient / +// testClient / RPC), so they are completely safe to run in parallel and do not +// depend on chain state or ordering. +// +// Everything NOT matched here touches the shared devnet (reads and/or +// evm_snapshot/evm_revert mutations). Those tests are order- and state-coupled +// to a single serial baseline, so they keep running exactly as before: +// sequentially, in one project (fileParallelism: false). +const pureLogicTests = [ + 'src/utils/**/*.test.ts', + 'src/actions/dns/getDnsOwner.test.ts', + 'src/actions/public/getNames.test.ts', + 'src/actions/public/resolveNameData.test.ts', +] + export default defineConfig({ test: { + // Root-only options (run once / aggregated across all projects). coverage: { enabled: true, include: ['src/**/*'], - exclude: ['data/**/*'], + exclude: ['data/**/*', 'src/test/seedTestNames.ts'], }, - environment: 'happy-dom', - setupFiles: ['./src/test/setup.ts'], - include: ['src/**/*.test.ts'], - exclude: ['data/**/*'], + globalSetup: ['./src/test/globalSetup.ts'], + + projects: [ + { + // Pure logic — no devnet, safe to run files in parallel. + test: { + name: 'parallel', + environment: 'node', + setupFiles: ['./src/test/setup.ts'], + include: pureLogicTests, + exclude: baseExclude, + fileParallelism: true, + }, + }, + { + // Everything that touches the shared devnet — reads and snapshot/revert + // mutators. These are coupled to a single serial baseline, so they run + // sequentially, exactly as the original single-project config did. + test: { + name: 'chain', + environment: 'node', + setupFiles: ['./src/test/setup.ts'], + include: ['src/**/*.test.ts'], + exclude: [...baseExclude, ...pureLogicTests], + fileParallelism: false, + // `fileParallelism: false` does not, by itself, prevent the forks pool + // from spawning multiple workers in v3 — which breaks global + // evm_snapshot/evm_revert IDs across files. Pin a single fork so these + // run strictly one-at-a-time against the shared devnet. + pool: 'forks', + poolOptions: { forks: { singleFork: true } }, + }, + }, + ], }, }) diff --git a/packages/query-core/package.json b/packages/query-core/package.json new file mode 100644 index 000000000..d55502e83 --- /dev/null +++ b/packages/query-core/package.json @@ -0,0 +1,85 @@ +{ + "name": "@ensdomains/ensjs-query-core", + "version": "0.0.1", + "description": "ENS javascript extension library for @wagmi/core", + "type": "module", + "module": "./dist/esm/exports/index.js", + "types": "./dist/types/exports/index.d.ts", + "typings": "./dist/types/exports/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/types/exports/index.d.ts", + "default": "./dist/esm/exports/index.js" + }, + "./actions/public": { + "types": "./dist/types/actions/public/index.d.ts", + "default": "./dist/esm/actions/public/index.js" + }, + "./actions/wallet": { + "types": "./dist/types/actions/wallet/index.d.ts", + "default": "./dist/esm/actions/wallet/index.js" + }, + "./query/public": { + "types": "./dist/types/query/public/index.d.ts", + "default": "./dist/esm/query/public/index.js" + }, + "./query/wallet": { + "types": "./dist/types/query/wallet/index.d.ts", + "default": "./dist/esm/query/wallet/index.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "actions/public": [ + "./dist/types/actions/public/index.d.ts" + ], + "actions/wallet": [ + "./dist/types/actions/wallet/index.d.ts" + ], + "query/public": [ + "./dist/types/query/public/index.d.ts" + ], + "query/wallet": [ + "./dist/types/query/wallet/index.d.ts" + ] + } + }, + "files": [ + "dist/", + "src/", + "!src/**/*.test.ts", + "!src/test" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/ensdomains/ensjs.git", + "directory": "packages/query-core" + }, + "author": "Jakob ", + "license": "MIT", + "scripts": { + "build": "pnpm run clean && pnpm run build:esm+types", + "clean": "rm -rf dist", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "lint": "biome check" + }, + "dependencies": {}, + "devDependencies": { + "typescript": "5.6.2", + "@ensdomains/ensjs": "workspace:*", + "@wagmi/core": "^2.17.2", + "viem": "^2.9.2" + }, + "peerDependencies": { + "@tanstack/query-core": ">=5.0.0", + "@ensdomains/ensjs": "^5.0.0", + "viem": "^2.9.2", + "@wagmi/core": "^2.17.2" + }, + "engines": { + "node": ">=18" + } +} diff --git a/packages/query-core/src/actions/public/getAbiRecord.ts b/packages/query-core/src/actions/public/getAbiRecord.ts new file mode 100644 index 000000000..db100897a --- /dev/null +++ b/packages/query-core/src/actions/public/getAbiRecord.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetAbiRecordErrorType as ensjs_GetAbiRecordErrorType, + type GetAbiRecordParameters as ensjs_GetAbiRecordParameters, + type GetAbiRecordReturnType as ensjs_GetAbiRecordReturnType, + getAbiRecord as ensjs_getAbiRecord, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetAbiRecordParameters = Prettify< + ensjs_GetAbiRecordParameters & ChainIdParameter +> + +export type GetAbiRecordReturnType = ensjs_GetAbiRecordReturnType + +export type GetAbiRecordErrorType = ensjs_GetAbiRecordErrorType + +export function getAbiRecord( + config: RequireConfigContracts, + parameters: GetAbiRecordParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getAbiRecord, + 'getAbiRecord', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getAddressRecord.ts b/packages/query-core/src/actions/public/getAddressRecord.ts new file mode 100644 index 000000000..5f995a5bc --- /dev/null +++ b/packages/query-core/src/actions/public/getAddressRecord.ts @@ -0,0 +1,38 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetAddressRecordErrorType as ensjs_GetAddressRecordErrorType, + type GetAddressRecordParameters as ensjs_GetAddressRecordParameters, + type GetAddressRecordReturnType as ensjs_GetAddressRecordReturnType, + getAddressRecord as ensjs_getAddressRecord, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetAddressRecordParameters = + Prettify> + +export type GetAddressRecordReturnType = ensjs_GetAddressRecordReturnType + +export type GetAddressRecordErrorType = ensjs_GetAddressRecordErrorType + +export function getAddressRecord( + config: RequireConfigContracts, + parameters: GetAddressRecordParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getAddressRecord, + 'getAddressRecord', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getAvailable.ts b/packages/query-core/src/actions/public/getAvailable.ts new file mode 100644 index 000000000..835704282 --- /dev/null +++ b/packages/query-core/src/actions/public/getAvailable.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetAvailableErrorType as ensjs_GetAvailableErrorType, + type GetAvailableParameters as ensjs_GetAvailableParameters, + type GetAvailableReturnType as ensjs_GetAvailableReturnType, + getAvailable as ensjs_getAvailable, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetAvailableParameters = Prettify< + ensjs_GetAvailableParameters & ChainIdParameter +> + +export type GetAvailableReturnType = ensjs_GetAvailableReturnType + +export type GetAvailableErrorType = ensjs_GetAvailableErrorType + +export function getAvailable( + config: RequireConfigContracts, + parameters: GetAvailableParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getAvailable, + 'getAvailable', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getContentHashRecord.ts b/packages/query-core/src/actions/public/getContentHashRecord.ts new file mode 100644 index 000000000..789d9efe5 --- /dev/null +++ b/packages/query-core/src/actions/public/getContentHashRecord.ts @@ -0,0 +1,41 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetContentHashRecordErrorType as ensjs_GetContentHashRecordErrorType, + type GetContentHashRecordParameters as ensjs_GetContentHashRecordParameters, + type GetContentHashRecordReturnType as ensjs_GetContentHashRecordReturnType, + getContentHashRecord as ensjs_getContentHashRecord, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetContentHashRecordParameters = + Prettify> + +export type GetContentHashRecordReturnType = + ensjs_GetContentHashRecordReturnType + +export type GetContentHashRecordErrorType = ensjs_GetContentHashRecordErrorType + +export function getContentHashRecord< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + parameters: GetContentHashRecordParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getContentHashRecord, + 'getContentHashRecord', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getCredentials.ts b/packages/query-core/src/actions/public/getCredentials.ts new file mode 100644 index 000000000..cc12b293c --- /dev/null +++ b/packages/query-core/src/actions/public/getCredentials.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetCredentialsErrorType as ensjs_GetCredentialsErrorType, + type GetCredentialsParameters as ensjs_GetCredentialsParameters, + type GetCredentialsReturnType as ensjs_GetCredentialsReturnType, + getCredentials as ensjs_getCredentials, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetCredentialsParameters = Prettify< + ensjs_GetCredentialsParameters & ChainIdParameter +> + +export type GetCredentialsReturnType = ensjs_GetCredentialsReturnType + +export type GetCredentialsErrorType = ensjs_GetCredentialsErrorType + +export function getCredentials( + config: RequireConfigContracts, + parameters: GetCredentialsParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getCredentials, + 'getCredentials', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getExpiry.ts b/packages/query-core/src/actions/public/getExpiry.ts new file mode 100644 index 000000000..11bc8f924 --- /dev/null +++ b/packages/query-core/src/actions/public/getExpiry.ts @@ -0,0 +1,42 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetExpiryErrorType as ensjs_GetExpiryErrorType, + type GetExpiryParameters as ensjs_GetExpiryParameters, + type GetExpiryReturnType as ensjs_GetExpiryReturnType, + getExpiry as ensjs_getExpiry, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetExpiryParameters = Prettify< + ensjs_GetExpiryParameters & ChainIdParameter +> + +export type GetExpiryReturnType = ensjs_GetExpiryReturnType + +export type GetExpiryErrorType = ensjs_GetExpiryErrorType + +export function getExpiry( + config: RequireConfigContracts< + chains, + 'ensNameWrapper' | 'ensBaseRegistrarImplementation' | 'multicall3' + >, + parameters: GetExpiryParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getExpiry, + 'getExpiry', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getName.ts b/packages/query-core/src/actions/public/getName.ts new file mode 100644 index 000000000..fa929b1b9 --- /dev/null +++ b/packages/query-core/src/actions/public/getName.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetNameErrorType as ensjs_GetNameErrorType, + type GetNameParameters as ensjs_GetNameParameters, + type GetNameReturnType as ensjs_GetNameReturnType, + getName as ensjs_getName, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetNameParameters = Prettify< + ensjs_GetNameParameters & ChainIdParameter +> + +export type GetNameReturnType = ensjs_GetNameReturnType + +export type GetNameErrorType = ensjs_GetNameErrorType + +export function getName( + config: RequireConfigContracts, + parameters: GetNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getName, + 'getName', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getOwner.ts b/packages/query-core/src/actions/public/getOwner.ts new file mode 100644 index 000000000..8e67389c2 --- /dev/null +++ b/packages/query-core/src/actions/public/getOwner.ts @@ -0,0 +1,45 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetOwnerErrorType as ensjs_GetOwnerErrorType, + type GetOwnerParameters as ensjs_GetOwnerParameters, + type GetOwnerReturnType as ensjs_GetOwnerReturnType, + getOwner as ensjs_getOwner, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetOwnerParameters = Prettify< + ensjs_GetOwnerParameters & ChainIdParameter +> + +export type GetOwnerReturnType = ensjs_GetOwnerReturnType + +export type GetOwnerErrorType = ensjs_GetOwnerErrorType + +export function getOwner( + config: RequireConfigContracts< + chains, + | 'ensNameWrapper' + | 'ensRegistry' + | 'ensBaseRegistrarImplementation' + | 'multicall3' + >, + parameters: GetOwnerParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getOwner, + 'getOwner', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getPrice.ts b/packages/query-core/src/actions/public/getPrice.ts new file mode 100644 index 000000000..eb26c117b --- /dev/null +++ b/packages/query-core/src/actions/public/getPrice.ts @@ -0,0 +1,42 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetPriceErrorType as ensjs_GetPriceErrorType, + type GetPriceParameters as ensjs_GetPriceParameters, + type GetPriceReturnType as ensjs_GetPriceReturnType, + getPrice as ensjs_getPrice, +} from '@ensdomains/ensjs/public/v1' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetPriceParameters = Prettify< + ensjs_GetPriceParameters & ChainIdParameter +> + +export type GetPriceReturnType = ensjs_GetPriceReturnType + +export type GetPriceErrorType = ensjs_GetPriceErrorType + +export function getPrice( + config: RequireConfigContracts< + chains, + 'ensEthRegistrarController' | 'multicall3' + >, + parameters: GetPriceParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getPrice, + 'getPrice', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getRecords.ts b/packages/query-core/src/actions/public/getRecords.ts new file mode 100644 index 000000000..559f8dd1a --- /dev/null +++ b/packages/query-core/src/actions/public/getRecords.ts @@ -0,0 +1,69 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetRecordsErrorType as ensjs_GetRecordsErrorType, + type GetRecordsParameters as ensjs_GetRecordsParameters, + type GetRecordsReturnType as ensjs_GetRecordsReturnType, + getRecords as ensjs_getRecords, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetRecordsParameters< + config extends Config = Config, + texts extends readonly string[] | undefined = readonly string[], + coins extends readonly (string | number)[] | undefined = readonly ( + | string + | number + )[], + contentHash extends boolean | undefined = true, + abi extends boolean | undefined = true, +> = Prettify< + ensjs_GetRecordsParameters & + ChainIdParameter +> + +export type GetRecordsReturnType< + texts extends readonly string[] | undefined = readonly string[], + coins extends readonly (string | number)[] | undefined = readonly ( + | string + | number + )[], + contentHash extends boolean | undefined = true, + abi extends boolean | undefined = true, +> = ensjs_GetRecordsReturnType + +export type GetRecordsErrorType = ensjs_GetRecordsErrorType + +export function getRecords< + chains extends readonly [Chain, ...Chain[]], + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, +>( + config: RequireConfigContracts, + parameters: GetRecordsParameters< + ExcludeTE, + texts, + coins, + contentHash, + abi + >, +): Promise> { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getRecords, + 'getRecords', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getResolver.ts b/packages/query-core/src/actions/public/getResolver.ts new file mode 100644 index 000000000..f25d63ca8 --- /dev/null +++ b/packages/query-core/src/actions/public/getResolver.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetResolverErrorType as ensjs_GetResolverErrorType, + type GetResolverParameters as ensjs_GetResolverParameters, + type GetResolverReturnType as ensjs_GetResolverReturnType, + getResolver as ensjs_getResolver, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetResolverParameters = Prettify< + ensjs_GetResolverParameters & ChainIdParameter +> + +export type GetResolverReturnType = ensjs_GetResolverReturnType + +export type GetResolverErrorType = ensjs_GetResolverErrorType + +export function getResolver( + config: RequireConfigContracts, + parameters: GetResolverParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getResolver, + 'getResolver', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getSupportedInterfaces.ts b/packages/query-core/src/actions/public/getSupportedInterfaces.ts new file mode 100644 index 000000000..ec5d12b17 --- /dev/null +++ b/packages/query-core/src/actions/public/getSupportedInterfaces.ts @@ -0,0 +1,53 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetSupportedInterfacesErrorType as ensjs_GetSupportedInterfacesErrorType, + type GetSupportedInterfacesParameters as ensjs_GetSupportedInterfacesParameters, + type GetSupportedInterfacesReturnType as ensjs_GetSupportedInterfacesReturnType, + getSupportedInterfaces as ensjs_getSupportedInterfaces, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Hex, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetSupportedInterfacesParameters< + config extends Config = Config, + interfaces extends readonly Hex[] = readonly Hex[], +> = Prettify< + ensjs_GetSupportedInterfacesParameters & ChainIdParameter +> + +export type GetSupportedInterfacesReturnType< + interfaces extends readonly Hex[] = readonly Hex[], +> = ensjs_GetSupportedInterfacesReturnType + +export type GetSupportedInterfacesErrorType = + ensjs_GetSupportedInterfacesErrorType + +export function getSupportedInterfaces< + chains extends readonly [Chain, ...Chain[]], + const interfaces extends readonly Hex[], +>( + config: RequireConfigContracts, + parameters: GetSupportedInterfacesParameters< + ExcludeTE, + interfaces + >, +): Promise> { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getSupportedInterfaces, + // TODO: Add getSupportedInterfaces to the client decorators + // @ts-expect-error - Not yet added to decorator + 'getSupportedInterfaces', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getTextRecord.ts b/packages/query-core/src/actions/public/getTextRecord.ts new file mode 100644 index 000000000..357aeb9a4 --- /dev/null +++ b/packages/query-core/src/actions/public/getTextRecord.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetTextRecordErrorType as ensjs_GetTextRecordErrorType, + type GetTextRecordParameters as ensjs_GetTextRecordParameters, + type GetTextRecordReturnType as ensjs_GetTextRecordReturnType, + getTextRecord as ensjs_getTextRecord, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetTextRecordParameters = Prettify< + ensjs_GetTextRecordParameters & ChainIdParameter +> + +export type GetTextRecordReturnType = ensjs_GetTextRecordReturnType + +export type GetTextRecordErrorType = ensjs_GetTextRecordErrorType + +export function getTextRecord( + config: RequireConfigContracts, + parameters: GetTextRecordParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getTextRecord, + 'getTextRecord', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getWrapperData.ts b/packages/query-core/src/actions/public/getWrapperData.ts new file mode 100644 index 000000000..c27914599 --- /dev/null +++ b/packages/query-core/src/actions/public/getWrapperData.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetWrapperDataErrorType as ensjs_GetWrapperDataErrorType, + type GetWrapperDataParameters as ensjs_GetWrapperDataParameters, + type GetWrapperDataReturnType as ensjs_GetWrapperDataReturnType, + getWrapperData as ensjs_getWrapperData, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetWrapperDataParameters = Prettify< + ensjs_GetWrapperDataParameters & ChainIdParameter +> + +export type GetWrapperDataReturnType = ensjs_GetWrapperDataReturnType + +export type GetWrapperDataErrorType = ensjs_GetWrapperDataErrorType + +export function getWrapperData( + config: RequireConfigContracts, + parameters: GetWrapperDataParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getWrapperData, + 'getWrapperData', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/getWrapperName.ts b/packages/query-core/src/actions/public/getWrapperName.ts new file mode 100644 index 000000000..db608d444 --- /dev/null +++ b/packages/query-core/src/actions/public/getWrapperName.ts @@ -0,0 +1,39 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type GetWrapperNameErrorType as ensjs_GetWrapperNameErrorType, + type GetWrapperNameParameters as ensjs_GetWrapperNameParameters, + type GetWrapperNameReturnType as ensjs_GetWrapperNameReturnType, + getWrapperName as ensjs_getWrapperName, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type GetWrapperNameParameters = Prettify< + ensjs_GetWrapperNameParameters & ChainIdParameter +> + +export type GetWrapperNameReturnType = ensjs_GetWrapperNameReturnType + +export type GetWrapperNameErrorType = ensjs_GetWrapperNameErrorType + +export function getWrapperName( + config: RequireConfigContracts, + parameters: GetWrapperNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_getWrapperName, + 'getWrapperName', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/public/resolveNameData.ts b/packages/query-core/src/actions/public/resolveNameData.ts new file mode 100644 index 000000000..b795971e7 --- /dev/null +++ b/packages/query-core/src/actions/public/resolveNameData.ts @@ -0,0 +1,46 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type ResolveNameDataErrorType as ensjs_ResolveNameDataErrorType, + type ResolveNameDataParameters as ensjs_ResolveNameDataParameters, + type ResolveNameDataReturnType as ensjs_ResolveNameDataReturnType, + resolveNameData as ensjs_resolveNameData, +} from '@ensdomains/ensjs/public' +import type { Config } from '@wagmi/core' +import type { ChainIdParameter } from '@wagmi/core/internal' +import type { Chain, Hex, Prettify } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type ResolveNameDataParameters< + data extends Hex | Hex[], + config extends Config = Config, +> = Prettify & ChainIdParameter> + +export type ResolveNameDataReturnType = + ensjs_ResolveNameDataReturnType + +export type ResolveNameDataErrorType = ensjs_ResolveNameDataErrorType + +export function resolveNameData< + chains extends readonly [Chain, ...Chain[]], + data extends Hex | Hex[], +>( + config: RequireConfigContracts, + parameters: ResolveNameDataParameters>, +): Promise> { + ASSERT_NO_TYPE_ERROR(config) + + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + ensjs_resolveNameData, + // TODO: Add resolveNameData to the client decorators + // @ts-expect-error - Not yet added to decorator + 'resolveNameData', + ) + return action(rest) +} diff --git a/packages/query-core/src/actions/wallet/clearRecords.ts b/packages/query-core/src/actions/wallet/clearRecords.ts new file mode 100644 index 000000000..d1a858ae7 --- /dev/null +++ b/packages/query-core/src/actions/wallet/clearRecords.ts @@ -0,0 +1,69 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type ClearRecordsErrorType as ensjs_ClearRecordsErrorType, + type ClearRecordsParameters as ensjs_ClearRecordsParameters, + type ClearRecordsReturnType as ensjs_ClearRecordsReturnType, + clearRecords as ensjs_clearRecords, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +// export type GetAbiRecordParameters = Prettify< +// ensjs_ClearRecordsParameters & ChainIdParameter +// > + +export type ClearRecordsParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_ClearRecordsParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type ClearRecordsReturnType = ensjs_ClearRecordsReturnType + +export type ClearRecordsErrorType = ensjs_ClearRecordsErrorType + +export async function clearRecords( + config: RequireConfigContracts, + parameters: ClearRecordsParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_clearRecords, 'clearRecords') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/commitName.ts b/packages/query-core/src/actions/wallet/commitName.ts new file mode 100644 index 000000000..80640d306 --- /dev/null +++ b/packages/query-core/src/actions/wallet/commitName.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type CommitNameErrorType as ensjs_CommitNameErrorType, + type CommitNameParameters as ensjs_CommitNameParameters, + type CommitNameReturnType as ensjs_CommitNameReturnType, + commitName as ensjs_commitName, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type CommitNameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type CommitNameReturnType = ensjs_CommitNameReturnType + +export type CommitNameErrorType = ensjs_CommitNameErrorType + +export async function commitName( + config: RequireConfigContracts, + parameters: CommitNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_commitName, 'commitName') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/createSubname.ts b/packages/query-core/src/actions/wallet/createSubname.ts new file mode 100644 index 000000000..d27e1cb78 --- /dev/null +++ b/packages/query-core/src/actions/wallet/createSubname.ts @@ -0,0 +1,67 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type CreateSubnameErrorType as ensjs_CreateSubnameErrorType, + type CreateSubnameParameters as ensjs_CreateSubnameParameters, + type CreateSubnameReturnType as ensjs_CreateSubnameReturnType, + createSubname as ensjs_createSubname, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type CreateSubnameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type CreateSubnameReturnType = ensjs_CreateSubnameReturnType + +export type CreateSubnameErrorType = ensjs_CreateSubnameErrorType + +export async function createSubname< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensNameWrapper' | 'ensPublicResolver' + >, + parameters: CreateSubnameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_createSubname, 'createSubname') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/deleteSubname.ts b/packages/query-core/src/actions/wallet/deleteSubname.ts new file mode 100644 index 000000000..49808c5f0 --- /dev/null +++ b/packages/query-core/src/actions/wallet/deleteSubname.ts @@ -0,0 +1,64 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type DeleteSubnameErrorType as ensjs_DeleteSubnameErrorType, + type DeleteSubnameParameters as ensjs_DeleteSubnameParameters, + type DeleteSubnameReturnType as ensjs_DeleteSubnameReturnType, + deleteSubname as ensjs_deleteSubname, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type DeleteSubnameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type DeleteSubnameReturnType = ensjs_DeleteSubnameReturnType + +export type DeleteSubnameErrorType = ensjs_DeleteSubnameErrorType + +export async function deleteSubname< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + parameters: DeleteSubnameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_deleteSubname, 'deleteSubname') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/index.ts b/packages/query-core/src/actions/wallet/index.ts new file mode 100644 index 000000000..3ace40f8d --- /dev/null +++ b/packages/query-core/src/actions/wallet/index.ts @@ -0,0 +1,84 @@ +export { + type ClearRecordsErrorType, + type ClearRecordsParameters, + type ClearRecordsReturnType, + clearRecords, +} from './clearRecords.js' +export { + type CommitNameErrorType, + type CommitNameParameters, + type CommitNameReturnType, + commitName, +} from './commitName.js' +export { + type CreateSubnameErrorType, + type CreateSubnameParameters, + type CreateSubnameReturnType, + createSubname, +} from './createSubname.js' +export { + type DeleteSubnameErrorType, + type DeleteSubnameParameters, + type DeleteSubnameReturnType, + deleteSubname, +} from './deleteSubname.js' +export { + type RegisterNameErrorType, + type RegisterNameParameters, + type RegisterNameReturnType, + registerName, +} from './registerName.js' +export { + type RenewNameErrorType, + type RenewNameParameters, + type RenewNameReturnType, + renewName, +} from './renewName.js' +export { + type SetAbiRecordErrorType, + type SetAbiRecordParameters, + type SetAbiRecordReturnType, + setAbiRecord, +} from './setAbiRecord.js' +export { + type SetAddressRecordErrorType, + type SetAddressRecordParameters, + type SetAddressRecordReturnType, + setAddressRecord, +} from './setAddressRecord.js' +export { + type SetContentHashRecordErrorType, + type SetContentHashRecordParameters, + type SetContentHashRecordReturnType, + setContentHashRecord, +} from './setContentHashRecord.js' +export { + type SetPrimaryNameErrorType, + type SetPrimaryNameParameters, + type SetPrimaryNameReturnType, + setPrimaryName, +} from './setPrimaryName.js' +export { + type SetRecordsErrorType, + type SetRecordsParameters, + type SetRecordsReturnType, + setRecords, +} from './setRecords.js' +export { + type SetResolverErrorType, + type SetResolverParameters, + type SetResolverReturnType, + setResolver, +} from './setResolver.js' +export { + type SetTextRecordErrorType, + type SetTextRecordParameters, + type SetTextRecordReturnType, + setTextRecord, +} from './setTextRecord.js' +export { + type TransferNameErrorType, + type TransferNameParameters, + type TransferNameReturnType, + transferName, +} from './transferName.js' diff --git a/packages/query-core/src/actions/wallet/registerName.ts b/packages/query-core/src/actions/wallet/registerName.ts new file mode 100644 index 000000000..12e1d2ff4 --- /dev/null +++ b/packages/query-core/src/actions/wallet/registerName.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type RegisterNameErrorType as ensjs_RegisterNameErrorType, + type RegisterNameParameters as ensjs_RegisterNameParameters, + type RegisterNameReturnType as ensjs_RegisterNameReturnType, + registerName as ensjs_registerName, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type RegisterNameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type RegisterNameReturnType = ensjs_RegisterNameReturnType + +export type RegisterNameErrorType = ensjs_RegisterNameErrorType + +export async function registerName( + config: RequireConfigContracts, + parameters: RegisterNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_registerName, 'registerName') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/renewName.ts b/packages/query-core/src/actions/wallet/renewName.ts new file mode 100644 index 000000000..a4e194b6a --- /dev/null +++ b/packages/query-core/src/actions/wallet/renewName.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type RenewNameErrorType as ensjs_RenewNameErrorType, + type RenewNameParameters as ensjs_RenewNameParameters, + type RenewNameReturnType as ensjs_RenewNameReturnType, + renewName as ensjs_renewName, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type RenewNameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type RenewNameReturnType = ensjs_RenewNameReturnType + +export type RenewNameErrorType = ensjs_RenewNameErrorType + +export async function renewName( + config: RequireConfigContracts, + parameters: RenewNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_renewName, 'renewName') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setAbiRecord.ts b/packages/query-core/src/actions/wallet/setAbiRecord.ts new file mode 100644 index 000000000..155a4f49f --- /dev/null +++ b/packages/query-core/src/actions/wallet/setAbiRecord.ts @@ -0,0 +1,63 @@ +import type { AbiEncodeAs } from '@ensdomains/ensjs/utils' +import { + type SetAbiRecordErrorType as ensjs_SetAbiRecordErrorType, + type SetAbiRecordParameters as ensjs_SetAbiRecordParameters, + type SetAbiRecordReturnType as ensjs_SetAbiRecordReturnType, + setAbiRecord as ensjs_setAbiRecord, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import { getAction } from '../../utils/getAction.js' + +export type SetAbiRecordParameters< + encodeAs extends AbiEncodeAs = AbiEncodeAs, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_SetAbiRecordParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetAbiRecordReturnType = ensjs_SetAbiRecordReturnType + +export type SetAbiRecordErrorType = ensjs_SetAbiRecordErrorType + +export async function setAbiRecord< + encodeAs extends AbiEncodeAs, + config extends Config, +>( + config: config, + parameters: SetAbiRecordParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setAbiRecord, 'setAbiRecord') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setAddressRecord.ts b/packages/query-core/src/actions/wallet/setAddressRecord.ts new file mode 100644 index 000000000..7cf743a1d --- /dev/null +++ b/packages/query-core/src/actions/wallet/setAddressRecord.ts @@ -0,0 +1,58 @@ +import { + type SetAddressRecordErrorType as ensjs_SetAddressRecordErrorType, + type SetAddressRecordParameters as ensjs_SetAddressRecordParameters, + type SetAddressRecordReturnType as ensjs_SetAddressRecordReturnType, + setAddressRecord as ensjs_setAddressRecord, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import { getAction } from '../../utils/getAction.js' + +export type SetAddressRecordParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_SetAddressRecordParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetAddressRecordReturnType = ensjs_SetAddressRecordReturnType + +export type SetAddressRecordErrorType = ensjs_SetAddressRecordErrorType + +export async function setAddressRecord( + config: config, + parameters: SetAddressRecordParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setAddressRecord, 'setAddressRecord') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setContentHashRecord.ts b/packages/query-core/src/actions/wallet/setContentHashRecord.ts new file mode 100644 index 000000000..55a900d21 --- /dev/null +++ b/packages/query-core/src/actions/wallet/setContentHashRecord.ts @@ -0,0 +1,63 @@ +import { + type SetContentHashRecordErrorType as ensjs_SetContentHashRecordErrorType, + type SetContentHashRecordParameters as ensjs_SetContentHashRecordParameters, + type SetContentHashRecordReturnType as ensjs_SetContentHashRecordReturnType, + setContentHashRecord as ensjs_setContentHashRecord, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import { getAction } from '../../utils/getAction.js' + +export type SetContentHashRecordParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_SetContentHashRecordParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetContentHashRecordReturnType = + ensjs_SetContentHashRecordReturnType + +export type SetContentHashRecordErrorType = ensjs_SetContentHashRecordErrorType + +export async function setContentHashRecord( + config: config, + parameters: SetContentHashRecordParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction( + client, + ensjs_setContentHashRecord, + 'setContentHashRecord', + ) + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setPrimaryName.ts b/packages/query-core/src/actions/wallet/setPrimaryName.ts new file mode 100644 index 000000000..773cf51db --- /dev/null +++ b/packages/query-core/src/actions/wallet/setPrimaryName.ts @@ -0,0 +1,67 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type SetPrimaryNameErrorType as ensjs_SetPrimaryNameErrorType, + type SetPrimaryNameParameters as ensjs_SetPrimaryNameParameters, + type SetPrimaryNameReturnType as ensjs_SetPrimaryNameReturnType, + setPrimaryName as ensjs_setPrimaryName, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type SetPrimaryNameParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetPrimaryNameReturnType = ensjs_SetPrimaryNameReturnType + +export type SetPrimaryNameErrorType = ensjs_SetPrimaryNameErrorType + +export async function setPrimaryName< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensPublicResolver' | 'ensReverseRegistrar' + >, + parameters: SetPrimaryNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setPrimaryName, 'setPrimaryName') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setRecords.ts b/packages/query-core/src/actions/wallet/setRecords.ts new file mode 100644 index 000000000..bbc40cbe4 --- /dev/null +++ b/packages/query-core/src/actions/wallet/setRecords.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type SetRecordsErrorType as ensjs_SetRecordsErrorType, + type SetRecordsParameters as ensjs_SetRecordsParameters, + type SetRecordsReturnType as ensjs_SetRecordsReturnType, + setRecords as ensjs_setRecords, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type SetRecordsParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetRecordsReturnType = ensjs_SetRecordsReturnType + +export type SetRecordsErrorType = ensjs_SetRecordsErrorType + +export async function setRecords( + config: RequireConfigContracts, + parameters: SetRecordsParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setRecords, 'setRecords') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setResolver.ts b/packages/query-core/src/actions/wallet/setResolver.ts new file mode 100644 index 000000000..1be45f6b6 --- /dev/null +++ b/packages/query-core/src/actions/wallet/setResolver.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type SetResolverErrorType as ensjs_SetResolverErrorType, + type SetResolverParameters as ensjs_SetResolverParameters, + type SetResolverReturnType as ensjs_SetResolverReturnType, + setResolver as ensjs_setResolver, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type SetResolverParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetResolverReturnType = ensjs_SetResolverReturnType + +export type SetResolverErrorType = ensjs_SetResolverErrorType + +export async function setResolver( + config: RequireConfigContracts, + parameters: SetResolverParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setResolver, 'setResolver') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/setTextRecord.ts b/packages/query-core/src/actions/wallet/setTextRecord.ts new file mode 100644 index 000000000..97d79c2da --- /dev/null +++ b/packages/query-core/src/actions/wallet/setTextRecord.ts @@ -0,0 +1,58 @@ +import { + type SetTextRecordErrorType as ensjs_SetTextRecordErrorType, + type SetTextRecordParameters as ensjs_SetTextRecordParameters, + type SetTextRecordReturnType as ensjs_SetTextRecordReturnType, + setTextRecord as ensjs_setTextRecord, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import { getAction } from '../../utils/getAction.js' + +export type SetTextRecordParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_SetTextRecordParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SetTextRecordReturnType = ensjs_SetTextRecordReturnType + +export type SetTextRecordErrorType = ensjs_SetTextRecordErrorType + +export async function setTextRecord( + config: config, + parameters: SetTextRecordParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_setTextRecord, 'setTextRecord') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/actions/wallet/transferName.ts b/packages/query-core/src/actions/wallet/transferName.ts new file mode 100644 index 000000000..8e0b5ed6c --- /dev/null +++ b/packages/query-core/src/actions/wallet/transferName.ts @@ -0,0 +1,73 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import { + type TransferNameErrorType as ensjs_TransferNameErrorType, + type TransferNameParameters as ensjs_TransferNameParameters, + type TransferNameReturnType as ensjs_TransferNameReturnType, + transferName as ensjs_transferName, + type TransferNameSupportedContract, +} from '@ensdomains/ensjs/wallet' +import { type Config, getConnectorClient, type SelectChains } from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, +} from '@wagmi/core/internal' +import type { Account, Chain, Client } from 'viem' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { getAction } from '../../utils/getAction.js' + +export type TransferNameParameters< + contract extends TransferNameSupportedContract, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + ensjs_TransferNameParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type TransferNameReturnType = ensjs_TransferNameReturnType + +export type TransferNameErrorType = ensjs_TransferNameErrorType + +export async function transferName< + contract extends TransferNameSupportedContract, + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation' + >, + parameters: TransferNameParameters>, +): Promise { + ASSERT_NO_TYPE_ERROR(config) + + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, ensjs_transferName, 'transferName') + return await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) +} diff --git a/packages/query-core/src/example.ts b/packages/query-core/src/example.ts new file mode 100644 index 000000000..c6dab22fb --- /dev/null +++ b/packages/query-core/src/example.ts @@ -0,0 +1,39 @@ +// import { extendChainWithEns } from '@ensdomains/ensjs/chain' +// import { Config, createConfig, http } from '@wagmi/core' +// import { Chain, mainnet, Prettify } from 'viem/chains' +// import { RequireConfigContracts, RequireConfigContracts2 } from './utils/chain' +// import { ASSERT_NO_TYPE_ERROR, EXCLUDE_TYPE_ERROR, ExcludeTE } from '@ensdomains/ensjs/internal' + +// const myConfig = createConfig({ +// chains: [mainnet], +// transports: { +// [mainnet.id]: http(), +// }, +// }) + +// const myConfigWithEns = createConfig({ +// chains: [extendChainWithEns(mainnet)], +// transports: { +// [mainnet.id]: http(), +// }, +// }) + +// const myAction = (config: RequireConfigContracts): ExcludeTE => { +// const config_ = config as ExcludeTE & {} + +// return {} as any +// } + +// const sdads = myAction(myConfig) +// const asdsad = myAction(myConfigWithEns) + +// const myAction2 = (config: RequireConfigContracts2): ExcludeTE => { +// const dasdas = EXCLUDE_TYPE_ERROR(config) + +// type asdasd = Prettify + +// return {} as any +// } + +// const asd = myAction2(myConfig) +// const jksdf = myAction2(myConfigWithEns) diff --git a/packages/query-core/src/query/public/getAbiRecord.ts b/packages/query-core/src/query/public/getAbiRecord.ts new file mode 100644 index 000000000..b7bca6949 --- /dev/null +++ b/packages/query-core/src/query/public/getAbiRecord.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetAbiRecordErrorType, + type GetAbiRecordParameters, + type GetAbiRecordReturnType, + getAbiRecord, +} from '../../actions/public/getAbiRecord.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetAbiRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getAbiRecordQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetAbiRecordOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getAbiRecord(config, { ...parameters, name }) + }, + queryKey: getAbiRecordQueryKey(options), + } as const satisfies QueryOptions< + GetAbiRecordQueryFnData, + GetAbiRecordErrorType, + GetAbiRecordData, + GetAbiRecordQueryKey + > +} + +export type GetAbiRecordQueryFnData = GetAbiRecordReturnType + +export type GetAbiRecordData = GetAbiRecordQueryFnData + +export function getAbiRecordQueryKey( + options: GetAbiRecordOptions = {}, +) { + return ['ensjs_abiRecord', filterQueryOptions(options)] as const +} + +export type GetAbiRecordQueryKey = ReturnType< + typeof getAbiRecordQueryKey +> diff --git a/packages/query-core/src/query/public/getAddressRecord.ts b/packages/query-core/src/query/public/getAddressRecord.ts new file mode 100644 index 000000000..062d3ea13 --- /dev/null +++ b/packages/query-core/src/query/public/getAddressRecord.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetAddressRecordErrorType, + type GetAddressRecordParameters, + type GetAddressRecordReturnType, + getAddressRecord, +} from '../../actions/public/getAddressRecord.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetAddressRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getAddressRecordQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetAddressRecordOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getAddressRecord(config, { ...parameters, name }) + }, + queryKey: getAddressRecordQueryKey(options), + } as const satisfies QueryOptions< + GetAddressRecordQueryFnData, + GetAddressRecordErrorType, + GetAddressRecordData, + GetAddressRecordQueryKey + > +} + +export type GetAddressRecordQueryFnData = GetAddressRecordReturnType + +export type GetAddressRecordData = GetAddressRecordQueryFnData + +export function getAddressRecordQueryKey( + options: GetAddressRecordOptions = {}, +) { + return ['ensjs_addressRecord', filterQueryOptions(options)] as const +} + +export type GetAddressRecordQueryKey = ReturnType< + typeof getAddressRecordQueryKey +> diff --git a/packages/query-core/src/query/public/getAvailable.ts b/packages/query-core/src/query/public/getAvailable.ts new file mode 100644 index 000000000..19179ffc8 --- /dev/null +++ b/packages/query-core/src/query/public/getAvailable.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetAvailableErrorType, + type GetAvailableParameters, + type GetAvailableReturnType, + getAvailable, +} from '../../actions/public/getAvailable.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetAvailableOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getAvailableQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetAvailableOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getAvailable(config, { ...parameters, name }) + }, + queryKey: getAvailableQueryKey(options), + } as const satisfies QueryOptions< + GetAvailableQueryFnData, + GetAvailableErrorType, + GetAvailableData, + GetAvailableQueryKey + > +} + +export type GetAvailableQueryFnData = GetAvailableReturnType + +export type GetAvailableData = GetAvailableQueryFnData + +export function getAvailableQueryKey( + options: GetAvailableOptions = {}, +) { + return ['ensjs_available', filterQueryOptions(options)] as const +} + +export type GetAvailableQueryKey = ReturnType< + typeof getAvailableQueryKey +> diff --git a/packages/query-core/src/query/public/getContentHashRecord.ts b/packages/query-core/src/query/public/getContentHashRecord.ts new file mode 100644 index 000000000..cd08aede1 --- /dev/null +++ b/packages/query-core/src/query/public/getContentHashRecord.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetContentHashRecordErrorType, + type GetContentHashRecordParameters, + type GetContentHashRecordReturnType, + getContentHashRecord, +} from '../../actions/public/getContentHashRecord.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetContentHashRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getContentHashRecordQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetContentHashRecordOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getContentHashRecord(config, { ...parameters, name }) + }, + queryKey: getContentHashRecordQueryKey(options), + } as const satisfies QueryOptions< + GetContentHashRecordQueryFnData, + GetContentHashRecordErrorType, + GetContentHashRecordData, + GetContentHashRecordQueryKey + > +} + +export type GetContentHashRecordQueryFnData = GetContentHashRecordReturnType + +export type GetContentHashRecordData = GetContentHashRecordQueryFnData + +export function getContentHashRecordQueryKey( + options: GetContentHashRecordOptions = {}, +) { + return ['ensjs_contentHashRecord', filterQueryOptions(options)] as const +} + +export type GetContentHashRecordQueryKey = ReturnType< + typeof getContentHashRecordQueryKey +> diff --git a/packages/query-core/src/query/public/getCredentials.ts b/packages/query-core/src/query/public/getCredentials.ts new file mode 100644 index 000000000..c8d9cbc3a --- /dev/null +++ b/packages/query-core/src/query/public/getCredentials.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetCredentialsErrorType, + type GetCredentialsParameters, + type GetCredentialsReturnType, + getCredentials, +} from '../../actions/public/getCredentials.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetCredentialsOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getCredentialsQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetCredentialsOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getCredentials(config, { ...parameters, name }) + }, + queryKey: getCredentialsQueryKey(options), + } as const satisfies QueryOptions< + GetCredentialsQueryFnData, + GetCredentialsErrorType, + GetCredentialsData, + GetCredentialsQueryKey + > +} + +export type GetCredentialsQueryFnData = GetCredentialsReturnType + +export type GetCredentialsData = GetCredentialsQueryFnData + +export function getCredentialsQueryKey( + options: GetCredentialsOptions = {}, +) { + return ['ensjs_credentials', filterQueryOptions(options)] as const +} + +export type GetCredentialsQueryKey = ReturnType< + typeof getCredentialsQueryKey +> diff --git a/packages/query-core/src/query/public/getExpiry.ts b/packages/query-core/src/query/public/getExpiry.ts new file mode 100644 index 000000000..f888cda96 --- /dev/null +++ b/packages/query-core/src/query/public/getExpiry.ts @@ -0,0 +1,64 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetExpiryErrorType, + type GetExpiryParameters, + type GetExpiryReturnType, + getExpiry, +} from '../../actions/public/getExpiry.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetExpiryOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getExpiryQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensNameWrapper' | 'ensBaseRegistrarImplementation' | 'multicall3' + >, + options: GetExpiryOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getExpiry(config, { ...parameters, name }) + }, + queryKey: getExpiryQueryKey(options), + } as const satisfies QueryOptions< + GetExpiryQueryFnData, + GetExpiryErrorType, + GetExpiryData, + GetExpiryQueryKey + > +} + +export type GetExpiryQueryFnData = GetExpiryReturnType + +export type GetExpiryData = GetExpiryQueryFnData + +export function getExpiryQueryKey( + options: GetExpiryOptions = {}, +) { + return ['ensjs_expiry', filterQueryOptions(options)] as const +} + +export type GetExpiryQueryKey = ReturnType< + typeof getExpiryQueryKey +> diff --git a/packages/query-core/src/query/public/getName.ts b/packages/query-core/src/query/public/getName.ts new file mode 100644 index 000000000..0770d97fb --- /dev/null +++ b/packages/query-core/src/query/public/getName.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetNameErrorType, + type GetNameParameters, + type GetNameReturnType, + getName, +} from '../../actions/public/getName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getNameQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetNameOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + return getName(config, { ...parameters, address }) + }, + queryKey: getNameQueryKey(options), + } as const satisfies QueryOptions< + GetNameQueryFnData, + GetNameErrorType, + GetNameData, + GetNameQueryKey + > +} + +export type GetNameQueryFnData = GetNameReturnType + +export type GetNameData = GetNameQueryFnData + +export function getNameQueryKey( + options: GetNameOptions = {}, +) { + return ['ensjs_name', filterQueryOptions(options)] as const +} + +export type GetNameQueryKey = ReturnType< + typeof getNameQueryKey +> diff --git a/packages/query-core/src/query/public/getOwner.ts b/packages/query-core/src/query/public/getOwner.ts new file mode 100644 index 000000000..bfe2dc428 --- /dev/null +++ b/packages/query-core/src/query/public/getOwner.ts @@ -0,0 +1,67 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetOwnerErrorType, + type GetOwnerParameters, + type GetOwnerReturnType, + getOwner, +} from '../../actions/public/getOwner.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetOwnerOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getOwnerQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + | 'ensNameWrapper' + | 'ensRegistry' + | 'ensBaseRegistrarImplementation' + | 'multicall3' + >, + options: GetOwnerOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getOwner(config, { ...parameters, name }) + }, + queryKey: getOwnerQueryKey(options), + } as const satisfies QueryOptions< + GetOwnerQueryFnData, + GetOwnerErrorType, + GetOwnerData, + GetOwnerQueryKey + > +} + +export type GetOwnerQueryFnData = GetOwnerReturnType + +export type GetOwnerData = GetOwnerQueryFnData + +export function getOwnerQueryKey( + options: GetOwnerOptions = {}, +) { + return ['ensjs_owner', filterQueryOptions(options)] as const +} + +export type GetOwnerQueryKey = ReturnType< + typeof getOwnerQueryKey +> diff --git a/packages/query-core/src/query/public/getPrice.ts b/packages/query-core/src/query/public/getPrice.ts new file mode 100644 index 000000000..32a92e530 --- /dev/null +++ b/packages/query-core/src/query/public/getPrice.ts @@ -0,0 +1,65 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetPriceErrorType, + type GetPriceParameters, + type GetPriceReturnType, + getPrice, +} from '../../actions/public/getPrice.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetPriceOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getPriceQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensEthRegistrarController' | 'multicall3' + >, + options: GetPriceOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { nameOrNames, duration, scopeKey: _, ...parameters } = queryKey[1] + if (!nameOrNames) throw new Error('nameOrNames is required') + if (duration === undefined) throw new Error('duration is required') + return getPrice(config, { ...parameters, nameOrNames, duration }) + }, + queryKey: getPriceQueryKey(options), + } as const satisfies QueryOptions< + GetPriceQueryFnData, + GetPriceErrorType, + GetPriceData, + GetPriceQueryKey + > +} + +export type GetPriceQueryFnData = GetPriceReturnType + +export type GetPriceData = GetPriceQueryFnData + +export function getPriceQueryKey( + options: GetPriceOptions = {}, +) { + return ['ensjs_price', filterQueryOptions(options)] as const +} + +export type GetPriceQueryKey = ReturnType< + typeof getPriceQueryKey +> diff --git a/packages/query-core/src/query/public/getRecords.ts b/packages/query-core/src/query/public/getRecords.ts new file mode 100644 index 000000000..913ee62b7 --- /dev/null +++ b/packages/query-core/src/query/public/getRecords.ts @@ -0,0 +1,83 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetRecordsErrorType, + type GetRecordsParameters, + type GetRecordsReturnType, + getRecords, +} from '../../actions/public/getRecords.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetRecordsOptions< + config extends Config, + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export function getRecordsQueryOptions< + chains extends readonly [Chain, ...Chain[]], + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, +>( + config: RequireConfigContracts, + options: GetRecordsOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getRecords(config, { + ...parameters, + name, + }) + }, + queryKey: getRecordsQueryKey(options), + } as const satisfies QueryOptions< + GetRecordsQueryFnData, + GetRecordsErrorType, + GetRecordsData, + GetRecordsQueryKey + > +} + +export type GetRecordsQueryFnData< + texts extends readonly string[] | undefined = readonly string[], + coins extends readonly (string | number)[] | undefined = readonly ( + | string + | number + )[], + contentHash extends boolean | undefined = true, + abi extends boolean | undefined = true, +> = GetRecordsReturnType + +export type GetRecordsData = GetRecordsQueryFnData + +export function getRecordsQueryKey( + options: GetRecordsOptions = {}, +) { + return ['ensjs_records', filterQueryOptions(options)] as const +} + +export type GetRecordsQueryKey = ReturnType< + typeof getRecordsQueryKey +> diff --git a/packages/query-core/src/query/public/getResolver.ts b/packages/query-core/src/query/public/getResolver.ts new file mode 100644 index 000000000..911451035 --- /dev/null +++ b/packages/query-core/src/query/public/getResolver.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetResolverErrorType, + type GetResolverParameters, + type GetResolverReturnType, + getResolver, +} from '../../actions/public/getResolver.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetResolverOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getResolverQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetResolverOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getResolver(config, { ...parameters, name }) + }, + queryKey: getResolverQueryKey(options), + } as const satisfies QueryOptions< + GetResolverQueryFnData, + GetResolverErrorType, + GetResolverData, + GetResolverQueryKey + > +} + +export type GetResolverQueryFnData = GetResolverReturnType + +export type GetResolverData = GetResolverQueryFnData + +export function getResolverQueryKey( + options: GetResolverOptions = {}, +) { + return ['ensjs_resolver', filterQueryOptions(options)] as const +} + +export type GetResolverQueryKey = ReturnType< + typeof getResolverQueryKey +> diff --git a/packages/query-core/src/query/public/getSupportedInterfaces.ts b/packages/query-core/src/query/public/getSupportedInterfaces.ts new file mode 100644 index 000000000..f917bf2af --- /dev/null +++ b/packages/query-core/src/query/public/getSupportedInterfaces.ts @@ -0,0 +1,80 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain, Hex } from 'viem' +import { + type GetSupportedInterfacesErrorType, + type GetSupportedInterfacesParameters, + type GetSupportedInterfacesReturnType, + getSupportedInterfaces, +} from '../../actions/public/getSupportedInterfaces.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetSupportedInterfacesOptions< + config extends Config, + interfaces extends readonly Hex[] = readonly Hex[], +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export function getSupportedInterfacesQueryOptions< + chains extends readonly [Chain, ...Chain[]], + const interfaces extends readonly Hex[], +>( + config: RequireConfigContracts, + options: GetSupportedInterfacesOptions< + ExcludeTE, + interfaces + > = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { address, interfaces, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + if (!interfaces) throw new Error('interfaces is required') + return getSupportedInterfaces(config, { + ...parameters, + address, + interfaces, + }) + }, + queryKey: getSupportedInterfacesQueryKey(options), + } as const satisfies QueryOptions< + GetSupportedInterfacesQueryFnData, + GetSupportedInterfacesErrorType, + GetSupportedInterfacesData, + GetSupportedInterfacesQueryKey + > +} + +export type GetSupportedInterfacesQueryFnData< + interfaces extends readonly Hex[] = readonly Hex[], +> = GetSupportedInterfacesReturnType + +export type GetSupportedInterfacesData< + interfaces extends readonly Hex[] = readonly Hex[], +> = GetSupportedInterfacesQueryFnData + +export function getSupportedInterfacesQueryKey< + config extends Config, + interfaces extends readonly Hex[] = readonly Hex[], +>(options: GetSupportedInterfacesOptions = {}) { + return ['ensjs_supportedInterfaces', filterQueryOptions(options)] as const +} + +export type GetSupportedInterfacesQueryKey< + config extends Config, + interfaces extends readonly Hex[] = readonly Hex[], +> = ReturnType> diff --git a/packages/query-core/src/query/public/getTextRecord.ts b/packages/query-core/src/query/public/getTextRecord.ts new file mode 100644 index 000000000..b0519fa40 --- /dev/null +++ b/packages/query-core/src/query/public/getTextRecord.ts @@ -0,0 +1,62 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetTextRecordErrorType, + type GetTextRecordParameters, + type GetTextRecordReturnType, + getTextRecord, +} from '../../actions/public/getTextRecord.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetTextRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getTextRecordQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetTextRecordOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, key, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + if (!key) throw new Error('key is required') + return getTextRecord(config, { ...parameters, name, key }) + }, + queryKey: getTextRecordQueryKey(options), + } as const satisfies QueryOptions< + GetTextRecordQueryFnData, + GetTextRecordErrorType, + GetTextRecordData, + GetTextRecordQueryKey + > +} + +export type GetTextRecordQueryFnData = GetTextRecordReturnType + +export type GetTextRecordData = GetTextRecordQueryFnData + +export function getTextRecordQueryKey( + options: GetTextRecordOptions = {}, +) { + return ['ensjs_textRecord', filterQueryOptions(options)] as const +} + +export type GetTextRecordQueryKey = ReturnType< + typeof getTextRecordQueryKey +> diff --git a/packages/query-core/src/query/public/getWrapperData.ts b/packages/query-core/src/query/public/getWrapperData.ts new file mode 100644 index 000000000..21218015e --- /dev/null +++ b/packages/query-core/src/query/public/getWrapperData.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetWrapperDataErrorType, + type GetWrapperDataParameters, + type GetWrapperDataReturnType, + getWrapperData, +} from '../../actions/public/getWrapperData.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetWrapperDataOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getWrapperDataQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetWrapperDataOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getWrapperData(config, { ...parameters, name }) + }, + queryKey: getWrapperDataQueryKey(options), + } as const satisfies QueryOptions< + GetWrapperDataQueryFnData, + GetWrapperDataErrorType, + GetWrapperDataData, + GetWrapperDataQueryKey + > +} + +export type GetWrapperDataQueryFnData = GetWrapperDataReturnType + +export type GetWrapperDataData = GetWrapperDataQueryFnData + +export function getWrapperDataQueryKey( + options: GetWrapperDataOptions = {}, +) { + return ['ensjs_wrapperData', filterQueryOptions(options)] as const +} + +export type GetWrapperDataQueryKey = ReturnType< + typeof getWrapperDataQueryKey +> diff --git a/packages/query-core/src/query/public/getWrapperName.ts b/packages/query-core/src/query/public/getWrapperName.ts new file mode 100644 index 000000000..436a6a824 --- /dev/null +++ b/packages/query-core/src/query/public/getWrapperName.ts @@ -0,0 +1,61 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { QueryOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type GetWrapperNameErrorType, + type GetWrapperNameParameters, + type GetWrapperNameReturnType, + getWrapperName, +} from '../../actions/public/getWrapperName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type GetWrapperNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getWrapperNameQueryOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: GetWrapperNameOptions> = {}, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getWrapperName(config, { ...parameters, name }) + }, + queryKey: getWrapperNameQueryKey(options), + } as const satisfies QueryOptions< + GetWrapperNameQueryFnData, + GetWrapperNameErrorType, + GetWrapperNameData, + GetWrapperNameQueryKey + > +} + +export type GetWrapperNameQueryFnData = GetWrapperNameReturnType + +export type GetWrapperNameData = GetWrapperNameQueryFnData + +export function getWrapperNameQueryKey( + options: GetWrapperNameOptions = {}, +) { + return ['ensjs_wrapperName', filterQueryOptions(options)] as const +} + +export type GetWrapperNameQueryKey = ReturnType< + typeof getWrapperNameQueryKey +> diff --git a/packages/query-core/src/query/public/index.ts b/packages/query-core/src/query/public/index.ts new file mode 100644 index 000000000..68936bedb --- /dev/null +++ b/packages/query-core/src/query/public/index.ts @@ -0,0 +1,134 @@ +export { + type GetAbiRecordData, + type GetAbiRecordOptions, + type GetAbiRecordQueryFnData, + type GetAbiRecordQueryKey, + getAbiRecordQueryKey, + getAbiRecordQueryOptions, +} from './getAbiRecord.js' + +export { + type GetAddressRecordData, + type GetAddressRecordOptions, + type GetAddressRecordQueryFnData, + type GetAddressRecordQueryKey, + getAddressRecordQueryKey, + getAddressRecordQueryOptions, +} from './getAddressRecord.js' + +export { + type GetAvailableData, + type GetAvailableOptions, + type GetAvailableQueryFnData, + type GetAvailableQueryKey, + getAvailableQueryKey, + getAvailableQueryOptions, +} from './getAvailable.js' + +export { + type GetContentHashRecordData, + type GetContentHashRecordOptions, + type GetContentHashRecordQueryFnData, + type GetContentHashRecordQueryKey, + getContentHashRecordQueryKey, + getContentHashRecordQueryOptions, +} from './getContentHashRecord.js' + +export { + type GetCredentialsData, + type GetCredentialsOptions, + type GetCredentialsQueryFnData, + type GetCredentialsQueryKey, + getCredentialsQueryKey, + getCredentialsQueryOptions, +} from './getCredentials.js' + +export { + type GetExpiryData, + type GetExpiryOptions, + type GetExpiryQueryFnData, + type GetExpiryQueryKey, + getExpiryQueryKey, + getExpiryQueryOptions, +} from './getExpiry.js' + +export { + type GetNameData, + type GetNameOptions, + type GetNameQueryFnData, + type GetNameQueryKey, + getNameQueryKey, + getNameQueryOptions, +} from './getName.js' + +export { + type GetOwnerData, + type GetOwnerOptions, + type GetOwnerQueryFnData, + type GetOwnerQueryKey, + getOwnerQueryKey, + getOwnerQueryOptions, +} from './getOwner.js' + +export { + type GetPriceData, + type GetPriceOptions, + type GetPriceQueryFnData, + type GetPriceQueryKey, + getPriceQueryKey, + getPriceQueryOptions, +} from './getPrice.js' + +export { + type GetRecordsData, + type GetRecordsOptions, + type GetRecordsQueryFnData, + type GetRecordsQueryKey, + getRecordsQueryKey, + getRecordsQueryOptions, +} from './getRecords.js' + +export { + type GetResolverData, + type GetResolverOptions, + type GetResolverQueryFnData, + type GetResolverQueryKey, + getResolverQueryKey, + getResolverQueryOptions, +} from './getResolver.js' + +export { + type GetSupportedInterfacesData, + type GetSupportedInterfacesOptions, + type GetSupportedInterfacesQueryFnData, + type GetSupportedInterfacesQueryKey, + getSupportedInterfacesQueryKey, + getSupportedInterfacesQueryOptions, +} from './getSupportedInterfaces.js' + +export { + type GetTextRecordData, + type GetTextRecordOptions, + type GetTextRecordQueryFnData, + type GetTextRecordQueryKey, + getTextRecordQueryKey, + getTextRecordQueryOptions, +} from './getTextRecord.js' + +export { + type GetWrapperDataData, + type GetWrapperDataOptions, + type GetWrapperDataQueryFnData, + type GetWrapperDataQueryKey, + getWrapperDataQueryKey, + getWrapperDataQueryOptions, +} from './getWrapperData.js' + +export { + type GetWrapperNameData, + type GetWrapperNameOptions, + type GetWrapperNameQueryFnData, + type GetWrapperNameQueryKey, + getWrapperNameQueryKey, + getWrapperNameQueryOptions, +} from './getWrapperName.js' diff --git a/packages/query-core/src/query/utils.ts b/packages/query-core/src/query/utils.ts new file mode 100644 index 000000000..f4dba601b --- /dev/null +++ b/packages/query-core/src/query/utils.ts @@ -0,0 +1,26 @@ +/* biome-ignore-all lint/correctness/noUnusedVariables: just for destructuring */ + +export function filterQueryOptions>( + options: type, +): type { + // destructuring is super fast + // biome-ignore format: no formatting + const { + // import('@tanstack/query-core').QueryOptions + _defaulted, behavior, gcTime, initialData, initialDataUpdatedAt, maxPages, meta, networkMode, queryFn, queryHash, queryKey, queryKeyHashFn, retry, retryDelay, structuralSharing, + + // import('@tanstack/query-core').InfiniteQueryObserverOptions + getPreviousPageParam, getNextPageParam, initialPageParam, + + // import('@tanstack/react-query').UseQueryOptions + _optimisticResults, enabled, notifyOnChangeProps, placeholderData, refetchInterval, refetchIntervalInBackground, refetchOnMount, refetchOnReconnect, refetchOnWindowFocus, retryOnMount, select, staleTime, suspense, throwOnError, + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // wagmi + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + config, connector, query, + ...rest + } = options + + return rest as type +} diff --git a/packages/query-core/src/query/wallet/clearRecords.ts b/packages/query-core/src/query/wallet/clearRecords.ts new file mode 100644 index 000000000..612abcc19 --- /dev/null +++ b/packages/query-core/src/query/wallet/clearRecords.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type ClearRecordsErrorType, + type ClearRecordsParameters, + type ClearRecordsReturnType, + clearRecords, +} from '../../actions/wallet/clearRecords.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type ClearRecordsOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function clearRecordsMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: ClearRecordsOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: ClearRecordsParameters) => { + return clearRecords(config, variables) + }, + mutationKey: clearRecordsMutationKey(options), + } as const satisfies MutationOptions< + ClearRecordsReturnType, + ClearRecordsErrorType, + ClearRecordsParameters + > +} + +export function clearRecordsMutationKey( + options: ClearRecordsOptions, +) { + return ['ensjs_clearRecords', filterQueryOptions(options)] as const +} + +export type ClearRecordsMutationKey = ReturnType< + typeof clearRecordsMutationKey +> diff --git a/packages/query-core/src/query/wallet/commitName.ts b/packages/query-core/src/query/wallet/commitName.ts new file mode 100644 index 000000000..805e4a553 --- /dev/null +++ b/packages/query-core/src/query/wallet/commitName.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type CommitNameErrorType, + type CommitNameParameters, + type CommitNameReturnType, + commitName, +} from '../../actions/wallet/commitName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type CommitNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function commitNameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: CommitNameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: CommitNameParameters) => { + return commitName(config, variables) + }, + mutationKey: commitNameMutationKey(options), + } as const satisfies MutationOptions< + CommitNameReturnType, + CommitNameErrorType, + CommitNameParameters + > +} + +export function commitNameMutationKey( + options: CommitNameOptions, +) { + return ['ensjs_commitName', filterQueryOptions(options)] as const +} + +export type CommitNameMutationKey = ReturnType< + typeof commitNameMutationKey +> diff --git a/packages/query-core/src/query/wallet/createSubname.ts b/packages/query-core/src/query/wallet/createSubname.ts new file mode 100644 index 000000000..ee8436e67 --- /dev/null +++ b/packages/query-core/src/query/wallet/createSubname.ts @@ -0,0 +1,57 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type CreateSubnameErrorType, + type CreateSubnameParameters, + type CreateSubnameReturnType, + createSubname, +} from '../../actions/wallet/createSubname.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type CreateSubnameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function createSubnameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensNameWrapper' | 'ensPublicResolver' + >, + options: CreateSubnameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: CreateSubnameParameters) => { + return createSubname(config, variables) + }, + mutationKey: createSubnameMutationKey(options), + } as const satisfies MutationOptions< + CreateSubnameReturnType, + CreateSubnameErrorType, + CreateSubnameParameters + > +} + +export function createSubnameMutationKey( + options: CreateSubnameOptions, +) { + return ['ensjs_createSubname', filterQueryOptions(options)] as const +} + +export type CreateSubnameMutationKey = ReturnType< + typeof createSubnameMutationKey +> diff --git a/packages/query-core/src/query/wallet/deleteSubname.ts b/packages/query-core/src/query/wallet/deleteSubname.ts new file mode 100644 index 000000000..b09ab41a8 --- /dev/null +++ b/packages/query-core/src/query/wallet/deleteSubname.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type DeleteSubnameErrorType, + type DeleteSubnameParameters, + type DeleteSubnameReturnType, + deleteSubname, +} from '../../actions/wallet/deleteSubname.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type DeleteSubnameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function deleteSubnameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: DeleteSubnameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: DeleteSubnameParameters) => { + return deleteSubname(config, variables) + }, + mutationKey: deleteSubnameMutationKey(options), + } as const satisfies MutationOptions< + DeleteSubnameReturnType, + DeleteSubnameErrorType, + DeleteSubnameParameters + > +} + +export function deleteSubnameMutationKey( + options: DeleteSubnameOptions, +) { + return ['ensjs_deleteSubname', filterQueryOptions(options)] as const +} + +export type DeleteSubnameMutationKey = ReturnType< + typeof deleteSubnameMutationKey +> diff --git a/packages/query-core/src/query/wallet/index.ts b/packages/query-core/src/query/wallet/index.ts new file mode 100644 index 000000000..9734d30bc --- /dev/null +++ b/packages/query-core/src/query/wallet/index.ts @@ -0,0 +1,85 @@ +export { + type ClearRecordsMutationKey, + type ClearRecordsOptions, + clearRecordsMutationKey, + clearRecordsMutationOptions, +} from './clearRecords.js' +export { + type CommitNameMutationKey, + type CommitNameOptions, + commitNameMutationKey, + commitNameMutationOptions, +} from './commitName.js' +export { + type CreateSubnameMutationKey, + type CreateSubnameOptions, + createSubnameMutationKey, + createSubnameMutationOptions, +} from './createSubname.js' +export { + type DeleteSubnameMutationKey, + type DeleteSubnameOptions, + deleteSubnameMutationKey, + deleteSubnameMutationOptions, +} from './deleteSubname.js' +export { + type RegisterNameMutationKey, + type RegisterNameOptions, + registerNameMutationKey, + registerNameMutationOptions, +} from './registerName.js' +export { + type RenewNameMutationKey, + type RenewNameOptions, + renewNameMutationKey, + renewNameMutationOptions, +} from './renewName.js' +export { + type SetAbiRecordMutationKey, + type SetAbiRecordOptions, + setAbiRecordMutationKey, + setAbiRecordMutationOptions, +} from './setAbiRecord.js' +export { + type SetAddressRecordMutationKey, + type SetAddressRecordOptions, + setAddressRecordMutationKey, + setAddressRecordMutationOptions, +} from './setAddressRecord.js' +export { + type SetContentHashRecordMutationKey, + type SetContentHashRecordOptions, + setContentHashRecordMutationKey, + setContentHashRecordMutationOptions, +} from './setContentHashRecord.js' +export { + type SetPrimaryNameMutationKey, + type SetPrimaryNameOptions, + setPrimaryNameMutationKey, + setPrimaryNameMutationOptions, +} from './setPrimaryName.js' +export { + type SetRecordsMutationKey, + type SetRecordsOptions, + setRecordsMutationKey, + setRecordsMutationOptions, +} from './setRecords.js' +export { + type SetResolverMutationKey, + type SetResolverOptions, + setResolverMutationKey, + setResolverMutationOptions, +} from './setResolver.js' +export { + type SetTextRecordMutationKey, + type SetTextRecordOptions, + setTextRecordMutationKey, + setTextRecordMutationOptions, +} from './setTextRecord.js' + +export { + type TransferNameMutationKey, + type TransferNameOptions, + transferNameMutationKey, + transferNameMutationOptions, +} from './transferName.js' diff --git a/packages/query-core/src/query/wallet/registerName.ts b/packages/query-core/src/query/wallet/registerName.ts new file mode 100644 index 000000000..36ab5cd99 --- /dev/null +++ b/packages/query-core/src/query/wallet/registerName.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type RegisterNameErrorType, + type RegisterNameParameters, + type RegisterNameReturnType, + registerName, +} from '../../actions/wallet/registerName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type RegisterNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function registerNameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: RegisterNameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: RegisterNameParameters) => { + return registerName(config, variables) + }, + mutationKey: registerNameMutationKey(options), + } as const satisfies MutationOptions< + RegisterNameReturnType, + RegisterNameErrorType, + RegisterNameParameters + > +} + +export function registerNameMutationKey( + options: RegisterNameOptions, +) { + return ['ensjs_registerName', filterQueryOptions(options)] as const +} + +export type RegisterNameMutationKey = ReturnType< + typeof registerNameMutationKey +> diff --git a/packages/query-core/src/query/wallet/renewName.ts b/packages/query-core/src/query/wallet/renewName.ts new file mode 100644 index 000000000..81fd3655a --- /dev/null +++ b/packages/query-core/src/query/wallet/renewName.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type RenewNameErrorType, + type RenewNameParameters, + type RenewNameReturnType, + renewName, +} from '../../actions/wallet/renewName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type RenewNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function renewNameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: RenewNameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: RenewNameParameters) => { + return renewName(config, variables) + }, + mutationKey: renewNameMutationKey(options), + } as const satisfies MutationOptions< + RenewNameReturnType, + RenewNameErrorType, + RenewNameParameters + > +} + +export function renewNameMutationKey( + options: RenewNameOptions, +) { + return ['ensjs_renewName', filterQueryOptions(options)] as const +} + +export type RenewNameMutationKey = ReturnType< + typeof renewNameMutationKey +> diff --git a/packages/query-core/src/query/wallet/setAbiRecord.ts b/packages/query-core/src/query/wallet/setAbiRecord.ts new file mode 100644 index 000000000..de1759081 --- /dev/null +++ b/packages/query-core/src/query/wallet/setAbiRecord.ts @@ -0,0 +1,53 @@ +import { ASSERT_NO_TYPE_ERROR } from '@ensdomains/ensjs/internal' +import type { AbiEncodeAs } from '@ensdomains/ensjs/utils' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import { + type SetAbiRecordErrorType, + type SetAbiRecordParameters, + type SetAbiRecordReturnType, + setAbiRecord, +} from '../../actions/wallet/setAbiRecord.js' +import { filterQueryOptions } from '../utils.js' + +export type SetAbiRecordOptions< + encodeAs extends AbiEncodeAs, + config extends Config, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setAbiRecordMutationOptions< + encodeAs extends AbiEncodeAs, + config extends Config, +>(config: config, options: SetAbiRecordOptions) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetAbiRecordParameters) => { + return setAbiRecord(config, variables) + }, + mutationKey: setAbiRecordMutationKey(options), + } as const satisfies MutationOptions< + SetAbiRecordReturnType, + SetAbiRecordErrorType, + SetAbiRecordParameters + > +} + +export function setAbiRecordMutationKey< + encodeAs extends AbiEncodeAs, + config extends Config, +>(options: SetAbiRecordOptions) { + return ['ensjs_setAbiRecord', filterQueryOptions(options)] as const +} + +export type SetAbiRecordMutationKey< + encodeAs extends AbiEncodeAs, + config extends Config, +> = ReturnType> diff --git a/packages/query-core/src/query/wallet/setAddressRecord.ts b/packages/query-core/src/query/wallet/setAddressRecord.ts new file mode 100644 index 000000000..283a0fb6c --- /dev/null +++ b/packages/query-core/src/query/wallet/setAddressRecord.ts @@ -0,0 +1,50 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import { + type SetAddressRecordErrorType, + type SetAddressRecordParameters, + type SetAddressRecordReturnType, + setAddressRecord, +} from '../../actions/wallet/setAddressRecord.js' +import { filterQueryOptions } from '../utils.js' + +export type SetAddressRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setAddressRecordMutationOptions( + config: config, + options: SetAddressRecordOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetAddressRecordParameters) => { + return setAddressRecord(config, variables) + }, + mutationKey: setAddressRecordMutationKey(options), + } as const satisfies MutationOptions< + SetAddressRecordReturnType, + SetAddressRecordErrorType, + SetAddressRecordParameters + > +} + +export function setAddressRecordMutationKey( + options: SetAddressRecordOptions, +) { + return ['ensjs_setAddressRecord', filterQueryOptions(options)] as const +} + +export type SetAddressRecordMutationKey = ReturnType< + typeof setAddressRecordMutationKey +> diff --git a/packages/query-core/src/query/wallet/setContentHashRecord.ts b/packages/query-core/src/query/wallet/setContentHashRecord.ts new file mode 100644 index 000000000..7a58d8354 --- /dev/null +++ b/packages/query-core/src/query/wallet/setContentHashRecord.ts @@ -0,0 +1,50 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import { + type SetContentHashRecordErrorType, + type SetContentHashRecordParameters, + type SetContentHashRecordReturnType, + setContentHashRecord, +} from '../../actions/wallet/setContentHashRecord.js' +import { filterQueryOptions } from '../utils.js' + +export type SetContentHashRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setContentHashRecordMutationOptions( + config: config, + options: SetContentHashRecordOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetContentHashRecordParameters) => { + return setContentHashRecord(config, variables) + }, + mutationKey: setContentHashRecordMutationKey(options), + } as const satisfies MutationOptions< + SetContentHashRecordReturnType, + SetContentHashRecordErrorType, + SetContentHashRecordParameters + > +} + +export function setContentHashRecordMutationKey( + options: SetContentHashRecordOptions, +) { + return ['ensjs_setContentHashRecord', filterQueryOptions(options)] as const +} + +export type SetContentHashRecordMutationKey = ReturnType< + typeof setContentHashRecordMutationKey +> diff --git a/packages/query-core/src/query/wallet/setPrimaryName.ts b/packages/query-core/src/query/wallet/setPrimaryName.ts new file mode 100644 index 000000000..7d5343bde --- /dev/null +++ b/packages/query-core/src/query/wallet/setPrimaryName.ts @@ -0,0 +1,57 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type SetPrimaryNameErrorType, + type SetPrimaryNameParameters, + type SetPrimaryNameReturnType, + setPrimaryName, +} from '../../actions/wallet/setPrimaryName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type SetPrimaryNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setPrimaryNameMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensPublicResolver' | 'ensReverseRegistrar' + >, + options: SetPrimaryNameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetPrimaryNameParameters) => { + return setPrimaryName(config, variables) + }, + mutationKey: setPrimaryNameMutationKey(options), + } as const satisfies MutationOptions< + SetPrimaryNameReturnType, + SetPrimaryNameErrorType, + SetPrimaryNameParameters + > +} + +export function setPrimaryNameMutationKey( + options: SetPrimaryNameOptions, +) { + return ['ensjs_setPrimaryName', filterQueryOptions(options)] as const +} + +export type SetPrimaryNameMutationKey = ReturnType< + typeof setPrimaryNameMutationKey +> diff --git a/packages/query-core/src/query/wallet/setRecords.ts b/packages/query-core/src/query/wallet/setRecords.ts new file mode 100644 index 000000000..e7742d0d2 --- /dev/null +++ b/packages/query-core/src/query/wallet/setRecords.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type SetRecordsErrorType, + type SetRecordsParameters, + type SetRecordsReturnType, + setRecords, +} from '../../actions/wallet/setRecords.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type SetRecordsOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setRecordsMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: SetRecordsOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetRecordsParameters) => { + return setRecords(config, variables) + }, + mutationKey: setRecordsMutationKey(options), + } as const satisfies MutationOptions< + SetRecordsReturnType, + SetRecordsErrorType, + SetRecordsParameters + > +} + +export function setRecordsMutationKey( + options: SetRecordsOptions, +) { + return ['ensjs_setRecords', filterQueryOptions(options)] as const +} + +export type SetRecordsMutationKey = ReturnType< + typeof setRecordsMutationKey +> diff --git a/packages/query-core/src/query/wallet/setResolver.ts b/packages/query-core/src/query/wallet/setResolver.ts new file mode 100644 index 000000000..9ea2d5949 --- /dev/null +++ b/packages/query-core/src/query/wallet/setResolver.ts @@ -0,0 +1,54 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem' +import { + type SetResolverErrorType, + type SetResolverParameters, + type SetResolverReturnType, + setResolver, +} from '../../actions/wallet/setResolver.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type SetResolverOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setResolverMutationOptions< + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts, + options: SetResolverOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetResolverParameters) => { + return setResolver(config, variables) + }, + mutationKey: setResolverMutationKey(options), + } as const satisfies MutationOptions< + SetResolverReturnType, + SetResolverErrorType, + SetResolverParameters + > +} + +export function setResolverMutationKey( + options: SetResolverOptions, +) { + return ['ensjs_setResolver', filterQueryOptions(options)] as const +} + +export type SetResolverMutationKey = ReturnType< + typeof setResolverMutationKey +> diff --git a/packages/query-core/src/query/wallet/setTextRecord.ts b/packages/query-core/src/query/wallet/setTextRecord.ts new file mode 100644 index 000000000..504fdabd1 --- /dev/null +++ b/packages/query-core/src/query/wallet/setTextRecord.ts @@ -0,0 +1,50 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import { + type SetTextRecordErrorType, + type SetTextRecordParameters, + type SetTextRecordReturnType, + setTextRecord, +} from '../../actions/wallet/setTextRecord.js' +import { filterQueryOptions } from '../utils.js' + +export type SetTextRecordOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function setTextRecordMutationOptions( + config: config, + options: SetTextRecordOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async (variables: SetTextRecordParameters) => { + return setTextRecord(config, variables) + }, + mutationKey: setTextRecordMutationKey(options), + } as const satisfies MutationOptions< + SetTextRecordReturnType, + SetTextRecordErrorType, + SetTextRecordParameters + > +} + +export function setTextRecordMutationKey( + options: SetTextRecordOptions, +) { + return ['ensjs_setTextRecord', filterQueryOptions(options)] as const +} + +export type SetTextRecordMutationKey = ReturnType< + typeof setTextRecordMutationKey +> diff --git a/packages/query-core/src/query/wallet/transferName.ts b/packages/query-core/src/query/wallet/transferName.ts new file mode 100644 index 000000000..bc3ed0464 --- /dev/null +++ b/packages/query-core/src/query/wallet/transferName.ts @@ -0,0 +1,66 @@ +import { + ASSERT_NO_TYPE_ERROR, + type ExcludeTE, +} from '@ensdomains/ensjs/internal' +import type { TransferNameSupportedContract } from '@ensdomains/ensjs/wallet' +import type { MutationOptions } from '@tanstack/query-core' +import type { Config } from '@wagmi/core' +import type { + Compute, + ExactPartial, + ScopeKeyParameter, +} from '@wagmi/core/internal' +import type { Chain } from 'viem/chains' +import { + type TransferNameErrorType, + type TransferNameParameters, + type TransferNameReturnType, + transferName, +} from '../../actions/wallet/transferName.js' +import type { RequireConfigContracts } from '../../utils/chain.js' +import { filterQueryOptions } from '../utils.js' + +export type TransferNameOptions< + contract extends TransferNameSupportedContract, + config extends Config, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function transferNameMutationOptions< + contract extends TransferNameSupportedContract, + chains extends readonly [Chain, ...Chain[]], +>( + config: RequireConfigContracts< + chains, + 'ensRegistry' | 'ensNameWrapper' | 'ensBaseRegistrarImplementation' + >, + options: TransferNameOptions>, +) { + ASSERT_NO_TYPE_ERROR(config) + + return { + mutationFn: async ( + variables: TransferNameParameters, + ) => { + return transferName(config, variables) + }, + mutationKey: transferNameMutationKey(options), + } as const satisfies MutationOptions< + TransferNameReturnType, + TransferNameErrorType, + TransferNameParameters + > +} + +export function transferNameMutationKey< + contract extends TransferNameSupportedContract, + config extends Config, +>(options: TransferNameOptions) { + return ['ensjs_transferName', filterQueryOptions(options)] as const +} + +export type TransferNameMutationKey< + contract extends TransferNameSupportedContract, + config extends Config, +> = ReturnType> diff --git a/packages/query-core/src/utils/chain.ts b/packages/query-core/src/utils/chain.ts new file mode 100644 index 000000000..26c2dcf1c --- /dev/null +++ b/packages/query-core/src/utils/chain.ts @@ -0,0 +1,50 @@ +import type { SupportedL1Contract } from '@ensdomains/ensjs/chain' +import type { + StringConcatenationOrder, + // biome-ignore lint/suspicious/noShadowRestrictedNames: import + TypeError, +} from '@ensdomains/ensjs/internal' +import type { Config, CreateConnectorFn, Transport } from '@wagmi/core' +import type { ChainContract } from 'viem' +import type { Chain } from 'viem/chains' + +export type BaseChainContracts = { + [key in keyof NonNullable as string extends key + ? never + : key]: NonNullable[key] +} + +type SuggestedContracts = + | SupportedL1Contract + | keyof BaseChainContracts + | (string & {}) + +export type RequireConfigContracts< + chains extends readonly [Chain, ...Chain[]], + contracts extends SuggestedContracts, + transports extends Record = Record< + chains[number]['id'], + Transport + >, + connectorFns extends + readonly CreateConnectorFn[] = readonly CreateConnectorFn[], +> = chains[number] extends Chain & { + contracts: { + [key in contracts]: ChainContract + } +} + ? Config + : TypeError<`One of the "${chains[number]['name']}" chains are missing a required contract: ${StringConcatenationOrder}`> + +export type RequireConfigContracts2< + config extends Config, + contracts extends SupportedL1Contract, +> = config extends Config + ? chains[number] extends Chain & { + contracts: { + [key in contracts]: ChainContract + } + } + ? Config + : TypeError<`One of the "${chains[number]['name']}" chains are missing a required contract: ${StringConcatenationOrder}`> + : TypeError<`Invalid config`> diff --git a/packages/query-core/src/utils/getAction.ts b/packages/query-core/src/utils/getAction.ts new file mode 100644 index 000000000..a016cfcba --- /dev/null +++ b/packages/query-core/src/utils/getAction.ts @@ -0,0 +1,56 @@ +// Source: https://github.com/wevm/wagmi/blob/main/packages/core/src/utils/getAction.ts + +import type { EnsPublicActions, EnsWalletActions } from '@ensdomains/ensjs' +import type { + ChainWithContracts, + SupportedL1Contract, +} from '@ensdomains/ensjs/chain' +import type { + Account, + Chain, + Client, + // PublicActions, + RpcSchema, + Transport, + // WalletActions, +} from 'viem' + +/** + * Retrieves and returns an action from the client (if exists), and falls + * back to the tree-shakable action. + * + * Useful for extracting overridden actions from a client (ie. if a consumer + * wants to override the `sendTransaction` implementation). + */ +export function getAction< + transport extends Transport, + chain extends Chain, + account extends Account | undefined, + rpcSchema extends RpcSchema | undefined, + extended extends { [key: string]: unknown }, + client extends Client, + parameters, + returnType, +>( + client: client, + actionFn: (_: any, parameters: parameters) => returnType, + // Some minifiers drop `Function.prototype.name`, or replace it with short letters, + // meaning that `actionFn.name` will not always work. For that case, the consumer + // needs to pass the name explicitly. + name: + | keyof EnsPublicActions + | keyof EnsWalletActions< + ChainWithContracts, + NonNullable + >, +): (parameters: parameters) => returnType { + const action_implicit = client[actionFn.name] + if (typeof action_implicit === 'function') + return action_implicit as (params: parameters) => returnType + + const action_explicit = client[name] + if (typeof action_explicit === 'function') + return action_explicit as (params: parameters) => returnType + + return (params) => actionFn(client, params) +} diff --git a/packages/query-core/tsconfig.build.json b/packages/query-core/tsconfig.build.json new file mode 100644 index 000000000..fbed2b103 --- /dev/null +++ b/packages/query-core/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.test-d.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/query-core/tsconfig.json b/packages/query-core/tsconfig.json new file mode 100644 index 000000000..f12b96cf0 --- /dev/null +++ b/packages/query-core/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": [] +} diff --git a/packages/react/README.md b/packages/react/README.md index 40aa413d8..e547750e6 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -7,7 +7,7 @@ React hooks & utilities to interact with the Ethereum Name Service using ENSjs After installing [wagmi](https://wagmi.sh), simply run ```sh -pnpm install @ensdomains/ensjs-react +pnpm install @ensdomains/ensjs-react @ensdomains/ensjs ``` ## Hooks diff --git a/packages/react/package.json b/packages/react/package.json index da008b777..63fce9177 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -16,28 +16,33 @@ "!src/**/*.test.ts", "!src/test" ], - "repository": "git@github.com:ensdomains/ensjs.git", - "author": "Lucemans ", + "repository": { + "type": "git", + "url": "git+https://github.com/ensdomains/ensjs.git", + "directory": "packages/react" + }, + "contributors": [ + "TateB ", + "Lucemans " + ], "license": "MIT", "scripts": { "clean": "rm -rf ./dist", "build": "tsc --project tsconfig.build.json" }, - "dependencies": { - "@ensdomains/ensjs": "workspace:*" - }, "devDependencies": { "@ensdomains/buffer": "^0.1.1", "@ensdomains/ens-test-env": "^1.0.1", - "ts-node": "^10.9.2", - "tslib": "^2.7.0", + "@ensdomains/ensjs": "workspace:*", + "@wagmi/core": "^3", "viem": "2.29.0", - "wagmi": "^2" + "wagmi": "^3" }, "peerDependencies": { + "@ensdomains/ensjs": "^4.0.1", "@tanstack/react-query": "^5.54", "viem": "^2.9.2", - "wagmi": "^2" + "wagmi": "^3" }, "engines": { "node": ">=22" diff --git a/packages/react/src/client.ts b/packages/react/src/client.ts deleted file mode 100644 index 13e3690c8..000000000 --- a/packages/react/src/client.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ClientWithEns } from '@ensdomains/ensjs/contracts' -import type { QueryClient } from '@tanstack/react-query' -import type { UseQueryParameters } from './hooks/useQuery.js' - -export type ParamWithClients = T & { - client: ClientWithEns -} - -export type QueryConfig = { - queryClient?: QueryClient -} & UseQueryParameters diff --git a/packages/react/src/hooks.ts b/packages/react/src/hooks.ts deleted file mode 100644 index c09474f25..000000000 --- a/packages/react/src/hooks.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { useEnsResolverInterfaces } from './hooks/useEnsResolverInterfaces.js' -export { useNamesForAddress } from './hooks/useNamesForAddress.js' -export { useEnsRecordsWrite } from './hooks/useEnsRecordsWrite.js' -export { useEnsCredentials } from './hooks/useEnsCredentials.js' -export { useEnsAvailable } from './hooks/useEnsAvailable.js' -export { useDecodedName } from './hooks/useDecodedName.js' -export { useEnsExpiry } from './hooks/useEnsExpiry.js' diff --git a/packages/react/src/hooks/useDecodedName.ts b/packages/react/src/hooks/useDecodedName.ts index d71131ef6..0696f9cb2 100644 --- a/packages/react/src/hooks/useDecodedName.ts +++ b/packages/react/src/hooks/useDecodedName.ts @@ -1,14 +1,23 @@ -import { - type GetDecodedNameParameters, - type GetDecodedNameReturnType, - getDecodedName, -} from '@ensdomains/ensjs/subgraph' -import type { ParamWithClients, QueryConfig } from '../client.js' +import type { GetDecodedNameReturnType } from '@ensdomains/ensjs/subgraph' +import { useChainId, useConfig } from 'wagmi' import { type UseQueryReturnType, useQuery } from './useQuery.js' -export type UseDecodedNameParams = ParamWithClients +export type UseDecodedNameParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetDecodedNameData, +> = Compute< + GetDecodedNameOptions & + ConfigParameter & + QueryParameter< + GetDecodedNameQueryFnData, + GetDecodedNameErrorType, + selectData, + GetDecodedNameQueryKey + > +> -export type UseDecodedNameReturnType = GetDecodedNameReturnType +export type UseDecodedNameReturnType = + UseQueryReturnType /** * Decode names returned using the subgraph @@ -18,21 +27,22 @@ export type UseDecodedNameReturnType = GetDecodedNameReturnType * @param params - {@link UseDecodedNameParams} * @returns - {@link GetDecodedNameReturnType} */ -export const useDecodedName = ( - params: UseDecodedNameParams, - query?: QueryConfig, -): UseQueryReturnType => { - const { client } = params +export const useDecodedName = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetDecodedNameData, +>( + parameters: UseDecodedNameParameters = {}, +): UseDecodedNameReturnType => { + const { name, query = {} } = parameters - return useQuery( - ['ensjs', 'decoded-subgraph-name', params.name], - { - queryFn: async () => { - const result = await getDecodedName(client, params) + const config = useConfig() + const chainId = useChainId({ config }) - return result - }, - }, - query, - ) + const options = getDecodedNameQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) } diff --git a/packages/react/src/hooks/useEnsAbi.ts b/packages/react/src/hooks/useEnsAbi.ts new file mode 100644 index 000000000..7b5390ce5 --- /dev/null +++ b/packages/react/src/hooks/useEnsAbi.ts @@ -0,0 +1,57 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsAbiData, + type GetEnsAbiErrorType, + type GetEnsAbiOptions, + type GetEnsAbiQueryFnData, + type GetEnsAbiQueryKey, + getEnsAbiQueryOptions, +} from '../query/getEnsAbi.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsAbiParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsAbiData, +> = Compute< + GetEnsAbiOptions & + ConfigParameter & + QueryParameter< + GetEnsAbiQueryFnData, + GetEnsAbiErrorType, + selectData, + GetEnsAbiQueryKey + > +> + +export type UseEnsAbiReturnType = + UseQueryReturnType + +/** + * Returns the ABI for a name + * + * @param parameters - {@link UseEnsAbiParameters} + * @returns - {@link UseEnsAbiReturnType} + */ +export const useEnsAbi = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsAbiData, +>( + parameters: UseEnsAbiParameters = {}, +): UseEnsAbiReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsAbiQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsAddress.ts b/packages/react/src/hooks/useEnsAddress.ts new file mode 100644 index 000000000..c83c4be4d --- /dev/null +++ b/packages/react/src/hooks/useEnsAddress.ts @@ -0,0 +1,57 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsAddressData, + type GetEnsAddressErrorType, + type GetEnsAddressOptions, + type GetEnsAddressQueryFnData, + type GetEnsAddressQueryKey, + getEnsAddressQueryOptions, +} from '../query/getEnsAddress.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsAddressParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsAddressData, +> = Compute< + GetEnsAddressOptions & + ConfigParameter & + QueryParameter< + GetEnsAddressQueryFnData, + GetEnsAddressErrorType, + selectData, + GetEnsAddressQueryKey + > +> + +export type UseEnsAddressReturnType = + UseQueryReturnType + +/** + * Returns the address for a name + * + * @param parameters - {@link UseEnsAddressParameters} + * @returns - {@link UseEnsAddressReturnType} + */ +export const useEnsAddress = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsAddressData, +>( + parameters: UseEnsAddressParameters = {}, +): UseEnsAddressReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsAddressQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsAvailable.ts b/packages/react/src/hooks/useEnsAvailable.ts index 4856899bf..b9ca3f54d 100644 --- a/packages/react/src/hooks/useEnsAvailable.ts +++ b/packages/react/src/hooks/useEnsAvailable.ts @@ -1,39 +1,45 @@ -import { - type GetAvailableParameters, - type GetAvailableReturnType, - getAvailable, -} from '@ensdomains/ensjs/public' -import type { ParamWithClients, QueryConfig } from '../client.js' +import { useChainId, useConfig } from 'wagmi' import { type UseQueryReturnType, useQuery } from './useQuery.js' -export type UseEnsAvailableParams = ParamWithClients +export type UseEnsAvailableParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsAvailableData, +> = Compute< + GetEnsAvailableOptions & + ConfigParameter & + QueryParameter< + GetEnsAvailableQueryFnData, + GetEnsAvailableErrorType, + selectData, + GetEnsAvailableQueryKey + > +> -export type UseEnsAvailableReturnType = GetAvailableReturnType +export type UseEnsAvailableReturnType = + UseQueryReturnType /** - * Returns a list of names for an address + * Check if a .eth name is available * - * Keep in mind that this function is limited to .eth names - * - * @param params - {@link UseEnsAvailableParams} + * @param parameters - {@link UseEnsAvailableParameters} * @returns - {@link UseEnsAvailableReturnType} */ -export const useEnsAvailable = ( - params: UseEnsAvailableParams, - query?: QueryConfig, -): UseQueryReturnType => { - const { client } = params +export const useEnsAvailable = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsAvailableData, +>( + parameters: UseEnsAvailableParameters = {}, +): UseEnsAvailableReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) - return useQuery( - ['ensjs', 'eth-name-available', params.name], - { - queryKey: [], - queryFn: async () => { - const result = await getAvailable(client, params) + const options = getEnsAvailableQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) - return result - }, - }, - query, - ) + return useQuery({ ...query, ...options, enabled }) } diff --git a/packages/react/src/hooks/useEnsContentHash.ts b/packages/react/src/hooks/useEnsContentHash.ts new file mode 100644 index 000000000..6c7a65492 --- /dev/null +++ b/packages/react/src/hooks/useEnsContentHash.ts @@ -0,0 +1,57 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsContentHashData, + type GetEnsContentHashErrorType, + type GetEnsContentHashOptions, + type GetEnsContentHashQueryFnData, + type GetEnsContentHashQueryKey, + getEnsContentHashQueryOptions, +} from '../query/getEnsContentHash.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsContentHashParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsContentHashData, +> = Compute< + GetEnsContentHashOptions & + ConfigParameter & + QueryParameter< + GetEnsContentHashQueryFnData, + GetEnsContentHashErrorType, + selectData, + GetEnsContentHashQueryKey + > +> + +export type UseEnsContentHashReturnType = + UseQueryReturnType + +/** + * Returns the content hash for a name + * + * @param parameters - {@link UseEnsContentHashParameters} + * @returns - {@link UseEnsContentHashReturnType} + */ +export const useEnsContentHash = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsContentHashData, +>( + parameters: UseEnsContentHashParameters = {}, +): UseEnsContentHashReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsContentHashQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsCredentials.ts b/packages/react/src/hooks/useEnsCredentials.ts index 4f59b02b2..eaeafa66f 100644 --- a/packages/react/src/hooks/useEnsCredentials.ts +++ b/packages/react/src/hooks/useEnsCredentials.ts @@ -1,49 +1,46 @@ -import { getTextRecord } from '@ensdomains/ensjs/public' -import type { ParamWithClients, QueryConfig } from '../client.js' import { type UseQueryReturnType, useQuery } from './useQuery.js' -export type UseEnsCredentialsParams = ParamWithClients<{ name: string }> - -export type ExternalCredential = { - url: string -} - -export type UseEnsCredentialsReturnType = ExternalCredential[] +export type UseEnsCredentialsParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsCredentialsData, +> = Compute< + GetEnsCredentialsOptions & + ConfigParameter & + QueryParameter< + GetEnsCredentialsQueryFnData, + GetEnsCredentialsErrorType, + selectData, + GetEnsCredentialsQueryKey + > +> + +export type UseEnsCredentialsReturnType = + UseQueryReturnType /** * Returns credentials from a name * - * @param params - {@link UseEnsCredentialsParams} + * @param parameters - {@link UseEnsCredentialsParameters} * @returns - {@link UseEnsCredentialsReturnType} * * @beta */ -export const useEnsCredentials = ( - params: UseEnsCredentialsParams, - query?: QueryConfig, -): UseQueryReturnType => { - const { name, client } = params - - return useQuery( - ['ensjs', 'credentials', params.name], - { - queryFn: async () => { - const result = await getTextRecord(client, { - name, - key: 'verifications', - }) - - if (!result) return [] - - const credentials = (JSON.parse(result) as string[]) - .filter((url) => new URL(url)) - .map((url) => ({ - url, - })) - - return credentials - }, - }, - query, - ) +export const useEnsCredentials = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsCredentialsData, +>( + parameters: UseEnsCredentialsParameters = {}, +): UseEnsCredentialsReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsCredentialsQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) } diff --git a/packages/react/src/hooks/useEnsExpiry.ts b/packages/react/src/hooks/useEnsExpiry.ts index 95173b9c6..674ecd98b 100644 --- a/packages/react/src/hooks/useEnsExpiry.ts +++ b/packages/react/src/hooks/useEnsExpiry.ts @@ -1,34 +1,47 @@ -import { - type GetExpiryParameters, - type GetExpiryReturnType, - getExpiry, -} from '@ensdomains/ensjs/public' -import type { ParamWithClients, QueryConfig } from '../client.js' +import { useChainId, useConfig } from 'wagmi' import { type UseQueryReturnType, useQuery } from './useQuery.js' -export type UseEnsExpiryParams = ParamWithClients +export type UseEnsExpiryParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsExpiryData, +> = Compute< + GetEnsExpiryOptions & + ConfigParameter & + QueryParameter< + GetEnsExpiryQueryFnData, + GetEnsExpiryErrorType, + selectData, + GetEnsExpiryQueryKey + > +> -export type UseEnsExpiryReturnType = GetExpiryReturnType +export type UseEnsExpiryReturnType = + UseQueryReturnType /** * Returns expiry of a name * * Keep in mind that this function is limited to second-level .eth names (luc.eth, nick.eth, etc) * - * @param params - {@link UseEnsExpiryParams} + * @param parameters - {@link UseEnsExpiryParameters} * @returns - {@link UseEnsExpiryReturnType} */ -export const useEnsExpiry = ( - params: UseEnsExpiryParams, - query?: QueryConfig, -): UseQueryReturnType => { - const { client } = params +export const useEnsExpiry = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsExpiryData, +>( + parameters: UseEnsExpiryParameters = {}, +): UseEnsExpiryReturnType => { + const { name, query = {} } = parameters - return useQuery( - ['ensjs', 'ens-expiry', params.name], - { - queryFn: async () => getExpiry(client, params), - }, - query, - ) + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsExpiryQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) } diff --git a/packages/react/src/hooks/useEnsNamesForAddress.ts b/packages/react/src/hooks/useEnsNamesForAddress.ts new file mode 100644 index 000000000..802100496 --- /dev/null +++ b/packages/react/src/hooks/useEnsNamesForAddress.ts @@ -0,0 +1,61 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsNamesForAddressData, + type GetEnsNamesForAddressErrorType, + type GetEnsNamesForAddressOptions, + type GetEnsNamesForAddressQueryFnData, + type GetEnsNamesForAddressQueryKey, + getEnsNamesForAddressQueryOptions, +} from '../query/getEnsNamesForAddress.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsNamesForAddressParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsNamesForAddressData, +> = Compute< + GetEnsNamesForAddressOptions & + ConfigParameter & + QueryParameter< + GetEnsNamesForAddressQueryFnData, + GetEnsNamesForAddressErrorType, + selectData, + GetEnsNamesForAddressQueryKey + > +> + +export type UseEnsNamesForAddressReturnType< + selectData = GetEnsNamesForAddressData, +> = UseQueryReturnType + +/** + * Returns a list of names for an address + * + * Keep in mind that this list will be loaded from the subgraph, and only include watchable names. + * Read more about enumeration and watchability here: https://docs.ens.domains/web/enumerate + * + * @param parameters - {@link UseEnsNamesForAddressParameters} + * @returns - {@link UseEnsNamesForAddressReturnType} + */ +export const useEnsNamesForAddress = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsNamesForAddressData, +>( + parameters: UseEnsNamesForAddressParameters = {}, +): UseEnsNamesForAddressReturnType => { + const { address, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsNamesForAddressQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsOwner.ts b/packages/react/src/hooks/useEnsOwner.ts new file mode 100644 index 000000000..89ee40160 --- /dev/null +++ b/packages/react/src/hooks/useEnsOwner.ts @@ -0,0 +1,57 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsOwnerData, + type GetEnsOwnerErrorType, + type GetEnsOwnerOptions, + type GetEnsOwnerQueryFnData, + type GetEnsOwnerQueryKey, + getEnsOwnerQueryOptions, +} from '../query/getEnsOwner.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsOwnerParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsOwnerData, +> = Compute< + GetEnsOwnerOptions & + ConfigParameter & + QueryParameter< + GetEnsOwnerQueryFnData, + GetEnsOwnerErrorType, + selectData, + GetEnsOwnerQueryKey + > +> + +export type UseEnsOwnerReturnType = + UseQueryReturnType + +/** + * Returns the owner of a name + * + * @param parameters - {@link UseEnsOwnerParameters} + * @returns - {@link UseEnsOwnerReturnType} + */ +export const useEnsOwner = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsOwnerData, +>( + parameters: UseEnsOwnerParameters = {}, +): UseEnsOwnerReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsOwnerQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsPrice.ts b/packages/react/src/hooks/useEnsPrice.ts new file mode 100644 index 000000000..0f6ef7029 --- /dev/null +++ b/packages/react/src/hooks/useEnsPrice.ts @@ -0,0 +1,57 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsPriceData, + type GetEnsPriceErrorType, + type GetEnsPriceOptions, + type GetEnsPriceQueryFnData, + type GetEnsPriceQueryKey, + getEnsPriceQueryOptions, +} from '../query/getEnsPrice.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsPriceParameters< + config extends ConfigWithEns = ConfigWithEns, + selectData = GetEnsPriceData, +> = Compute< + GetEnsPriceOptions & + ConfigParameter & + QueryParameter< + GetEnsPriceQueryFnData, + GetEnsPriceErrorType, + selectData, + GetEnsPriceQueryKey + > +> + +export type UseEnsPriceReturnType = + UseQueryReturnType + +/** + * Returns the price for registering or renewing a name + * + * @param parameters - {@link UseEnsPriceParameters} + * @returns - {@link UseEnsPriceReturnType} + */ +export const useEnsPrice = < + config extends ConfigWithEns = ResolvedRegister['config'], + selectData = GetEnsPriceData, +>( + parameters: UseEnsPriceParameters = {}, +): UseEnsPriceReturnType => { + const { nameOrNames, duration, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsPriceQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(nameOrNames && duration && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsRecords.ts b/packages/react/src/hooks/useEnsRecords.ts new file mode 100644 index 000000000..7f844ee41 --- /dev/null +++ b/packages/react/src/hooks/useEnsRecords.ts @@ -0,0 +1,77 @@ +import { useChainId, useConfig } from 'wagmi' +import { type UseQueryReturnType, useQuery } from 'wagmi/query' +import { + type GetEnsRecordsData, + type GetEnsRecordsErrorType, + type GetEnsRecordsOptions, + type GetEnsRecordsQueryFnData, + type GetEnsRecordsQueryKey, + getEnsRecordsQueryOptions, +} from '../query/getEnsRecords.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseEnsRecordsParameters< + config extends ConfigWithEns = ConfigWithEns, + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, + selectData = GetEnsRecordsData, +> = Compute< + GetEnsRecordsOptions & + ConfigParameter & + QueryParameter< + GetEnsRecordsQueryFnData, + GetEnsRecordsErrorType, + selectData, + GetEnsRecordsQueryKey + > +> + +export type UseEnsRecordsReturnType< + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, + selectData = GetEnsRecordsData, +> = UseQueryReturnType + +/** + * Returns arbitrary records for a name + * + * @param parameters - {@link UseEnsRecordsParameters} + * @returns - {@link UseEnsRecordsReturnType} + */ +export const useEnsRecords = < + config extends ConfigWithEns = ResolvedRegister['config'], + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, + selectData = GetEnsRecordsData, +>( + parameters: UseEnsRecordsParameters< + config, + texts, + coins, + contentHash, + abi, + selectData + > = {}, +): UseEnsRecordsReturnType => { + const { name, query = {} } = parameters + + const config = useConfig() + const chainId = useChainId({ config }) + + const options = getEnsRecordsQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsRecordsWrite.ts b/packages/react/src/hooks/useEnsRecordsWrite.ts deleted file mode 100644 index fa9439667..000000000 --- a/packages/react/src/hooks/useEnsRecordsWrite.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useAccount } from 'wagmi' -import type { ParamWithClients } from '../client.js' - -export type UseEnsRecordsWriteParams = ParamWithClients<{}> - -/** - * Allows you to write records to a name, provided the name supports it and the signed in user has the required permissions - * - * You can use the {@link resolverInterfaces} shorthand, or manually specify a Hex value - * - * @param params - {@link UseEnsResolverInterfacesParams} - * @returns - {@link boolean[]} - * - * @alpha - */ -export const useEnsRecordsWrite = ( - _params: UseEnsRecordsWriteParams, - config?: any, -) => { - const { address } = useAccount({ config }) - // const client = useWalletClient() - - console.log('Hello ', address) - - return { data: undefined } - // return setRecords(client as any, params) -} diff --git a/packages/react/src/hooks/useEnsResolverInterfaces.ts b/packages/react/src/hooks/useEnsResolverInterfaces.ts index 2376d6793..dd0acda35 100644 --- a/packages/react/src/hooks/useEnsResolverInterfaces.ts +++ b/packages/react/src/hooks/useEnsResolverInterfaces.ts @@ -1,44 +1,56 @@ -import { - type GetSupportedInterfacesParameters, - type GetSupportedInterfacesReturnType, - getSupportedInterfaces, -} from '@ensdomains/ensjs/public' import type { Hex } from 'viem' -import type { ParamWithClients, QueryConfig } from '../client.js' import { type UseQueryReturnType, useQuery } from './useQuery.js' -export type UseEnsResolverInterfacesParams< - Interfaces extends readonly Hex[] = [Hex, Hex], -> = ParamWithClients> +export type UseEnsResolverInterfacesParameters< + config extends ConfigWithEns = ConfigWithEns, + interfaces extends readonly Hex[] = readonly Hex[], + selectData = GetEnsResolverInterfacesData, +> = Compute< + GetEnsResolverInterfacesOptions & + ConfigParameter & + QueryParameter< + GetEnsResolverInterfacesQueryFnData, + GetEnsResolverInterfacesErrorType, + selectData, + GetEnsResolverInterfacesQueryKey + > +> export type UseEnsResolverInterfacesReturnType< - Interfaces extends readonly Hex[], -> = GetSupportedInterfacesReturnType + interfaces extends readonly Hex[], + selectData = GetEnsResolverInterfacesData, +> = UseQueryReturnType /** - * Returns a wether or not the interfaces are supported by the resolver + * Returns whether or not the interfaces are supported by the resolver * You can find a list of interfaces at https://docs.ens.domains/resolvers/interfaces * * You can use the {@link resolverInterfaces} shorthand, or manually specify a Hex value * - * @param params - {@link UseEnsResolverInterfacesParams} - * @returns - {@link boolean[]} + * @param parameters - {@link UseEnsResolverInterfacesParameters} + * @returns - {@link UseEnsResolverInterfacesReturnType} */ -export const useEnsResolverInterfaces = ( - params: UseEnsResolverInterfacesParams, - query?: QueryConfig, -): UseQueryReturnType> => { - const { client } = params +export const useEnsResolverInterfaces = < + config extends ConfigWithEns = ResolvedRegister['config'], + const interfaces extends readonly Hex[] = readonly Hex[], + selectData = GetEnsResolverInterfacesData, +>( + parameters: UseEnsResolverInterfacesParameters< + config, + interfaces, + selectData + > = {}, +): UseEnsResolverInterfacesReturnType => { + const { address, interfaces, query = {} } = parameters - return useQuery( - ['ensjs', 'resolver-interfaces', params.address], - { - queryFn: async () => { - const result = await getSupportedInterfaces(client, params) + const config = useConfig() + const chainId = useChainId({ config }) - return result - }, - }, - query, - ) + const options = getEnsResolverInterfacesQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && interfaces && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) } diff --git a/packages/react/src/hooks/useWriteEnsRecords.ts b/packages/react/src/hooks/useWriteEnsRecords.ts new file mode 100644 index 000000000..4023788bd --- /dev/null +++ b/packages/react/src/hooks/useWriteEnsRecords.ts @@ -0,0 +1,73 @@ +import { useConfig } from 'wagmi' +import { + type UseMutationParameters, + type UseMutationReturnType, + useMutation, +} from 'wagmi/query' +import { + type WriteEnsRecordsData, + type WriteEnsRecordsErrorType, + type WriteEnsRecordsMutate, + type WriteEnsRecordsMutateAsync, + type WriteEnsRecordsVariables, + writeEnsRecordsMutationOptions, +} from '../query/writeEnsRecords.js' +import type { ConfigWithEns } from '../types/config.js' +import type { ConfigParameter } from '../types/properties.js' +import type { ResolvedRegister } from '../types/register.js' +import type { Compute } from '../types/utils.js' + +export type UseWriteEnsRecordsParameters< + config extends ConfigWithEns = ConfigWithEns, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + WriteEnsRecordsData, + WriteEnsRecordsErrorType, + WriteEnsRecordsVariables, + context + > + | undefined + } +> + +export type UseWriteEnsRecordsReturnType< + config extends ConfigWithEns = ConfigWithEns, + context = unknown, +> = Compute< + UseMutationReturnType< + WriteEnsRecordsData, + WriteEnsRecordsErrorType, + WriteEnsRecordsVariables, + context + > & { + writeEnsRecords: WriteEnsRecordsMutate + writeEnsRecordsAsync: WriteEnsRecordsMutateAsync + } +> + +export const useWriteEnsRecords = < + config extends ConfigWithEns = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseWriteEnsRecordsParameters = {}, +): UseWriteEnsRecordsReturnType => { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = writeEnsRecordsMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseWriteEnsRecordsReturnType + return { + ...result, + writeEnsRecords: mutate as Return['writeEnsRecords'], + writeEnsRecordsAsync: mutateAsync as Return['writeEnsRecordsAsync'], + } +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 26593bd8d..8ce1b6f6d 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,3 +1,66 @@ -export * from './hooks.js' +export { + type UseDecodedNameParameters, + type UseDecodedNameReturnType, + useDecodedName, +} from './hooks/useDecodedName.js' +export { + type UseEnsAbiParameters, + type UseEnsAbiReturnType, + useEnsAbi, +} from './hooks/useEnsAbi.js' +export { + type UseEnsAddressParameters, + type UseEnsAddressReturnType, + useEnsAddress, +} from './hooks/useEnsAddress.js' +export { + type UseEnsAvailableParameters, + type UseEnsAvailableReturnType, + useEnsAvailable, +} from './hooks/useEnsAvailable.js' +export { + type UseEnsContentHashParameters, + type UseEnsContentHashReturnType, + useEnsContentHash, +} from './hooks/useEnsContentHash.js' +export { + type UseEnsCredentialsParameters, + type UseEnsCredentialsReturnType, + useEnsCredentials, +} from './hooks/useEnsCredentials.js' +export { + type UseEnsExpiryParameters, + type UseEnsExpiryReturnType, + useEnsExpiry, +} from './hooks/useEnsExpiry.js' +export { + type UseEnsNamesForAddressParameters, + type UseEnsNamesForAddressReturnType, + useEnsNamesForAddress, +} from './hooks/useEnsNamesForAddress.js' +export { + type UseEnsOwnerParameters, + type UseEnsOwnerReturnType, + useEnsOwner, +} from './hooks/useEnsOwner.js' +export { + type UseEnsPriceParameters, + type UseEnsPriceReturnType, + useEnsPrice, +} from './hooks/useEnsPrice.js' +export { + type UseEnsRecordsParameters, + type UseEnsRecordsReturnType, + useEnsRecords, +} from './hooks/useEnsRecords.js' +export { + type UseEnsResolverInterfacesParameters, + type UseEnsResolverInterfacesReturnType, + useEnsResolverInterfaces, +} from './hooks/useEnsResolverInterfaces.js' +export { + type UseWriteEnsRecordsParameters, + type UseWriteEnsRecordsReturnType, + useWriteEnsRecords, +} from './hooks/useWriteEnsRecords.js' export { resolverInterfaces } from './interfaces.js' -export type { ParamWithClients } from './client.js' diff --git a/packages/react/src/query.ts b/packages/react/src/query.ts index abd7d6ba2..76f4417e0 100644 --- a/packages/react/src/query.ts +++ b/packages/react/src/query.ts @@ -1,4 +1,132 @@ -import { QueryClient } from '@tanstack/react-query' - -// TODO: figure out why not taking from provider -export const fallbackQueryClient = new QueryClient() +export { + type GetDecodedNameData, + type GetDecodedNameErrorType, + type GetDecodedNameOptions, + type GetDecodedNameParameters, + type GetDecodedNameQueryFnData, + type GetDecodedNameQueryKey, + type GetDecodedNameReturnType, + getDecodedNameQueryKey, + getDecodedNameQueryOptions, +} from './query/getDecodedName.js' +export { + type GetEnsAbiData, + type GetEnsAbiErrorType, + type GetEnsAbiOptions, + type GetEnsAbiParameters, + type GetEnsAbiQueryFnData, + type GetEnsAbiQueryKey, + type GetEnsAbiReturnType, + getEnsAbiQueryOptions, +} from './query/getEnsAbi.js' +export { + type GetEnsAddressData, + type GetEnsAddressErrorType, + type GetEnsAddressOptions, + type GetEnsAddressParameters, + type GetEnsAddressQueryFnData, + type GetEnsAddressQueryKey, + type GetEnsAddressReturnType, + getEnsAddressQueryOptions, +} from './query/getEnsAddress.js' +export { + type GetEnsAvailableData, + type GetEnsAvailableErrorType, + type GetEnsAvailableOptions, + type GetEnsAvailableParameters, + type GetEnsAvailableQueryFnData, + type GetEnsAvailableQueryKey, + type GetEnsAvailableReturnType, + getEnsAvailableQueryOptions, +} from './query/getEnsAvailable.js' +export { + type GetEnsContentHashData, + type GetEnsContentHashErrorType, + type GetEnsContentHashOptions, + type GetEnsContentHashParameters, + type GetEnsContentHashQueryFnData, + type GetEnsContentHashQueryKey, + type GetEnsContentHashReturnType, + getEnsContentHashQueryOptions, +} from './query/getEnsContentHash.js' +export { + type GetEnsCredentialsData, + type GetEnsCredentialsErrorType, + type GetEnsCredentialsOptions, + type GetEnsCredentialsParameters, + type GetEnsCredentialsQueryFnData, + type GetEnsCredentialsQueryKey, + type GetEnsCredentialsReturnType, + getEnsCredentialsQueryOptions, +} from './query/getEnsCredentials.js' +export { + type GetEnsExpiryData, + type GetEnsExpiryErrorType, + type GetEnsExpiryOptions, + type GetEnsExpiryParameters, + type GetEnsExpiryQueryFnData, + type GetEnsExpiryQueryKey, + type GetEnsExpiryReturnType, + getEnsExpiryQueryOptions, +} from './query/getEnsExpiry.js' +export { + type GetEnsNamesForAddressData, + type GetEnsNamesForAddressErrorType, + type GetEnsNamesForAddressOptions, + type GetEnsNamesForAddressParameters, + type GetEnsNamesForAddressQueryFnData, + type GetEnsNamesForAddressQueryKey, + type GetEnsNamesForAddressReturnType, + getEnsNamesForAddressQueryOptions, +} from './query/getEnsNamesForAddress.js' +export { + type GetEnsOwnerData, + type GetEnsOwnerErrorType, + type GetEnsOwnerOptions, + type GetEnsOwnerParameters, + type GetEnsOwnerQueryFnData, + type GetEnsOwnerQueryKey, + type GetEnsOwnerReturnType, + getEnsOwnerQueryOptions, +} from './query/getEnsOwner.js' +export { + type GetEnsPriceData, + type GetEnsPriceErrorType, + type GetEnsPriceOptions, + type GetEnsPriceParameters, + type GetEnsPriceQueryFnData, + type GetEnsPriceQueryKey, + type GetEnsPriceReturnType, + getEnsPriceQueryOptions, +} from './query/getEnsPrice.js' +export { + type GetEnsRecordsData, + type GetEnsRecordsErrorType, + type GetEnsRecordsOptions, + type GetEnsRecordsParameters, + type GetEnsRecordsQueryFnData, + type GetEnsRecordsQueryKey, + type GetEnsRecordsReturnType, + getEnsRecordsQueryOptions, +} from './query/getEnsRecords.js' +export { + type GetEnsResolverInterfacesData, + type GetEnsResolverInterfacesErrorType, + type GetEnsResolverInterfacesOptions, + type GetEnsResolverInterfacesParameters, + type GetEnsResolverInterfacesQueryFnData, + type GetEnsResolverInterfacesQueryKey, + type GetEnsResolverInterfacesReturnType, + getEnsResolverInterfacesQueryOptions, +} from './query/getEnsResolverInterfaces.js' +export { + type WriteEnsRecordsData, + type WriteEnsRecordsErrorType, + type WriteEnsRecordsMutate, + type WriteEnsRecordsMutateAsync, + type WriteEnsRecordsParameters, + type WriteEnsRecordsReturnType, + type WriteEnsRecordsVariables, + writeEnsRecords, + writeEnsRecordsMutationOptions, +} from './query/writeEnsRecords.js' diff --git a/packages/react/src/query/getDecodedName.ts b/packages/react/src/query/getDecodedName.ts new file mode 100644 index 000000000..45b145329 --- /dev/null +++ b/packages/react/src/query/getDecodedName.ts @@ -0,0 +1,64 @@ +import { + type GetDecodedNameErrorType as ensjs_GetDecodedNameErrorType, + type GetDecodedNameParameters as ensjs_GetDecodedNameParameters, + type GetDecodedNameReturnType as ensjs_GetDecodedNameReturnType, + getDecodedName, +} from '@ensdomains/ensjs/subgraph' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetDecodedNameParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetDecodedNameReturnType = ensjs_GetDecodedNameReturnType + +export type GetDecodedNameErrorType = ensjs_GetDecodedNameErrorType + +export type GetDecodedNameOptions< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> & ScopeKeyParameter> + +export type GetDecodedNameQueryFnData = GetDecodedNameReturnType + +export type GetDecodedNameData = GetDecodedNameReturnType + +export const getDecodedNameQueryKey = ( + options: GetDecodedNameOptions, +) => { + return ['getDecodedName', filterQueryOptions(options)] as const +} + +export type GetDecodedNameQueryKey = ReturnType< + typeof getDecodedNameQueryKey +> + +export const getDecodedNameQueryOptions = ( + config: config, + options: GetDecodedNameOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getDecodedName(client, parameters) + }, + queryKey: getDecodedNameQueryKey(options), + } as const satisfies QueryOptions< + GetDecodedNameQueryFnData, + GetDecodedNameErrorType, + GetDecodedNameData, + GetDecodedNameQueryKey + > +} diff --git a/packages/react/src/query/getEnsAbi.ts b/packages/react/src/query/getEnsAbi.ts new file mode 100644 index 000000000..a530f0e84 --- /dev/null +++ b/packages/react/src/query/getEnsAbi.ts @@ -0,0 +1,62 @@ +import { + type GetAbiRecordErrorType as ensjs_GetEnsAbiErrorType, + type GetAbiRecordParameters as ensjs_GetEnsAbiParameters, + type GetAbiRecordReturnType as ensjs_GetEnsAbiReturnType, + getAbiRecord, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsAbiParameters = + Compute> + +export type GetEnsAbiReturnType = ensjs_GetEnsAbiReturnType + +export type GetEnsAbiErrorType = ensjs_GetEnsAbiErrorType + +export type GetEnsAbiOptions = + Compute> & ScopeKeyParameter> + +export type GetEnsAbiQueryFnData = GetEnsAbiReturnType + +export type GetEnsAbiData = GetEnsAbiReturnType + +export const getEnsAbiQueryKey = ( + options: GetEnsAbiOptions, +) => { + return ['getEnsAbi', filterQueryOptions(options)] as const +} + +export type GetEnsAbiQueryKey = ReturnType< + typeof getEnsAbiQueryKey +> + +export const getEnsAbiQueryOptions = ( + config: config, + options: GetEnsAbiOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getAbiRecord(client, parameters) + }, + queryKey: getEnsAbiQueryKey(options), + } as const satisfies QueryOptions< + GetEnsAbiQueryFnData, + GetEnsAbiErrorType, + GetEnsAbiData, + GetEnsAbiQueryKey + > +} diff --git a/packages/react/src/query/getEnsAddress.ts b/packages/react/src/query/getEnsAddress.ts new file mode 100644 index 000000000..d08e8747b --- /dev/null +++ b/packages/react/src/query/getEnsAddress.ts @@ -0,0 +1,63 @@ +import { + type GetAddressRecordErrorType as ensjs_GetEnsAddressErrorType, + type GetAddressRecordParameters as ensjs_GetEnsAddressParameters, + type GetAddressRecordReturnType as ensjs_GetEnsAddressReturnType, + getAddressRecord, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsAddressParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsAddressReturnType = ensjs_GetEnsAddressReturnType + +export type GetEnsAddressErrorType = ensjs_GetEnsAddressErrorType + +export type GetEnsAddressOptions = + Compute> & ScopeKeyParameter> + +export type GetEnsAddressQueryFnData = GetEnsAddressReturnType + +export type GetEnsAddressData = GetEnsAddressReturnType + +export const getEnsAddressQueryKey = ( + options: GetEnsAddressOptions, +) => { + return ['getEnsAddress', filterQueryOptions(options)] as const +} + +export type GetEnsAddressQueryKey = ReturnType< + typeof getEnsAddressQueryKey +> + +export const getEnsAddressQueryOptions = ( + config: config, + options: GetEnsAddressOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getAddressRecord(client, parameters) + }, + queryKey: getEnsAddressQueryKey(options), + } as const satisfies QueryOptions< + GetEnsAddressQueryFnData, + GetEnsAddressErrorType, + GetEnsAddressData, + GetEnsAddressQueryKey + > +} diff --git a/packages/react/src/query/getEnsAvailable.ts b/packages/react/src/query/getEnsAvailable.ts new file mode 100644 index 000000000..819be04d9 --- /dev/null +++ b/packages/react/src/query/getEnsAvailable.ts @@ -0,0 +1,64 @@ +import { + type GetAvailableErrorType, + type GetAvailableParameters, + type GetAvailableReturnType, + getAvailable, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsAvailableParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsAvailableReturnType = GetAvailableReturnType + +export type GetEnsAvailableErrorType = GetAvailableErrorType + +export type GetEnsAvailableOptions< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> & ScopeKeyParameter> + +export type GetEnsAvailableQueryFnData = GetAvailableReturnType + +export type GetEnsAvailableData = GetAvailableReturnType + +export const getEnsAvailableQueryKey = ( + options: GetEnsAvailableOptions, +) => { + return ['getEnsAvailable', filterQueryOptions(options)] as const +} + +export type GetEnsAvailableQueryKey = ReturnType< + typeof getEnsAvailableQueryKey +> + +export const getEnsAvailableQueryOptions = ( + config: config, + options: GetEnsAvailableOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getAvailable(client, parameters) + }, + queryKey: getEnsAvailableQueryKey(options), + } as const satisfies QueryOptions< + GetEnsAvailableQueryFnData, + GetEnsAvailableErrorType, + GetEnsAvailableData, + GetEnsAvailableQueryKey + > +} diff --git a/packages/react/src/query/getEnsContentHash.ts b/packages/react/src/query/getEnsContentHash.ts new file mode 100644 index 000000000..00fffd463 --- /dev/null +++ b/packages/react/src/query/getEnsContentHash.ts @@ -0,0 +1,65 @@ +import { + type GetContentHashRecordErrorType as ensjs_GetEnsContentHashErrorType, + type GetContentHashRecordParameters as ensjs_GetEnsContentHashParameters, + type GetContentHashRecordReturnType as ensjs_GetEnsContentHashReturnType, + getContentHashRecord, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsContentHashParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsContentHashReturnType = ensjs_GetEnsContentHashReturnType + +export type GetEnsContentHashErrorType = ensjs_GetEnsContentHashErrorType + +export type GetEnsContentHashOptions< + config extends ConfigWithEns = ConfigWithEns, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export type GetEnsContentHashQueryFnData = GetEnsContentHashReturnType + +export type GetEnsContentHashData = GetEnsContentHashReturnType + +export const getEnsContentHashQueryKey = ( + options: GetEnsContentHashOptions, +) => { + return ['getEnsContentHash', filterQueryOptions(options)] as const +} + +export type GetEnsContentHashQueryKey = + ReturnType> + +export const getEnsContentHashQueryOptions = ( + config: config, + options: GetEnsContentHashOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getContentHashRecord(client, parameters) + }, + queryKey: getEnsContentHashQueryKey(options), + } as const satisfies QueryOptions< + GetEnsContentHashQueryFnData, + GetEnsContentHashErrorType, + GetEnsContentHashData, + GetEnsContentHashQueryKey + > +} diff --git a/packages/react/src/query/getEnsCredentials.ts b/packages/react/src/query/getEnsCredentials.ts new file mode 100644 index 000000000..cd3d6ccdb --- /dev/null +++ b/packages/react/src/query/getEnsCredentials.ts @@ -0,0 +1,66 @@ +import { + type GetCredentialsErrorType as ensjs_GetCredentialsErrorType, + type GetCredentialsParameters as ensjs_GetCredentialsParameters, + type GetCredentialsReturnType as ensjs_GetCredentialsReturnType, + getCredentials, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsCredentialsParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsCredentialsReturnType = ensjs_GetCredentialsReturnType + +export type GetEnsCredentialsErrorType = ensjs_GetCredentialsErrorType + +export type GetEnsCredentialsOptions< + config extends ConfigWithEns = ConfigWithEns, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export type GetEnsCredentialsQueryFnData = GetEnsCredentialsReturnType + +export type GetEnsCredentialsData = GetEnsCredentialsReturnType + +export const getEnsCredentialsQueryKey = ( + options: GetEnsCredentialsOptions, +) => { + return ['getEnsCredentials', filterQueryOptions(options)] as const +} + +export type GetEnsCredentialsQueryKey = + ReturnType> + +export const getEnsCredentialsQueryOptions = ( + config: config, + options: GetEnsCredentialsOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + + return getCredentials(client, parameters) + }, + queryKey: getEnsCredentialsQueryKey(options), + } as const satisfies QueryOptions< + GetEnsCredentialsQueryFnData, + GetEnsCredentialsErrorType, + GetEnsCredentialsData, + GetEnsCredentialsQueryKey + > +} diff --git a/packages/react/src/query/getEnsExpiry.ts b/packages/react/src/query/getEnsExpiry.ts new file mode 100644 index 000000000..964adef6c --- /dev/null +++ b/packages/react/src/query/getEnsExpiry.ts @@ -0,0 +1,64 @@ +import { + type GetExpiryErrorType as ensjs_GetExpiryErrorType, + type GetExpiryParameters as ensjs_GetExpiryParameters, + type GetExpiryReturnType as ensjs_GetExpiryReturnType, + getExpiry, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsExpiryParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsExpiryReturnType = ensjs_GetExpiryReturnType + +export type GetEnsExpiryErrorType = ensjs_GetExpiryErrorType + +export type GetEnsExpiryOptions = + Compute> & ScopeKeyParameter> + +export type GetEnsExpiryQueryFnData = GetEnsExpiryReturnType + +export type GetEnsExpiryData = GetEnsExpiryReturnType + +export const getEnsExpiryQueryKey = ( + options: GetEnsExpiryOptions, +) => { + return ['getEnsExpiry', filterQueryOptions(options)] as const +} + +export type GetEnsExpiryQueryKey = ReturnType< + typeof getEnsExpiryQueryKey +> + +export const getEnsExpiryQueryOptions = ( + config: config, + options: GetEnsExpiryOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + + return getExpiry(client, parameters) + }, + queryKey: getEnsExpiryQueryKey(options), + } as const satisfies QueryOptions< + GetEnsExpiryQueryFnData, + GetEnsExpiryErrorType, + GetEnsExpiryData, + GetEnsExpiryQueryKey + > +} diff --git a/packages/react/src/query/getEnsNamesForAddress.ts b/packages/react/src/query/getEnsNamesForAddress.ts new file mode 100644 index 000000000..4a0dffc87 --- /dev/null +++ b/packages/react/src/query/getEnsNamesForAddress.ts @@ -0,0 +1,66 @@ +import { + type GetNamesForAddressErrorType as ensjs_GetNamesForAddressErrorType, + type GetNamesForAddressParameters as ensjs_GetNamesForAddressParameters, + type GetNamesForAddressReturnType as ensjs_GetNamesForAddressReturnType, + getNamesForAddress, +} from '@ensdomains/ensjs/subgraph' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsNamesForAddressParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsNamesForAddressReturnType = ensjs_GetNamesForAddressReturnType + +export type GetEnsNamesForAddressErrorType = ensjs_GetNamesForAddressErrorType + +export type GetEnsNamesForAddressOptions< + config extends ConfigWithEns = ConfigWithEns, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export type GetEnsNamesForAddressQueryFnData = GetEnsNamesForAddressReturnType + +export type GetEnsNamesForAddressData = GetEnsNamesForAddressReturnType + +export const getEnsNamesForAddressQueryKey = ( + options: GetEnsNamesForAddressOptions, +) => { + return ['getEnsNamesForAddress', filterQueryOptions(options)] as const +} + +export type GetEnsNamesForAddressQueryKey = + ReturnType> + +export const getEnsNamesForAddressQueryOptions = ( + config: config, + options: GetEnsNamesForAddressOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { address, ...rest } = queryKey[1] + if (!address) throw new Error('address is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + address, + }) + + return getNamesForAddress(client, parameters) + }, + queryKey: getEnsNamesForAddressQueryKey(options), + } as const satisfies QueryOptions< + GetEnsNamesForAddressQueryFnData, + GetEnsNamesForAddressErrorType, + GetEnsNamesForAddressData, + GetEnsNamesForAddressQueryKey + > +} diff --git a/packages/react/src/query/getEnsOwner.ts b/packages/react/src/query/getEnsOwner.ts new file mode 100644 index 000000000..ea9b87f10 --- /dev/null +++ b/packages/react/src/query/getEnsOwner.ts @@ -0,0 +1,63 @@ +import { + type GetOwnerErrorType as ensjs_GetEnsOwnerErrorType, + type GetOwnerParameters as ensjs_GetEnsOwnerParameters, + type GetOwnerReturnType as ensjs_GetEnsOwnerReturnType, + getOwner, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsOwnerParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsOwnerReturnType = ensjs_GetEnsOwnerReturnType + +export type GetEnsOwnerErrorType = ensjs_GetEnsOwnerErrorType + +export type GetEnsOwnerOptions = + Compute> & ScopeKeyParameter> + +export type GetEnsOwnerQueryFnData = GetEnsOwnerReturnType + +export type GetEnsOwnerData = GetEnsOwnerReturnType + +export const getEnsOwnerQueryKey = ( + options: GetEnsOwnerOptions, +) => { + return ['getEnsOwner', filterQueryOptions(options)] as const +} + +export type GetEnsOwnerQueryKey = ReturnType< + typeof getEnsOwnerQueryKey +> + +export const getEnsOwnerQueryOptions = ( + config: config, + options: GetEnsOwnerOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getOwner(client, parameters) + }, + queryKey: getEnsOwnerQueryKey(options), + } as const satisfies QueryOptions< + GetEnsOwnerQueryFnData, + GetEnsOwnerErrorType, + GetEnsOwnerData, + GetEnsOwnerQueryKey + > +} diff --git a/packages/react/src/query/getEnsPrice.ts b/packages/react/src/query/getEnsPrice.ts new file mode 100644 index 000000000..939939f62 --- /dev/null +++ b/packages/react/src/query/getEnsPrice.ts @@ -0,0 +1,65 @@ +import { + type GetPriceErrorType as ensjs_GetEnsPriceErrorType, + type GetPriceParameters as ensjs_GetEnsPriceParameters, + type GetPriceReturnType as ensjs_GetEnsPriceReturnType, + getPrice, +} from '@ensdomains/ensjs/public/v1' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsPriceParameters< + config extends ConfigWithEns = ConfigWithEns, +> = Compute> + +export type GetEnsPriceReturnType = ensjs_GetEnsPriceReturnType + +export type GetEnsPriceErrorType = ensjs_GetEnsPriceErrorType + +export type GetEnsPriceOptions = + Compute> & ScopeKeyParameter> + +export type GetEnsPriceQueryFnData = GetEnsPriceReturnType + +export type GetEnsPriceData = GetEnsPriceReturnType + +export const getEnsPriceQueryKey = ( + options: GetEnsPriceOptions, +) => { + return ['getEnsPrice', filterQueryOptions(options)] as const +} + +export type GetEnsPriceQueryKey = ReturnType< + typeof getEnsPriceQueryKey +> + +export const getEnsPriceQueryOptions = ( + config: config, + options: GetEnsPriceOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { nameOrNames, duration, ...rest } = queryKey[1] + if (!nameOrNames) throw new Error('nameOrNames is required') + if (!duration) throw new Error('duration is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + nameOrNames, + duration, + }) + return getPrice(client, parameters) + }, + queryKey: getEnsPriceQueryKey(options), + } as const satisfies QueryOptions< + GetEnsPriceQueryFnData, + GetEnsPriceErrorType, + GetEnsPriceData, + GetEnsPriceQueryKey + > +} diff --git a/packages/react/src/query/getEnsRecords.ts b/packages/react/src/query/getEnsRecords.ts new file mode 100644 index 000000000..118b63778 --- /dev/null +++ b/packages/react/src/query/getEnsRecords.ts @@ -0,0 +1,113 @@ +import { + type GetRecordsErrorType as ensjs_GetEnsRecordsErrorType, + type GetRecordsParameters as ensjs_GetEnsRecordsParameters, + type GetRecordsReturnType as ensjs_GetEnsRecordsReturnType, + getRecords, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsRecordsParameters< + config extends ConfigWithEns = ConfigWithEns, + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = Compute< + ensjs_GetEnsRecordsParameters & + ChainIdParameter +> + +export type GetEnsRecordsReturnType< + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = ensjs_GetEnsRecordsReturnType + +export type GetEnsRecordsErrorType = ensjs_GetEnsRecordsErrorType + +export type GetEnsRecordsOptions< + config extends ConfigWithEns = ConfigWithEns, + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = Compute< + ExactPartial< + GetEnsRecordsParameters + > & + ScopeKeyParameter +> + +export type GetEnsRecordsQueryFnData< + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = GetEnsRecordsReturnType + +export type GetEnsRecordsData< + texts extends readonly string[] | undefined = undefined, + coins extends readonly (string | number)[] | undefined = undefined, + contentHash extends boolean | undefined = undefined, + abi extends boolean | undefined = undefined, +> = GetEnsRecordsReturnType + +export const getEnsRecordsQueryKey = < + config extends ConfigWithEns, + texts extends readonly string[] | undefined, + coins extends readonly (string | number)[] | undefined, + contentHash extends boolean | undefined, + abi extends boolean | undefined, +>( + options: GetEnsRecordsOptions, +) => { + return ['getEnsRecords', filterQueryOptions(options)] as const +} + +export type GetEnsRecordsQueryKey< + config extends ConfigWithEns, + texts extends readonly string[] | undefined, + coins extends readonly (string | number)[] | undefined, + contentHash extends boolean | undefined, + abi extends boolean | undefined, +> = ReturnType< + typeof getEnsRecordsQueryKey +> + +export const getEnsRecordsQueryOptions = < + config extends ConfigWithEns, + const texts extends readonly string[] | undefined = undefined, + const coins extends readonly (string | number)[] | undefined = undefined, + const contentHash extends boolean | undefined = undefined, + const abi extends boolean | undefined = undefined, +>( + config: config, + options: GetEnsRecordsOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { name, ...rest } = queryKey[1] + if (!name) throw new Error('name is required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + name, + }) + return getRecords(client, parameters) as any + }, + queryKey: getEnsRecordsQueryKey(options), + } as const satisfies QueryOptions< + GetEnsRecordsQueryFnData, + GetEnsRecordsErrorType, + GetEnsRecordsData, + GetEnsRecordsQueryKey + > +} diff --git a/packages/react/src/query/getEnsResolverInterfaces.ts b/packages/react/src/query/getEnsResolverInterfaces.ts new file mode 100644 index 000000000..5757291df --- /dev/null +++ b/packages/react/src/query/getEnsResolverInterfaces.ts @@ -0,0 +1,88 @@ +import { + type GetSupportedInterfacesErrorType as ensjs_GetSupportedInterfacesErrorType, + type GetSupportedInterfacesParameters as ensjs_GetSupportedInterfacesParameters, + type GetSupportedInterfacesReturnType as ensjs_GetSupportedInterfacesReturnType, + getSupportedInterfaces, +} from '@ensdomains/ensjs/public' +import type { QueryOptions } from '@tanstack/react-query' +import type { Hex } from 'viem' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions, getClientAndParameters } from './utils.js' + +export type GetEnsResolverInterfacesParameters< + config extends ConfigWithEns = ConfigWithEns, + interfaces extends readonly Hex[] = readonly Hex[], +> = Compute< + ensjs_GetSupportedInterfacesParameters & ChainIdParameter +> + +export type GetEnsResolverInterfacesReturnType< + interfaces extends readonly Hex[], +> = ensjs_GetSupportedInterfacesReturnType + +export type GetEnsResolverInterfacesErrorType = + ensjs_GetSupportedInterfacesErrorType + +export type GetEnsResolverInterfacesOptions< + config extends ConfigWithEns = ConfigWithEns, + interfaces extends readonly Hex[] = readonly Hex[], +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export type GetEnsResolverInterfacesQueryFnData< + interfaces extends readonly Hex[], +> = GetEnsResolverInterfacesReturnType + +export type GetEnsResolverInterfacesData = + GetEnsResolverInterfacesReturnType + +export const getEnsResolverInterfacesQueryKey = < + config extends ConfigWithEns, + const interfaces extends readonly Hex[], +>( + options: GetEnsResolverInterfacesOptions, +) => { + return ['getEnsResolverInterfaces', filterQueryOptions(options)] as const +} + +export type GetEnsResolverInterfacesQueryKey< + config extends ConfigWithEns, + interfaces extends readonly Hex[], +> = ReturnType> + +export const getEnsResolverInterfacesQueryOptions = < + config extends ConfigWithEns, + const interfaces extends readonly Hex[], +>( + config: config, + options: GetEnsResolverInterfacesOptions = {}, +) => { + return { + async queryFn({ queryKey }) { + const { address, interfaces, ...rest } = queryKey[1] + if (!address) throw new Error('address is required') + if (!interfaces) throw new Error('interfaces are required') + + const { client, parameters } = getClientAndParameters(config, { + ...rest, + address, + interfaces, + }) + + return getSupportedInterfaces(client, parameters) + }, + queryKey: getEnsResolverInterfacesQueryKey(options), + } as const satisfies QueryOptions< + GetEnsResolverInterfacesQueryFnData, + GetEnsResolverInterfacesErrorType, + GetEnsResolverInterfacesData, + GetEnsResolverInterfacesQueryKey + > +} diff --git a/packages/react/src/query/utils.ts b/packages/react/src/query/utils.ts new file mode 100644 index 000000000..68c97f317 --- /dev/null +++ b/packages/react/src/query/utils.ts @@ -0,0 +1,73 @@ +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' + +export const filterQueryOptions = >( + options: type, +): type => { + const { + // import('@tanstack/query-core').QueryOptions + _defaulted, + behavior, + gcTime, + initialData, + initialDataUpdatedAt, + maxPages, + meta, + networkMode, + queryFn, + queryHash, + queryKey, + queryKeyHashFn, + retry, + retryDelay, + structuralSharing, + + // import('@tanstack/query-core').InfiniteQueryObserverOptions + getPreviousPageParam, + getNextPageParam, + initialPageParam, + + // import('@tanstack/react-query').UseQueryOptions + _optimisticResults, + enabled, + notifyOnChangeProps, + placeholderData, + refetchInterval, + refetchIntervalInBackground, + refetchOnMount, + refetchOnReconnect, + refetchOnWindowFocus, + retryOnMount, + select, + staleTime, + suspense, + throwOnError, + + /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // wagmi + /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + config, + connector, + query, + ...rest + } = options + + return rest as type +} + +export const getClientAndParameters = < + config extends ConfigWithEns, + parameters extends Record & + ChainIdParameter & + ScopeKeyParameter, +>( + config: config, + parameters: parameters, +) => { + const { chainId, scopeKey: _, ...rest } = parameters + const client = config.getClient({ chainId }) + return { client, parameters: rest } as const +} diff --git a/packages/react/src/query/writeEnsRecords.ts b/packages/react/src/query/writeEnsRecords.ts new file mode 100644 index 000000000..a4319fed1 --- /dev/null +++ b/packages/react/src/query/writeEnsRecords.ts @@ -0,0 +1,116 @@ +import type { ChainWithEns } from '@ensdomains/ensjs/contracts' +import { + encodeSetRecordsData, + type SetRecordsErrorType as ensjs_WriteEnsRecordsErrorType, + type SetRecordsParameters as ensjs_WriteEnsRecordsParameters, + type SetRecordsReturnType as ensjs_WriteEnsRecordsReturnType, +} from '@ensdomains/ensjs/wallet' +import type { MutateOptions, MutationOptions } from '@tanstack/react-query' +import type { Account } from 'viem' +import { type SendTransactionParameters, sendTransaction } from 'wagmi/actions' +import type { ConfigWithEns } from '../types/config.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' + +export type WriteEnsRecordsParameters< + config extends ConfigWithEns = ConfigWithEns, + chains extends readonly ChainWithEns[] = config['chains'], +> = ensjs_WriteEnsRecordsParameters & + ConnectorParameter & + ChainIdParameter + +export type WriteEnsRecordsReturnType = ensjs_WriteEnsRecordsReturnType + +export type WriteEnsRecordsErrorType = ensjs_WriteEnsRecordsErrorType + +export const writeEnsRecords = async < + config extends ConfigWithEns, + chains extends readonly ChainWithEns[] = config['chains'], +>( + config: config, + { + name, + resolverAddress, + clearRecords, + contentHash, + texts, + coins, + abi, + ...transactionParameters + }: WriteEnsRecordsParameters, +): Promise => { + const client = config.getClient({ chainId: transactionParameters.chainId }) + const data = encodeSetRecordsData(client, { + name, + resolverAddress, + clearRecords, + contentHash, + texts, + coins, + abi, + }) + + const parameters = { + ...data, + ...transactionParameters, + } as unknown as SendTransactionParameters + + return sendTransaction(config, parameters) +} + +export type WriteEnsRecordsData = Compute + +export type WriteEnsRecordsVariables = + WriteEnsRecordsParameters + +export type WriteEnsRecordsMutate< + config extends ConfigWithEns, + context = unknown, +> = ( + variables: WriteEnsRecordsVariables, + options?: + | Compute< + MutateOptions< + WriteEnsRecordsData, + WriteEnsRecordsErrorType, + Compute>, + context + > + > + | undefined, +) => WriteEnsRecordsData + +export type WriteEnsRecordsMutateAsync< + config extends ConfigWithEns, + context = unknown, +> = ( + variables: WriteEnsRecordsVariables, + options?: + | Compute< + MutateOptions< + WriteEnsRecordsData, + WriteEnsRecordsErrorType, + Compute>, + context + > + > + | undefined, +) => Promise + +export const writeEnsRecordsMutationOptions = ( + config: config, +) => { + return { + mutationFn(variables) { + return writeEnsRecords(config, variables) + }, + mutationKey: ['writeEnsRecords'], + } as const satisfies MutationOptions< + WriteEnsRecordsData, + WriteEnsRecordsErrorType, + WriteEnsRecordsVariables + > +} diff --git a/packages/react/src/types/chain.ts b/packages/react/src/types/chain.ts new file mode 100644 index 000000000..7ac11c74a --- /dev/null +++ b/packages/react/src/types/chain.ts @@ -0,0 +1,27 @@ +import type { ChainWithEns } from '@ensdomains/ensjs/contracts' +import type { ChainFormatters } from 'viem' +import type { ConfigWithEns } from './config.js' +import type { IsNarrowable, Merge } from './utils.js' + +/** Filters {@link Config} chains by {@link chainId} or simplifies if no `ChainFormatters` are present. */ +export type SelectChains< + config extends ConfigWithEns, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = ConfigWithEns extends config + ? readonly [ChainWithEns] // chains not inferrable, return default + : IsNarrowable extends true + ? readonly [Extract] // select specific chain + : HasFormatter extends true + ? config['chains'] // return all chains since one has formatter + : // return default chain with ID set to union (allows for more simple type since the only thing that is different is the chain ID for each chain) + readonly [Merge] + +type HasFormatter = + chains extends readonly [ + infer head extends ChainWithEns, + ...infer tail extends readonly ChainWithEns[], + ] + ? IsNarrowable extends true + ? true + : HasFormatter + : false diff --git a/packages/react/src/types/config.ts b/packages/react/src/types/config.ts new file mode 100644 index 000000000..945538ba3 --- /dev/null +++ b/packages/react/src/types/config.ts @@ -0,0 +1,4 @@ +import type { ChainWithEns } from '@ensdomains/ensjs/contracts' +import type { Config } from 'wagmi' + +export type ConfigWithEns = Config diff --git a/packages/react/src/types/properties.ts b/packages/react/src/types/properties.ts new file mode 100644 index 000000000..7f6bf243f --- /dev/null +++ b/packages/react/src/types/properties.ts @@ -0,0 +1,41 @@ +import type { DefaultError, QueryKey } from '@tanstack/react-query' +import type { Connector } from 'wagmi' +import type { UseQueryParameters } from 'wagmi/query' +import type { ConfigWithEns } from './config.js' + +export type ChainIdParameter< + config extends ConfigWithEns, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +> = { + chainId?: + | (chainId extends config['chains'][number]['id'] ? chainId : undefined) + | config['chains'][number]['id'] + | undefined +} + +export type ScopeKeyParameter = { scopeKey?: string | undefined } + +// TODO(tate): not sure if this is needed yet +export type ConnectorParameter = { + connector?: Connector | undefined +} + +export type QueryParameter< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryKey extends QueryKey = QueryKey, +> = { + query?: + | Omit< + UseQueryParameters, + 'queryFn' | 'queryHash' | 'queryKey' | 'queryKeyHashFn' | 'throwOnError' + > + | undefined +} + +export type ConfigParameter = { + config?: ConfigWithEns | config | undefined +} diff --git a/packages/react/src/types/register.ts b/packages/react/src/types/register.ts new file mode 100644 index 000000000..9bbfca5e8 --- /dev/null +++ b/packages/react/src/types/register.ts @@ -0,0 +1,8 @@ +import type { Register } from 'wagmi' +import type { ConfigWithEns } from './config.js' + +export type ResolvedRegister = { + config: Register extends { config: infer config extends ConfigWithEns } + ? config + : ConfigWithEns +} diff --git a/packages/react/src/types/utils.ts b/packages/react/src/types/utils.ts new file mode 100644 index 000000000..e50027973 --- /dev/null +++ b/packages/react/src/types/utils.ts @@ -0,0 +1,48 @@ +/** Combines members of an intersection into a readable type. */ +// https://twitter.com/mattpocockuk/status/1622730173446557697?s=20&t=NdpAcmEFXY01xkqU3KO0Mg +export type Compute = { [key in keyof type]: type[key] } & unknown + +/** + * Makes all properties of an object optional. + * + * Compatible with [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes). + */ +export type ExactPartial = { + [key in keyof type]?: type[key] | undefined +} + +/** Checks if {@link type} can be narrowed further than {@link type2} */ +export type IsNarrowable = IsUnknown extends true + ? false + : undefined extends type + ? false + : IsNever< + (type extends type2 ? true : false) & + (type2 extends type ? false : true) + > extends true + ? false + : true + +/** + * @internal + * Checks if {@link type} is `never` + */ +export type IsNever = [type] extends [never] ? true : false + +/** + * @internal + * Checks if {@link type} is `unknown` + */ +export type IsUnknown = unknown extends type ? true : false + +/** Merges two object types into new type */ +export type Merge = Compute< + LooseOmit & + obj2 +> + +/** Loose version of {@link StrictOmit} */ +export type LooseOmit = Pick< + type, + Exclude +> diff --git a/packages/react/src/utils/types.ts b/packages/react/src/utils/types.ts deleted file mode 100644 index 14d346663..000000000 --- a/packages/react/src/utils/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type Compute = { [key in keyof Type]: Type[key] } & unknown diff --git a/patches/hardhat-deploy.patch b/patches/hardhat-deploy.patch deleted file mode 100644 index f0bc55020..000000000 --- a/patches/hardhat-deploy.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/dist/src/DeploymentsManager.js b/dist/src/DeploymentsManager.js -index 9ff5b84084cc6a6ce05e6d41efd2e95e868a0082..4cf3d260350d91dc93289e213163f806ffad165e 100644 ---- a/dist/src/DeploymentsManager.js -+++ b/dist/src/DeploymentsManager.js -@@ -733,7 +733,7 @@ class DeploymentsManager { - // console.log("fetching " + scriptFilePath); - try { - delete require.cache[scriptFilePath]; // ensure we reload it every time, so changes are taken in consideration -- deployFunc = require(scriptFilePath); -+ deployFunc = await import(scriptFilePath); - if (deployFunc.default) { - deployFunc = deployFunc.default; - } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7b077809..78645708b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,11 +7,6 @@ settings: overrides: '@nomiclabs/hardhat-ethers': npm:hardhat-deploy-ethers@0.3.0-beta.13 -patchedDependencies: - hardhat-deploy: - hash: c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61 - path: patches/hardhat-deploy.patch - importers: .: @@ -21,44 +16,32 @@ importers: unplugged: true devDependencies: '@biomejs/biome': - specifier: ^1.9.4 - version: 1.9.4 + specifier: 2.0.6 + version: 2.0.6 '@changesets/changelog-github': specifier: ^0.5.0 - version: 0.5.1 + version: 0.5.2 '@changesets/cli': specifier: ^2.27.8 - version: 2.29.4 - ts-node: - specifier: ^10.7.0 - version: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) + version: 2.30.0(@types/node@22.19.17) typescript: - specifier: ^5.8.3 - version: 5.8.3 - - examples/basic-esm: - dependencies: - '@ensdomains/ensjs': - specifier: workspace:* - version: link:../../packages/ensjs + specifier: ^5.9.3 + version: 5.9.3 viem: - specifier: ^2.30.6 - version: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + specifier: ^2.38.3 + version: 2.47.10(typescript@5.9.3) - examples/basic-tsnode-esm: + examples/basic: dependencies: '@ensdomains/ensjs': specifier: workspace:* version: link:../../packages/ensjs - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) typescript: specifier: ^5.6.2 - version: 5.8.3 + version: 5.9.3 viem: specifier: ^2.30.6 - version: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + version: 2.47.10(typescript@5.9.3) packages/ensjs: dependencies: @@ -71,239 +54,212 @@ importers: '@ensdomains/content-hash': specifier: 3.1.0-rc.1 version: 3.1.0-rc.1 - '@ensdomains/dnsprovejs': - specifier: ^0.5.1 - version: 0.5.1 - abitype: - specifier: ^1.0.0 - version: 1.0.8(typescript@5.8.3)(zod@3.22.4) + '@ensdomains/ensjs-abi': + specifier: workspace:* + version: link:../ensjs-abi dns-packet: specifier: ^5.3.1 version: 5.6.1 graphql: specifier: ^16.11.0 - version: 16.11.0 + version: 16.13.2 graphql-request: - specifier: 7.1.2 - version: 7.1.2(graphql@16.11.0) - pako: - specifier: ^2.1.0 - version: 2.1.0 + specifier: 7.2.0 + version: 7.2.0(graphql@16.13.2) ts-pattern: specifier: ^5.4.0 - version: 5.7.1 + version: 5.9.0 devDependencies: '@ensdomains/buffer': - specifier: ^0.1.1 - version: 0.1.1 - '@ensdomains/ens-contracts': - specifier: 1.4.0-beta.2 - version: 1.4.0-beta.2(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + specifier: ^0.1.3 + version: 0.1.3 + '@ensdomains/dnsprovejs': + specifier: ^0.5.2 + version: 0.5.2 '@ensdomains/ens-test-env': specifier: ^1.0.1 version: 1.0.1 - '@ensdomains/hardhat-toolbox-viem-extended': - specifier: ^0.0.5 - version: 0.0.5(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))(hardhat-deploy@1.0.2(patch_hash=c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61)(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3) - '@nomicfoundation/hardhat-toolbox-viem': - specifier: ^3.0.0 - version: 3.0.0(@nomicfoundation/hardhat-verify@2.0.14(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))(@types/node@22.15.29)(chai@4.5.0)(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)) - '@nomicfoundation/hardhat-viem': - specifier: 2.0.6 - version: 2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) '@openzeppelin/contracts': specifier: ^4.5.0 version: 4.9.6 - '@openzeppelin/test-helpers': - specifier: ^0.5.16 - version: 0.5.16(bn.js@5.2.2)(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@types/jsonfile': - specifier: ^6.1.4 - version: 6.1.4 + '@types/dns-packet': + specifier: ^5.6.5 + version: 5.6.5 '@types/node': specifier: ^22.15.14 - version: 22.15.29 + version: 22.19.17 '@types/pako': - specifier: ^2.0.0 - version: 2.0.3 + specifier: ^2.0.3 + version: 2.0.4 '@vitest/coverage-v8': - specifier: ^1.3.1 - version: 1.6.1(vitest@1.6.1(@types/node@22.15.29)(happy-dom@13.10.1)) - cbor: - specifier: ^8.1.0 - version: 8.1.0 - dotenv: - specifier: ^16.0.0 - version: 16.5.0 - ethers: - specifier: ^5.7.2 - version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - happy-dom: - specifier: ^13.3.8 - version: 13.10.1 - hardhat: - specifier: 2.24.1 - version: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - hardhat-abi-exporter: - specifier: ^2.8.0 - version: 2.11.0(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) - hardhat-deploy: - specifier: ^1.0.2 - version: 1.0.2(patch_hash=c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61)(bufferutil@4.0.9)(utf-8-validate@5.0.10) - jsonfile: - specifier: ^6.1.0 - version: 6.1.0 - multiformats: - specifier: ^12.0.1 - version: 12.1.3 - solc: - specifier: ^0.8.13 - version: 0.8.30 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) + specifier: ^3.2.6 + version: 3.2.6(vitest@3.2.6(@types/node@22.19.17)(yaml@2.8.3)) + pako: + specifier: ^2.1.0 + version: 2.1.0 typedoc: specifier: ^0.24.8 - version: 0.24.8(typescript@5.8.3) + version: 0.24.8(typescript@5.9.3) typedoc-plugin-markdown: specifier: ^4.0.0-next.16 - version: 4.6.4(typedoc@0.24.8(typescript@5.8.3)) + version: 4.11.0(typedoc@0.24.8(typescript@5.9.3)) viem: - specifier: ^2.30.6 - version: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + specifier: ^2.47.10 + version: 2.47.10(typescript@5.9.3) vitest: - specifier: ^1.3.1 - version: 1.6.1(@types/node@22.15.29)(happy-dom@13.10.1) + specifier: ^3.2.6 + version: 3.2.6(@types/node@22.19.17)(yaml@2.8.3) - packages/react: - dependencies: + packages/ensjs-abi: {} + + packages/query-core: + devDependencies: '@ensdomains/ensjs': specifier: workspace:* version: link:../ensjs + '@wagmi/core': + specifier: ^2.17.2 + version: 2.22.1(typescript@5.6.2)(use-sync-external-store@1.4.0)(viem@2.47.10(typescript@5.6.2)) + typescript: + specifier: 5.6.2 + version: 5.6.2 + viem: + specifier: ^2.9.2 + version: 2.47.10(typescript@5.6.2) + + packages/react: devDependencies: '@ensdomains/buffer': specifier: ^0.1.1 - version: 0.1.1 + version: 0.1.3 '@ensdomains/ens-test-env': specifier: ^1.0.1 version: 1.0.1 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) - tslib: - specifier: ^2.7.0 - version: 2.8.1 + '@ensdomains/ensjs': + specifier: workspace:* + version: link:../ensjs + '@wagmi/core': + specifier: ^3 + version: 3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3)) viem: specifier: 2.29.0 - version: 2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) + version: 2.29.0(typescript@5.9.3) wagmi: - specifier: ^2 - version: 2.15.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) + specifier: ^3 + version: 3.6.21(typescript@5.9.3)(viem@2.29.0(typescript@5.9.3)) packages: '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.4': - resolution: {integrity: sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.27.4': - resolution: {integrity: sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.3': - resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} - '@biomejs/biome@1.9.4': - resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + '@biomejs/biome@2.0.6': + resolution: {integrity: sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@1.9.4': - resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + '@biomejs/cli-darwin-arm64@2.0.6': + resolution: {integrity: sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@1.9.4': - resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + '@biomejs/cli-darwin-x64@2.0.6': + resolution: {integrity: sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@1.9.4': - resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + '@biomejs/cli-linux-arm64-musl@2.0.6': + resolution: {integrity: sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] - '@biomejs/cli-linux-arm64@1.9.4': - resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + '@biomejs/cli-linux-arm64@2.0.6': + resolution: {integrity: sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] - '@biomejs/cli-linux-x64-musl@1.9.4': - resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + '@biomejs/cli-linux-x64-musl@2.0.6': + resolution: {integrity: sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] - '@biomejs/cli-linux-x64@1.9.4': - resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + '@biomejs/cli-linux-x64@2.0.6': + resolution: {integrity: sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] - '@biomejs/cli-win32-arm64@1.9.4': - resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + '@biomejs/cli-win32-arm64@2.0.6': + resolution: {integrity: sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@1.9.4': - resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + '@biomejs/cli-win32-x64@2.0.6': + resolution: {integrity: sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] - '@changesets/apply-release-plan@7.0.12': - resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} + '@changesets/apply-release-plan@7.1.0': + resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} - '@changesets/assemble-release-plan@6.0.8': - resolution: {integrity: sha512-y8+8LvZCkKJdbUlpXFuqcavpzJR80PN0OIfn8HZdwK7Sh6MgLXm4hKY5vu6/NDoKp8lAlM4ERZCqRMLxP4m+MQ==} + '@changesets/assemble-release-plan@6.0.9': + resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/changelog-github@0.5.1': - resolution: {integrity: sha512-BVuHtF+hrhUScSoHnJwTELB4/INQxVFc+P/Qdt20BLiBFIHFJDDUaGsZw+8fQeJTRP5hJZrzpt3oZWh0G19rAQ==} + '@changesets/changelog-github@0.5.2': + resolution: {integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==} - '@changesets/cli@2.29.4': - resolution: {integrity: sha512-VW30x9oiFp/un/80+5jLeWgEU6Btj8IqOgI+X/zAYu4usVOWXjPIK5jSSlt5jsCU7/6Z7AxEkarxBxGUqkAmNg==} + '@changesets/cli@2.30.0': + resolution: {integrity: sha512-5D3Nk2JPqMI1wK25pEymeWRSlSMdo5QOGlyfrKg0AOufrUcjEE3RQgaCpHoBiM31CSNrtSgdJ0U6zL1rLDDfBA==} hasBin: true - '@changesets/config@3.1.1': - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + '@changesets/config@3.1.3': + resolution: {integrity: sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} @@ -311,11 +267,11 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-github-info@0.6.0': - resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} + '@changesets/get-github-info@0.7.0': + resolution: {integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==} - '@changesets/get-release-plan@4.0.12': - resolution: {integrity: sha512-KukdEgaafnyGryUwpHG2kZ7xJquOmWWWk5mmoeQaSvZTWH1DC5D/Sw6ClgGFYtQnOMSQhgoEbDxAbpIIayKH1g==} + '@changesets/get-release-plan@4.0.15': + resolution: {integrity: sha512-Q04ZaRPuEVZtA+auOYgFaVQQSA98dXiVe/yFaZfY7hoSmQICHGvP0TF4u3EDNHWmmCS4ekA/XSpKlSM2PyTS2g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -326,14 +282,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.1': - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} '@changesets/pre@2.0.2': resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.5': - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} '@changesets/should-skip-package@0.1.2': resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} @@ -347,483 +303,251 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@coinbase/wallet-sdk@3.9.3': - resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} - - '@coinbase/wallet-sdk@4.3.0': - resolution: {integrity: sha512-T3+SNmiCw4HzDm4we9wCHCxlP0pqCiwKe4sOwPH3YAK2KSKjxPRydKu6UQJrdONFVLG7ujXvbd/6ZqmvJb8rkw==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@ecies/ciphers@0.2.3': - resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} - peerDependencies: - '@noble/ciphers': ^1.0.0 - - '@ensdomains/address-encoder@0.1.9': - resolution: {integrity: sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg==} - '@ensdomains/address-encoder@1.0.0-rc.3': resolution: {integrity: sha512-8o6zH69rObIqDY4PusEWuN9jvVOct+9jj9AOPO7ifc3ev8nmsly0e8TE1sHkhk0iKFbd3DlSsUnJ+yuRWmdLCQ==} '@ensdomains/address-encoder@1.1.1': resolution: {integrity: sha512-yg7s+suCuKRhaGsgLu57W/jxIs/Lnqs/SU7jT7UwS4ATSnW94jbUCbmyyZ82CQwKsmwaUE8uYvvVb4N6lfz29A==} - '@ensdomains/buffer@0.1.1': - resolution: {integrity: sha512-92SfSiNS8XorgU7OUBHo/i1ZU7JV7iz/6bKuLPNVsMxV79/eI7fJR6jfJJc40zAHjs3ha+Xo965Idomlq3rqnw==} + '@ensdomains/buffer@0.1.3': + resolution: {integrity: sha512-L/GoCkQFePjSn4DeVnBNfkGdm2CI8UxTxwgQztcZ1L9aHaFefRD+mTUXxtikk946KwwMHRQJDn38wo1IWtEpSg==} '@ensdomains/content-hash@3.1.0-rc.1': resolution: {integrity: sha512-OzdkXgdFmduzcJKU4KRkkJkQHnm5p7m7FkX8k+bHOEoOIzc0ueGT/Jay4nnb60wNk1wSHRmzY+hHBMpFDiGReg==} - '@ensdomains/dnsprovejs@0.5.1': - resolution: {integrity: sha512-nfm4ggpK5YBVwVwLZKF9WPjRGRTL9aUxX2O4pqv/AnQCz3WeGHsW7VhVFLj2s4EoWSzCXwR1E6nuqgUwnH692w==} + '@ensdomains/dnsprovejs@0.5.2': + resolution: {integrity: sha512-lSuYnUvUV+kBviQ82XUldtpiE0lA4gWrCilsoCWbTKtc/fH6re9NaTkhbPEy/7OF96MMAG1BDnuoTsfSMAzFBg==} engines: {node: '>=16.8'} - '@ensdomains/ens-contracts@1.4.0-beta.2': - resolution: {integrity: sha512-ClaD9sLLJccQY4XS/dy9wtmVdfn2wXxvK30gVtNIAl9THHrIUqgl/vRjBn70elWV8DV5hdVP9oQZyTTTBC21vg==} - '@ensdomains/ens-test-env@1.0.1': resolution: {integrity: sha512-it+5gN/CXCpcrhULClm/aVFoVry5MS5+xlE7g3LlTXDvGd0tBcBBF8yOMvACZRuZPljDA0CtxXngrAkwmxxp6w==} engines: {node: '>=20'} hasBin: true - '@ensdomains/ens@0.4.5': - resolution: {integrity: sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw==} - deprecated: Please use @ensdomains/ens-contracts - - '@ensdomains/ensjs@2.1.0': - resolution: {integrity: sha512-GRbGPT8Z/OJMDuxs75U/jUNEC0tbL0aj7/L/QQznGYKm/tiasp+ndLOaoULy9kKJFC0TBByqfFliEHDgoLhyog==} - - '@ensdomains/hardhat-toolbox-viem-extended@0.0.5': - resolution: {integrity: sha512-swnGEV5shoHXOrTRkTJgWvMrem9RU6NPr76TsaLJQlJ0RQYKHZ67CvstaQrFHTk6eKP8TE4ktDDw2jURPXwtnQ==} - peerDependencies: - '@nomicfoundation/hardhat-viem': ^2.0.3 - hardhat: ^2.22.9 - hardhat-deploy: ^0.12.4 - typescript: ^5.0.0 - - '@ensdomains/resolver@0.2.4': - resolution: {integrity: sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==} - deprecated: Please use @ensdomains/ens-contracts - - '@ensdomains/solsha1@0.0.3': - resolution: {integrity: sha512-uhuG5LzRt/UJC0Ux83cE2rCKwSleRePoYdQVcqPN1wyf3/ekMzT/KZUF9+v7/AG5w9jlMLCQkUM50vfjr0Yu9Q==} - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@ethereumjs/common@2.5.0': - resolution: {integrity: sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==} - - '@ethereumjs/common@2.6.5': - resolution: {integrity: sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==} - - '@ethereumjs/common@3.2.0': - resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==} + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@ethereumjs/rlp@4.0.1': - resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} - engines: {node: '>=14'} - hasBin: true + '@hapi/address@5.1.1': + resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} + engines: {node: '>=14.0.0'} - '@ethereumjs/rlp@5.0.2': - resolution: {integrity: sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==} - engines: {node: '>=18'} - hasBin: true + '@hapi/formula@3.0.2': + resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} - '@ethereumjs/tx@3.3.2': - resolution: {integrity: sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==} + '@hapi/hoek@11.0.7': + resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} - '@ethereumjs/tx@3.5.2': - resolution: {integrity: sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==} + '@hapi/pinpoint@2.0.1': + resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} - '@ethereumjs/tx@4.2.0': - resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==} - engines: {node: '>=14'} + '@hapi/tlds@1.1.6': + resolution: {integrity: sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==} + engines: {node: '>=14.0.0'} - '@ethereumjs/util@8.1.0': - resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} - engines: {node: '>=14'} + '@hapi/topo@6.0.2': + resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} - '@ethereumjs/util@9.1.0': - resolution: {integrity: sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==} + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} - - '@ethersproject/abi@5.8.0': - resolution: {integrity: sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==} - - '@ethersproject/abstract-provider@5.8.0': - resolution: {integrity: sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==} - - '@ethersproject/abstract-signer@5.8.0': - resolution: {integrity: sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==} - - '@ethersproject/address@5.8.0': - resolution: {integrity: sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==} - - '@ethersproject/base64@5.8.0': - resolution: {integrity: sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==} - - '@ethersproject/basex@5.8.0': - resolution: {integrity: sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==} - - '@ethersproject/bignumber@5.8.0': - resolution: {integrity: sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==} - - '@ethersproject/bytes@5.8.0': - resolution: {integrity: sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==} - - '@ethersproject/constants@5.8.0': - resolution: {integrity: sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==} - - '@ethersproject/contracts@5.8.0': - resolution: {integrity: sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==} - - '@ethersproject/hash@5.8.0': - resolution: {integrity: sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==} - - '@ethersproject/hdnode@5.8.0': - resolution: {integrity: sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==} - - '@ethersproject/json-wallets@5.8.0': - resolution: {integrity: sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==} - - '@ethersproject/keccak256@5.8.0': - resolution: {integrity: sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==} - - '@ethersproject/logger@5.8.0': - resolution: {integrity: sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==} - - '@ethersproject/networks@5.8.0': - resolution: {integrity: sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==} - - '@ethersproject/pbkdf2@5.8.0': - resolution: {integrity: sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==} - - '@ethersproject/properties@5.8.0': - resolution: {integrity: sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==} - - '@ethersproject/providers@5.8.0': - resolution: {integrity: sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==} - - '@ethersproject/random@5.8.0': - resolution: {integrity: sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==} - - '@ethersproject/rlp@5.8.0': - resolution: {integrity: sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==} - - '@ethersproject/sha2@5.8.0': - resolution: {integrity: sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==} - - '@ethersproject/signing-key@5.8.0': - resolution: {integrity: sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==} - - '@ethersproject/solidity@5.8.0': - resolution: {integrity: sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==} - - '@ethersproject/strings@5.8.0': - resolution: {integrity: sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==} - - '@ethersproject/transactions@5.8.0': - resolution: {integrity: sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==} - - '@ethersproject/units@5.8.0': - resolution: {integrity: sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==} - - '@ethersproject/wallet@5.8.0': - resolution: {integrity: sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==} - - '@ethersproject/web@5.8.0': - resolution: {integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==} - - '@ethersproject/wordlists@5.8.0': - resolution: {integrity: sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==} - - '@fastify/busboy@2.1.1': - resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} - engines: {node: '>=14'} - - '@graphql-typed-document-node/core@3.2.0': - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@hapi/hoek@9.3.0': - resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@hapi/topo@5.1.0': - resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@lit-labs/ssr-dom-shim@1.3.0': - resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} - - '@lit/reactive-element@2.1.0': - resolution: {integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@metamask/eth-json-rpc-provider@1.0.1': - resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} - engines: {node: '>=14.0.0'} - - '@metamask/json-rpc-engine@7.3.3': - resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==} - engines: {node: '>=16.0.0'} - - '@metamask/json-rpc-engine@8.0.2': - resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==} - engines: {node: '>=16.0.0'} - - '@metamask/json-rpc-middleware-stream@7.0.2': - resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==} - engines: {node: '>=16.0.0'} - - '@metamask/object-multiplex@2.1.0': - resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==} - engines: {node: ^16.20 || ^18.16 || >=20} - - '@metamask/onboarding@1.0.1': - resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==} - - '@metamask/providers@16.1.0': - resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==} - engines: {node: ^18.18 || >=20} - - '@metamask/rpc-errors@6.4.0': - resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} - engines: {node: '>=16.0.0'} - - '@metamask/safe-event-emitter@2.0.0': - resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} - - '@metamask/safe-event-emitter@3.1.2': - resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==} - engines: {node: '>=12.0.0'} - - '@metamask/sdk-communication-layer@0.32.0': - resolution: {integrity: sha512-dmj/KFjMi1fsdZGIOtbhxdg3amxhKL/A5BqSU4uh/SyDKPub/OT+x5pX8bGjpTL1WPWY/Q0OIlvFyX3VWnT06Q==} - peerDependencies: - cross-fetch: ^4.0.0 - eciesjs: '*' - eventemitter2: ^6.4.9 - readable-stream: ^3.6.2 - socket.io-client: ^4.5.1 - - '@metamask/sdk-install-modal-web@0.32.0': - resolution: {integrity: sha512-TFoktj0JgfWnQaL3yFkApqNwcaqJ+dw4xcnrJueMP3aXkSNev2Ido+WVNOg4IIMxnmOrfAC9t0UJ0u/dC9MjOQ==} - - '@metamask/sdk@0.32.0': - resolution: {integrity: sha512-WmGAlP1oBuD9hk4CsdlG1WJFuPtYJY+dnTHJMeCyohTWD2GgkcLMUUuvu9lO1/NVzuOoSi1OrnjbuY1O/1NZ1g==} - - '@metamask/superstruct@3.2.1': - resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} - engines: {node: '>=16.0.0'} - - '@metamask/utils@5.0.2': - resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} - engines: {node: '>=14.0.0'} - - '@metamask/utils@8.5.0': - resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==} - engines: {node: '>=16.0.0'} - - '@metamask/utils@9.3.0': - resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} - engines: {node: '>=16.0.0'} - - '@noble/ciphers@1.2.1': - resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} - engines: {node: ^14.21.3 || >=16} - '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.4.2': - resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} - - '@noble/curves@1.8.0': - resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} - engines: {node: ^14.21.3 || >=16} - - '@noble/curves@1.8.1': - resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} - engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.8.2': resolution: {integrity: sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==} engines: {node: ^14.21.3 || >=16} @@ -832,19 +556,8 @@ packages: resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.2.0': - resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} - - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} - - '@noble/hashes@1.7.0': - resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.7.1': - resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} '@noble/hashes@1.7.2': @@ -855,9 +568,6 @@ packages: resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@noble/secp256k1@1.7.1': - resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -870,482 +580,264 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nomicfoundation/edr-darwin-arm64@0.11.0': - resolution: {integrity: sha512-aYTVdcSs27XG7ayTzvZ4Yn9z/ABSaUwicrtrYK2NR8IH0ik4N4bWzo/qH8rax6rewVLbHUkGyGYnsy5ZN4iiMw==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-darwin-x64@0.11.0': - resolution: {integrity: sha512-RxX7UYgvJrfcyT/uHUn44Nsy1XaoW+Q1khKMdHKxeW7BrgIi+Lz+siz3bX5vhSoAnKilDPhIVLrnC8zxQhjR2A==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-linux-arm64-gnu@0.11.0': - resolution: {integrity: sha512-J0j+rs0s11FuSipt/ymqrFmpJ7c0FSz1/+FohCIlUXDxFv//+1R/8lkGPjEYFmy8DPpk/iO8mcpqHTGckREbqA==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-linux-arm64-musl@0.11.0': - resolution: {integrity: sha512-4r32zkGMN7WT/CMEuW0VjbuEdIeCskHNDMW4SSgQSJOE/N9L1KSLJCSsAbPD3aYE+e4WRDTyOwmuLjeUTcLZKQ==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-linux-x64-gnu@0.11.0': - resolution: {integrity: sha512-SmdncQHLYtVNWLIMyGaY6LpAfamzTDe3fxjkirmJv3CWR5tcEyC6LMui/GsIVnJzXeNJBXAzwl8hTUAxHTM6kQ==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-linux-x64-musl@0.11.0': - resolution: {integrity: sha512-w6hUqpn/trwiH6SRuRGysj37LsQVCX5XDCA3Xi81sbOaLhbHrNvK9TXWyZmcuzbdTKQQW6VNywcSxDdOiChcJg==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr-win32-x64-msvc@0.11.0': - resolution: {integrity: sha512-BLmULjRKoH9BsX+c4Na2ypV7NGeJ+M6Zpqj/faPOwleVscDdSr/IhriyPaXCe8dyfwbge7lWsbekiADtPSnB2Q==} - engines: {node: '>= 18'} - - '@nomicfoundation/edr@0.11.0': - resolution: {integrity: sha512-36WERf8ldvyHR6UAbcYsa+vpbW7tCrJGBwF4gXSsb8+STj1n66Hz85Y/O7B9+8AauX3PhglvV5dKl91tk43mWw==} - engines: {node: '>= 18'} - - '@nomicfoundation/hardhat-toolbox-viem@3.0.0': - resolution: {integrity: sha512-cr+aRozCtTwaRz5qc9OVY1kegWrnVwyhHZonICmlcm21cvJ31uvJnuPG688tMbjUvwRDw8tpZYZK0kI5M+4CKg==} - peerDependencies: - '@nomicfoundation/hardhat-ignition-viem': ^0.15.0 - '@nomicfoundation/hardhat-network-helpers': ^1.0.0 - '@nomicfoundation/hardhat-verify': ^2.0.0 - '@nomicfoundation/hardhat-viem': ^2.0.0 - '@types/chai': ^4.2.0 - '@types/chai-as-promised': ^7.1.6 - '@types/mocha': '>=9.1.0' - '@types/node': '>=18.0.0' - chai: ^4.2.0 - hardhat: ^2.11.0 - hardhat-gas-reporter: ^1.0.8 - solidity-coverage: ^0.8.1 - ts-node: '>=8.0.0' - typescript: ^5.0.4 - viem: ^2.7.6 - - '@nomicfoundation/hardhat-verify@2.0.14': - resolution: {integrity: sha512-z3iVF1WYZHzcdMMUuureFpSAfcnlfJbJx3faOnGrOYg6PRTki1Ut9JAuRccnFzMHf1AmTEoSUpWcyvBCoxL5Rg==} - peerDependencies: - hardhat: ^2.24.1 - - '@nomicfoundation/hardhat-viem@2.0.6': - resolution: {integrity: sha512-Pl5pvYK5VYKflfoUk4fVBESqKMNBtAIGPIT4j+Q8KNFueAe1vB2PsbRESeNJyW5YLL9pqKaD1RVqLmgIa1yvDg==} - peerDependencies: - hardhat: ^2.17.0 - viem: ^2.7.6 - - '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2': - resolution: {integrity: sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2': - resolution: {integrity: sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2': - resolution: {integrity: sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2': - resolution: {integrity: sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2': - resolution: {integrity: sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2': - resolution: {integrity: sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2': - resolution: {integrity: sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==} - engines: {node: '>= 12'} - - '@nomicfoundation/solidity-analyzer@0.1.2': - resolution: {integrity: sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==} - engines: {node: '>= 12'} - - '@openzeppelin/contract-loader@0.6.3': - resolution: {integrity: sha512-cOFIjBjwbGgZhDZsitNgJl0Ye1rd5yu/Yx5LMgeq3u0ZYzldm4uObzHDFq4gjDdoypvyORjjJa3BlFA7eAnVIg==} - - '@openzeppelin/contracts@4.9.3': - resolution: {integrity: sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==} - '@openzeppelin/contracts@4.9.6': resolution: {integrity: sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==} - '@openzeppelin/contracts@5.1.0': - resolution: {integrity: sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==} - - '@openzeppelin/test-helpers@0.5.16': - resolution: {integrity: sha512-T1EvspSfH1qQO/sgGlskLfYVBbqzJR23SZzYl/6B2JnT4EhThcI85UpvDk0BkLWKaDScQTabGHt4GzHW+3SfZg==} - deprecated: Package no longer maintained. Consider using @nomicfoundation/hardhat-chai-matchers and @nomicfoundation/hardhat-network-helpers instead. - - '@paulmillr/qr@0.2.1': - resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} - deprecated: 'The package is now available as "qr": npm install qr' - - '@reown/appkit-common@1.7.3': - resolution: {integrity: sha512-wKTr6N3z8ly17cc51xBEVkZK4zAd8J1m7RubgsdQ1olFY9YJGe61RYoNv9yFjt6tUVeYT+z7iMUwPhX2PziefQ==} - - '@reown/appkit-controllers@1.7.3': - resolution: {integrity: sha512-aqAcX/nZe0gwqjncyCkVrAk3lEw0qZ9xGrdLOmA207RreO4J0Vxu8OJXCBn4C2AUI2OpBxCPah+vyuKTUJTeHQ==} - - '@reown/appkit-polyfills@1.7.3': - resolution: {integrity: sha512-vQUiAyI7WiNTUV4iNwv27iigdeg8JJTEo6ftUowIrKZ2/gtE2YdMtGpavuztT/qrXhrIlTjDGp5CIyv9WOTu4g==} - - '@reown/appkit-scaffold-ui@1.7.3': - resolution: {integrity: sha512-ssB15fcjmoKQ+VfoCo7JIIK66a4SXFpCH8uK1CsMmXmKIKqPN54ohLo291fniV6mKtnJxh5Xm68slGtGrO3bmA==} - - '@reown/appkit-ui@1.7.3': - resolution: {integrity: sha512-zKmFIjLp0X24pF9KtPtSHmdsh/RjEWIvz+faIbPGm4tQbwcxdg9A35HeoP0rMgKYx49SX51LgPwVXne2gYacqQ==} - - '@reown/appkit-utils@1.7.3': - resolution: {integrity: sha512-8/MNhmfri+2uu8WzBhZ5jm5llofOIa1dyXDXRC/hfrmGmCFJdrQKPpuqOFYoimo2s2g70pK4PYefvOKgZOWzgg==} - - '@reown/appkit-wallet@1.7.3': - resolution: {integrity: sha512-D0pExd0QUE71ursQPp3pq/0iFrz2oz87tOyFifrPANvH5X0RQCYn/34/kXr+BFVQzNFfCBDlYP+CniNA/S0KiQ==} - - '@reown/appkit@1.7.3': - resolution: {integrity: sha512-aA/UIwi/dVzxEB62xlw3qxHa3RK1YcPMjNxoGj/fHNCqL2qWmbcOXT7coCUa9RG7/Bh26FZ3vdVT2v71j6hebQ==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.41.1': - resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==} + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.41.1': - resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==} + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.41.1': - resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==} + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.41.1': - resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==} + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.41.1': - resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==} + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.41.1': - resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==} + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.41.1': - resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.41.1': - resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] + libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.41.1': - resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==} + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.41.1': - resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==} + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + libc: [glibc] - '@rollup/rollup-linux-loongarch64-gnu@4.41.1': - resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==} + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': - resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==} + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.41.1': - resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==} - cpu: [riscv64] + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.41.1': - resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==} + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.41.1': - resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==} + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.41.1': - resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.41.1': - resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==} + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.41.1': - resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==} + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.41.1': - resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==} + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.41.1': - resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==} + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - '@safe-global/safe-apps-provider@0.18.6': - resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} - - '@safe-global/safe-apps-sdk@9.1.0': - resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} - - '@safe-global/safe-gateway-typescript-sdk@3.23.1': - resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} - engines: {node: '>=16'} - - '@scure/base@1.1.9': - resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] '@scure/base@1.2.6': resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - '@scure/bip32@1.1.5': - resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} - - '@scure/bip32@1.4.0': - resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} - '@scure/bip32@1.6.2': resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} '@scure/bip32@1.7.0': resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} - '@scure/bip39@1.1.1': - resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==} - - '@scure/bip39@1.3.0': - resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} - '@scure/bip39@1.5.4': resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - '@sentry/core@5.30.0': - resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} - engines: {node: '>=6'} - - '@sentry/hub@5.30.0': - resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==} - engines: {node: '>=6'} - - '@sentry/minimal@5.30.0': - resolution: {integrity: sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==} - engines: {node: '>=6'} - - '@sentry/node@5.30.0': - resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==} - engines: {node: '>=6'} - - '@sentry/tracing@5.30.0': - resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==} - engines: {node: '>=6'} - - '@sentry/types@5.30.0': - resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==} - engines: {node: '>=6'} - - '@sentry/utils@5.30.0': - resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} - engines: {node: '>=6'} - - '@sideway/address@4.1.5': - resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} - - '@sideway/formula@3.0.1': - resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@sideway/pinpoint@2.0.0': - resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - - '@socket.io/component-emitter@3.1.2': - resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} - - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} - - '@truffle/abi-utils@1.0.3': - resolution: {integrity: sha512-AWhs01HCShaVKjml7Z4AbVREr/u4oiWxCcoR7Cktm0mEvtT04pvnxW5xB/cI4znRkrbPdFQlFt67kgrAjesYkw==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/blockchain-utils@0.1.9': - resolution: {integrity: sha512-RHfumgbIVo68Rv9ofDYfynjnYZIfP/f1vZy4RoqkfYAO+fqfc58PDRzB1WAGq2U6GPuOnipOJxQhnqNnffORZg==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/codec@0.17.3': - resolution: {integrity: sha512-Ko/+dsnntNyrJa57jUD9u4qx9nQby+H4GsUO6yjiCPSX0TQnEHK08XWqBSg0WdmCH2+h0y1nr2CXSx8gbZapxg==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/compile-common@0.9.8': - resolution: {integrity: sha512-DTpiyo32t/YhLI1spn84D3MHYHrnoVqO+Gp7ZHrYNwDs86mAxtNiH5lsVzSb8cPgiqlvNsRCU9nm9R0YmKMTBQ==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/contract-schema@3.4.16': - resolution: {integrity: sha512-g0WNYR/J327DqtJPI70ubS19K1Fth/1wxt2jFqLsPmz5cGZVjCwuhiie+LfBde4/Mc9QR8G+L3wtmT5cyoBxAg==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/contract@4.6.31': - resolution: {integrity: sha512-s+oHDpXASnZosiCdzu+X1Tx5mUJUs1L1CYXIcgRmzMghzqJkaUFmR6NpNo7nJYliYbO+O9/aW8oCKqQ7rCHfmQ==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/debug-utils@6.0.57': - resolution: {integrity: sha512-Q6oI7zLaeNLB69ixjwZk2UZEWBY6b2OD1sjLMGDKBGR7GaHYiw96GLR2PFgPH1uwEeLmV4N78LYaQCrDsHbNeA==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/error@0.2.2': - resolution: {integrity: sha512-TqbzJ0O8DHh34cu8gDujnYl4dUl6o2DE4PR6iokbybvnIm/L2xl6+Gv1VC+YJS45xfH83Yo3/Zyg/9Oq8/xZWg==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@truffle/interface-adapter@0.5.37': - resolution: {integrity: sha512-lPH9MDgU+7sNDlJSClwyOwPCfuOimqsCx0HfGkznL3mcFRymc1pukAR1k17zn7ErHqBwJjiKAZ6Ri72KkS+IWw==} - engines: {node: ^16.20 || ^18.16 || >=20} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@trufflesuite/chromafi@3.0.0': - resolution: {integrity: sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@types/dns-packet@5.6.5': + resolution: {integrity: sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==} - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@types/bn.js@5.1.6': - resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - - '@types/jsonfile@6.1.4': - resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} - - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - - '@types/lru-cache@5.1.1': - resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@22.15.29': - resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==} - - '@types/pako@2.0.3': - resolution: {integrity: sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==} + '@types/node@22.19.17': + resolution: {integrity: sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==} - '@types/pbkdf2@3.1.2': - resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==} + '@types/pako@2.0.4': + resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==} - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - - '@types/secp256k1@4.0.6': - resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} + '@vitest/coverage-v8@3.2.6': + resolution: {integrity: sha512-LsAdmUapA0qSN306d8+zOyawM0hFm2m2Hg9IwVNIKBm+qJV8cijiq2c+gxKZcB1HCfIWAy+0qEZDCUQA58A1cw==} + peerDependencies: + '@vitest/browser': 3.2.6 + vitest: 3.2.6 + peerDependenciesMeta: + '@vitest/browser': + optional: true - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@vitest/expect@3.2.6': + resolution: {integrity: sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ==} - '@vitest/coverage-v8@1.6.1': - resolution: {integrity: sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==} + '@vitest/mocker@3.2.6': + resolution: {integrity: sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw==} peerDependencies: - vitest: 1.6.1 + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@vitest/expect@1.6.1': - resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==} + '@vitest/pretty-format@3.2.6': + resolution: {integrity: sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==} - '@vitest/runner@1.6.1': - resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==} + '@vitest/runner@3.2.6': + resolution: {integrity: sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q==} - '@vitest/snapshot@1.6.1': - resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==} + '@vitest/snapshot@3.2.6': + resolution: {integrity: sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw==} - '@vitest/spy@1.6.1': - resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==} + '@vitest/spy@3.2.6': + resolution: {integrity: sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg==} - '@vitest/utils@1.6.1': - resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==} + '@vitest/utils@3.2.6': + resolution: {integrity: sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg==} - '@wagmi/connectors@5.8.3': - resolution: {integrity: sha512-U4SJgi91+ny/XDGQWAMmawMafDx1BofcbYkPT/WSU6XrGL+apa7VltscqY7PVmwVGi/CYTqe8nlQiK/wmQ8D3A==} + '@wagmi/connectors@8.0.20': + resolution: {integrity: sha512-c6BRWBJ2bnYq/Spxpc7H/mdnlZ90CPSEZ4NKCin3gM8yRwqkI4J1BzSXF1jAoHO0QjME1rajGGQKrCli3tV3hw==} peerDependencies: - '@wagmi/core': 2.17.2 - typescript: '>=5.0.4' + '@base-org/account': ^2.5.1 + '@coinbase/wallet-sdk': ^4.3.6 + '@metamask/connect-evm': ^2.1.0 + '@safe-global/safe-apps-provider': ~0.18.6 + '@safe-global/safe-apps-sdk': ^9.1.0 + '@wagmi/core': 3.5.5 + '@walletconnect/ethereum-provider': ^2.21.1 + accounts: ~0.14 + porto: ~0.2.35 + typescript: '>=5.9.3' viem: 2.x peerDependenciesMeta: + '@base-org/account': + optional: true + '@coinbase/wallet-sdk': + optional: true + '@metamask/connect-evm': + optional: true + '@safe-global/safe-apps-provider': + optional: true + '@safe-global/safe-apps-sdk': + optional: true + '@walletconnect/ethereum-provider': + optional: true + accounts: + optional: true + porto: + optional: true typescript: optional: true - '@wagmi/core@2.17.2': - resolution: {integrity: sha512-p1z8VU0YuRClx2bdPoFObDF7M2Reitz9AdByrJ+i5zcPCHuJ/UjaWPv6xD7ydhkWVK0hoa8vQ/KtaiEwEQS7Mg==} + '@wagmi/core@2.22.1': + resolution: {integrity: sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==} peerDependencies: '@tanstack/query-core': '>=5.0.0' typescript: '>=5.0.4' @@ -1356,96 +848,23 @@ packages: typescript: optional: true - '@walletconnect/core@2.19.2': - resolution: {integrity: sha512-iu0mgLj51AXcKpdNj8+4EdNNBd/mkNjLEhZn6UMc/r7BM9WbmpPMEydA39WeRLbdLO4kbpmq4wTbiskI1rg+HA==} - engines: {node: '>=18'} - - '@walletconnect/core@2.20.2': - resolution: {integrity: sha512-48XnarxQQrpJ0KZJOjit56DxuzfVRYUdL8XVMvUh/ZNUiX2FB5w6YuljUUeTLfYOf04Et6qhVGEUkmX3W+9/8w==} - engines: {node: '>=18'} - - '@walletconnect/environment@1.0.1': - resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} - - '@walletconnect/ethereum-provider@2.20.2': - resolution: {integrity: sha512-fGNJtytHuBWZcmMXRIG1djlfEiPMvPJ0R3JlfJjAx2VfVN+O+1xdF6QSWcZxFizviIUFJV+f1zWt0V2VVD61Rg==} - - '@walletconnect/events@1.0.1': - resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} - - '@walletconnect/heartbeat@1.2.2': - resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} - - '@walletconnect/jsonrpc-http-connection@1.0.8': - resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} - - '@walletconnect/jsonrpc-provider@1.0.14': - resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} - - '@walletconnect/jsonrpc-types@1.0.4': - resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} - - '@walletconnect/jsonrpc-utils@1.0.8': - resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} - - '@walletconnect/jsonrpc-ws-connection@1.0.16': - resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} - - '@walletconnect/keyvaluestorage@1.1.1': - resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + '@wagmi/core@3.5.5': + resolution: {integrity: sha512-JEJAwo25p9c52JwQs1WunqANPs4PjJ+eepDLXvQJ390vEXsBexYdCDmsPPcYZaGpk0Umx0w85U1ruRodXusMzg==} peerDependencies: - '@react-native-async-storage/async-storage': 1.x + '@tanstack/query-core': '>=5.0.0' + accounts: ~0.14 + typescript: '>=5.9.3' + viem: 2.x peerDependenciesMeta: - '@react-native-async-storage/async-storage': + '@tanstack/query-core': + optional: true + accounts: + optional: true + typescript: optional: true - '@walletconnect/logger@2.1.2': - resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} - - '@walletconnect/relay-api@1.0.11': - resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} - - '@walletconnect/relay-auth@1.1.0': - resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} - - '@walletconnect/safe-json@1.0.2': - resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} - - '@walletconnect/sign-client@2.19.2': - resolution: {integrity: sha512-a/K5PRIFPCjfHq5xx3WYKHAAF8Ft2I1LtxloyibqiQOoUtNLfKgFB1r8sdMvXM7/PADNPe4iAw4uSE6PrARrfg==} - - '@walletconnect/sign-client@2.20.2': - resolution: {integrity: sha512-KyeDToypZ1OjCbij4Jx0cAg46bMwZ6zCKt0HzCkqENcex3Zchs7xBp9r8GtfEMGw+PUnXwqrhzmLBH0x/43oIQ==} - - '@walletconnect/time@1.0.2': - resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} - - '@walletconnect/types@2.19.2': - resolution: {integrity: sha512-/LZWhkVCUN+fcTgQUxArxhn2R8DF+LSd/6Wh9FnpjeK/Sdupx1EPS8okWG6WPAqq2f404PRoNAfQytQ82Xdl3g==} - - '@walletconnect/types@2.20.2': - resolution: {integrity: sha512-XPPbJM/mGU05i6jUxhC3yI/YvhSF6TYJQ5SXTWM53lVe6hs6ukvlEhPctu9ZBTGwGFhwPXIjtK/eWx+v4WY5iw==} - - '@walletconnect/universal-provider@2.19.2': - resolution: {integrity: sha512-LkKg+EjcSUpPUhhvRANgkjPL38wJPIWumAYD8OK/g4OFuJ4W3lS/XTCKthABQfFqmiNbNbVllmywiyE44KdpQg==} - - '@walletconnect/universal-provider@2.20.2': - resolution: {integrity: sha512-6uVu1E88tioaXEEJCbJKwCIQlOHif1nmfY092BznZEnBn2lli5ICzQh2bxtUDNmNNLKsMDI3FV1fODFeWMVJTQ==} - - '@walletconnect/utils@2.19.2': - resolution: {integrity: sha512-VU5CcUF4sZDg8a2/ov29OJzT3KfLuZqJUM0GemW30dlipI5fkpb0VPenZK7TcdLPXc1LN+Q+7eyTqHRoAu/BIA==} - - '@walletconnect/utils@2.20.2': - resolution: {integrity: sha512-2uRUDvpYSIJFYcr1WIuiFy6CEszLF030o6W8aDMkGk9/MfAZYEJQHMJcjWyaNMPHLJT0POR5lPaqkYOpuyPIQQ==} - - '@walletconnect/window-getters@1.0.1': - resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} - - '@walletconnect/window-metadata@1.0.1': - resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} - - abitype@0.9.10: - resolution: {integrity: sha512-FIS7U4n7qwAT58KibwYig5iFG4K61rbhAqaQh/UWj8v1Y8mjX3F8TC9gd8cz9yT1TYel9f8nS5NO5kZp2RW0jQ==} + abitype@1.0.8: + resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} peerDependencies: typescript: '>=5.0.4' zod: ^3 >=3.22.0 @@ -1455,102 +874,39 @@ packages: zod: optional: true - abitype@1.0.8: - resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} peerDependencies: typescript: '>=5.0.4' - zod: ^3 >=3.22.0 + zod: ^3.22.0 || ^4.0.0 peerDependenciesMeta: typescript: optional: true zod: optional: true - abortcontroller-polyfill@1.7.8: - resolution: {integrity: sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ==} - - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - - adm-zip@0.4.16: - resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} - engines: {node: '>=0.3.0'} - - aes-js@3.0.0: - resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - - ansi-colors@3.2.4: - resolution: {integrity: sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==} - engines: {node: '>=6'} - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@2.1.1: - resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} - engines: {node: '>=0.10.0'} - - ansi-regex@3.0.1: - resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} - engines: {node: '>=4'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-sequence-parser@1.1.3: resolution: {integrity: sha512-+fksAx9eG3Ab6LDnLs3ZqZa8KVJ/jYnX+D4Qe1azX+LFGFAXqynCQLOdLpNYN/l9e7l6hMWwZbrnctqr6eSQSw==} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1558,67 +914,48 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} - async-mutex@0.2.6: - resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} + ast-v8-to-istanbul@0.3.12: + resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - aws-sign2@0.7.0: - resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - - aws4@1.13.2: - resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - - axios@0.21.4: - resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} - - axios@1.9.0: - resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} - b4a@1.6.7: - resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + b4a@1.8.0: + resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.5.4: - resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true - bare-fs@4.1.5: - resolution: {integrity: sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==} + bare-fs@4.6.0: + resolution: {integrity: sha512-2YkS7NuiJceSEbyEOdSNLE9tsGd+f4+f7C+Nik/MCk27SYdwIMPT/yRKvg++FZhQXgk0KWJKJyXX9RhVV0RGqA==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -1626,9838 +963,2845 @@ packages: bare-buffer: optional: true - bare-os@3.6.1: - resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} + bare-os@3.8.7: + resolution: {integrity: sha512-G4Gr1UsGeEy2qtDTZwL7JFLo2wapUarz7iTMcYcMFdS89AIQuBoyjgXZz0Utv7uHs3xA9LckhVbeBi8lEQrC+w==} engines: {bare: '>=1.14.0'} bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-stream@2.6.5: - resolution: {integrity: sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==} + bare-stream@2.12.0: + resolution: {integrity: sha512-w28i8lkBgREV3rPXGbgK+BO66q+ZpKqRWrZLiCdmmUlLPrQ45CzkvRhN+7lnv00Gpi2zy5naRxnUFAxCECDm9g==} peerDependencies: + bare-abort-controller: '*' bare-buffer: '*' bare-events: '*' peerDependenciesMeta: + bare-abort-controller: + optional: true bare-buffer: optional: true bare-events: optional: true - base-x@3.0.11: - resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - - base-x@5.0.1: - resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - - bech32@1.1.4: - resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} + bare-url@2.4.0: + resolution: {integrity: sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==} better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - big-integer@1.6.36: - resolution: {integrity: sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==} - engines: {node: '>=0.6'} + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} - big.js@6.2.2: - resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + engines: {node: 18 || 20 || >=22} - bignumber.js@7.2.1: - resolution: {integrity: sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==} - - bignumber.js@9.3.0: - resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - blakejs@1.2.1: - resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} - bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} - bn.js@4.11.6: - resolution: {integrity: sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - bn.js@5.2.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} - body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + cli-progress@3.12.0: + resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} + engines: {node: '>=4'} - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} - bowser@2.11.0: - resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} - boxen@5.1.2: - resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} - engines: {node: '>=10'} + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} + engines: {node: '>=18'} + hasBin: true - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} - browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} - browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true - bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} - bs58@6.0.0: - resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} - bs58check@2.1.2: - resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} - buffer-to-arraybuffer@0.0.5: - resolution: {integrity: sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==} + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} - buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + docker-compose@1.4.2: + resolution: {integrity: sha512-rPHigTKGaEHpkUmfd69QgaOp+Os5vGJwG/Ry8lcr8W/382AmI+z/D7qoa9BybKIkqNppaIbs8RYeHSevdQjWww==} + engines: {node: '>= 6.0.0'} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} - bufferutil@4.0.9: - resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} - engines: {node: '>=6.14.2'} + dunai@0.0.0-dev.2: + resolution: {integrity: sha512-+xodG+LL8JVXz3Vfx/xI1qEcXwBUjfvTMFKNiYDXIk7FJDUiGi1zOUQrpQTn3Zd9n7lxPGSAmH9Z3wDrUac+xw==} + engines: {node: '>=18'} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - cacheable-lookup@6.1.0: - resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==} - engines: {node: '>=10.6.0'} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + error-stack-parser@1.3.6: + resolution: {integrity: sha512-xhuSYd8wLgOXwNgjcPeXMPL/IiiA1Huck+OPvClpJViVNNlJVtM41o+1emp7bPvlCJwCatFX2DWc05/DgfbWzA==} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - camel-case@3.0.0: - resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - camelcase@3.0.0: - resolution: {integrity: sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==} - engines: {node: '>=0.10.0'} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} - camelcase@4.1.0: - resolution: {integrity: sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==} - engines: {node: '>=4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true - caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - cbor@5.2.0: - resolution: {integrity: sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==} - engines: {node: '>=6.0.0'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - cbor@8.1.0: - resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==} - engines: {node: '>=12.19'} + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - chai-as-promised@7.1.2: - resolution: {integrity: sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==} - peerDependencies: - chai: '>= 2.1.2 < 6' + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - chai-bn@0.2.2: - resolution: {integrity: sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: - bn.js: ^4.11.0 - chai: ^4.0.0 + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true - change-case@3.0.2: - resolution: {integrity: sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} - cheerio-select@2.1.0: - resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] - cheerio@1.0.0: - resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} - engines: {node: '>=18.17'} + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} - ci-info@2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true - cids@0.7.5: - resolution: {integrity: sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==} - engines: {node: '>=4.0.0', npm: '>=3.0.0'} - deprecated: This module has been superseded by the multiformats module + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} - cipher-base@1.0.6: - resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==} - engines: {node: '>= 0.10'} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} - class-is@1.1.0: - resolution: {integrity: sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} + graphql-request@7.2.0: + resolution: {integrity: sha512-0GR7eQHBFYz372u9lxS16cOtEekFlZYB2qOyq8wDvzRmdRSJ0mgUVX1tzNcIzk3G+4NY+mGtSz411wZdeDF/+A==} + peerDependencies: + graphql: 14 - 16 - cli-boxes@2.2.1: - resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} - engines: {node: '>=6'} + graphql@16.13.2: + resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - cli-progress@3.12.0: - resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} - engines: {node: '>=4'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} - cliui@3.2.0: - resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} + hasBin: true - clones-with-immutable-args@https://codeload.github.com/Arachnid/clones-with-immutable-args/tar.gz/23768824cdc037f361f7065538b8f949cae9d3d1: - resolution: {tarball: https://codeload.github.com/Arachnid/clones-with-immutable-args/tar.gz/23768824cdc037f361f7065538b8f949cae9d3d1} - version: 1.1.0 + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} - clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} - code-point-at@1.1.0: - resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} - colors@1.4.0: - resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} - engines: {node: '>=0.1.90'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - command-exists@1.2.9: - resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' - commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} - engines: {node: '>=20'} + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} - concurrently@9.1.2: - resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} - engines: {node: '>=18'} - hasBin: true + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} - constant-case@2.0.0: - resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + joi@18.1.2: + resolution: {integrity: sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==} + engines: {node: '>= 20'} - content-hash@2.5.2: - resolution: {integrity: sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - cookie-es@1.2.2: - resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true - cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true - cookie@0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} - engines: {node: '>= 0.6'} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} - engines: {node: '>= 0.6'} + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - cross-fetch@3.2.0: - resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - cross-fetch@4.1.0: - resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} + marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true - crossws@0.3.5: - resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} - crypto-addr-codec@0.1.8: - resolution: {integrity: sha512-GqAK90iLLgP3FvhNmHbpT3wR6dEdaM8hZyZtLX29SPardh3OA13RFLHDR6sntGCgRWOfiHqW6sIyohpNqOtV/g==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} - d@1.0.2: - resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} - engines: {node: '>=0.12'} + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} - dashdash@1.14.1: - resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} - engines: {node: '>=0.10'} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} - dataloader@1.4.0: - resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} peerDependencies: - supports-color: '*' + typescript: '>=5.0.4' peerDependenciesMeta: - supports-color: + typescript: optional: true - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: - supports-color: '*' + encoding: ^0.1.0 peerDependenciesMeta: - supports-color: + encoding: optional: true - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - decode-uri-component@0.2.2: - resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} - engines: {node: '>=0.10'} + ox@0.14.7: + resolution: {integrity: sha512-zSQ/cfBdolj7U4++NAvH7sI+VG0T3pEohITCgcQj8KlawvTDY4vGVhDT64Atsm0d6adWfIYHDpu88iUBMMp+AQ==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true - decompress-response@3.3.0: - resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} - engines: {node: '>=4'} + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} - deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} - delete-empty@3.0.0: - resolution: {integrity: sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==} - engines: {node: '>=10'} - hasBin: true + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - derive-valtio@0.1.0: - resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} - peerDependencies: - valtio: '*' + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} - detect-browser@5.3.0: - resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} - detect-indent@5.0.0: - resolution: {integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==} - engines: {node: '>=4'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} - dns-packet@5.6.1: - resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - docker-compose@1.2.0: - resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==} - engines: {node: '>= 6.0.0'} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - dom-walk@0.1.2: - resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} - dot-case@2.1.1: - resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - dotenv@16.5.0: - resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} - engines: {node: '>=12'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - dotenv@8.6.0: - resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} - engines: {node: '>=10'} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dunai@0.0.0-dev.2: - resolution: {integrity: sha512-+xodG+LL8JVXz3Vfx/xI1qEcXwBUjfvTMFKNiYDXIk7FJDUiGi1zOUQrpQTn3Zd9n7lxPGSAmH9Z3wDrUac+xw==} - engines: {node: '>=18'} + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} - duplexify@4.1.3: - resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} - ecc-jsbn@0.1.2: - resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - eciesjs@0.4.15: - resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - elliptic@6.5.4: - resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - elliptic@6.6.1: - resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true - encode-utf8@1.0.3: - resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} - encoding-sniffer@0.2.0: - resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + shiki@0.14.7: + resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - engine.io-client@6.6.3: - resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} - engine.io-parser@5.2.3: - resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} - engines: {node: '>=10.0.0'} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} - entities@6.0.0: - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} - engines: {node: '>=0.12'} + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + stack-generator@1.1.0: + resolution: {integrity: sha512-sZDVjwC56vZoo+a5t0LH/1sMQLWYLi/r+Z2ztyCAOhOX3QBP34GWxK0FWf2eU1TIU2CJKCKBAtDZycUh/ZKMlw==} - error-stack-parser@1.3.6: - resolution: {integrity: sha512-xhuSYd8wLgOXwNgjcPeXMPL/IiiA1Huck+OPvClpJViVNNlJVtM41o+1emp7bPvlCJwCatFX2DWc05/DgfbWzA==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} + stackframe@0.3.1: + resolution: {integrity: sha512-XmoiF4T5nuWEp2x2w92WdGjdHGY/cZa6LIbRsDRQR/Xlk4uW0PAUlH1zJYVffocwKpCdwyuypIp25xsSXEtZHw==} - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} + stacktrace-gps@2.4.4: + resolution: {integrity: sha512-msFhuMEEklQLUtaJ+GeCDjzUN+PamfHWQiK3C1LnbHjoxSeF5dAxiE+aJkptNMmMNOropGFJ7G3ZT7dPZHgDaQ==} - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} + stacktrace-js@1.3.1: + resolution: {integrity: sha512-b+5voFnXqg9TWdOE50soXL+WuOreYUm1Ukg9U7rzEWGL4+gcVxIcFasNBtOffVX0I1lYqVZj0PZXZvTt5e3YRQ==} - es-toolkit@1.33.0: - resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - es5-ext@0.10.64: - resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} - engines: {node: '>=0.10'} + streamx@2.25.0: + resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} - es6-iterator@2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} - es6-promise@4.2.8: - resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} - es6-symbol@3.1.4: - resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} - engines: {node: '>=0.12'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - esniff@2.0.1: - resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} - engines: {node: '>=0.10'} + tar-fs@3.1.2: + resolution: {integrity: sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true + tar-stream@3.1.8: + resolution: {integrity: sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==} - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} - eth-block-tracker@7.1.0: - resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} - engines: {node: '>=14.0.0'} + test-exclude@7.0.2: + resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} + engines: {node: '>=18'} - eth-ens-namehash@2.0.8: - resolution: {integrity: sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==} + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - eth-json-rpc-filters@6.0.1: - resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==} - engines: {node: '>=14.0.0'} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - eth-lib@0.1.29: - resolution: {integrity: sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - eth-lib@0.2.8: - resolution: {integrity: sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} - eth-query@2.1.2: - resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} - eth-rpc-errors@4.0.3: - resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} - ethereum-bloom-filters@1.2.0: - resolution: {integrity: sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==} + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} - ethereum-cryptography@0.1.3: - resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} - ethereum-cryptography@1.2.0: - resolution: {integrity: sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ethereum-cryptography@2.2.1: - resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true - ethereumjs-util@7.1.5: - resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} - engines: {node: '>=10.0.0'} + ts-pattern@5.9.0: + resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} - ethers@4.0.49: - resolution: {integrity: sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - ethers@5.8.0: - resolution: {integrity: sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==} + typedoc-plugin-markdown@4.11.0: + resolution: {integrity: sha512-2iunh2ALyfyh204OF7h2u0kuQ84xB3jFZtFyUr01nThJkLvR8oGGSSDlyt2gyO4kXhvUxDcVbO0y43+qX+wFbw==} + engines: {node: '>= 18'} + peerDependencies: + typedoc: 0.28.x - ethjs-abi@0.2.1: - resolution: {integrity: sha512-g2AULSDYI6nEJyJaEVEXtTimRY2aPC2fi7ddSy0W+LXvEVL8Fe1y76o43ecbgdUKwZD+xsmEgX1yJr1Ia3r1IA==} - engines: {node: '>=6.5.0', npm: '>=3'} + typedoc@0.24.8: + resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==} + engines: {node: '>= 14.14'} + hasBin: true + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - ethjs-unit@0.1.6: - resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==} - engines: {node: '>=6.5.0', npm: '>=3'} + typescript-logging@1.0.1: + resolution: {integrity: sha512-zp28ABme0m5q/nXabBaY9Hv/35N8lMH4FsvhpUO0zVi4vFs3uKlb5br2it61HAZF5k+U0aP6E67j0VD0IzXGpQ==} - event-emitter@0.3.5: - resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - - eventemitter2@6.4.9: - resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} - - eventemitter3@4.0.4: - resolution: {integrity: sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==} - - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - express@4.21.2: - resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} - engines: {node: '>= 0.10.0'} - - ext@1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - extension-port-stream@3.0.0: - resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} - engines: {node: '>=12.0.0'} - - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - extsprintf@1.3.0: - resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} - engines: {'0': node >=0.6.0} - - fast-check@3.1.1: - resolution: {integrity: sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA==} - engines: {node: '>=8.0.0'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true - fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - fdir@6.4.5: - resolution: {integrity: sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==} + viem@2.29.0: + resolution: {integrity: sha512-N6GeIuuay/spDyw+5FbSuNIkVN0da+jGOjdlC0bdatIN+N0jtOf9Zfj0pbXgpIJGwnM9ocxzTRt0HZVbHBdL2Q==} peerDependencies: - picomatch: ^3 || ^4 + typescript: '>=5.0.4' peerDependenciesMeta: - picomatch: + typescript: optional: true - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - filter-obj@1.1.0: - resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} - engines: {node: '>=0.10.0'} - - finalhandler@1.3.1: - resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} - engines: {node: '>= 0.8'} - - find-up@1.1.2: - resolution: {integrity: sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==} - engines: {node: '>=0.10.0'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - - fmix@0.1.0: - resolution: {integrity: sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==} - - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} + viem@2.47.10: + resolution: {integrity: sha512-D+l6SDDZWB5bh8u9hgICzMX2/egMrgEQ+Pef/QkZgmOl6bOTyCQMSgWAH8jZTWJ/218J9QNv7s/9BH6Wu5oPDg==} peerDependencies: - debug: '*' + typescript: '>=5.0.4' peerDependenciesMeta: - debug: + typescript: optional: true - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - forever-agent@0.6.1: - resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - - form-data-encoder@1.7.1: - resolution: {integrity: sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==} - - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} - engines: {node: '>= 6'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fp-ts@1.19.3: - resolution: {integrity: sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==} - - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - - fs-extra@0.30.0: - resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} - - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - - fs-extra@4.0.3: - resolution: {integrity: sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs-minipass@1.2.7: - resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-caller-file@1.0.3: - resolution: {integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - getpass@0.1.7: - resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported - - global@4.4.0: - resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - - got@12.1.0: - resolution: {integrity: sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==} - engines: {node: '>=14.16'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphql-request@7.1.2: - resolution: {integrity: sha512-+XE3iuC55C2di5ZUrB4pjgwe+nIQBuXVIK9J98wrVwojzDW3GMdSBZfxUk8l4j9TieIpjpggclxhNEU9ebGF8w==} - peerDependencies: - graphql: 14 - 16 - - graphql@16.11.0: - resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - - h3@1.15.3: - resolution: {integrity: sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==} - - happy-dom@13.10.1: - resolution: {integrity: sha512-9GZLEFvQL5EgfJX2zcBgu1nsPUn98JF/EiJnSfQbdxI6YEQGqpd09lXXxOmYonRBIEFz9JlGCOiPflDzgS1p8w==} - engines: {node: '>=16.0.0'} - - har-schema@2.0.0: - resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} - engines: {node: '>=4'} - - har-validator@5.1.5: - resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} - engines: {node: '>=6'} - deprecated: this library is no longer supported - - hardhat-abi-exporter@2.11.0: - resolution: {integrity: sha512-hBC4Xzncew9pdqVpzWoEEBJUthp99TCH39cHlMehVxBBQ6EIsIFyj3N0yd0hkVDfM8/s/FMRAuO5jntZBpwCZQ==} - engines: {node: '>=14.14.0'} - peerDependencies: - hardhat: ^2.0.0 - - hardhat-deploy@1.0.2: - resolution: {integrity: sha512-FCeoGpYLQiGjROURihth6Qye+GWvRqalGbRsCTW33canhAS4g6mJEILbkWbWF8P2cPWgcpxe3cCJksk6n+oD7Q==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true - hardhat@2.24.1: - resolution: {integrity: sha512-3iwrO2liEGCw1rz/l/mlB1rSNexCc4CFcMj0DlvjXGChzmD3sGUgLwWDOZPf+ya8MEm5ZhO1oprRVmb/wVi0YA==} + vite@7.3.6: + resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - ts-node: '*' - typescript: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: - ts-node: + '@types/node': optional: true - typescript: + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: optional: true - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} - - hash-test-vectors@1.3.2: - resolution: {integrity: sha512-PKd/fitmsrlWGh3OpKbgNLE04ZQZsvs1ZkuLoQpeIKuwx+6CYVNdW6LaPIS1QAdZvV40+skk0w4YomKnViUnvQ==} - - hash.js@1.1.3: - resolution: {integrity: sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==} - - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - header-case@1.0.1: - resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - highlightjs-solidity@2.0.6: - resolution: {integrity: sha512-DySXWfQghjm2l6a/flF+cteroJqD4gI8GSdL4PtvxZSsAHie8m3yVe2JFoRg03ROKT6hp2Lc/BxXkqerNmtQYg==} - - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - htmlparser2@9.1.0: - resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - http-https@1.0.0: - resolution: {integrity: sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==} - - http-signature@1.2.0: - resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} - engines: {node: '>=0.8', npm: '>=1.3.7'} - - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-id@4.1.1: - resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} + vitest@3.2.6: + resolution: {integrity: sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.6 + '@vitest/ui': 3.2.6 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - idb-keyval@6.2.2: - resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} - - idna-uts46-hx@2.3.1: - resolution: {integrity: sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==} - engines: {node: '>=4.0.0'} + vscode-oniguruma@1.7.0: + resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + vscode-textmate@8.0.0: + resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - - imul@1.0.1: - resolution: {integrity: sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==} - engines: {node: '>=0.10.0'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - invert-kv@1.0.0: - resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==} - engines: {node: '>=0.10.0'} - - io-ts@1.10.4: - resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - iron-webcrypto@1.2.1: - resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} - - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@1.0.0: - resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-function@1.0.2: - resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} - - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-hex-prefixed@1.0.0: - resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} - engines: {node: '>=6.5.0', npm: '>=3'} - - is-lower-case@1.1.3: - resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-upper-case@1.1.2: - resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} - - is-utf8@0.2.1: - resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isows@1.0.6: - resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} - peerDependencies: - ws: '*' - - isows@1.0.7: - resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} - peerDependencies: - ws: '*' - - isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - - joi@17.13.3: - resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} - - js-sha3@0.5.5: - resolution: {integrity: sha512-yLLwn44IVeunwjpDVTDZmQeVbB0h+dZpY2eO68B/Zik8hu6dH+rKeLxwua79GGIvW6xr8NBAcrtiUbYrTjEFTA==} - - js-sha3@0.5.7: - resolution: {integrity: sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==} - - js-sha3@0.8.0: - resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsbn@0.1.1: - resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-rpc-engine@6.1.0: - resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} - engines: {node: '>=10.0.0'} - - json-rpc-random-id@1.0.1: - resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - - json-stream-stringify@3.1.6: - resolution: {integrity: sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==} - engines: {node: '>=7.10.1'} - - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - - jsonfile@2.4.0: - resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - jsprim@1.4.2: - resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} - engines: {node: '>=0.6.0'} - - keccak@3.0.4: - resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} - engines: {node: '>=10.0.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - keyvaluestorage-interface@1.0.0: - resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} - - klaw@1.3.1: - resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} - - lcid@1.0.0: - resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==} - engines: {node: '>=0.10.0'} - - lit-element@4.2.0: - resolution: {integrity: sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==} - - lit-html@3.3.0: - resolution: {integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==} - - lit@3.1.0: - resolution: {integrity: sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==} - - load-json-file@1.1.0: - resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==} - engines: {node: '>=0.10.0'} - - local-pkg@0.5.1: - resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} - engines: {node: '>=14'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.assign@4.2.0: - resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} - - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - - lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - - lower-case-first@1.0.2: - resolution: {integrity: sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==} - - lower-case@1.1.4: - resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} - - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru_map@0.3.3: - resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} - - lunr@2.3.9: - resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} - engines: {node: '>= 12'} - hasBin: true - - match-all@1.2.7: - resolution: {integrity: sha512-qSpsBKarh55r9KyXzFC3xBLRf2GlGasba2em9kbpRsSlGvdTAqjx3QD0r3FKSARiW+OE4iMHYsolM3aX9n5djw==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - - micro-eth-signer@0.14.0: - resolution: {integrity: sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==} - - micro-ftch@0.3.1: - resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} - - micro-packed@0.7.3: - resolution: {integrity: sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - min-document@2.19.0: - resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@2.9.0: - resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==} - - minizlib@1.3.3: - resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==} - - mipd@0.0.7: - resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + wagmi@3.6.21: + resolution: {integrity: sha512-ao/Zb4Uz1KJvFNpo13DVeWo4eMkNb06RU1uk/xHm+PTGURwP2NHAysZx/CHCV2WS2b1f9MlhYgSCiI5shx5vUA==} peerDependencies: - typescript: '>=5.0.4' + '@tanstack/react-query': '>=5.0.0' + react: '>=18' + typescript: '>=5.9.3' + viem: 2.x peerDependenciesMeta: typescript: optional: true - mkdirp-promise@5.0.1: - resolution: {integrity: sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==} - engines: {node: '>=4'} - deprecated: This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that. - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - - mnemonist@0.38.5: - resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} - - mocha@10.8.2: - resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} - engines: {node: '>= 14.0.0'} - hasBin: true - - mock-fs@4.14.0: - resolution: {integrity: sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==} - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - multibase@0.6.1: - resolution: {integrity: sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==} - deprecated: This module has been superseded by the multiformats module - - multibase@0.7.0: - resolution: {integrity: sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==} - deprecated: This module has been superseded by the multiformats module - - multicodec@0.5.7: - resolution: {integrity: sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==} - deprecated: This module has been superseded by the multiformats module - - multicodec@1.0.4: - resolution: {integrity: sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==} - deprecated: This module has been superseded by the multiformats module - - multiformats@12.1.3: - resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} - engines: {node: '>=16.0.0', npm: '>=7.0.0'} - - multiformats@9.9.0: - resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} - - multihashes@0.4.21: - resolution: {integrity: sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==} - - murmur-128@0.2.1: - resolution: {integrity: sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg==} - - nano-base32@1.0.1: - resolution: {integrity: sha512-sxEtoTqAPdjWVGv71Q17koMFGsOMSiHsIFEvzOM7cNp8BXB4AnEwmDabm5dorusJf/v1z7QxaZYxUorU9RKaAw==} - - nano-json-stream-parser@0.1.2: - resolution: {integrity: sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - next-tick@1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - - no-case@2.3.2: - resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} - - node-addon-api@2.0.2: - resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - - node-addon-api@5.1.0: - resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - - node-fetch-native@1.6.6: - resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - node-mock-http@1.0.0: - resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==} - - nofilter@1.0.4: - resolution: {integrity: sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==} - engines: {node: '>=8'} - - nofilter@3.1.0: - resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} - engines: {node: '>=12.19'} - - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - number-is-nan@1.0.1: - resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} - engines: {node: '>=0.10.0'} - - number-to-bn@1.7.0: - resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==} - engines: {node: '>=6.5.0', npm: '>=3'} - - oauth-sign@0.9.0: - resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - - obj-multiplex@1.0.0: - resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - obliterator@2.0.5: - resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} - - oboe@2.1.5: - resolution: {integrity: sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==} - - ofetch@1.4.1: - resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} - - on-exit-leak-free@0.2.0: - resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - os-locale@1.4.0: - resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==} - engines: {node: '>=0.10.0'} - - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - ox@0.6.7: - resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - ox@0.6.9: - resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - ox@0.7.1: - resolution: {integrity: sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - - pako@2.1.0: - resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - - param-case@2.1.1: - resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} - - parse-headers@2.0.6: - resolution: {integrity: sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==} - - parse-json@2.2.0: - resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==} - engines: {node: '>=0.10.0'} - - parse5-htmlparser2-tree-adapter@7.1.0: - resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} - - parse5-parser-stream@7.1.2: - resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - pascal-case@2.0.1: - resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==} - - path-case@2.1.1: - resolution: {integrity: sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==} - - path-exists@2.1.0: - resolution: {integrity: sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==} - engines: {node: '>=0.10.0'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-starts-with@2.0.1: - resolution: {integrity: sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==} - engines: {node: '>=8'} - - path-to-regexp@0.1.12: - resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - - path-type@1.1.0: - resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} - engines: {node: '>=0.10.0'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - - pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} - - performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pify@5.0.0: - resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} - engines: {node: '>=10'} - - pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - - pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - - pino-abstract-transport@0.5.0: - resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} - - pino-std-serializers@4.0.0: - resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} - - pino@7.11.0: - resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} - hasBin: true - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} - - pony-cause@2.1.11: - resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==} - engines: {node: '>=12.0.0'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - postcss@8.5.4: - resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} - engines: {node: ^10 || ^12 || >=14} - - preact@10.26.8: - resolution: {integrity: sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process-warning@1.0.0: - resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - proxy-compare@2.6.0: - resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} - - punycode@2.1.0: - resolution: {integrity: sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==} - engines: {node: '>=6'} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - pure-rand@5.0.5: - resolution: {integrity: sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==} - - qrcode@1.5.3: - resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} - engines: {node: '>=10.13.0'} - hasBin: true - - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - qs@6.5.3: - resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} - engines: {node: '>=0.6'} - - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - - query-string@5.1.1: - resolution: {integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==} - engines: {node: '>=0.10.0'} - - query-string@7.1.3: - resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} - engines: {node: '>=6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - radix3@1.1.2: - resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} - - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - read-pkg-up@1.0.1: - resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} - engines: {node: '>=0.10.0'} - - read-pkg@1.1.0: - resolution: {integrity: sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==} - engines: {node: '>=0.10.0'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - real-require@0.1.0: - resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} - engines: {node: '>= 12.13.0'} - - request@2.88.2: - resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} - engines: {node: '>= 6'} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@1.2.1: - resolution: {integrity: sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - require-main-filename@1.0.1: - resolution: {integrity: sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.17.0: - resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==} - - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - ripemd160-min@0.0.6: - resolution: {integrity: sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==} - engines: {node: '>=8'} - - ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - - rlp@2.2.7: - resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} - hasBin: true - - rollup@4.41.1: - resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - scrypt-js@2.0.4: - resolution: {integrity: sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==} - - scrypt-js@3.0.1: - resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} - - secp256k1@4.0.4: - resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==} - engines: {node: '>=18.0.0'} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} - - sentence-case@2.1.1: - resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==} - - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} - engines: {node: '>= 0.8.0'} - - servify@0.1.12: - resolution: {integrity: sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==} - engines: {node: '>=6'} - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setimmediate@1.0.4: - resolution: {integrity: sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - - sha3@2.1.4: - resolution: {integrity: sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} - engines: {node: '>= 0.4'} - - shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@2.8.2: - resolution: {integrity: sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - - snake-case@2.1.0: - resolution: {integrity: sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==} - - socket.io-client@4.8.1: - resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} - engines: {node: '>=10.0.0'} - - socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} - engines: {node: '>=10.0.0'} - - solc@0.4.26: - resolution: {integrity: sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==} - hasBin: true - - solc@0.8.26: - resolution: {integrity: sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==} - engines: {node: '>=10.0.0'} - hasBin: true - - solc@0.8.30: - resolution: {integrity: sha512-9Srk/gndtBmoUbg4CE6ypAzPQlElv8ntbnl6SigUBAzgXKn35v87sj04uZeoZWjtDkdzT0qKFcIo/wl63UMxdw==} - engines: {node: '>=12.0.0'} - hasBin: true - - sonic-boom@2.8.0: - resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.5.6: - resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.21: - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} - - split-on-first@1.1.0: - resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} - engines: {node: '>=6'} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sshpk@1.18.0: - resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} - engines: {node: '>=0.10.0'} - hasBin: true - - stack-generator@1.1.0: - resolution: {integrity: sha512-sZDVjwC56vZoo+a5t0LH/1sMQLWYLi/r+Z2ztyCAOhOX3QBP34GWxK0FWf2eU1TIU2CJKCKBAtDZycUh/ZKMlw==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - stackframe@0.3.1: - resolution: {integrity: sha512-XmoiF4T5nuWEp2x2w92WdGjdHGY/cZa6LIbRsDRQR/Xlk4uW0PAUlH1zJYVffocwKpCdwyuypIp25xsSXEtZHw==} - - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - stacktrace-gps@2.4.4: - resolution: {integrity: sha512-msFhuMEEklQLUtaJ+GeCDjzUN+PamfHWQiK3C1LnbHjoxSeF5dAxiE+aJkptNMmMNOropGFJ7G3ZT7dPZHgDaQ==} - - stacktrace-js@1.3.1: - resolution: {integrity: sha512-b+5voFnXqg9TWdOE50soXL+WuOreYUm1Ukg9U7rzEWGL4+gcVxIcFasNBtOffVX0I1lYqVZj0PZXZvTt5e3YRQ==} - - stacktrace-parser@0.1.11: - resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} - engines: {node: '>=6'} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - - stream-shift@1.0.3: - resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - - streamx@2.22.0: - resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==} - - strict-uri-encode@1.1.0: - resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} - engines: {node: '>=0.10.0'} - - strict-uri-encode@2.0.0: - resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} - engines: {node: '>=4'} - - string-width@1.0.2: - resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} - engines: {node: '>=0.10.0'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} - - strip-ansi@4.0.0: - resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} - engines: {node: '>=4'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-bom@2.0.0: - resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} - engines: {node: '>=0.10.0'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-hex-prefix@1.0.0: - resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} - engines: {node: '>=6.5.0', npm: '>=3'} - - strip-indent@2.0.0: - resolution: {integrity: sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==} - engines: {node: '>=4'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strip-literal@2.1.1: - resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} - - superstruct@1.0.4: - resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} - engines: {node: '>=14.0.0'} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - swap-case@1.1.2: - resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} - - swarm-js@0.1.42: - resolution: {integrity: sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==} - - table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} - - tar-fs@3.0.9: - resolution: {integrity: sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==} - - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - - tar@4.4.19: - resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==} - engines: {node: '>=4.5'} - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - - testrpc@0.0.1: - resolution: {integrity: sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA==} - deprecated: testrpc has been renamed to ganache-cli, please use this package from now on. - - text-decoder@1.2.3: - resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} - - thread-stream@0.15.2: - resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} - - timed-out@4.0.1: - resolution: {integrity: sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==} - engines: {node: '>=0.10.0'} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} - engines: {node: '>=12.0.0'} - - tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} - - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - - title-case@2.1.1: - resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - tough-cookie@2.5.0: - resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} - engines: {node: '>=0.8'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - ts-pattern@5.7.1: - resolution: {integrity: sha512-EGs8PguQqAAUIcQfK4E9xdXxB6s2GK4sJfT/vcc9V1ELIvC4LH/zXu2t/5fajtv6oiRCxdv7BgtVK3vWgROxag==} - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsort@0.0.1: - resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} - - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@0.7.1: - resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} - engines: {node: '>=8'} - - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - - type@2.7.3: - resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} - - typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - - typedoc-plugin-markdown@4.6.4: - resolution: {integrity: sha512-AnbToFS1T1H+n40QbO2+i0wE6L+55rWnj7zxnM1r781+2gmhMF2dB6dzFpaylWLQYkbg4D1Y13sYnne/6qZwdw==} - engines: {node: '>= 18'} - peerDependencies: - typedoc: 0.28.x - - typedoc@0.24.8: - resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==} - engines: {node: '>= 14.14'} - hasBin: true - peerDependencies: - typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - - typescript-logging@1.0.1: - resolution: {integrity: sha512-zp28ABme0m5q/nXabBaY9Hv/35N8lMH4FsvhpUO0zVi4vFs3uKlb5br2it61HAZF5k+U0aP6E67j0VD0IzXGpQ==} - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - - uint8arrays@3.1.0: - resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} - - ultron@1.1.1: - resolution: {integrity: sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==} - - uncrypto@0.1.3: - resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - undici@5.29.0: - resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} - engines: {node: '>=14.0'} - - undici@6.21.3: - resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} - engines: {node: '>=18.17'} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - unstorage@1.16.0: - resolution: {integrity: sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA==} - peerDependencies: - '@azure/app-configuration': ^1.8.0 - '@azure/cosmos': ^4.2.0 - '@azure/data-tables': ^13.3.0 - '@azure/identity': ^4.6.0 - '@azure/keyvault-secrets': ^4.9.0 - '@azure/storage-blob': ^12.26.0 - '@capacitor/preferences': ^6.0.3 || ^7.0.0 - '@deno/kv': '>=0.9.0' - '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 - '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.34.3 - '@vercel/blob': '>=0.27.1' - '@vercel/kv': ^1.0.1 - aws4fetch: ^1.0.20 - db0: '>=0.2.1' - idb-keyval: ^6.2.1 - ioredis: ^5.4.2 - uploadthing: ^7.4.4 - peerDependenciesMeta: - '@azure/app-configuration': - optional: true - '@azure/cosmos': - optional: true - '@azure/data-tables': - optional: true - '@azure/identity': - optional: true - '@azure/keyvault-secrets': - optional: true - '@azure/storage-blob': - optional: true - '@capacitor/preferences': - optional: true - '@deno/kv': - optional: true - '@netlify/blobs': - optional: true - '@planetscale/database': - optional: true - '@upstash/redis': - optional: true - '@vercel/blob': - optional: true - '@vercel/kv': - optional: true - aws4fetch: - optional: true - db0: - optional: true - idb-keyval: - optional: true - ioredis: - optional: true - uploadthing: - optional: true - - upper-case-first@1.1.2: - resolution: {integrity: sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==} - - upper-case@1.1.3: - resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - url-set-query@1.0.0: - resolution: {integrity: sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==} - - use-sync-external-store@1.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - - use-sync-external-store@1.4.0: - resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - utf-8-validate@5.0.10: - resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} - engines: {node: '>=6.14.2'} - - utf8@3.0.0: - resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - uuid@2.0.1: - resolution: {integrity: sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - - uuid@3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - valtio@1.13.2: - resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=16.8' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - - varint@5.0.2: - resolution: {integrity: sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - verror@1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - engines: {'0': node >=0.6.0} - - viem@2.23.2: - resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - viem@2.29.0: - resolution: {integrity: sha512-N6GeIuuay/spDyw+5FbSuNIkVN0da+jGOjdlC0bdatIN+N0jtOf9Zfj0pbXgpIJGwnM9ocxzTRt0HZVbHBdL2Q==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - viem@2.30.6: - resolution: {integrity: sha512-N3vGy3pZ+EVgQRuWqQhZPFXxQE8qMRrBd3uM+KLc1Ub2w6+vkyr7umeWQCM4c+wlsCdByUgh2630MDMLquMtpg==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - vite-node@1.6.1: - resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest@1.6.1: - resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.6.1 - '@vitest/ui': 1.6.1 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - - vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - - wagmi@2.15.4: - resolution: {integrity: sha512-0m7uo6t/oSFS+4UCUTBnmIhDSP7PGJz1qx4VtALcsBnw81UPPIXMSM8oGVrUNV9CptryiDgBlh4iYmRldg9iaA==} - peerDependencies: - '@tanstack/react-query': '>=5.0.0' - react: '>=18' - typescript: '>=5.0.4' - viem: 2.x - peerDependenciesMeta: - typescript: - optional: true - - wait-on@8.0.3: - resolution: {integrity: sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==} - engines: {node: '>=12.0.0'} - hasBin: true - - web3-bzz@1.10.0: - resolution: {integrity: sha512-o9IR59io3pDUsXTsps5pO5hW1D5zBmg46iNc2t4j2DkaYHNdDLwk2IP9ukoM2wg47QILfPEJYzhTfkS/CcX0KA==} - engines: {node: '>=8.0.0'} - - web3-bzz@1.10.4: - resolution: {integrity: sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw==} - engines: {node: '>=8.0.0'} - - web3-core-helpers@1.10.0: - resolution: {integrity: sha512-pIxAzFDS5vnbXvfvLSpaA1tfRykAe9adw43YCKsEYQwH0gCLL0kMLkaCX3q+Q8EVmAh+e1jWL/nl9U0de1+++g==} - engines: {node: '>=8.0.0'} - - web3-core-helpers@1.10.4: - resolution: {integrity: sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g==} - engines: {node: '>=8.0.0'} - - web3-core-method@1.10.0: - resolution: {integrity: sha512-4R700jTLAMKDMhQ+nsVfIXvH6IGJlJzGisIfMKWAIswH31h5AZz7uDUW2YctI+HrYd+5uOAlS4OJeeT9bIpvkA==} - engines: {node: '>=8.0.0'} - - web3-core-method@1.10.4: - resolution: {integrity: sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA==} - engines: {node: '>=8.0.0'} - - web3-core-promievent@1.10.0: - resolution: {integrity: sha512-68N7k5LWL5R38xRaKFrTFT2pm2jBNFaM4GioS00YjAKXRQ3KjmhijOMG3TICz6Aa5+6GDWYelDNx21YAeZ4YTg==} - engines: {node: '>=8.0.0'} - - web3-core-promievent@1.10.4: - resolution: {integrity: sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ==} - engines: {node: '>=8.0.0'} - - web3-core-requestmanager@1.10.0: - resolution: {integrity: sha512-3z/JKE++Os62APml4dvBM+GAuId4h3L9ckUrj7ebEtS2AR0ixyQPbrBodgL91Sv7j7cQ3Y+hllaluqjguxvSaQ==} - engines: {node: '>=8.0.0'} - - web3-core-requestmanager@1.10.4: - resolution: {integrity: sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg==} - engines: {node: '>=8.0.0'} - - web3-core-subscriptions@1.10.0: - resolution: {integrity: sha512-HGm1PbDqsxejI075gxBc5OSkwymilRWZufIy9zEpnWKNmfbuv5FfHgW1/chtJP6aP3Uq2vHkvTDl3smQBb8l+g==} - engines: {node: '>=8.0.0'} - - web3-core-subscriptions@1.10.4: - resolution: {integrity: sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw==} - engines: {node: '>=8.0.0'} - - web3-core@1.10.0: - resolution: {integrity: sha512-fWySwqy2hn3TL89w5TM8wXF1Z2Q6frQTKHWmP0ppRQorEK8NcHJRfeMiv/mQlSKoTS1F6n/nv2uyZsixFycjYQ==} - engines: {node: '>=8.0.0'} - - web3-core@1.10.4: - resolution: {integrity: sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww==} - engines: {node: '>=8.0.0'} - - web3-eth-abi@1.10.0: - resolution: {integrity: sha512-cwS+qRBWpJ43aI9L3JS88QYPfFcSJJ3XapxOQ4j40v6mk7ATpA8CVK1vGTzpihNlOfMVRBkR95oAj7oL6aiDOg==} - engines: {node: '>=8.0.0'} - - web3-eth-abi@1.10.4: - resolution: {integrity: sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==} - engines: {node: '>=8.0.0'} - - web3-eth-accounts@1.10.0: - resolution: {integrity: sha512-wiq39Uc3mOI8rw24wE2n15hboLE0E9BsQLdlmsL4Zua9diDS6B5abXG0XhFcoNsXIGMWXVZz4TOq3u4EdpXF/Q==} - engines: {node: '>=8.0.0'} - - web3-eth-accounts@1.10.4: - resolution: {integrity: sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg==} - engines: {node: '>=8.0.0'} - - web3-eth-contract@1.10.0: - resolution: {integrity: sha512-MIC5FOzP/+2evDksQQ/dpcXhSqa/2hFNytdl/x61IeWxhh6vlFeSjq0YVTAyIzdjwnL7nEmZpjfI6y6/Ufhy7w==} - engines: {node: '>=8.0.0'} - - web3-eth-contract@1.10.4: - resolution: {integrity: sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A==} - engines: {node: '>=8.0.0'} - - web3-eth-ens@1.10.0: - resolution: {integrity: sha512-3hpGgzX3qjgxNAmqdrC2YUQMTfnZbs4GeLEmy8aCWziVwogbuqQZ+Gzdfrym45eOZodk+lmXyLuAdqkNlvkc1g==} - engines: {node: '>=8.0.0'} - - web3-eth-ens@1.10.4: - resolution: {integrity: sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg==} - engines: {node: '>=8.0.0'} - - web3-eth-iban@1.10.0: - resolution: {integrity: sha512-0l+SP3IGhInw7Q20LY3IVafYEuufo4Dn75jAHT7c2aDJsIolvf2Lc6ugHkBajlwUneGfbRQs/ccYPQ9JeMUbrg==} - engines: {node: '>=8.0.0'} - - web3-eth-iban@1.10.4: - resolution: {integrity: sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw==} - engines: {node: '>=8.0.0'} - - web3-eth-personal@1.10.0: - resolution: {integrity: sha512-anseKn98w/d703eWq52uNuZi7GhQeVjTC5/svrBWEKob0WZ5kPdo+EZoFN0sp5a5ubbrk/E0xSl1/M5yORMtpg==} - engines: {node: '>=8.0.0'} - - web3-eth-personal@1.10.4: - resolution: {integrity: sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w==} - engines: {node: '>=8.0.0'} - - web3-eth@1.10.0: - resolution: {integrity: sha512-Z5vT6slNMLPKuwRyKGbqeGYC87OAy8bOblaqRTgg94CXcn/mmqU7iPIlG4506YdcdK3x6cfEDG7B6w+jRxypKA==} - engines: {node: '>=8.0.0'} - - web3-eth@1.10.4: - resolution: {integrity: sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA==} - engines: {node: '>=8.0.0'} - - web3-net@1.10.0: - resolution: {integrity: sha512-NLH/N3IshYWASpxk4/18Ge6n60GEvWBVeM8inx2dmZJVmRI6SJIlUxbL8jySgiTn3MMZlhbdvrGo8fpUW7a1GA==} - engines: {node: '>=8.0.0'} - - web3-net@1.10.4: - resolution: {integrity: sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow==} - engines: {node: '>=8.0.0'} - - web3-providers-http@1.10.0: - resolution: {integrity: sha512-eNr965YB8a9mLiNrkjAWNAPXgmQWfpBfkkn7tpEFlghfww0u3I0tktMZiaToJVcL2+Xq+81cxbkpeWJ5XQDwOA==} - engines: {node: '>=8.0.0'} - - web3-providers-http@1.10.4: - resolution: {integrity: sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ==} - engines: {node: '>=8.0.0'} - - web3-providers-ipc@1.10.0: - resolution: {integrity: sha512-OfXG1aWN8L1OUqppshzq8YISkWrYHaATW9H8eh0p89TlWMc1KZOL9vttBuaBEi96D/n0eYDn2trzt22bqHWfXA==} - engines: {node: '>=8.0.0'} - - web3-providers-ipc@1.10.4: - resolution: {integrity: sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw==} - engines: {node: '>=8.0.0'} - - web3-providers-ws@1.10.0: - resolution: {integrity: sha512-sK0fNcglW36yD5xjnjtSGBnEtf59cbw4vZzJ+CmOWIKGIR96mP5l684g0WD0Eo+f4NQc2anWWXG74lRc9OVMCQ==} - engines: {node: '>=8.0.0'} - - web3-providers-ws@1.10.4: - resolution: {integrity: sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA==} - engines: {node: '>=8.0.0'} - - web3-shh@1.10.0: - resolution: {integrity: sha512-uNUUuNsO2AjX41GJARV9zJibs11eq6HtOe6Wr0FtRUcj8SN6nHeYIzwstAvJ4fXA53gRqFMTxdntHEt9aXVjpg==} - engines: {node: '>=8.0.0'} - - web3-shh@1.10.4: - resolution: {integrity: sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw==} - engines: {node: '>=8.0.0'} - - web3-utils@1.10.0: - resolution: {integrity: sha512-kSaCM0uMcZTNUSmn5vMEhlo02RObGNRRCkdX0V9UTAU0+lrvn0HSaudyCo6CQzuXUsnuY2ERJGCGPfeWmv19Rg==} - engines: {node: '>=8.0.0'} - - web3-utils@1.10.4: - resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==} - engines: {node: '>=8.0.0'} - - web3@1.10.0: - resolution: {integrity: sha512-YfKY9wSkGcM8seO+daR89oVTcbu18NsVfvOngzqMYGUU0pPSQmE57qQDvQzUeoIOHAnXEBNzrhjQJmm8ER0rng==} - engines: {node: '>=8.0.0'} - - web3@1.10.4: - resolution: {integrity: sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA==} - engines: {node: '>=8.0.0'} - - webextension-polyfill@0.10.0: - resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - websocket@1.0.35: - resolution: {integrity: sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==} - engines: {node: '>=4.0.0'} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - - whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which-module@1.0.0: - resolution: {integrity: sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - widest-line@3.1.0: - resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} - engines: {node: '>=8'} - - window-size@0.2.0: - resolution: {integrity: sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==} - engines: {node: '>= 0.10.0'} - hasBin: true - - workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} - - wrap-ansi@2.1.0: - resolution: {integrity: sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==} - engines: {node: '>=0.10.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@3.3.3: - resolution: {integrity: sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xhr-request-promise@0.1.3: - resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==} - - xhr-request@1.1.0: - resolution: {integrity: sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==} - - xhr@2.6.0: - resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==} - - xmlhttprequest-ssl@2.1.2: - resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} - engines: {node: '>=0.4.0'} - - xmlhttprequest@1.8.0: - resolution: {integrity: sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==} - engines: {node: '>=0.4.0'} - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - y18n@3.2.2: - resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yaeti@0.0.6: - resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} - engines: {node: '>=0.10.32'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} - engines: {node: '>= 14.6'} - hasBin: true - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs-parser@2.4.1: - resolution: {integrity: sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==} - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yargs@4.8.1: - resolution: {integrity: sha512-LqodLrnIDM3IFT+Hf/5sxBnEGECrfdC1uIbgZeJmESCSo4HoCAaKEus8MylXHAkdacGc0ye+Qa+dpkuom8uVYA==} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yocto-queue@1.2.1: - resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} - engines: {node: '>=12.20'} - - zksync-ethers@5.10.0: - resolution: {integrity: sha512-OAjTGAHF9wbdkRGkj7XZuF/a1Sk/FVbwH4pmLjAKlR7mJ7sQtQhBhrPU2dCc67xLaNvEESPfwil19ES5wooYFg==} - engines: {node: '>=16.0.0'} - peerDependencies: - ethers: ~5.7.0 - - zod@3.22.4: - resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - - zustand@5.0.0: - resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - -snapshots: - - '@adraffy/ens-normalize@1.10.1': {} - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.27.1': {} - - '@babel/parser@7.27.4': - dependencies: - '@babel/types': 7.27.3 - - '@babel/runtime@7.27.4': {} - - '@babel/types@7.27.3': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - - '@bcoe/v8-coverage@0.2.3': {} - - '@biomejs/biome@1.9.4': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.9.4 - '@biomejs/cli-darwin-x64': 1.9.4 - '@biomejs/cli-linux-arm64': 1.9.4 - '@biomejs/cli-linux-arm64-musl': 1.9.4 - '@biomejs/cli-linux-x64': 1.9.4 - '@biomejs/cli-linux-x64-musl': 1.9.4 - '@biomejs/cli-win32-arm64': 1.9.4 - '@biomejs/cli-win32-x64': 1.9.4 - - '@biomejs/cli-darwin-arm64@1.9.4': - optional: true - - '@biomejs/cli-darwin-x64@1.9.4': - optional: true - - '@biomejs/cli-linux-arm64-musl@1.9.4': - optional: true - - '@biomejs/cli-linux-arm64@1.9.4': - optional: true - - '@biomejs/cli-linux-x64-musl@1.9.4': - optional: true - - '@biomejs/cli-linux-x64@1.9.4': - optional: true - - '@biomejs/cli-win32-arm64@1.9.4': - optional: true - - '@biomejs/cli-win32-x64@1.9.4': - optional: true - - '@changesets/apply-release-plan@7.0.12': - dependencies: - '@changesets/config': 3.1.1 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.7.2 - - '@changesets/assemble-release-plan@6.0.8': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.7.2 - - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/changelog-github@0.5.1': - dependencies: - '@changesets/get-github-info': 0.6.0 - '@changesets/types': 6.1.0 - dotenv: 8.6.0 - transitivePeerDependencies: - - encoding - - '@changesets/cli@2.29.4': - dependencies: - '@changesets/apply-release-plan': 7.0.12 - '@changesets/assemble-release-plan': 6.0.8 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.12 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - mri: 1.2.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.7.2 - spawndamnit: 3.0.1 - term-size: 2.2.1 - - '@changesets/config@3.1.1': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.1.3': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.7.2 - - '@changesets/get-github-info@0.6.0': - dependencies: - dataloader: 1.4.0 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@changesets/get-release-plan@4.0.12': - dependencies: - '@changesets/assemble-release-plan': 6.0.8 - '@changesets/config': 3.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.4': - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - - '@changesets/logger@0.1.1': - dependencies: - picocolors: 1.1.1 - - '@changesets/parse@0.4.1': - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 3.14.1 - - '@changesets/pre@2.0.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.5': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - - '@changesets/should-skip-package@0.1.2': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.1.0': {} - - '@changesets/write@0.4.0': - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.1.1 - prettier: 2.8.8 - - '@coinbase/wallet-sdk@3.9.3': - dependencies: - bn.js: 5.2.2 - buffer: 6.0.3 - clsx: 1.2.1 - eth-block-tracker: 7.1.0 - eth-json-rpc-filters: 6.0.1 - eventemitter3: 5.0.1 - keccak: 3.0.4 - preact: 10.26.8 - sha.js: 2.4.11 - transitivePeerDependencies: - - supports-color - - '@coinbase/wallet-sdk@4.3.0': - dependencies: - '@noble/hashes': 1.8.0 - clsx: 1.2.1 - eventemitter3: 5.0.1 - preact: 10.26.8 - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)': - dependencies: - '@noble/ciphers': 1.3.0 - - '@ensdomains/address-encoder@0.1.9': - dependencies: - bech32: 1.1.4 - blakejs: 1.2.1 - bn.js: 4.12.2 - bs58: 4.0.1 - crypto-addr-codec: 0.1.8 - nano-base32: 1.0.1 - ripemd160: 2.0.2 - - '@ensdomains/address-encoder@1.0.0-rc.3': - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@ensdomains/address-encoder@1.1.1': - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@ensdomains/buffer@0.1.1': {} - - '@ensdomains/content-hash@3.1.0-rc.1': - dependencies: - '@ensdomains/address-encoder': 1.0.0-rc.3 - '@noble/curves': 1.9.1 - '@scure/base': 1.2.6 - - '@ensdomains/dnsprovejs@0.5.1': - dependencies: - '@noble/hashes': 1.8.0 - dns-packet: 5.6.1 - typescript-logging: 1.0.1 - - '@ensdomains/ens-contracts@1.4.0-beta.2(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@ensdomains/buffer': 0.1.1 - '@ensdomains/solsha1': 0.0.3 - '@nomicfoundation/hardhat-verify': 2.0.14(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@openzeppelin/contracts': 4.9.3 - '@openzeppelin/contracts-v5': '@openzeppelin/contracts@5.1.0' - clones-with-immutable-args: https://codeload.github.com/Arachnid/clones-with-immutable-args/tar.gz/23768824cdc037f361f7065538b8f949cae9d3d1 - dns-packet: 5.6.1 - transitivePeerDependencies: - - hardhat - - supports-color - - '@ensdomains/ens-test-env@1.0.1': - dependencies: - cli-progress: 3.12.0 - commander: 14.0.0 - concurrently: 9.1.2 - docker-compose: 1.2.0 - dotenv: 16.5.0 - dunai: 0.0.0-dev.2 - js-yaml: 4.1.0 - picocolors: 1.1.1 - tar-fs: 3.0.9 - wait-on: 8.0.3 - transitivePeerDependencies: - - bare-buffer - - debug - - '@ensdomains/ens@0.4.5': - dependencies: - bluebird: 3.7.2 - eth-ens-namehash: 2.0.8 - solc: 0.4.26 - testrpc: 0.0.1 - web3-utils: 1.10.4 - - '@ensdomains/ensjs@2.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@babel/runtime': 7.27.4 - '@ensdomains/address-encoder': 0.1.9 - '@ensdomains/ens': 0.4.5 - '@ensdomains/resolver': 0.2.4 - content-hash: 2.5.2 - eth-ens-namehash: 2.0.8 - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-sha3: 0.8.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@ensdomains/hardhat-toolbox-viem-extended@0.0.5(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))(hardhat-deploy@1.0.2(patch_hash=c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61)(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)': - dependencies: - '@nomicfoundation/hardhat-viem': 2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) - hardhat: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - hardhat-deploy: 1.0.2(patch_hash=c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61)(bufferutil@4.0.9)(utf-8-validate@5.0.10) - typescript: 5.8.3 - - '@ensdomains/resolver@0.2.4': {} - - '@ensdomains/solsha1@0.0.3': - dependencies: - hash-test-vectors: 1.3.2 - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@ethereumjs/common@2.5.0': - dependencies: - crc-32: 1.2.2 - ethereumjs-util: 7.1.5 - - '@ethereumjs/common@2.6.5': - dependencies: - crc-32: 1.2.2 - ethereumjs-util: 7.1.5 - - '@ethereumjs/common@3.2.0': - dependencies: - '@ethereumjs/util': 8.1.0 - crc-32: 1.2.2 - - '@ethereumjs/rlp@4.0.1': {} - - '@ethereumjs/rlp@5.0.2': {} - - '@ethereumjs/tx@3.3.2': - dependencies: - '@ethereumjs/common': 2.5.0 - ethereumjs-util: 7.1.5 - - '@ethereumjs/tx@3.5.2': - dependencies: - '@ethereumjs/common': 2.6.5 - ethereumjs-util: 7.1.5 - - '@ethereumjs/tx@4.2.0': - dependencies: - '@ethereumjs/common': 3.2.0 - '@ethereumjs/rlp': 4.0.1 - '@ethereumjs/util': 8.1.0 - ethereum-cryptography: 2.2.1 - - '@ethereumjs/util@8.1.0': - dependencies: - '@ethereumjs/rlp': 4.0.1 - ethereum-cryptography: 2.2.1 - micro-ftch: 0.3.1 - - '@ethereumjs/util@9.1.0': - dependencies: - '@ethereumjs/rlp': 5.0.2 - ethereum-cryptography: 2.2.1 - - '@ethersproject/abi@5.8.0': - dependencies: - '@ethersproject/address': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@ethersproject/abstract-provider@5.8.0': - dependencies: - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/web': 5.8.0 - - '@ethersproject/abstract-signer@5.8.0': - dependencies: - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - - '@ethersproject/address@5.8.0': - dependencies: - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/rlp': 5.8.0 - - '@ethersproject/base64@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - - '@ethersproject/basex@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/properties': 5.8.0 - - '@ethersproject/bignumber@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - bn.js: 5.2.2 - - '@ethersproject/bytes@5.8.0': - dependencies: - '@ethersproject/logger': 5.8.0 - - '@ethersproject/constants@5.8.0': - dependencies: - '@ethersproject/bignumber': 5.8.0 - - '@ethersproject/contracts@5.8.0': - dependencies: - '@ethersproject/abi': 5.8.0 - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/transactions': 5.8.0 - - '@ethersproject/hash@5.8.0': - dependencies: - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/base64': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@ethersproject/hdnode@5.8.0': - dependencies: - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/basex': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/pbkdf2': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/sha2': 5.8.0 - '@ethersproject/signing-key': 5.8.0 - '@ethersproject/strings': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/wordlists': 5.8.0 - - '@ethersproject/json-wallets@5.8.0': - dependencies: - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/hdnode': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/pbkdf2': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/random': 5.8.0 - '@ethersproject/strings': 5.8.0 - '@ethersproject/transactions': 5.8.0 - aes-js: 3.0.0 - scrypt-js: 3.0.1 - - '@ethersproject/keccak256@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - js-sha3: 0.8.0 - - '@ethersproject/logger@5.8.0': {} - - '@ethersproject/networks@5.8.0': - dependencies: - '@ethersproject/logger': 5.8.0 - - '@ethersproject/pbkdf2@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/sha2': 5.8.0 - - '@ethersproject/properties@5.8.0': - dependencies: - '@ethersproject/logger': 5.8.0 - - '@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/base64': 5.8.0 - '@ethersproject/basex': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/random': 5.8.0 - '@ethersproject/rlp': 5.8.0 - '@ethersproject/sha2': 5.8.0 - '@ethersproject/strings': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/web': 5.8.0 - bech32: 1.1.4 - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@ethersproject/random@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - - '@ethersproject/rlp@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - - '@ethersproject/sha2@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - hash.js: 1.1.7 - - '@ethersproject/signing-key@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - bn.js: 5.2.2 - elliptic: 6.6.1 - hash.js: 1.1.7 - - '@ethersproject/solidity@5.8.0': - dependencies: - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/sha2': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@ethersproject/strings@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/logger': 5.8.0 - - '@ethersproject/transactions@5.8.0': - dependencies: - '@ethersproject/address': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/rlp': 5.8.0 - '@ethersproject/signing-key': 5.8.0 - - '@ethersproject/units@5.8.0': - dependencies: - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/logger': 5.8.0 - - '@ethersproject/wallet@5.8.0': - dependencies: - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/hdnode': 5.8.0 - '@ethersproject/json-wallets': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/random': 5.8.0 - '@ethersproject/signing-key': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/wordlists': 5.8.0 - - '@ethersproject/web@5.8.0': - dependencies: - '@ethersproject/base64': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@ethersproject/wordlists@5.8.0': - dependencies: - '@ethersproject/bytes': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@fastify/busboy@2.1.1': {} - - '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': - dependencies: - graphql: 16.11.0 - - '@hapi/hoek@9.3.0': {} - - '@hapi/topo@5.1.0': - dependencies: - '@hapi/hoek': 9.3.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@leichtgewicht/ip-codec@2.0.5': {} - - '@lit-labs/ssr-dom-shim@1.3.0': {} - - '@lit/reactive-element@2.1.0': - dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.27.4 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.27.4 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@metamask/eth-json-rpc-provider@1.0.1': - dependencies: - '@metamask/json-rpc-engine': 7.3.3 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 - transitivePeerDependencies: - - supports-color - - '@metamask/json-rpc-engine@7.3.3': - dependencies: - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - transitivePeerDependencies: - - supports-color - - '@metamask/json-rpc-engine@8.0.2': - dependencies: - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - transitivePeerDependencies: - - supports-color - - '@metamask/json-rpc-middleware-stream@7.0.2': - dependencies: - '@metamask/json-rpc-engine': 8.0.2 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - readable-stream: 3.6.2 - transitivePeerDependencies: - - supports-color - - '@metamask/object-multiplex@2.1.0': - dependencies: - once: 1.4.0 - readable-stream: 3.6.2 - - '@metamask/onboarding@1.0.1': - dependencies: - bowser: 2.11.0 - - '@metamask/providers@16.1.0': - dependencies: - '@metamask/json-rpc-engine': 8.0.2 - '@metamask/json-rpc-middleware-stream': 7.0.2 - '@metamask/object-multiplex': 2.1.0 - '@metamask/rpc-errors': 6.4.0 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 - detect-browser: 5.3.0 - extension-port-stream: 3.0.0 - fast-deep-equal: 3.1.3 - is-stream: 2.0.1 - readable-stream: 3.6.2 - webextension-polyfill: 0.10.0 - transitivePeerDependencies: - - supports-color - - '@metamask/rpc-errors@6.4.0': - dependencies: - '@metamask/utils': 9.3.0 - fast-safe-stringify: 2.1.1 - transitivePeerDependencies: - - supports-color - - '@metamask/safe-event-emitter@2.0.0': {} - - '@metamask/safe-event-emitter@3.1.2': {} - - '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - bufferutil: 4.0.9 - cross-fetch: 4.1.0 - date-fns: 2.30.0 - debug: 4.4.1(supports-color@8.1.1) - eciesjs: 0.4.15 - eventemitter2: 6.4.9 - readable-stream: 3.6.2 - socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - utf-8-validate: 5.0.10 - uuid: 8.3.2 - transitivePeerDependencies: - - supports-color - - '@metamask/sdk-install-modal-web@0.32.0': - dependencies: - '@paulmillr/qr': 0.2.1 - - '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@babel/runtime': 7.27.4 - '@metamask/onboarding': 1.0.1 - '@metamask/providers': 16.1.0 - '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@metamask/sdk-install-modal-web': 0.32.0 - '@paulmillr/qr': 0.2.1 - bowser: 2.11.0 - cross-fetch: 4.1.0 - debug: 4.4.1(supports-color@8.1.1) - eciesjs: 0.4.15 - eth-rpc-errors: 4.0.3 - eventemitter2: 6.4.9 - obj-multiplex: 1.0.0 - pump: 3.0.2 - readable-stream: 3.6.2 - socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - tslib: 2.8.1 - util: 0.12.5 - uuid: 8.3.2 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - - '@metamask/superstruct@3.2.1': {} - - '@metamask/utils@5.0.2': - dependencies: - '@ethereumjs/tx': 4.2.0 - '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) - semver: 7.7.2 - superstruct: 1.0.4 - transitivePeerDependencies: - - supports-color - - '@metamask/utils@8.5.0': - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) - pony-cause: 2.1.11 - semver: 7.7.2 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - - '@metamask/utils@9.3.0': - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) - pony-cause: 2.1.11 - semver: 7.7.2 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - - '@noble/ciphers@1.2.1': {} - - '@noble/ciphers@1.3.0': {} - - '@noble/curves@1.4.2': - dependencies: - '@noble/hashes': 1.4.0 - - '@noble/curves@1.8.0': - dependencies: - '@noble/hashes': 1.7.0 - - '@noble/curves@1.8.1': - dependencies: - '@noble/hashes': 1.7.1 - - '@noble/curves@1.8.2': - dependencies: - '@noble/hashes': 1.7.2 - - '@noble/curves@1.9.1': - dependencies: - '@noble/hashes': 1.8.0 - - '@noble/hashes@1.2.0': {} - - '@noble/hashes@1.4.0': {} - - '@noble/hashes@1.7.0': {} - - '@noble/hashes@1.7.1': {} - - '@noble/hashes@1.7.2': {} - - '@noble/hashes@1.8.0': {} - - '@noble/secp256k1@1.7.1': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@nomicfoundation/edr-darwin-arm64@0.11.0': {} - - '@nomicfoundation/edr-darwin-x64@0.11.0': {} - - '@nomicfoundation/edr-linux-arm64-gnu@0.11.0': {} - - '@nomicfoundation/edr-linux-arm64-musl@0.11.0': {} - - '@nomicfoundation/edr-linux-x64-gnu@0.11.0': {} - - '@nomicfoundation/edr-linux-x64-musl@0.11.0': {} - - '@nomicfoundation/edr-win32-x64-msvc@0.11.0': {} - - '@nomicfoundation/edr@0.11.0': - dependencies: - '@nomicfoundation/edr-darwin-arm64': 0.11.0 - '@nomicfoundation/edr-darwin-x64': 0.11.0 - '@nomicfoundation/edr-linux-arm64-gnu': 0.11.0 - '@nomicfoundation/edr-linux-arm64-musl': 0.11.0 - '@nomicfoundation/edr-linux-x64-gnu': 0.11.0 - '@nomicfoundation/edr-linux-x64-musl': 0.11.0 - '@nomicfoundation/edr-win32-x64-msvc': 0.11.0 - - '@nomicfoundation/hardhat-toolbox-viem@3.0.0(@nomicfoundation/hardhat-verify@2.0.14(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))(@types/node@22.15.29)(chai@4.5.0)(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))': - dependencies: - '@nomicfoundation/hardhat-verify': 2.0.14(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@nomicfoundation/hardhat-viem': 2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) - '@types/node': 22.15.29 - chai: 4.5.0 - chai-as-promised: 7.1.2(chai@4.5.0) - hardhat: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - ts-node: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) - typescript: 5.8.3 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - - '@nomicfoundation/hardhat-verify@2.0.14(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@ethersproject/abi': 5.8.0 - '@ethersproject/address': 5.8.0 - cbor: 8.1.0 - debug: 4.4.1(supports-color@8.1.1) - hardhat: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - lodash.clonedeep: 4.5.0 - picocolors: 1.1.1 - semver: 6.3.1 - table: 6.9.0 - undici: 5.29.0 - transitivePeerDependencies: - - supports-color - - '@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typescript@5.8.3)(viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)': - dependencies: - abitype: 0.9.10(typescript@5.8.3)(zod@3.22.4) - hardhat: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - lodash.memoize: 4.1.2 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - typescript - - zod - - '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2': - optional: true - - '@nomicfoundation/solidity-analyzer@0.1.2': - optionalDependencies: - '@nomicfoundation/solidity-analyzer-darwin-arm64': 0.1.2 - '@nomicfoundation/solidity-analyzer-darwin-x64': 0.1.2 - '@nomicfoundation/solidity-analyzer-linux-arm64-gnu': 0.1.2 - '@nomicfoundation/solidity-analyzer-linux-arm64-musl': 0.1.2 - '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.2 - '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.2 - '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.2 - - '@openzeppelin/contract-loader@0.6.3': - dependencies: - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@openzeppelin/contracts@4.9.3': {} - - '@openzeppelin/contracts@4.9.6': {} - - '@openzeppelin/contracts@5.1.0': {} - - '@openzeppelin/test-helpers@0.5.16(bn.js@5.2.2)(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@openzeppelin/contract-loader': 0.6.3 - '@truffle/contract': 4.6.31(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ansi-colors: 3.2.4 - chai: 4.5.0 - chai-bn: 0.2.2(bn.js@5.2.2)(chai@4.5.0) - ethjs-abi: 0.2.1 - lodash.flatten: 4.4.0 - semver: 5.7.2 - web3: 1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - web3-utils: 1.10.4 - transitivePeerDependencies: - - bn.js - - bufferutil - - encoding - - supports-color - - utf-8-validate - - '@paulmillr/qr@0.2.1': {} - - '@reown/appkit-common@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - big.js: 6.2.2 - dayjs: 1.11.13 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - - '@reown/appkit-controllers@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - valtio: 1.13.2 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-polyfills@1.7.3': - dependencies: - buffer: 6.0.3 - - '@reown/appkit-scaffold-ui@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-controllers': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-ui': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-utils': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - lit: 3.1.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-ui@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-controllers': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - lit: 3.1.0 - qrcode: 1.5.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-utils@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-controllers': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - valtio: 1.13.2 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@reown/appkit-wallet@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-polyfills': 1.7.3 - '@walletconnect/logger': 2.1.2 - zod: 3.22.4 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - '@reown/appkit@1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit-common': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-controllers': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-polyfills': 1.7.3 - '@reown/appkit-scaffold-ui': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-ui': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-utils': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@reown/appkit-wallet': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.19.2 - '@walletconnect/universal-provider': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - bs58: 6.0.0 - valtio: 1.13.2 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@rollup/rollup-android-arm-eabi@4.41.1': - optional: true - - '@rollup/rollup-android-arm64@4.41.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.41.1': - optional: true - - '@rollup/rollup-darwin-x64@4.41.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.41.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.41.1': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.41.1': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.41.1': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.41.1': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.41.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.41.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.41.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.41.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.41.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.41.1': - optional: true - - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - events: 3.3.0 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - zod - - '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} - - '@scure/base@1.1.9': {} - - '@scure/base@1.2.6': {} - - '@scure/bip32@1.1.5': - dependencies: - '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.1 - '@scure/base': 1.1.9 - - '@scure/bip32@1.4.0': - dependencies: - '@noble/curves': 1.4.2 - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - - '@scure/bip32@1.6.2': - dependencies: - '@noble/curves': 1.8.2 - '@noble/hashes': 1.7.2 - '@scure/base': 1.2.6 - - '@scure/bip32@1.7.0': - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@scure/bip39@1.1.1': - dependencies: - '@noble/hashes': 1.2.0 - '@scure/base': 1.1.9 - - '@scure/bip39@1.3.0': - dependencies: - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - - '@scure/bip39@1.5.4': - dependencies: - '@noble/hashes': 1.7.2 - '@scure/base': 1.2.6 - - '@scure/bip39@1.6.0': - dependencies: - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - - '@sentry/core@5.30.0': - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/minimal': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - - '@sentry/hub@5.30.0': - dependencies: - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - - '@sentry/minimal@5.30.0': - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/types': 5.30.0 - tslib: 1.14.1 - - '@sentry/node@5.30.0': - dependencies: - '@sentry/core': 5.30.0 - '@sentry/hub': 5.30.0 - '@sentry/tracing': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - cookie: 0.4.2 - https-proxy-agent: 5.0.1 - lru_map: 0.3.3 - tslib: 1.14.1 - transitivePeerDependencies: - - supports-color - - '@sentry/tracing@5.30.0': - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/minimal': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - - '@sentry/types@5.30.0': {} - - '@sentry/utils@5.30.0': - dependencies: - '@sentry/types': 5.30.0 - tslib: 1.14.1 - - '@sideway/address@4.1.5': - dependencies: - '@hapi/hoek': 9.3.0 - - '@sideway/formula@3.0.1': {} - - '@sideway/pinpoint@2.0.0': {} - - '@sinclair/typebox@0.27.8': {} - - '@sindresorhus/is@4.6.0': {} - - '@socket.io/component-emitter@3.1.2': {} - - '@szmarczak/http-timer@4.0.6': - dependencies: - defer-to-connect: 2.0.1 - - '@szmarczak/http-timer@5.0.1': - dependencies: - defer-to-connect: 2.0.1 - - '@truffle/abi-utils@1.0.3': - dependencies: - change-case: 3.0.2 - fast-check: 3.1.1 - web3-utils: 1.10.0 - - '@truffle/blockchain-utils@0.1.9': {} - - '@truffle/codec@0.17.3': - dependencies: - '@truffle/abi-utils': 1.0.3 - '@truffle/compile-common': 0.9.8 - big.js: 6.2.2 - bn.js: 5.2.2 - cbor: 5.2.0 - debug: 4.4.1(supports-color@8.1.1) - lodash: 4.17.21 - semver: 7.7.2 - utf8: 3.0.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - supports-color - - '@truffle/compile-common@0.9.8': - dependencies: - '@truffle/error': 0.2.2 - colors: 1.4.0 - - '@truffle/contract-schema@3.4.16': - dependencies: - ajv: 6.12.6 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - '@truffle/contract@4.6.31(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@ensdomains/ensjs': 2.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@truffle/blockchain-utils': 0.1.9 - '@truffle/contract-schema': 3.4.16 - '@truffle/debug-utils': 6.0.57 - '@truffle/error': 0.2.2 - '@truffle/interface-adapter': 0.5.37(bufferutil@4.0.9)(utf-8-validate@5.0.10) - bignumber.js: 7.2.1 - debug: 4.4.1(supports-color@8.1.1) - ethers: 4.0.49 - web3: 1.10.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - web3-core-helpers: 1.10.0 - web3-core-promievent: 1.10.0 - web3-eth-abi: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - - '@truffle/debug-utils@6.0.57': - dependencies: - '@truffle/codec': 0.17.3 - '@trufflesuite/chromafi': 3.0.0 - bn.js: 5.2.2 - chalk: 2.4.2 - debug: 4.4.1(supports-color@8.1.1) - highlightjs-solidity: 2.0.6 - transitivePeerDependencies: - - supports-color - - '@truffle/error@0.2.2': {} - - '@truffle/interface-adapter@0.5.37(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - bn.js: 5.2.2 - ethers: 4.0.49 - web3: 1.10.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - - '@trufflesuite/chromafi@3.0.0': - dependencies: - camelcase: 4.1.0 - chalk: 2.4.2 - cheerio: 1.0.0 - detect-indent: 5.0.0 - highlight.js: 10.7.3 - lodash.merge: 4.6.2 - strip-ansi: 4.0.0 - strip-indent: 2.0.0 - - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@types/bn.js@5.1.6': - dependencies: - '@types/node': 22.15.29 - - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.0.4 - '@types/keyv': 3.1.4 - '@types/node': 22.15.29 - '@types/responselike': 1.0.3 - - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - - '@types/estree@1.0.7': {} - - '@types/http-cache-semantics@4.0.4': {} - - '@types/jsonfile@6.1.4': - dependencies: - '@types/node': 22.15.29 - - '@types/keyv@3.1.4': - dependencies: - '@types/node': 22.15.29 - - '@types/lru-cache@5.1.1': {} - - '@types/ms@2.1.0': {} - - '@types/node@12.20.55': {} - - '@types/node@22.15.29': - dependencies: - undici-types: 6.21.0 - - '@types/pako@2.0.3': {} - - '@types/pbkdf2@3.1.2': - dependencies: - '@types/node': 22.15.29 - - '@types/qs@6.14.0': {} - - '@types/responselike@1.0.3': - dependencies: - '@types/node': 22.15.29 - - '@types/secp256k1@4.0.6': - dependencies: - '@types/node': 22.15.29 - - '@types/trusted-types@2.0.7': {} - - '@vitest/coverage-v8@1.6.1(vitest@1.6.1(@types/node@22.15.29)(happy-dom@13.10.1))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 0.2.3 - debug: 4.4.1(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - picocolors: 1.1.1 - std-env: 3.9.0 - strip-literal: 2.1.1 - test-exclude: 6.0.0 - vitest: 1.6.1(@types/node@22.15.29)(happy-dom@13.10.1) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@1.6.1': - dependencies: - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - chai: 4.5.0 - - '@vitest/runner@1.6.1': - dependencies: - '@vitest/utils': 1.6.1 - p-limit: 5.0.0 - pathe: 1.1.2 - - '@vitest/snapshot@1.6.1': - dependencies: - magic-string: 0.30.17 - pathe: 1.1.2 - pretty-format: 29.7.0 - - '@vitest/spy@1.6.1': - dependencies: - tinyspy: 2.2.1 - - '@vitest/utils@1.6.1': - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - - '@wagmi/connectors@5.8.3(@wagmi/core@2.17.2(typescript@5.8.3)(use-sync-external-store@1.4.0)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)': - dependencies: - '@coinbase/wallet-sdk': 4.3.0 - '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@wagmi/core': 2.17.2(typescript@5.8.3)(use-sync-external-store@1.4.0)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)) - '@walletconnect/ethereum-provider': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - supports-color - - uploadthing - - utf-8-validate - - zod - - '@wagmi/core@2.17.2(typescript@5.8.3)(use-sync-external-store@1.4.0)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))': - dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.8.3) - viem: 2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - zustand: 5.0.0(use-sync-external-store@1.4.0) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - '@types/react' - - immer - - react - - use-sync-external-store - - '@walletconnect/core@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/window-getters': 1.0.1 - es-toolkit: 1.33.0 - events: 3.3.0 - uint8arrays: 3.1.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/core@2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.20.2 - '@walletconnect/utils': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/window-getters': 1.0.1 - es-toolkit: 1.33.0 - events: 3.3.0 - uint8arrays: 3.1.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/environment@1.0.1': - dependencies: - tslib: 1.14.1 - - '@walletconnect/ethereum-provider@2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@reown/appkit': 1.7.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/types': 2.20.2 - '@walletconnect/universal-provider': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/utils': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/events@1.0.1': - dependencies: - keyvaluestorage-interface: 1.0.0 - tslib: 1.14.1 - - '@walletconnect/heartbeat@1.2.2': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/time': 1.0.2 - events: 3.3.0 - - '@walletconnect/jsonrpc-http-connection@1.0.8': - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - cross-fetch: 3.2.0 - events: 3.3.0 - transitivePeerDependencies: - - encoding - - '@walletconnect/jsonrpc-provider@1.0.14': - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - events: 3.3.0 - - '@walletconnect/jsonrpc-types@1.0.4': - dependencies: - events: 3.3.0 - keyvaluestorage-interface: 1.0.0 - - '@walletconnect/jsonrpc-utils@1.0.8': - dependencies: - '@walletconnect/environment': 1.0.1 - '@walletconnect/jsonrpc-types': 1.0.4 - tslib: 1.14.1 - - '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/safe-json': 1.0.2 - events: 3.3.0 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@walletconnect/keyvaluestorage@1.1.1': - dependencies: - '@walletconnect/safe-json': 1.0.2 - idb-keyval: 6.2.2 - unstorage: 1.16.0(idb-keyval@6.2.2) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/logger@2.1.2': - dependencies: - '@walletconnect/safe-json': 1.0.2 - pino: 7.11.0 - - '@walletconnect/relay-api@1.0.11': - dependencies: - '@walletconnect/jsonrpc-types': 1.0.4 - - '@walletconnect/relay-auth@1.1.0': - dependencies: - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - uint8arrays: 3.1.0 - - '@walletconnect/safe-json@1.0.2': - dependencies: - tslib: 1.14.1 - - '@walletconnect/sign-client@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/core': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/logger': 2.1.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/sign-client@2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/core': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/logger': 2.1.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.20.2 - '@walletconnect/utils': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/time@1.0.2': - dependencies: - tslib: 1.14.1 - - '@walletconnect/types@2.19.2': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/types@2.20.2': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/heartbeat': 1.2.2 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - db0 - - ioredis - - uploadthing - - '@walletconnect/universal-provider@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/types': 2.19.2 - '@walletconnect/utils': 2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/universal-provider@2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8 - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - '@walletconnect/types': 2.20.2 - '@walletconnect/utils': 2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/utils@2.19.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@noble/ciphers': 1.2.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.2 - '@walletconnect/window-getters': 1.0.1 - '@walletconnect/window-metadata': 1.0.1 - bs58: 6.0.0 - detect-browser: 5.3.0 - query-string: 7.1.3 - uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/utils@2.20.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': - dependencies: - '@noble/ciphers': 1.2.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/relay-api': 1.0.11 - '@walletconnect/relay-auth': 1.1.0 - '@walletconnect/safe-json': 1.0.2 - '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.20.2 - '@walletconnect/window-getters': 1.0.1 - '@walletconnect/window-metadata': 1.0.1 - bs58: 6.0.0 - detect-browser: 5.3.0 - query-string: 7.1.3 - uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - - '@walletconnect/window-getters@1.0.1': - dependencies: - tslib: 1.14.1 - - '@walletconnect/window-metadata@1.0.1': - dependencies: - '@walletconnect/window-getters': 1.0.1 - tslib: 1.14.1 - - abitype@0.9.10(typescript@5.8.3)(zod@3.22.4): - optionalDependencies: - typescript: 5.8.3 - zod: 3.22.4 - - abitype@1.0.8(typescript@5.8.3)(zod@3.22.4): - optionalDependencies: - typescript: 5.8.3 - zod: 3.22.4 - - abortcontroller-polyfill@1.7.8: {} - - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - - acorn-walk@8.3.4: - dependencies: - acorn: 8.14.1 - - acorn@8.14.1: {} - - adm-zip@0.4.16: {} - - aes-js@3.0.0: {} - - agent-base@6.0.2: - dependencies: - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-align@3.0.1: - dependencies: - string-width: 4.2.3 - - ansi-colors@3.2.4: {} - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@2.1.1: {} - - ansi-regex@3.0.1: {} - - ansi-regex@5.0.1: {} - - ansi-sequence-parser@1.1.3: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-flatten@1.1.1: {} - - array-union@2.1.0: {} - - asn1@0.2.6: - dependencies: - safer-buffer: 2.1.2 - - assert-plus@1.0.0: {} - - assertion-error@1.1.0: {} - - astral-regex@2.0.0: {} - - async-limiter@1.0.1: {} - - async-mutex@0.2.6: - dependencies: - tslib: 2.8.1 - - asynckit@0.4.0: {} - - atomic-sleep@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - aws-sign2@0.7.0: {} - - aws4@1.13.2: {} - - axios@0.21.4(debug@4.4.1): - dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - transitivePeerDependencies: - - debug - - axios@1.9.0: - dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.2 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - b4a@1.6.7: {} - - balanced-match@1.0.2: {} - - bare-events@2.5.4: - optional: true - - bare-fs@4.1.5: - dependencies: - bare-events: 2.5.4 - bare-path: 3.0.0 - bare-stream: 2.6.5(bare-events@2.5.4) - optional: true - - bare-os@3.6.1: - optional: true - - bare-path@3.0.0: - dependencies: - bare-os: 3.6.1 - optional: true - - bare-stream@2.6.5(bare-events@2.5.4): - dependencies: - streamx: 2.22.0 - optionalDependencies: - bare-events: 2.5.4 - optional: true - - base-x@3.0.11: - dependencies: - safe-buffer: 5.2.1 - - base-x@5.0.1: {} - - base64-js@1.5.1: {} - - bcrypt-pbkdf@1.0.2: - dependencies: - tweetnacl: 0.14.5 - - bech32@1.1.4: {} - - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - - big-integer@1.6.36: {} - - big.js@6.2.2: {} - - bignumber.js@7.2.1: {} - - bignumber.js@9.3.0: {} - - binary-extensions@2.3.0: {} - - blakejs@1.2.1: {} - - bluebird@3.7.2: {} - - bn.js@4.11.6: {} - - bn.js@4.12.2: {} - - bn.js@5.2.2: {} - - body-parser@1.20.3: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.13.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - boolbase@1.0.0: {} - - bowser@2.11.0: {} - - boxen@5.1.2: - dependencies: - ansi-align: 3.0.1 - camelcase: 6.3.0 - chalk: 4.1.2 - cli-boxes: 2.2.1 - string-width: 4.2.3 - type-fest: 0.20.2 - widest-line: 3.1.0 - wrap-ansi: 7.0.0 - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - brorand@1.1.0: {} - - browser-stdout@1.3.1: {} - - browserify-aes@1.2.0: - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.6 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - bs58@4.0.1: - dependencies: - base-x: 3.0.11 - - bs58@6.0.0: - dependencies: - base-x: 5.0.1 - - bs58check@2.1.2: - dependencies: - bs58: 4.0.1 - create-hash: 1.2.0 - safe-buffer: 5.2.1 - - buffer-from@1.1.2: {} - - buffer-to-arraybuffer@0.0.5: {} - - buffer-xor@1.0.3: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bufferutil@4.0.9: - dependencies: - node-gyp-build: 4.8.4 - - bytes@3.1.2: {} - - cac@6.7.14: {} - - cacheable-lookup@5.0.4: {} - - cacheable-lookup@6.1.0: {} - - cacheable-request@7.0.4: - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - camel-case@3.0.0: - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - - camelcase@3.0.0: {} - - camelcase@4.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caseless@0.12.0: {} - - cbor@5.2.0: - dependencies: - bignumber.js: 9.3.0 - nofilter: 1.0.4 - - cbor@8.1.0: - dependencies: - nofilter: 3.1.0 - - chai-as-promised@7.1.2(chai@4.5.0): - dependencies: - chai: 4.5.0 - check-error: 1.0.3 - - chai-bn@0.2.2(bn.js@5.2.2)(chai@4.5.0): - dependencies: - bn.js: 5.2.2 - chai: 4.5.0 - - chai@4.5.0: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.4 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.1.0 - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - change-case@3.0.2: - dependencies: - camel-case: 3.0.0 - constant-case: 2.0.0 - dot-case: 2.1.1 - header-case: 1.0.1 - is-lower-case: 1.1.3 - is-upper-case: 1.1.2 - lower-case: 1.1.4 - lower-case-first: 1.0.2 - no-case: 2.3.2 - param-case: 2.1.1 - pascal-case: 2.0.1 - path-case: 2.1.1 - sentence-case: 2.1.1 - snake-case: 2.1.0 - swap-case: 1.1.2 - title-case: 2.1.1 - upper-case: 1.1.3 - upper-case-first: 1.1.2 - - chardet@0.7.0: {} - - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - - cheerio-select@2.1.0: - dependencies: - boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - - cheerio@1.0.0: - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.2.2 - encoding-sniffer: 0.2.0 - htmlparser2: 9.1.0 - parse5: 7.3.0 - parse5-htmlparser2-tree-adapter: 7.1.0 - parse5-parser-stream: 7.1.2 - undici: 6.21.3 - whatwg-mimetype: 4.0.0 - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - chownr@1.1.4: {} - - ci-info@2.0.0: {} - - ci-info@3.9.0: {} - - cids@0.7.5: - dependencies: - buffer: 5.7.1 - class-is: 1.1.0 - multibase: 0.6.1 - multicodec: 1.0.4 - multihashes: 0.4.21 - - cipher-base@1.0.6: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - class-is@1.1.0: {} - - clean-stack@2.2.0: {} - - cli-boxes@2.2.1: {} - - cli-progress@3.12.0: - dependencies: - string-width: 4.2.3 - - cliui@3.2.0: - dependencies: - string-width: 1.0.2 - strip-ansi: 3.0.1 - wrap-ansi: 2.1.0 - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-response@1.0.3: - dependencies: - mimic-response: 1.0.1 - - clones-with-immutable-args@https://codeload.github.com/Arachnid/clones-with-immutable-args/tar.gz/23768824cdc037f361f7065538b8f949cae9d3d1: {} - - clsx@1.2.1: {} - - code-point-at@1.1.0: {} - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.3: {} - - color-name@1.1.4: {} - - colors@1.4.0: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - command-exists@1.2.9: {} - - commander@14.0.0: {} - - commander@8.3.0: {} - - concat-map@0.0.1: {} - - concurrently@9.1.2: - dependencies: - chalk: 4.1.2 - lodash: 4.17.21 - rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 - tree-kill: 1.2.2 - yargs: 17.7.2 - - confbox@0.1.8: {} - - constant-case@2.0.0: - dependencies: - snake-case: 2.1.0 - upper-case: 1.1.3 - - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - content-hash@2.5.2: - dependencies: - cids: 0.7.5 - multicodec: 0.5.7 - multihashes: 0.4.21 - - content-type@1.0.5: {} - - cookie-es@1.2.2: {} - - cookie-signature@1.0.6: {} - - cookie@0.4.2: {} - - cookie@0.7.1: {} - - core-util-is@1.0.2: {} - - core-util-is@1.0.3: {} - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - crc-32@1.2.2: {} - - create-hash@1.2.0: - dependencies: - cipher-base: 1.0.6 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.2 - sha.js: 2.4.11 - - create-hmac@1.1.7: - dependencies: - cipher-base: 1.0.6 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - - create-require@1.1.1: {} - - cross-fetch@3.2.0: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - cross-fetch@4.1.0: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - crossws@0.3.5: - dependencies: - uncrypto: 0.1.3 - - crypto-addr-codec@0.1.8: - dependencies: - base-x: 3.0.11 - big-integer: 1.6.36 - blakejs: 1.2.1 - bs58: 4.0.1 - ripemd160-min: 0.0.6 - safe-buffer: 5.2.1 - sha3: 2.1.4 - - css-select@5.1.0: - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-what@6.1.0: {} - - d@1.0.2: - dependencies: - es5-ext: 0.10.64 - type: 2.7.3 - - dashdash@1.14.1: - dependencies: - assert-plus: 1.0.0 - - dataloader@1.4.0: {} - - date-fns@2.30.0: - dependencies: - '@babel/runtime': 7.27.4 - - dayjs@1.11.13: {} - - debug@2.6.9: - dependencies: - ms: 2.0.0 - - debug@4.3.7: - dependencies: - ms: 2.1.3 - - debug@4.4.1(supports-color@8.1.1): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 - - decamelize@1.2.0: {} - - decamelize@4.0.0: {} - - decode-uri-component@0.2.2: {} - - decompress-response@3.3.0: - dependencies: - mimic-response: 1.0.1 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-eql@4.1.4: - dependencies: - type-detect: 4.1.0 - - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - defu@6.1.4: {} - - delayed-stream@1.0.0: {} - - delete-empty@3.0.0: - dependencies: - ansi-colors: 4.1.3 - minimist: 1.2.8 - path-starts-with: 2.0.1 - rimraf: 2.7.1 - - depd@2.0.0: {} - - derive-valtio@0.1.0(valtio@1.13.2): - dependencies: - valtio: 1.13.2 - - destr@2.0.5: {} - - destroy@1.2.0: {} - - detect-browser@5.3.0: {} - - detect-indent@5.0.0: {} - - detect-indent@6.1.0: {} - - diff-sequences@29.6.3: {} - - diff@4.0.2: {} - - diff@5.2.0: {} - - dijkstrajs@1.0.3: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dns-packet@5.6.1: - dependencies: - '@leichtgewicht/ip-codec': 2.0.5 - - docker-compose@1.2.0: - dependencies: - yaml: 2.8.0 - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - dom-walk@0.1.2: {} - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - - dot-case@2.1.1: - dependencies: - no-case: 2.3.2 - - dotenv@16.5.0: {} - - dotenv@8.6.0: {} - - dunai@0.0.0-dev.2: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - duplexify@4.1.3: - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.3 - - ecc-jsbn@0.1.2: - dependencies: - jsbn: 0.1.1 - safer-buffer: 2.1.2 - - eciesjs@0.4.15: - dependencies: - '@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0) - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - - ee-first@1.1.1: {} - - elliptic@6.5.4: - dependencies: - bn.js: 4.12.2 - brorand: 1.1.0 - hash.js: 1.1.3 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - elliptic@6.6.1: - dependencies: - bn.js: 4.12.2 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - emoji-regex@8.0.0: {} - - encode-utf8@1.0.3: {} - - encodeurl@1.0.2: {} - - encodeurl@2.0.0: {} - - encoding-sniffer@0.2.0: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding: 3.1.1 - - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - - engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - xmlhttprequest-ssl: 2.1.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - engine.io-parser@5.2.3: {} - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@4.5.0: {} - - entities@6.0.0: {} - - env-paths@2.2.1: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - error-stack-parser@1.3.6: - dependencies: - stackframe: 0.3.1 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-toolkit@1.33.0: {} - - es5-ext@0.10.64: - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - esniff: 2.0.1 - next-tick: 1.1.0 - - es6-iterator@2.0.3: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-symbol: 3.1.4 - - es6-promise@4.2.8: {} - - es6-symbol@3.1.4: - dependencies: - d: 1.0.2 - ext: 1.7.0 - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - escalade@3.2.0: {} - - escape-html@1.0.3: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - esniff@2.0.1: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - type: 2.7.3 - - esprima@4.0.1: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.7 - - etag@1.8.1: {} - - eth-block-tracker@7.1.0: - dependencies: - '@metamask/eth-json-rpc-provider': 1.0.1 - '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 - json-rpc-random-id: 1.0.1 - pify: 3.0.0 - transitivePeerDependencies: - - supports-color - - eth-ens-namehash@2.0.8: - dependencies: - idna-uts46-hx: 2.3.1 - js-sha3: 0.5.7 - - eth-json-rpc-filters@6.0.1: - dependencies: - '@metamask/safe-event-emitter': 3.1.2 - async-mutex: 0.2.6 - eth-query: 2.1.2 - json-rpc-engine: 6.1.0 - pify: 5.0.0 - - eth-lib@0.1.29(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - bn.js: 4.12.2 - elliptic: 6.6.1 - nano-json-stream-parser: 0.1.2 - servify: 0.1.12 - ws: 3.3.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - xhr-request-promise: 0.1.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - eth-lib@0.2.8: - dependencies: - bn.js: 4.12.2 - elliptic: 6.6.1 - xhr-request-promise: 0.1.3 - - eth-query@2.1.2: - dependencies: - json-rpc-random-id: 1.0.1 - xtend: 4.0.2 - - eth-rpc-errors@4.0.3: - dependencies: - fast-safe-stringify: 2.1.1 - - ethereum-bloom-filters@1.2.0: - dependencies: - '@noble/hashes': 1.8.0 - - ethereum-cryptography@0.1.3: - dependencies: - '@types/pbkdf2': 3.1.2 - '@types/secp256k1': 4.0.6 - blakejs: 1.2.1 - browserify-aes: 1.2.0 - bs58check: 2.1.2 - create-hash: 1.2.0 - create-hmac: 1.1.7 - hash.js: 1.1.7 - keccak: 3.0.4 - pbkdf2: 3.1.2 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - scrypt-js: 3.0.1 - secp256k1: 4.0.4 - setimmediate: 1.0.5 - - ethereum-cryptography@1.2.0: - dependencies: - '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.1 - '@scure/bip32': 1.1.5 - '@scure/bip39': 1.1.1 - - ethereum-cryptography@2.2.1: - dependencies: - '@noble/curves': 1.4.2 - '@noble/hashes': 1.4.0 - '@scure/bip32': 1.4.0 - '@scure/bip39': 1.3.0 - - ethereumjs-util@7.1.5: - dependencies: - '@types/bn.js': 5.1.6 - bn.js: 5.2.2 - create-hash: 1.2.0 - ethereum-cryptography: 0.1.3 - rlp: 2.2.7 - - ethers@4.0.49: - dependencies: - aes-js: 3.0.0 - bn.js: 4.12.2 - elliptic: 6.5.4 - hash.js: 1.1.3 - js-sha3: 0.5.7 - scrypt-js: 2.0.4 - setimmediate: 1.0.4 - uuid: 2.0.1 - xmlhttprequest: 1.8.0 - - ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@ethersproject/abi': 5.8.0 - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/base64': 5.8.0 - '@ethersproject/basex': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/contracts': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/hdnode': 5.8.0 - '@ethersproject/json-wallets': 5.8.0 - '@ethersproject/keccak256': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks': 5.8.0 - '@ethersproject/pbkdf2': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@ethersproject/random': 5.8.0 - '@ethersproject/rlp': 5.8.0 - '@ethersproject/sha2': 5.8.0 - '@ethersproject/signing-key': 5.8.0 - '@ethersproject/solidity': 5.8.0 - '@ethersproject/strings': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/units': 5.8.0 - '@ethersproject/wallet': 5.8.0 - '@ethersproject/web': 5.8.0 - '@ethersproject/wordlists': 5.8.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - ethjs-abi@0.2.1: - dependencies: - bn.js: 4.11.6 - js-sha3: 0.5.5 - number-to-bn: 1.7.0 - - ethjs-unit@0.1.6: - dependencies: - bn.js: 4.11.6 - number-to-bn: 1.7.0 - - event-emitter@0.3.5: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - - eventemitter2@6.4.9: {} - - eventemitter3@4.0.4: {} - - eventemitter3@5.0.1: {} - - events@3.3.0: {} - - evp_bytestokey@1.0.3: - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - express@4.21.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.3 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.1 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.12 - proxy-addr: 2.0.7 - qs: 6.13.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.0 - serve-static: 1.16.2 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - ext@1.7.0: - dependencies: - type: 2.7.3 - - extend@3.0.2: {} - - extendable-error@0.1.7: {} - - extension-port-stream@3.0.0: - dependencies: - readable-stream: 3.6.2 - webextension-polyfill: 0.10.0 - - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - - extsprintf@1.3.0: {} - - fast-check@3.1.1: - dependencies: - pure-rand: 5.0.5 - - fast-deep-equal@3.1.3: {} - - fast-fifo@1.3.2: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-redact@3.5.0: {} - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.0.6: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fdir@6.4.5(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - filter-obj@1.1.0: {} - - finalhandler@1.3.1: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - find-up@1.1.2: - dependencies: - path-exists: 2.1.0 - pinkie-promise: 2.0.1 - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat@5.0.2: {} - - fmix@0.1.0: - dependencies: - imul: 1.0.1 - - follow-redirects@1.15.9(debug@4.4.1): - optionalDependencies: - debug: 4.4.1(supports-color@8.1.1) - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - forever-agent@0.6.1: {} - - form-data-encoder@1.7.1: {} - - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - form-data@4.0.2: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - mime-types: 2.1.35 - - forwarded@0.2.0: {} - - fp-ts@1.19.3: {} - - fresh@0.5.2: {} - - fs-extra@0.30.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 2.4.0 - klaw: 1.3.1 - path-is-absolute: 1.0.1 - rimraf: 2.7.1 - - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fs-extra@4.0.3: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-minipass@1.2.7: - dependencies: - minipass: 2.9.0 - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-caller-file@1.0.3: {} - - get-caller-file@2.0.5: {} - - get-func-name@2.0.2: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@5.2.0: - dependencies: - pump: 3.0.2 - - get-stream@6.0.1: {} - - get-stream@8.0.1: {} - - getpass@0.1.7: - dependencies: - assert-plus: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - glob@8.1.0: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - - global@4.4.0: - dependencies: - min-document: 2.19.0 - process: 0.11.10 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - got@12.1.0: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 5.0.1 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 6.1.0 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - form-data-encoder: 1.7.1 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 2.0.1 - - graceful-fs@4.2.11: {} - - graphql-request@7.1.2(graphql@16.11.0): - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) - graphql: 16.11.0 - - graphql@16.11.0: {} - - h3@1.15.3: - dependencies: - cookie-es: 1.2.2 - crossws: 0.3.5 - defu: 6.1.4 - destr: 2.0.5 - iron-webcrypto: 1.2.1 - node-mock-http: 1.0.0 - radix3: 1.1.2 - ufo: 1.6.1 - uncrypto: 0.1.3 - - happy-dom@13.10.1: - dependencies: - entities: 4.5.0 - webidl-conversions: 7.0.0 - whatwg-mimetype: 3.0.0 - - har-schema@2.0.0: {} - - har-validator@5.1.5: - dependencies: - ajv: 6.12.6 - har-schema: 2.0.0 - - hardhat-abi-exporter@2.11.0(hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): - dependencies: - '@ethersproject/abi': 5.8.0 - delete-empty: 3.0.0 - hardhat: 2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) - - hardhat-deploy@1.0.2(patch_hash=c9b002689c306ba5344f19cacaf42b412c5f9ed46771e6667640e123ae4dcd61)(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@ethersproject/abi': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/contracts': 5.8.0 - '@ethersproject/providers': 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@ethersproject/solidity': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/wallet': 5.8.0 - '@types/qs': 6.14.0 - axios: 0.21.4(debug@4.4.1) - chalk: 4.1.2 - chokidar: 3.6.0 - debug: 4.4.1(supports-color@8.1.1) - enquirer: 2.4.1 - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - form-data: 4.0.2 - fs-extra: 10.1.0 - match-all: 1.2.7 - murmur-128: 0.2.1 - qs: 6.14.0 - zksync-ethers: 5.10.0(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - hardhat@2.24.1(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10): - dependencies: - '@ethereumjs/util': 9.1.0 - '@ethersproject/abi': 5.8.0 - '@nomicfoundation/edr': 0.11.0 - '@nomicfoundation/solidity-analyzer': 0.1.2 - '@sentry/node': 5.30.0 - '@types/bn.js': 5.1.6 - '@types/lru-cache': 5.1.1 - adm-zip: 0.4.16 - aggregate-error: 3.1.0 - ansi-escapes: 4.3.2 - boxen: 5.1.2 - chokidar: 4.0.3 - ci-info: 2.0.0 - debug: 4.4.1(supports-color@8.1.1) - enquirer: 2.4.1 - env-paths: 2.2.1 - ethereum-cryptography: 1.2.0 - find-up: 5.0.0 - fp-ts: 1.19.3 - fs-extra: 7.0.1 - immutable: 4.3.7 - io-ts: 1.10.4 - json-stream-stringify: 3.1.6 - keccak: 3.0.4 - lodash: 4.17.21 - micro-eth-signer: 0.14.0 - mnemonist: 0.38.5 - mocha: 10.8.2 - p-map: 4.0.0 - picocolors: 1.1.1 - raw-body: 2.5.2 - resolve: 1.17.0 - semver: 6.3.1 - solc: 0.8.26(debug@4.4.1) - source-map-support: 0.5.21 - stacktrace-parser: 0.1.11 - tinyglobby: 0.2.14 - tsort: 0.0.1 - undici: 5.29.0 - uuid: 8.3.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - ts-node: 10.9.2(@types/node@22.15.29)(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hash-base@3.1.0: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - safe-buffer: 5.2.1 - - hash-test-vectors@1.3.2: {} - - hash.js@1.1.3: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - he@1.2.0: {} - - header-case@1.0.1: - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - - highlight.js@10.7.3: {} - - highlightjs-solidity@2.0.6: {} - - hmac-drbg@1.0.1: - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - hosted-git-info@2.8.9: {} - - html-escaper@2.0.2: {} - - htmlparser2@9.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 4.5.0 - - http-cache-semantics@4.2.0: {} - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - http-https@1.0.0: {} - - http-signature@1.2.0: - dependencies: - assert-plus: 1.0.0 - jsprim: 1.4.2 - sshpk: 1.18.0 - - http2-wrapper@1.0.3: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - http2-wrapper@2.2.1: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.1(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - human-id@4.1.1: {} - - human-signals@5.0.0: {} - - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - idb-keyval@6.2.2: {} - - idna-uts46-hx@2.3.1: - dependencies: - punycode: 2.1.0 - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - immutable@4.3.7: {} - - imul@1.0.1: {} - - indent-string@4.0.0: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - invert-kv@1.0.0: {} - - io-ts@1.10.4: - dependencies: - fp-ts: 1.19.3 - - ipaddr.js@1.9.1: {} - - iron-webcrypto@1.2.1: {} - - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-arrayish@0.2.1: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@1.0.0: - dependencies: - number-is-nan: 1.0.1 - - is-fullwidth-code-point@3.0.0: {} - - is-function@1.0.2: {} - - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hex-prefixed@1.0.0: {} - - is-lower-case@1.1.3: - dependencies: - lower-case: 1.1.4 - - is-number@7.0.0: {} - - is-plain-obj@2.1.0: {} - - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-stream@2.0.1: {} - - is-stream@3.0.0: {} - - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - - is-typedarray@1.0.0: {} - - is-unicode-supported@0.1.0: {} - - is-upper-case@1.1.2: - dependencies: - upper-case: 1.1.3 - - is-utf8@0.2.1: {} - - is-windows@1.0.2: {} - - isarray@1.0.0: {} - - isexe@2.0.0: {} - - isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - - isows@1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - - isows@1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) - - isstream@0.1.2: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.1(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - joi@17.13.3: - dependencies: - '@hapi/hoek': 9.3.0 - '@hapi/topo': 5.1.0 - '@sideway/address': 4.1.5 - '@sideway/formula': 3.0.1 - '@sideway/pinpoint': 2.0.0 - - js-sha3@0.5.5: {} - - js-sha3@0.5.7: {} - - js-sha3@0.8.0: {} - - js-tokens@9.0.1: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - jsbn@0.1.1: {} - - json-buffer@3.0.1: {} - - json-rpc-engine@6.1.0: - dependencies: - '@metamask/safe-event-emitter': 2.0.0 - eth-rpc-errors: 4.0.3 - - json-rpc-random-id@1.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-schema@0.4.0: {} - - json-stream-stringify@3.1.6: {} - - json-stringify-safe@5.0.1: {} - - jsonc-parser@3.3.1: {} - - jsonfile@2.4.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsprim@1.4.2: - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - - keccak@3.0.4: - dependencies: - node-addon-api: 2.0.2 - node-gyp-build: 4.8.4 - readable-stream: 3.6.2 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - keyvaluestorage-interface@1.0.0: {} - - klaw@1.3.1: - optionalDependencies: - graceful-fs: 4.2.11 - - lcid@1.0.0: - dependencies: - invert-kv: 1.0.0 - - lit-element@4.2.0: - dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 - '@lit/reactive-element': 2.1.0 - lit-html: 3.3.0 - - lit-html@3.3.0: - dependencies: - '@types/trusted-types': 2.0.7 - - lit@3.1.0: - dependencies: - '@lit/reactive-element': 2.1.0 - lit-element: 4.2.0 - lit-html: 3.3.0 - - load-json-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - parse-json: 2.2.0 - pify: 2.3.0 - pinkie-promise: 2.0.1 - strip-bom: 2.0.0 - - local-pkg@0.5.1: - dependencies: - mlly: 1.7.4 - pkg-types: 1.3.1 + wait-on@8.0.5: + resolution: {integrity: sha512-J3WlS0txVHkhLRb2FsmRg3dkMTCV1+M6Xra3Ho7HzZDHpE7DCOnoSoCJsZotrmW3uRMhvIJGSKUKrh/MeF4iag==} + engines: {node: '>=12.0.0'} + hasBin: true - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - lodash.assign@4.2.0: {} + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true - lodash.clonedeep@4.5.0: {} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true - lodash.flatten@4.4.0: {} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} - lodash.memoize@4.1.2: {} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} - lodash.merge@4.6.2: {} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - lodash.startcase@4.4.0: {} + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true - lodash.truncate@4.4.2: {} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} - lodash@4.17.21: {} + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} - lower-case-first@1.0.2: - dependencies: - lower-case: 1.1.4 + zustand@5.0.0: + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true - lower-case@1.1.4: {} +snapshots: - lowercase-keys@2.0.0: {} + '@adraffy/ens-normalize@1.10.1': {} - lowercase-keys@3.0.0: {} + '@adraffy/ens-normalize@1.11.1': {} - lru-cache@10.4.3: {} + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - lru_map@0.3.3: {} + '@babel/helper-string-parser@7.29.7': {} - lunr@2.3.9: {} + '@babel/helper-validator-identifier@7.29.7': {} - magic-string@0.30.17: + '@babel/parser@7.29.7': dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@babel/types': 7.29.7 - magicast@0.3.5: - dependencies: - '@babel/parser': 7.27.4 - '@babel/types': 7.27.3 - source-map-js: 1.2.1 + '@babel/runtime@7.29.2': {} - make-dir@4.0.0: + '@babel/types@7.29.7': dependencies: - semver: 7.7.2 - - make-error@1.3.6: {} + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - marked@4.3.0: {} + '@bcoe/v8-coverage@1.0.2': {} - match-all@1.2.7: {} + '@biomejs/biome@2.0.6': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.0.6 + '@biomejs/cli-darwin-x64': 2.0.6 + '@biomejs/cli-linux-arm64': 2.0.6 + '@biomejs/cli-linux-arm64-musl': 2.0.6 + '@biomejs/cli-linux-x64': 2.0.6 + '@biomejs/cli-linux-x64-musl': 2.0.6 + '@biomejs/cli-win32-arm64': 2.0.6 + '@biomejs/cli-win32-x64': 2.0.6 - math-intrinsics@1.1.0: {} + '@biomejs/cli-darwin-arm64@2.0.6': + optional: true - md5.js@1.3.5: - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 + '@biomejs/cli-darwin-x64@2.0.6': + optional: true - media-typer@0.3.0: {} + '@biomejs/cli-linux-arm64-musl@2.0.6': + optional: true - memorystream@0.3.1: {} + '@biomejs/cli-linux-arm64@2.0.6': + optional: true - merge-descriptors@1.0.3: {} + '@biomejs/cli-linux-x64-musl@2.0.6': + optional: true - merge-stream@2.0.0: {} + '@biomejs/cli-linux-x64@2.0.6': + optional: true - merge2@1.4.1: {} + '@biomejs/cli-win32-arm64@2.0.6': + optional: true - methods@1.1.2: {} + '@biomejs/cli-win32-x64@2.0.6': + optional: true - micro-eth-signer@0.14.0: + '@changesets/apply-release-plan@7.1.0': dependencies: - '@noble/curves': 1.8.2 - '@noble/hashes': 1.7.2 - micro-packed: 0.7.3 - - micro-ftch@0.3.1: {} + '@changesets/config': 3.1.3 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.7.4 - micro-packed@0.7.3: + '@changesets/assemble-release-plan@6.0.9': dependencies: - '@scure/base': 1.2.6 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.7.4 - micromatch@4.0.8: + '@changesets/changelog-git@0.2.1': dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} + '@changesets/types': 6.1.0 - mime-types@2.1.35: + '@changesets/changelog-github@0.5.2': dependencies: - mime-db: 1.52.0 - - mime@1.6.0: {} - - mimic-fn@4.0.0: {} - - mimic-response@1.0.1: {} - - mimic-response@3.1.0: {} + '@changesets/get-github-info': 0.7.0 + '@changesets/types': 6.1.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding - min-document@2.19.0: + '@changesets/cli@2.30.0(@types/node@22.19.17)': dependencies: - dom-walk: 0.1.2 - - minimalistic-assert@1.0.1: {} - - minimalistic-crypto-utils@1.0.1: {} + '@changesets/apply-release-plan': 7.1.0 + '@changesets/assemble-release-plan': 6.0.9 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.3 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-release-plan': 4.0.15 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@22.19.17) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.7.4 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' - minimatch@3.1.2: + '@changesets/config@3.1.3': dependencies: - brace-expansion: 1.1.11 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 - minimatch@5.1.6: + '@changesets/errors@0.2.0': dependencies: - brace-expansion: 2.0.1 + extendable-error: 0.1.7 - minimatch@9.0.5: + '@changesets/get-dependents-graph@2.1.3': dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.7.4 - minipass@2.9.0: + '@changesets/get-github-info@0.7.0': dependencies: - safe-buffer: 5.2.1 - yallist: 3.1.1 + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding - minizlib@1.3.3: + '@changesets/get-release-plan@4.0.15': dependencies: - minipass: 2.9.0 + '@changesets/assemble-release-plan': 6.0.9 + '@changesets/config': 3.1.3 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 - mipd@0.0.7(typescript@5.8.3): - optionalDependencies: - typescript: 5.8.3 + '@changesets/get-version-range-type@0.4.0': {} - mkdirp-promise@5.0.1: + '@changesets/git@3.0.4': dependencies: - mkdirp: 3.0.1 + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 - mkdirp@0.5.6: + '@changesets/logger@0.1.1': dependencies: - minimist: 1.2.8 - - mkdirp@3.0.1: {} + picocolors: 1.1.1 - mlly@1.7.4: + '@changesets/parse@0.4.3': dependencies: - acorn: 8.14.1 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 + '@changesets/types': 6.1.0 + js-yaml: 4.1.1 - mnemonist@0.38.5: + '@changesets/pre@2.0.2': dependencies: - obliterator: 2.0.5 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 - mocha@10.8.2: + '@changesets/read@0.6.7': dependencies: - ansi-colors: 4.1.3 - browser-stdout: 1.3.1 - chokidar: 3.6.0 - debug: 4.4.1(supports-color@8.1.1) - diff: 5.2.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 8.1.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.1.6 - ms: 2.1.3 - serialize-javascript: 6.0.2 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.5.1 - yargs: 16.2.0 - yargs-parser: 20.2.9 - yargs-unparser: 2.0.0 - - mock-fs@4.14.0: {} - - mri@1.2.0: {} + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 - ms@2.0.0: {} + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 - ms@2.1.3: {} + '@changesets/types@4.1.0': {} - multibase@0.6.1: - dependencies: - base-x: 3.0.11 - buffer: 5.7.1 + '@changesets/types@6.1.0': {} - multibase@0.7.0: + '@changesets/write@0.4.0': dependencies: - base-x: 3.0.11 - buffer: 5.7.1 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.1.3 + prettier: 2.8.8 - multicodec@0.5.7: + '@ensdomains/address-encoder@1.0.0-rc.3': dependencies: - varint: 5.0.2 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 - multicodec@1.0.4: + '@ensdomains/address-encoder@1.1.1': dependencies: - buffer: 5.7.1 - varint: 5.0.2 - - multiformats@12.1.3: {} + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 - multiformats@9.9.0: {} + '@ensdomains/buffer@0.1.3': {} - multihashes@0.4.21: + '@ensdomains/content-hash@3.1.0-rc.1': dependencies: - buffer: 5.7.1 - multibase: 0.7.0 - varint: 5.0.2 + '@ensdomains/address-encoder': 1.0.0-rc.3 + '@noble/curves': 1.9.7 + '@scure/base': 1.2.6 - murmur-128@0.2.1: + '@ensdomains/dnsprovejs@0.5.2': dependencies: - encode-utf8: 1.0.3 - fmix: 0.1.0 - imul: 1.0.1 - - nano-base32@1.0.1: {} - - nano-json-stream-parser@0.1.2: {} + '@noble/hashes': 1.8.0 + dns-packet: 5.6.1 + typescript-logging: 1.0.1 - nanoid@3.3.11: {} + '@ensdomains/ens-test-env@1.0.1': + dependencies: + cli-progress: 3.12.0 + commander: 14.0.3 + concurrently: 9.2.1 + docker-compose: 1.4.2 + dotenv: 16.6.1 + dunai: 0.0.0-dev.2 + js-yaml: 4.1.1 + picocolors: 1.1.1 + tar-fs: 3.1.2 + wait-on: 8.0.5 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - debug + - react-native-b4a - negotiator@0.6.3: {} + '@esbuild/aix-ppc64@0.27.7': + optional: true - next-tick@1.1.0: {} + '@esbuild/android-arm64@0.27.7': + optional: true - no-case@2.3.2: - dependencies: - lower-case: 1.1.4 + '@esbuild/android-arm@0.27.7': + optional: true - node-addon-api@2.0.2: {} + '@esbuild/android-x64@0.27.7': + optional: true - node-addon-api@5.1.0: {} + '@esbuild/darwin-arm64@0.27.7': + optional: true - node-fetch-native@1.6.6: {} + '@esbuild/darwin-x64@0.27.7': + optional: true - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 + '@esbuild/freebsd-arm64@0.27.7': + optional: true - node-gyp-build@4.8.4: {} + '@esbuild/freebsd-x64@0.27.7': + optional: true - node-mock-http@1.0.0: {} + '@esbuild/linux-arm64@0.27.7': + optional: true - nofilter@1.0.4: {} + '@esbuild/linux-arm@0.27.7': + optional: true - nofilter@3.1.0: {} + '@esbuild/linux-ia32@0.27.7': + optional: true - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.10 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 + '@esbuild/linux-loong64@0.27.7': + optional: true - normalize-path@3.0.0: {} + '@esbuild/linux-mips64el@0.27.7': + optional: true - normalize-url@6.1.0: {} + '@esbuild/linux-ppc64@0.27.7': + optional: true - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 + '@esbuild/linux-riscv64@0.27.7': + optional: true - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 + '@esbuild/linux-s390x@0.27.7': + optional: true - number-is-nan@1.0.1: {} + '@esbuild/linux-x64@0.27.7': + optional: true - number-to-bn@1.7.0: - dependencies: - bn.js: 4.11.6 - strip-hex-prefix: 1.0.0 + '@esbuild/netbsd-arm64@0.27.7': + optional: true - oauth-sign@0.9.0: {} + '@esbuild/netbsd-x64@0.27.7': + optional: true - obj-multiplex@1.0.0: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - readable-stream: 2.3.8 + '@esbuild/openbsd-arm64@0.27.7': + optional: true - object-assign@4.1.1: {} + '@esbuild/openbsd-x64@0.27.7': + optional: true - object-inspect@1.13.4: {} + '@esbuild/openharmony-arm64@0.27.7': + optional: true - obliterator@2.0.5: {} + '@esbuild/sunos-x64@0.27.7': + optional: true - oboe@2.1.5: - dependencies: - http-https: 1.0.0 + '@esbuild/win32-arm64@0.27.7': + optional: true - ofetch@1.4.1: - dependencies: - destr: 2.0.5 - node-fetch-native: 1.6.6 - ufo: 1.6.1 + '@esbuild/win32-ia32@0.27.7': + optional: true - on-exit-leak-free@0.2.0: {} + '@esbuild/win32-x64@0.27.7': + optional: true - on-finished@2.4.1: + '@graphql-typed-document-node/core@3.2.0(graphql@16.13.2)': dependencies: - ee-first: 1.1.1 + graphql: 16.13.2 - once@1.4.0: + '@hapi/address@5.1.1': dependencies: - wrappy: 1.0.2 + '@hapi/hoek': 11.0.7 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 + '@hapi/formula@3.0.2': {} - os-locale@1.4.0: - dependencies: - lcid: 1.0.0 + '@hapi/hoek@11.0.7': {} - os-tmpdir@1.0.2: {} + '@hapi/pinpoint@2.0.1': {} - outdent@0.5.0: {} + '@hapi/tlds@1.1.6': {} - ox@0.6.7(typescript@5.8.3)(zod@3.22.4): + '@hapi/topo@6.0.2': dependencies: - '@adraffy/ens-normalize': 1.10.1 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod + '@hapi/hoek': 11.0.7 - ox@0.6.9(typescript@5.8.3)(zod@3.22.4): + '@inquirer/external-editor@1.0.3(@types/node@22.19.17)': dependencies: - '@adraffy/ens-normalize': 1.10.1 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - eventemitter3: 5.0.1 + chardet: 2.1.1 + iconv-lite: 0.7.2 optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod + '@types/node': 22.19.17 - ox@0.7.1(typescript@5.8.3)(zod@3.22.4): + '@isaacs/cliui@8.0.2': dependencies: - '@adraffy/ens-normalize': 1.10.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod - - p-cancelable@2.1.1: {} + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 - p-cancelable@3.0.0: {} + '@istanbuljs/schema@0.1.6': {} - p-filter@2.1.0: + '@jridgewell/gen-mapping@0.3.13': dependencies: - p-map: 2.1.0 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 + '@jridgewell/resolve-uri@3.1.2': {} - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 + '@jridgewell/sourcemap-codec@1.5.5': {} - p-limit@5.0.0: + '@jridgewell/trace-mapping@0.3.31': dependencies: - yocto-queue: 1.2.1 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 + '@leichtgewicht/ip-codec@2.0.5': {} - p-locate@5.0.0: + '@manypkg/find-root@1.1.0': dependencies: - p-limit: 3.1.0 - - p-map@2.1.0: {} + '@babel/runtime': 7.29.2 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 - p-map@4.0.0: + '@manypkg/get-packages@1.1.3': dependencies: - aggregate-error: 3.1.0 + '@babel/runtime': 7.29.2 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 - p-try@2.2.0: {} + '@noble/ciphers@1.3.0': {} - package-manager-detector@0.2.11: + '@noble/curves@1.8.2': dependencies: - quansync: 0.2.10 - - pako@2.1.0: {} + '@noble/hashes': 1.7.2 - param-case@2.1.1: + '@noble/curves@1.9.1': dependencies: - no-case: 2.3.2 - - parse-headers@2.0.6: {} + '@noble/hashes': 1.8.0 - parse-json@2.2.0: + '@noble/curves@1.9.7': dependencies: - error-ex: 1.3.2 + '@noble/hashes': 1.8.0 - parse5-htmlparser2-tree-adapter@7.1.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.3.0 + '@noble/hashes@1.7.2': {} - parse5-parser-stream@7.1.2: - dependencies: - parse5: 7.3.0 + '@noble/hashes@1.8.0': {} - parse5@7.3.0: + '@nodelib/fs.scandir@2.1.5': dependencies: - entities: 6.0.0 + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 - parseurl@1.3.3: {} + '@nodelib/fs.stat@2.0.5': {} - pascal-case@2.0.1: + '@nodelib/fs.walk@1.2.8': dependencies: - camel-case: 3.0.0 - upper-case-first: 1.1.2 + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 - path-case@2.1.1: - dependencies: - no-case: 2.3.2 + '@openzeppelin/contracts@4.9.6': {} - path-exists@2.1.0: - dependencies: - pinkie-promise: 2.0.1 + '@pkgjs/parseargs@0.11.0': + optional: true - path-exists@4.0.0: {} + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true - path-is-absolute@1.0.1: {} + '@rollup/rollup-android-arm64@4.62.2': + optional: true - path-key@3.1.1: {} + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true - path-key@4.0.0: {} + '@rollup/rollup-darwin-x64@4.62.2': + optional: true - path-parse@1.0.7: {} + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true - path-starts-with@2.0.1: {} + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true - path-to-regexp@0.1.12: {} + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true - path-type@1.1.0: - dependencies: - graceful-fs: 4.2.11 - pify: 2.3.0 - pinkie-promise: 2.0.1 + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true - path-type@4.0.0: {} + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true - pathe@1.1.2: {} + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true - pathe@2.0.3: {} + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true - pathval@1.1.1: {} + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true - pbkdf2@3.1.2: - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true - performance-now@2.1.0: {} + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true - picocolors@1.1.1: {} + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true - picomatch@2.3.1: {} + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true - picomatch@4.0.2: {} + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true - pify@2.3.0: {} + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true - pify@3.0.0: {} + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true - pify@4.0.1: {} + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true - pify@5.0.0: {} + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true - pinkie-promise@2.0.1: - dependencies: - pinkie: 2.0.4 + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true - pinkie@2.0.4: {} + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true - pino-abstract-transport@0.5.0: - dependencies: - duplexify: 4.1.3 - split2: 4.2.0 + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true - pino-std-serializers@4.0.0: {} + '@scure/base@1.2.6': {} - pino@7.11.0: + '@scure/bip32@1.6.2': dependencies: - atomic-sleep: 1.0.0 - fast-redact: 3.5.0 - on-exit-leak-free: 0.2.0 - pino-abstract-transport: 0.5.0 - pino-std-serializers: 4.0.0 - process-warning: 1.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.1.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 2.8.0 - thread-stream: 0.15.2 + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/base': 1.2.6 - pkg-types@1.3.1: + '@scure/bip32@1.7.0': dependencies: - confbox: 0.1.8 - mlly: 1.7.4 - pathe: 2.0.3 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 - pngjs@5.0.0: {} + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.2 + '@scure/base': 1.2.6 - pony-cause@2.1.11: {} + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 - possible-typed-array-names@1.1.0: {} + '@standard-schema/spec@1.1.0': {} - postcss@8.5.4: + '@types/chai@5.2.3': dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - preact@10.26.8: {} + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - prettier@2.8.8: {} + '@types/deep-eql@4.0.2': {} - pretty-format@29.7.0: + '@types/dns-packet@5.6.5': dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - process-nextick-args@2.0.1: {} + '@types/node': 22.19.17 - process-warning@1.0.0: {} + '@types/estree@1.0.9': {} - process@0.11.10: {} + '@types/node@12.20.55': {} - proxy-addr@2.0.7: + '@types/node@22.19.17': dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - proxy-compare@2.6.0: {} + undici-types: 6.21.0 - proxy-from-env@1.1.0: {} + '@types/pako@2.0.4': {} - psl@1.15.0: + '@vitest/coverage-v8@3.2.6(vitest@3.2.6(@types/node@22.19.17)(yaml@2.8.3))': dependencies: - punycode: 2.3.1 + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + ast-v8-to-istanbul: 0.3.12 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magic-string: 0.30.21 + magicast: 0.3.5 + std-env: 3.10.0 + test-exclude: 7.0.2 + tinyrainbow: 2.0.0 + vitest: 3.2.6(@types/node@22.19.17)(yaml@2.8.3) + transitivePeerDependencies: + - supports-color - pump@3.0.2: + '@vitest/expect@3.2.6': dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - punycode@2.1.0: {} + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.6 + '@vitest/utils': 3.2.6 + chai: 5.3.3 + tinyrainbow: 2.0.0 - punycode@2.3.1: {} + '@vitest/mocker@3.2.6(vite@7.3.6(@types/node@22.19.17)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 3.2.6 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.6(@types/node@22.19.17)(yaml@2.8.3) - pure-rand@5.0.5: {} + '@vitest/pretty-format@3.2.6': + dependencies: + tinyrainbow: 2.0.0 - qrcode@1.5.3: + '@vitest/runner@3.2.6': dependencies: - dijkstrajs: 1.0.3 - encode-utf8: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 + '@vitest/utils': 3.2.6 + pathe: 2.0.3 + strip-literal: 3.1.0 - qs@6.13.0: + '@vitest/snapshot@3.2.6': dependencies: - side-channel: 1.1.0 + '@vitest/pretty-format': 3.2.6 + magic-string: 0.30.21 + pathe: 2.0.3 - qs@6.14.0: + '@vitest/spy@3.2.6': dependencies: - side-channel: 1.1.0 + tinyspy: 4.0.4 - qs@6.5.3: {} + '@vitest/utils@3.2.6': + dependencies: + '@vitest/pretty-format': 3.2.6 + loupe: 3.2.1 + tinyrainbow: 2.0.0 - quansync@0.2.10: {} + '@wagmi/connectors@8.0.20(@wagmi/core@3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3)))(typescript@5.9.3)(viem@2.29.0(typescript@5.9.3))': + dependencies: + '@wagmi/core': 3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3)) + viem: 2.29.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 - query-string@5.1.1: + '@wagmi/core@2.22.1(typescript@5.6.2)(use-sync-external-store@1.4.0)(viem@2.47.10(typescript@5.6.2))': dependencies: - decode-uri-component: 0.2.2 - object-assign: 4.1.1 - strict-uri-encode: 1.1.0 + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.6.2) + viem: 2.47.10(typescript@5.6.2) + zustand: 5.0.0(use-sync-external-store@1.4.0) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store - query-string@7.1.3: + '@wagmi/core@3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3))': dependencies: - decode-uri-component: 0.2.2 - filter-obj: 1.1.0 - split-on-first: 1.1.0 - strict-uri-encode: 2.0.0 + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.3) + viem: 2.29.0(typescript@5.9.3) + zustand: 5.0.0(use-sync-external-store@1.4.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store - queue-microtask@1.2.3: {} + abitype@1.0.8(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + abitype@1.2.3(typescript@5.6.2): + optionalDependencies: + typescript: 5.6.2 - quick-format-unescaped@4.0.4: {} + abitype@1.2.3(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 - quick-lru@5.1.1: {} + ansi-colors@4.1.3: {} - radix3@1.1.2: {} + ansi-regex@5.0.1: {} - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 + ansi-regex@6.2.2: {} - range-parser@1.2.1: {} + ansi-sequence-parser@1.1.3: {} - raw-body@2.5.2: + ansi-styles@4.3.0: dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 + color-convert: 2.0.1 - react-is@18.3.1: {} + ansi-styles@6.2.3: {} - read-pkg-up@1.0.1: + argparse@1.0.10: dependencies: - find-up: 1.1.2 - read-pkg: 1.1.0 + sprintf-js: 1.0.3 - read-pkg@1.1.0: - dependencies: - load-json-file: 1.1.0 - normalize-package-data: 2.5.0 - path-type: 1.1.0 + argparse@2.0.1: {} - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 + array-union@2.1.0: {} - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 + assertion-error@2.0.1: {} - readable-stream@3.6.2: + ast-v8-to-istanbul@0.3.12: dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 + asynckit@0.4.0: {} - readdirp@4.1.2: {} + axios@1.15.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug - real-require@0.1.0: {} + b4a@1.8.0: {} - request@2.88.2: - dependencies: - aws-sign2: 0.7.0 - aws4: 1.13.2 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - har-validator: 5.1.5 - http-signature: 1.2.0 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - oauth-sign: 0.9.0 - performance-now: 2.1.0 - qs: 6.5.3 - safe-buffer: 5.2.1 - tough-cookie: 2.5.0 - tunnel-agent: 0.6.0 - uuid: 3.4.0 + balanced-match@1.0.2: {} - require-directory@2.1.1: {} + balanced-match@4.0.4: {} - require-from-string@1.2.1: {} + bare-events@2.8.2: {} - require-from-string@2.0.2: {} + bare-fs@4.6.0: + dependencies: + bare-events: 2.8.2 + bare-path: 3.0.0 + bare-stream: 2.12.0(bare-events@2.8.2) + bare-url: 2.4.0 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a - require-main-filename@1.0.1: {} + bare-os@3.8.7: {} - require-main-filename@2.0.0: {} + bare-path@3.0.0: + dependencies: + bare-os: 3.8.7 - resolve-alpn@1.2.1: {} + bare-stream@2.12.0(bare-events@2.8.2): + dependencies: + streamx: 2.25.0 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - react-native-b4a - resolve-from@5.0.0: {} + bare-url@2.4.0: + dependencies: + bare-path: 3.0.0 - resolve@1.17.0: + better-path-resolve@1.0.0: dependencies: - path-parse: 1.0.7 + is-windows: 1.0.2 - resolve@1.22.10: + brace-expansion@2.0.3: dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + balanced-match: 1.0.2 - responselike@2.0.1: + brace-expansion@5.0.7: dependencies: - lowercase-keys: 2.0.0 - - reusify@1.1.0: {} + balanced-match: 4.0.4 - rimraf@2.7.1: + braces@3.0.3: dependencies: - glob: 7.2.3 + fill-range: 7.1.1 - ripemd160-min@0.0.6: {} + cac@6.7.14: {} - ripemd160@2.0.2: + call-bind-apply-helpers@1.0.2: dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 + es-errors: 1.3.0 + function-bind: 1.1.2 - rlp@2.2.7: + chai@5.3.3: dependencies: - bn.js: 5.2.2 + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 - rollup@4.41.1: + chalk@4.1.2: dependencies: - '@types/estree': 1.0.7 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.41.1 - '@rollup/rollup-android-arm64': 4.41.1 - '@rollup/rollup-darwin-arm64': 4.41.1 - '@rollup/rollup-darwin-x64': 4.41.1 - '@rollup/rollup-freebsd-arm64': 4.41.1 - '@rollup/rollup-freebsd-x64': 4.41.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.41.1 - '@rollup/rollup-linux-arm-musleabihf': 4.41.1 - '@rollup/rollup-linux-arm64-gnu': 4.41.1 - '@rollup/rollup-linux-arm64-musl': 4.41.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.41.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1 - '@rollup/rollup-linux-riscv64-gnu': 4.41.1 - '@rollup/rollup-linux-riscv64-musl': 4.41.1 - '@rollup/rollup-linux-s390x-gnu': 4.41.1 - '@rollup/rollup-linux-x64-gnu': 4.41.1 - '@rollup/rollup-linux-x64-musl': 4.41.1 - '@rollup/rollup-win32-arm64-msvc': 4.41.1 - '@rollup/rollup-win32-ia32-msvc': 4.41.1 - '@rollup/rollup-win32-x64-msvc': 4.41.1 - fsevents: 2.3.3 + ansi-styles: 4.3.0 + supports-color: 7.2.0 - run-parallel@1.2.0: + chardet@2.1.1: {} + + check-error@2.1.3: {} + + cli-progress@3.12.0: dependencies: - queue-microtask: 1.2.3 + string-width: 4.2.3 - rxjs@7.8.2: + cliui@8.0.1: dependencies: - tslib: 2.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 - safe-buffer@5.1.2: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 - safe-buffer@5.2.1: {} + color-name@1.1.4: {} - safe-regex-test@1.1.0: + combined-stream@1.0.8: dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 + delayed-stream: 1.0.0 - safe-stable-stringify@2.5.0: {} + commander@14.0.3: {} - safer-buffer@2.1.2: {} + concurrently@9.2.1: + dependencies: + chalk: 4.1.2 + rxjs: 7.8.2 + shell-quote: 1.8.3 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 - scrypt-js@2.0.4: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - scrypt-js@3.0.1: {} + dataloader@1.4.0: {} - secp256k1@4.0.4: + debug@4.4.3: dependencies: - elliptic: 6.6.1 - node-addon-api: 5.1.0 - node-gyp-build: 4.8.4 + ms: 2.1.3 - semver@5.7.2: {} + deep-eql@5.0.2: {} - semver@6.3.1: {} + delayed-stream@1.0.0: {} - semver@7.7.2: {} + detect-indent@6.1.0: {} - send@0.19.0: + dir-glob@3.0.1: dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color + path-type: 4.0.0 - sentence-case@2.1.1: + dns-packet@5.6.1: dependencies: - no-case: 2.3.2 - upper-case-first: 1.1.2 + '@leichtgewicht/ip-codec': 2.0.5 - serialize-javascript@6.0.2: + docker-compose@1.4.2: dependencies: - randombytes: 2.1.0 + yaml: 2.8.3 - serve-static@1.16.2: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.0 - transitivePeerDependencies: - - supports-color + dotenv@16.6.1: {} - servify@0.1.12: - dependencies: - body-parser: 1.20.3 - cors: 2.8.5 - express: 4.21.2 - request: 2.88.2 - xhr: 2.6.0 - transitivePeerDependencies: - - supports-color + dotenv@8.6.0: {} - set-blocking@2.0.0: {} + dunai@0.0.0-dev.2: {} - set-function-length@1.2.2: + dunder-proto@1.0.1: dependencies: - define-data-property: 1.1.4 + call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 gopd: 1.2.0 - has-property-descriptors: 1.0.2 - setimmediate@1.0.4: {} + eastasianwidth@0.2.0: {} - setimmediate@1.0.5: {} + emoji-regex@8.0.0: {} - setprototypeof@1.2.0: {} + emoji-regex@9.2.2: {} - sha.js@2.4.11: + end-of-stream@1.4.5: dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 + once: 1.4.0 - sha3@2.1.4: + enquirer@2.4.1: dependencies: - buffer: 6.0.3 + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 - shebang-command@2.0.0: + error-stack-parser@1.3.6: dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} + stackframe: 0.3.1 - shell-quote@1.8.3: {} + es-define-property@1.0.1: {} - shiki@0.14.7: - dependencies: - ansi-sequence-parser: 1.1.3 - jsonc-parser: 3.3.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 + es-errors@1.3.0: {} - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 + es-module-lexer@1.7.0: {} - side-channel-map@1.0.1: + es-object-atoms@1.1.1: dependencies: - call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-weakmap@1.0.2: + es-set-tostringtag@2.1.0: dependencies: - call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} + has-tostringtag: 1.0.2 + hasown: 2.0.2 - simple-concat@1.0.1: {} + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 - simple-get@2.8.2: - dependencies: - decompress-response: 3.3.0 - once: 1.4.0 - simple-concat: 1.0.1 + escalade@3.2.0: {} - slash@3.0.0: {} + esprima@4.0.1: {} - slice-ansi@4.0.0: + estree-walker@3.0.3: dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 + '@types/estree': 1.0.9 - snake-case@2.1.0: - dependencies: - no-case: 2.3.2 + eventemitter3@5.0.1: {} - socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + events-universal@1.0.1: dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - socket.io-parser: 4.2.4 + bare-events: 2.8.2 transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + - bare-abort-controller - socket.io-parser@4.2.4: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - transitivePeerDependencies: - - supports-color + expect-type@1.4.0: {} - solc@0.4.26: - dependencies: - fs-extra: 0.30.0 - memorystream: 0.3.1 - require-from-string: 1.2.1 - semver: 5.7.2 - yargs: 4.8.1 + extendable-error@0.1.7: {} - solc@0.8.26(debug@4.4.1): - dependencies: - command-exists: 1.2.9 - commander: 8.3.0 - follow-redirects: 1.15.9(debug@4.4.1) - js-sha3: 0.8.0 - memorystream: 0.3.1 - semver: 5.7.2 - tmp: 0.0.33 - transitivePeerDependencies: - - debug + fast-fifo@1.3.2: {} - solc@0.8.30: + fast-glob@3.3.3: dependencies: - command-exists: 1.2.9 - commander: 8.3.0 - follow-redirects: 1.15.9(debug@4.4.1) - js-sha3: 0.8.0 - memorystream: 0.3.1 - semver: 5.7.2 - tmp: 0.0.33 - transitivePeerDependencies: - - debug + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 - sonic-boom@2.8.0: + fastq@1.20.1: dependencies: - atomic-sleep: 1.0.0 + reusify: 1.1.0 - source-map-js@1.2.1: {} + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 - source-map-support@0.5.21: + fill-range@7.1.1: dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + to-regex-range: 5.0.1 - source-map@0.5.6: {} + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 - source-map@0.6.1: {} + follow-redirects@1.15.11: {} - spawndamnit@3.0.1: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 - spdx-correct@3.2.0: + form-data@4.0.5: dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.21 - - spdx-exceptions@2.5.0: {} + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 - spdx-expression-parse@3.0.1: + fs-extra@7.0.1: dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.21 + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 - spdx-license-ids@3.0.21: {} + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 - split-on-first@1.1.0: {} + fsevents@2.3.3: + optional: true - split2@4.2.0: {} + function-bind@1.1.2: {} - sprintf-js@1.0.3: {} + get-caller-file@2.0.5: {} - sshpk@1.18.0: + get-intrinsic@1.3.0: dependencies: - asn1: 0.2.6 - assert-plus: 1.0.0 - bcrypt-pbkdf: 1.0.2 - dashdash: 1.14.1 - ecc-jsbn: 0.1.2 - getpass: 0.1.7 - jsbn: 0.1.1 - safer-buffer: 2.1.2 - tweetnacl: 0.14.5 + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 - stack-generator@1.1.0: + get-proto@1.0.1: dependencies: - stackframe: 1.3.4 - - stackback@0.0.2: {} - - stackframe@0.3.1: {} - - stackframe@1.3.4: {} + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 - stacktrace-gps@2.4.4: + glob-parent@5.1.2: dependencies: - source-map: 0.5.6 - stackframe: 0.3.1 + is-glob: 4.0.3 - stacktrace-js@1.3.1: + glob@10.5.0: dependencies: - error-stack-parser: 1.3.6 - stack-generator: 1.1.0 - stacktrace-gps: 2.4.4 + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 - stacktrace-parser@0.1.11: + globby@11.1.0: dependencies: - type-fest: 0.7.1 - - statuses@2.0.1: {} + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 - std-env@3.9.0: {} + gopd@1.2.0: {} - stream-shift@1.0.3: {} + graceful-fs@4.2.11: {} - streamx@2.22.0: + graphql-request@7.2.0(graphql@16.13.2): dependencies: - fast-fifo: 1.3.2 - text-decoder: 1.2.3 - optionalDependencies: - bare-events: 2.5.4 - - strict-uri-encode@1.1.0: {} + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.2) + graphql: 16.13.2 - strict-uri-encode@2.0.0: {} + graphql@16.13.2: {} - string-width@1.0.2: - dependencies: - code-point-at: 1.1.0 - is-fullwidth-code-point: 1.0.0 - strip-ansi: 3.0.1 + has-flag@4.0.0: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 + has-symbols@1.1.0: {} - string_decoder@1.1.1: + has-tostringtag@1.0.2: dependencies: - safe-buffer: 5.1.2 + has-symbols: 1.1.0 - string_decoder@1.3.0: + hasown@2.0.2: dependencies: - safe-buffer: 5.2.1 + function-bind: 1.1.2 - strip-ansi@3.0.1: - dependencies: - ansi-regex: 2.1.1 + html-escaper@2.0.2: {} - strip-ansi@4.0.0: - dependencies: - ansi-regex: 3.0.1 + human-id@4.1.3: {} - strip-ansi@6.0.1: + iconv-lite@0.7.2: dependencies: - ansi-regex: 5.0.1 + safer-buffer: 2.1.2 - strip-bom@2.0.0: - dependencies: - is-utf8: 0.2.1 + ignore@5.3.2: {} - strip-bom@3.0.0: {} + is-extglob@2.1.1: {} - strip-final-newline@3.0.0: {} + is-fullwidth-code-point@3.0.0: {} - strip-hex-prefix@1.0.0: + is-glob@4.0.3: dependencies: - is-hex-prefixed: 1.0.0 - - strip-indent@2.0.0: {} + is-extglob: 2.1.1 - strip-json-comments@3.1.1: {} + is-number@7.0.0: {} - strip-literal@2.1.1: + is-subdir@1.2.0: dependencies: - js-tokens: 9.0.1 + better-path-resolve: 1.0.0 - superstruct@1.0.4: {} + is-windows@1.0.2: {} - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 + isexe@2.0.0: {} - supports-color@7.2.0: + isows@1.0.6(ws@8.18.1): dependencies: - has-flag: 4.0.0 + ws: 8.18.1 - supports-color@8.1.1: + isows@1.0.7(ws@8.18.3): dependencies: - has-flag: 4.0.0 + ws: 8.18.3 - supports-preserve-symlinks-flag@1.0.0: {} + istanbul-lib-coverage@3.2.2: {} - swap-case@1.1.2: + istanbul-lib-report@3.0.1: dependencies: - lower-case: 1.1.4 - upper-case: 1.1.3 + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 - swarm-js@0.1.42(bufferutil@4.0.9)(utf-8-validate@5.0.10): + istanbul-lib-source-maps@5.0.6: dependencies: - bluebird: 3.7.2 - buffer: 5.7.1 - eth-lib: 0.1.29(bufferutil@4.0.9)(utf-8-validate@5.0.10) - fs-extra: 4.0.3 - got: 11.8.6 - mime-types: 2.1.35 - mkdirp-promise: 5.0.1 - mock-fs: 4.14.0 - setimmediate: 1.0.5 - tar: 4.4.19 - xhr-request: 1.1.0 + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate - table@6.9.0: + istanbul-reports@3.2.0: dependencies: - ajv: 8.17.1 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 - tar-fs@3.0.9: + jackspeak@3.4.3: dependencies: - pump: 3.0.2 - tar-stream: 3.1.7 + '@isaacs/cliui': 8.0.2 optionalDependencies: - bare-fs: 4.1.5 - bare-path: 3.0.0 - transitivePeerDependencies: - - bare-buffer + '@pkgjs/parseargs': 0.11.0 - tar-stream@3.1.7: + joi@18.1.2: dependencies: - b4a: 1.6.7 - fast-fifo: 1.3.2 - streamx: 2.22.0 + '@hapi/address': 5.1.1 + '@hapi/formula': 3.0.2 + '@hapi/hoek': 11.0.7 + '@hapi/pinpoint': 2.0.1 + '@hapi/tlds': 1.1.6 + '@hapi/topo': 6.0.2 + '@standard-schema/spec': 1.1.0 - tar@4.4.19: - dependencies: - chownr: 1.1.4 - fs-minipass: 1.2.7 - minipass: 2.9.0 - minizlib: 1.3.3 - mkdirp: 0.5.6 - safe-buffer: 5.2.1 - yallist: 3.1.1 + js-tokens@10.0.0: {} - term-size@2.2.1: {} + js-tokens@9.0.1: {} - test-exclude@6.0.0: + js-yaml@3.14.2: dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - - testrpc@0.0.1: {} + argparse: 1.0.10 + esprima: 4.0.1 - text-decoder@1.2.3: + js-yaml@4.1.1: dependencies: - b4a: 1.6.7 + argparse: 2.0.1 + + jsonc-parser@3.3.1: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 - thread-stream@0.15.2: + locate-path@5.0.0: dependencies: - real-require: 0.1.0 + p-locate: 4.1.0 - timed-out@4.0.1: {} + lodash.startcase@4.4.0: {} - tinybench@2.9.0: {} + lodash@4.18.1: {} - tinyglobby@0.2.14: - dependencies: - fdir: 6.4.5(picomatch@4.0.2) - picomatch: 4.0.2 + loupe@3.2.1: {} - tinypool@0.8.4: {} + lru-cache@10.4.3: {} - tinyspy@2.2.1: {} + lunr@2.3.9: {} - title-case@2.1.1: + magic-string@0.30.21: dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 + '@jridgewell/sourcemap-codec': 1.5.5 - tmp@0.0.33: + magicast@0.3.5: dependencies: - os-tmpdir: 1.0.2 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 - to-regex-range@5.0.1: + make-dir@4.0.0: dependencies: - is-number: 7.0.0 + semver: 7.7.4 - toidentifier@1.0.1: {} + marked@4.3.0: {} - tough-cookie@2.5.0: - dependencies: - psl: 1.15.0 - punycode: 2.3.1 + math-intrinsics@1.1.0: {} - tr46@0.0.3: {} + merge2@1.4.1: {} - tree-kill@1.2.2: {} + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} - ts-node@10.9.2(@types/node@22.15.29)(typescript@5.8.3): + mime-types@2.1.35: dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.29 - acorn: 8.14.1 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.8.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 + mime-db: 1.52.0 - ts-pattern@5.7.1: {} + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.7 - tslib@1.14.1: {} + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.3 - tslib@2.8.1: {} + minimist@1.2.8: {} - tsort@0.0.1: {} + minipass@7.1.3: {} - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 + mipd@0.0.7(typescript@5.6.2): + optionalDependencies: + typescript: 5.6.2 - tweetnacl@0.14.5: {} + mipd@0.0.7(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 - type-detect@4.1.0: {} + mri@1.2.0: {} - type-fest@0.20.2: {} + ms@2.1.3: {} - type-fest@0.21.3: {} + nanoid@3.3.15: {} - type-fest@0.7.1: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 - type-is@1.6.18: + once@1.4.0: dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 + wrappy: 1.0.2 - type@2.7.3: {} + outdent@0.5.0: {} - typedarray-to-buffer@3.1.5: + ox@0.14.7(typescript@5.6.2): dependencies: - is-typedarray: 1.0.0 + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.6.2) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - zod - typedoc-plugin-markdown@4.6.4(typedoc@0.24.8(typescript@5.8.3)): + ox@0.14.7(typescript@5.9.3): dependencies: - typedoc: 0.24.8(typescript@5.8.3) + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod - typedoc@0.24.8(typescript@5.8.3): + ox@0.6.9(typescript@5.9.3): dependencies: - lunr: 2.3.9 - marked: 4.3.0 - minimatch: 9.0.5 - shiki: 0.14.7 - typescript: 5.8.3 + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod - typescript-logging@1.0.1: + p-filter@2.1.0: dependencies: - stacktrace-js: 1.3.1 - - typescript@5.8.3: {} + p-map: 2.1.0 - ufo@1.6.1: {} + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 - uint8arrays@3.1.0: + p-locate@4.1.0: dependencies: - multiformats: 9.9.0 + p-limit: 2.3.0 - ultron@1.1.1: {} + p-map@2.1.0: {} - uncrypto@0.1.3: {} + p-try@2.2.0: {} - undici-types@6.21.0: {} + package-json-from-dist@1.0.1: {} - undici@5.29.0: + package-manager-detector@0.2.11: dependencies: - '@fastify/busboy': 2.1.1 + quansync: 0.2.11 - undici@6.21.3: {} - - universalify@0.1.2: {} + pako@2.1.0: {} - universalify@2.0.1: {} + path-exists@4.0.0: {} - unpipe@1.0.0: {} + path-key@3.1.1: {} - unstorage@1.16.0(idb-keyval@6.2.2): + path-scurry@1.11.1: dependencies: - anymatch: 3.1.3 - chokidar: 4.0.3 - destr: 2.0.5 - h3: 1.15.3 lru-cache: 10.4.3 - node-fetch-native: 1.6.6 - ofetch: 1.4.1 - ufo: 1.6.1 - optionalDependencies: - idb-keyval: 6.2.2 + minipass: 7.1.3 - upper-case-first@1.1.2: - dependencies: - upper-case: 1.1.3 + path-type@4.0.0: {} - upper-case@1.1.3: {} + pathe@2.0.3: {} - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 + pathval@2.0.1: {} - url-set-query@1.0.0: {} + picocolors@1.1.1: {} - use-sync-external-store@1.2.0: {} + picomatch@2.3.2: {} - use-sync-external-store@1.4.0: {} + picomatch@4.0.4: {} - utf-8-validate@5.0.10: + pify@4.0.1: {} + + postcss@8.5.16: dependencies: - node-gyp-build: 4.8.4 + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 - utf8@3.0.0: {} + prettier@2.8.8: {} - util-deprecate@1.0.2: {} + proxy-from-env@2.1.0: {} - util@0.12.5: + pump@3.0.4: dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.0 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 + end-of-stream: 1.4.5 + once: 1.4.0 - utils-merge@1.0.1: {} + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} - uuid@2.0.1: {} + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.2 + pify: 4.0.1 + strip-bom: 3.0.0 - uuid@3.4.0: {} + require-directory@2.1.1: {} - uuid@8.3.2: {} + resolve-from@5.0.0: {} - uuid@9.0.1: {} + reusify@1.1.0: {} - v8-compile-cache-lib@3.0.1: {} + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 - validate-npm-package-license@3.0.4: + run-parallel@1.2.0: dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 + queue-microtask: 1.2.3 - valtio@1.13.2: + rxjs@7.8.2: dependencies: - derive-valtio: 0.1.0(valtio@1.13.2) - proxy-compare: 2.6.0 - use-sync-external-store: 1.2.0 + tslib: 2.8.1 - varint@5.0.2: {} + safer-buffer@2.1.2: {} - vary@1.1.2: {} + semver@7.7.4: {} - verror@1.10.0: + shebang-command@2.0.0: dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.3.0 + shebang-regex: 3.0.0 - viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): - dependencies: - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@scure/bip32': 1.6.2 - '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.8.3)(zod@3.22.4) - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod + shebang-regex@3.0.0: {} - viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): - dependencies: - '@noble/curves': 1.8.2 - '@noble/hashes': 1.7.2 - '@scure/bip32': 1.6.2 - '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.9(typescript@5.8.3)(zod@3.22.4) - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod + shell-quote@1.8.3: {} - viem@2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): + shiki@0.14.7: dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) - isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.7.1(typescript@5.8.3)(zod@3.22.4) - ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod + ansi-sequence-parser: 1.1.3 + jsonc-parser: 3.3.1 + vscode-oniguruma: 1.7.0 + vscode-textmate: 8.0.0 - vite-node@1.6.1(@types/node@22.15.29): - dependencies: - cac: 6.7.14 - debug: 4.4.1(supports-color@8.1.1) - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.4.19(@types/node@22.15.29) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser + siginfo@2.0.0: {} - vite@5.4.19(@types/node@22.15.29): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.4 - rollup: 4.41.1 - optionalDependencies: - '@types/node': 22.15.29 - fsevents: 2.3.3 + signal-exit@4.1.0: {} - vitest@1.6.1(@types/node@22.15.29)(happy-dom@13.10.1): - dependencies: - '@vitest/expect': 1.6.1 - '@vitest/runner': 1.6.1 - '@vitest/snapshot': 1.6.1 - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - acorn-walk: 8.3.4 - chai: 4.5.0 - debug: 4.4.1(supports-color@8.1.1) - execa: 8.0.1 - local-pkg: 0.5.1 - magic-string: 0.30.17 - pathe: 1.1.2 - picocolors: 1.1.1 - std-env: 3.9.0 - strip-literal: 2.1.1 - tinybench: 2.9.0 - tinypool: 0.8.4 - vite: 5.4.19(@types/node@22.15.29) - vite-node: 1.6.1(@types/node@22.15.29) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.15.29 - happy-dom: 13.10.1 - transitivePeerDependencies: - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser + slash@3.0.0: {} - vscode-oniguruma@1.7.0: {} + source-map-js@1.2.1: {} - vscode-textmate@8.0.0: {} + source-map@0.5.6: {} - wagmi@2.15.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4): + spawndamnit@3.0.1: dependencies: - '@wagmi/connectors': 5.8.3(@wagmi/core@2.17.2(typescript@5.8.3)(use-sync-external-store@1.4.0)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) - '@wagmi/core': 2.17.2(typescript@5.8.3)(use-sync-external-store@1.4.0)(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)) - use-sync-external-store: 1.4.0 - viem: 2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/query-core' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - immer - - ioredis - - supports-color - - uploadthing - - utf-8-validate - - zod + cross-spawn: 7.0.6 + signal-exit: 4.1.0 - wait-on@8.0.3: - dependencies: - axios: 1.9.0 - joi: 17.13.3 - lodash: 4.17.21 - minimist: 1.2.8 - rxjs: 7.8.2 - transitivePeerDependencies: - - debug + sprintf-js@1.0.3: {} - web3-bzz@1.10.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + stack-generator@1.1.0: dependencies: - '@types/node': 12.20.55 - got: 12.1.0 - swarm-js: 0.1.42(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + stackframe: 1.3.4 + + stackback@0.0.2: {} + + stackframe@0.3.1: {} + + stackframe@1.3.4: {} - web3-bzz@1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10): + stacktrace-gps@2.4.4: dependencies: - '@types/node': 12.20.55 - got: 12.1.0 - swarm-js: 0.1.42(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + source-map: 0.5.6 + stackframe: 0.3.1 - web3-core-helpers@1.10.0: + stacktrace-js@1.3.1: dependencies: - web3-eth-iban: 1.10.0 - web3-utils: 1.10.0 + error-stack-parser: 1.3.6 + stack-generator: 1.1.0 + stacktrace-gps: 2.4.4 - web3-core-helpers@1.10.4: - dependencies: - web3-eth-iban: 1.10.4 - web3-utils: 1.10.4 + std-env@3.10.0: {} - web3-core-method@1.10.0: + streamx@2.25.0: dependencies: - '@ethersproject/transactions': 5.8.0 - web3-core-helpers: 1.10.0 - web3-core-promievent: 1.10.0 - web3-core-subscriptions: 1.10.0 - web3-utils: 1.10.0 + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a - web3-core-method@1.10.4: + string-width@4.2.3: dependencies: - '@ethersproject/transactions': 5.8.0 - web3-core-helpers: 1.10.4 - web3-core-promievent: 1.10.4 - web3-core-subscriptions: 1.10.4 - web3-utils: 1.10.4 + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 - web3-core-promievent@1.10.0: + string-width@5.1.2: dependencies: - eventemitter3: 4.0.4 + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 - web3-core-promievent@1.10.4: + strip-ansi@6.0.1: dependencies: - eventemitter3: 4.0.4 + ansi-regex: 5.0.1 - web3-core-requestmanager@1.10.0: + strip-ansi@7.2.0: dependencies: - util: 0.12.5 - web3-core-helpers: 1.10.0 - web3-providers-http: 1.10.0 - web3-providers-ipc: 1.10.0 - web3-providers-ws: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} - web3-core-requestmanager@1.10.4: + strip-literal@3.1.0: dependencies: - util: 0.12.5 - web3-core-helpers: 1.10.4 - web3-providers-http: 1.10.4 - web3-providers-ipc: 1.10.4 - web3-providers-ws: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + js-tokens: 9.0.1 - web3-core-subscriptions@1.10.0: + supports-color@7.2.0: dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.10.0 + has-flag: 4.0.0 - web3-core-subscriptions@1.10.4: + supports-color@8.1.1: dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.10.4 + has-flag: 4.0.0 - web3-core@1.10.0: + tar-fs@3.1.2: dependencies: - '@types/bn.js': 5.1.6 - '@types/node': 12.20.55 - bignumber.js: 9.3.0 - web3-core-helpers: 1.10.0 - web3-core-method: 1.10.0 - web3-core-requestmanager: 1.10.0 - web3-utils: 1.10.0 + pump: 3.0.4 + tar-stream: 3.1.8 + optionalDependencies: + bare-fs: 4.6.0 + bare-path: 3.0.0 transitivePeerDependencies: - - encoding - - supports-color + - bare-abort-controller + - bare-buffer + - react-native-b4a - web3-core@1.10.4: + tar-stream@3.1.8: dependencies: - '@types/bn.js': 5.1.6 - '@types/node': 12.20.55 - bignumber.js: 9.3.0 - web3-core-helpers: 1.10.4 - web3-core-method: 1.10.4 - web3-core-requestmanager: 1.10.4 - web3-utils: 1.10.4 + b4a: 1.8.0 + bare-fs: 4.6.0 + fast-fifo: 1.3.2 + streamx: 2.25.0 transitivePeerDependencies: - - encoding - - supports-color + - bare-abort-controller + - bare-buffer + - react-native-b4a - web3-eth-abi@1.10.0: + teex@1.0.1: dependencies: - '@ethersproject/abi': 5.8.0 - web3-utils: 1.10.0 + streamx: 2.25.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + term-size@2.2.1: {} - web3-eth-abi@1.10.4: + test-exclude@7.0.2: dependencies: - '@ethersproject/abi': 5.8.0 - web3-utils: 1.10.4 + '@istanbuljs/schema': 0.1.6 + glob: 10.5.0 + minimatch: 10.2.5 - web3-eth-accounts@1.10.0: + text-decoder@1.2.7: dependencies: - '@ethereumjs/common': 2.5.0 - '@ethereumjs/tx': 3.3.2 - eth-lib: 0.2.8 - ethereumjs-util: 7.1.5 - scrypt-js: 3.0.1 - uuid: 9.0.1 - web3-core: 1.10.0 - web3-core-helpers: 1.10.0 - web3-core-method: 1.10.0 - web3-utils: 1.10.0 + b4a: 1.8.0 transitivePeerDependencies: - - encoding - - supports-color + - react-native-b4a - web3-eth-accounts@1.10.4: - dependencies: - '@ethereumjs/common': 2.6.5 - '@ethereumjs/tx': 3.5.2 - '@ethereumjs/util': 8.1.0 - eth-lib: 0.2.8 - scrypt-js: 3.0.1 - uuid: 9.0.1 - web3-core: 1.10.4 - web3-core-helpers: 1.10.4 - web3-core-method: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + tinybench@2.9.0: {} - web3-eth-contract@1.10.0: - dependencies: - '@types/bn.js': 5.1.6 - web3-core: 1.10.0 - web3-core-helpers: 1.10.0 - web3-core-method: 1.10.0 - web3-core-promievent: 1.10.0 - web3-core-subscriptions: 1.10.0 - web3-eth-abi: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + tinyexec@0.3.2: {} - web3-eth-contract@1.10.4: + tinyglobby@0.2.17: dependencies: - '@types/bn.js': 5.1.6 - web3-core: 1.10.4 - web3-core-helpers: 1.10.4 - web3-core-method: 1.10.4 - web3-core-promievent: 1.10.4 - web3-core-subscriptions: 1.10.4 - web3-eth-abi: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 - web3-eth-ens@1.10.0: - dependencies: - content-hash: 2.5.2 - eth-ens-namehash: 2.0.8 - web3-core: 1.10.0 - web3-core-helpers: 1.10.0 - web3-core-promievent: 1.10.0 - web3-eth-abi: 1.10.0 - web3-eth-contract: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + tinypool@1.1.1: {} - web3-eth-ens@1.10.4: - dependencies: - content-hash: 2.5.2 - eth-ens-namehash: 2.0.8 - web3-core: 1.10.4 - web3-core-helpers: 1.10.4 - web3-core-promievent: 1.10.4 - web3-eth-abi: 1.10.4 - web3-eth-contract: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + tinyrainbow@2.0.0: {} - web3-eth-iban@1.10.0: - dependencies: - bn.js: 5.2.2 - web3-utils: 1.10.0 + tinyspy@4.0.4: {} - web3-eth-iban@1.10.4: + to-regex-range@5.0.1: dependencies: - bn.js: 5.2.2 - web3-utils: 1.10.4 + is-number: 7.0.0 - web3-eth-personal@1.10.0: - dependencies: - '@types/node': 12.20.55 - web3-core: 1.10.0 - web3-core-helpers: 1.10.0 - web3-core-method: 1.10.0 - web3-net: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + tr46@0.0.3: {} - web3-eth-personal@1.10.4: - dependencies: - '@types/node': 12.20.55 - web3-core: 1.10.4 - web3-core-helpers: 1.10.4 - web3-core-method: 1.10.4 - web3-net: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + tree-kill@1.2.2: {} - web3-eth@1.10.0: - dependencies: - web3-core: 1.10.0 - web3-core-helpers: 1.10.0 - web3-core-method: 1.10.0 - web3-core-subscriptions: 1.10.0 - web3-eth-abi: 1.10.0 - web3-eth-accounts: 1.10.0 - web3-eth-contract: 1.10.0 - web3-eth-ens: 1.10.0 - web3-eth-iban: 1.10.0 - web3-eth-personal: 1.10.0 - web3-net: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + ts-pattern@5.9.0: {} - web3-eth@1.10.4: - dependencies: - web3-core: 1.10.4 - web3-core-helpers: 1.10.4 - web3-core-method: 1.10.4 - web3-core-subscriptions: 1.10.4 - web3-eth-abi: 1.10.4 - web3-eth-accounts: 1.10.4 - web3-eth-contract: 1.10.4 - web3-eth-ens: 1.10.4 - web3-eth-iban: 1.10.4 - web3-eth-personal: 1.10.4 - web3-net: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + tslib@2.8.1: {} - web3-net@1.10.0: + typedoc-plugin-markdown@4.11.0(typedoc@0.24.8(typescript@5.9.3)): dependencies: - web3-core: 1.10.0 - web3-core-method: 1.10.0 - web3-utils: 1.10.0 - transitivePeerDependencies: - - encoding - - supports-color + typedoc: 0.24.8(typescript@5.9.3) - web3-net@1.10.4: + typedoc@0.24.8(typescript@5.9.3): dependencies: - web3-core: 1.10.4 - web3-core-method: 1.10.4 - web3-utils: 1.10.4 - transitivePeerDependencies: - - encoding - - supports-color + lunr: 2.3.9 + marked: 4.3.0 + minimatch: 9.0.9 + shiki: 0.14.7 + typescript: 5.9.3 - web3-providers-http@1.10.0: + typescript-logging@1.0.1: dependencies: - abortcontroller-polyfill: 1.7.8 - cross-fetch: 3.2.0 - es6-promise: 4.2.8 - web3-core-helpers: 1.10.0 - transitivePeerDependencies: - - encoding + stacktrace-js: 1.3.1 - web3-providers-http@1.10.4: - dependencies: - abortcontroller-polyfill: 1.7.8 - cross-fetch: 4.1.0 - es6-promise: 4.2.8 - web3-core-helpers: 1.10.4 - transitivePeerDependencies: - - encoding + typescript@5.6.2: {} - web3-providers-ipc@1.10.0: - dependencies: - oboe: 2.1.5 - web3-core-helpers: 1.10.0 + typescript@5.9.3: {} - web3-providers-ipc@1.10.4: - dependencies: - oboe: 2.1.5 - web3-core-helpers: 1.10.4 + undici-types@6.21.0: {} - web3-providers-ws@1.10.0: - dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.10.0 - websocket: 1.0.35 - transitivePeerDependencies: - - supports-color + universalify@0.1.2: {} - web3-providers-ws@1.10.4: - dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.10.4 - websocket: 1.0.35 - transitivePeerDependencies: - - supports-color + use-sync-external-store@1.4.0: {} - web3-shh@1.10.0: + viem@2.29.0(typescript@5.9.3): dependencies: - web3-core: 1.10.0 - web3-core-method: 1.10.0 - web3-core-subscriptions: 1.10.0 - web3-net: 1.10.0 + '@noble/curves': 1.8.2 + '@noble/hashes': 1.7.2 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.9.3) + isows: 1.0.6(ws@8.18.1) + ox: 0.6.9(typescript@5.9.3) + ws: 8.18.1 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - - encoding - - supports-color + - bufferutil + - utf-8-validate + - zod - web3-shh@1.10.4: + viem@2.47.10(typescript@5.6.2): dependencies: - web3-core: 1.10.4 - web3-core-method: 1.10.4 - web3-core-subscriptions: 1.10.4 - web3-net: 1.10.4 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.6.2) + isows: 1.0.7(ws@8.18.3) + ox: 0.14.7(typescript@5.6.2) + ws: 8.18.3 + optionalDependencies: + typescript: 5.6.2 transitivePeerDependencies: - - encoding - - supports-color + - bufferutil + - utf-8-validate + - zod - web3-utils@1.10.0: - dependencies: - bn.js: 5.2.2 - ethereum-bloom-filters: 1.2.0 - ethereumjs-util: 7.1.5 - ethjs-unit: 0.1.6 - number-to-bn: 1.7.0 - randombytes: 2.1.0 - utf8: 3.0.0 - - web3-utils@1.10.4: - dependencies: - '@ethereumjs/util': 8.1.0 - bn.js: 5.2.2 - ethereum-bloom-filters: 1.2.0 - ethereum-cryptography: 2.2.1 - ethjs-unit: 0.1.6 - number-to-bn: 1.7.0 - randombytes: 2.1.0 - utf8: 3.0.0 - - web3@1.10.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - web3-bzz: 1.10.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - web3-core: 1.10.0 - web3-eth: 1.10.0 - web3-eth-personal: 1.10.0 - web3-net: 1.10.0 - web3-shh: 1.10.0 - web3-utils: 1.10.0 + viem@2.47.10(typescript@5.9.3): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + isows: 1.0.7(ws@8.18.3) + ox: 0.14.7(typescript@5.9.3) + ws: 8.18.3 + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - bufferutil - - encoding - - supports-color - utf-8-validate + - zod - web3@1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10): + vite-node@3.2.4(@types/node@22.19.17)(yaml@2.8.3): dependencies: - web3-bzz: 1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - web3-core: 1.10.4 - web3-eth: 1.10.4 - web3-eth-personal: 1.10.4 - web3-net: 1.10.4 - web3-shh: 1.10.4 - web3-utils: 1.10.4 + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.6(@types/node@22.19.17)(yaml@2.8.3) transitivePeerDependencies: - - bufferutil - - encoding + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss - supports-color - - utf-8-validate + - terser + - tsx + - yaml - webextension-polyfill@0.10.0: {} + vite@7.3.6(@types/node@22.19.17)(yaml@2.8.3): + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.16 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.19.17 + fsevents: 2.3.3 + yaml: 2.8.3 + + vitest@3.2.6(@types/node@22.19.17)(yaml@2.8.3): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.6 + '@vitest/mocker': 3.2.6(vite@7.3.6(@types/node@22.19.17)(yaml@2.8.3)) + '@vitest/pretty-format': 3.2.6 + '@vitest/runner': 3.2.6 + '@vitest/snapshot': 3.2.6 + '@vitest/spy': 3.2.6 + '@vitest/utils': 3.2.6 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.6(@types/node@22.19.17)(yaml@2.8.3) + vite-node: 3.2.4(@types/node@22.19.17)(yaml@2.8.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.17 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml - webidl-conversions@3.0.1: {} + vscode-oniguruma@1.7.0: {} - webidl-conversions@7.0.0: {} + vscode-textmate@8.0.0: {} - websocket@1.0.35: + wagmi@3.6.21(typescript@5.9.3)(viem@2.29.0(typescript@5.9.3)): dependencies: - bufferutil: 4.0.9 - debug: 2.6.9 - es5-ext: 0.10.64 - typedarray-to-buffer: 3.1.5 - utf-8-validate: 5.0.10 - yaeti: 0.0.6 + '@wagmi/connectors': 8.0.20(@wagmi/core@3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3)))(typescript@5.9.3)(viem@2.29.0(typescript@5.9.3)) + '@wagmi/core': 3.5.5(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.29.0(typescript@5.9.3)) + use-sync-external-store: 1.4.0 + viem: 2.29.0(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - - supports-color + - '@base-org/account' + - '@coinbase/wallet-sdk' + - '@metamask/connect-evm' + - '@safe-global/safe-apps-provider' + - '@safe-global/safe-apps-sdk' + - '@tanstack/query-core' + - '@types/react' + - '@walletconnect/ethereum-provider' + - accounts + - immer + - porto - whatwg-encoding@3.1.1: + wait-on@8.0.5: dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@3.0.0: {} + axios: 1.15.0 + joi: 18.1.2 + lodash: 4.18.1 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug - whatwg-mimetype@4.0.0: {} + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-module@1.0.0: {} - - which-module@2.0.1: {} - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -11467,151 +3811,30 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - widest-line@3.1.0: - dependencies: - string-width: 4.2.3 - - window-size@0.2.0: {} - - workerpool@6.5.1: {} - - wrap-ansi@2.1.0: - dependencies: - string-width: 1.0.2 - strip-ansi: 3.0.1 - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - wrappy@1.0.2: {} - - ws@3.3.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - async-limiter: 1.0.1 - safe-buffer: 5.1.2 - ultron: 1.1.1 - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - xhr-request-promise@0.1.3: - dependencies: - xhr-request: 1.1.0 - - xhr-request@1.1.0: + wrap-ansi@8.1.0: dependencies: - buffer-to-arraybuffer: 0.0.5 - object-assign: 4.1.1 - query-string: 5.1.1 - simple-get: 2.8.2 - timed-out: 4.0.1 - url-set-query: 1.0.0 - xhr: 2.6.0 - - xhr@2.6.0: - dependencies: - global: 4.4.0 - is-function: 1.0.2 - parse-headers: 2.0.6 - xtend: 4.0.2 - - xmlhttprequest-ssl@2.1.2: {} - - xmlhttprequest@1.8.0: {} + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 - xtend@4.0.2: {} + wrappy@1.0.2: {} - y18n@3.2.2: {} + ws@8.18.1: {} - y18n@4.0.3: {} + ws@8.18.3: {} y18n@5.0.8: {} - yaeti@0.0.6: {} - - yallist@3.1.1: {} - - yaml@2.8.0: {} - - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - - yargs-parser@2.4.1: - dependencies: - camelcase: 3.0.0 - lodash.assign: 4.2.0 - - yargs-parser@20.2.9: {} + yaml@2.8.3: {} yargs-parser@21.1.1: {} - yargs-unparser@2.0.0: - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -11622,35 +3845,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yargs@4.8.1: - dependencies: - cliui: 3.2.0 - decamelize: 1.2.0 - get-caller-file: 1.0.3 - lodash.assign: 4.2.0 - os-locale: 1.4.0 - read-pkg-up: 1.0.1 - require-directory: 2.1.1 - require-main-filename: 1.0.1 - set-blocking: 2.0.0 - string-width: 1.0.2 - which-module: 1.0.0 - window-size: 0.2.0 - y18n: 3.2.2 - yargs-parser: 2.4.1 - - yn@3.1.1: {} - - yocto-queue@0.1.0: {} - - yocto-queue@1.2.1: {} - - zksync-ethers@5.10.0(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - - zod@3.22.4: {} - zustand@5.0.0(use-sync-external-store@1.4.0): optionalDependencies: use-sync-external-store: 1.4.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 15539e796..592792ce6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ packages: - packages/* - examples/* -patchedDependencies: - hardhat-deploy: patches/hardhat-deploy.patch +minimumReleaseAge: 1440 +trustPolicy: no-downgrade diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 000000000..667cb72f6 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,41 @@ +{ + // This tsconfig file contains the shared config for the build (tsconfig.build.json) and type checking (tsconfig.json) config. + "include": [], + "compilerOptions": { + // Incremental builds + // NOTE: Enabling incremental builds speeds up `tsc`. Keep in mind though that it does not reliably bust the cache when the `tsconfig.json` file changes. + "incremental": true, + + // Type checking + "strict": true, + // "exactOptionalPropertyTypes": true, // Enabled once supported in tanstack query https://github.com/TanStack/query/issues/7641 + "noFallthroughCasesInSwitch": true, // Not enabled by default in `strict` mode. + "noImplicitOverride": true, // Not enabled by default in `strict` mode. + "noImplicitReturns": true, // Not enabled by default in `strict` mode. + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, // Not enabled by default in `strict` mode. + "noUnusedParameters": true, // Not enabled by default in `strict` mode. + "useDefineForClassFields": true, // Not enabled by default in `strict` mode unless we bump `target` to ES2022. + "useUnknownInCatchVariables": true, + + // JavaScript support + "allowJs": false, + "checkJs": false, + + // Interop constraints + "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": true, + + // Language and environment + "moduleResolution": "NodeNext", + "module": "NodeNext", + "target": "ES2021", // Setting this to `ES2021` enables native support for `Node v16+`: https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping. + "lib": [ + "ES2022", // By using ES2022 we get access to the `.cause` property on `Error` instances. + "DOM" // We are adding `DOM` here to get the `fetch`, etc. types. This should be removed once these types are available via DefinitelyTyped. + ], + + // Skip type checking for node modules + "skipLibCheck": true + } +}