├── .changeset ├── README.md └── config.json ├── .devcontainer └── devcontainer.json ├── .github └── workflows │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── biome.json ├── docs ├── README.md ├── basics │ ├── README.md │ ├── batching-calls.md │ ├── custom-subgraph-uris.md │ ├── extending-the-viem-client.md │ ├── fetching-a-profile.md │ └── using-the-viem-client.md ├── dns │ ├── README.md │ ├── function.getDnsImportData.md │ ├── function.getDnsOwner.md │ └── function.importDnsName.md ├── index │ ├── README.md │ ├── function.addEnsContracts.md │ ├── function.createEnsPublicClient.md │ ├── function.createEnsSubgraphClient.md │ ├── function.createEnsWalletClient.md │ ├── function.ensPublicActions.md │ ├── function.ensSubgraphActions.md │ └── function.ensWalletActions.md ├── public │ ├── README.md │ ├── function.batch.md │ ├── function.getAbiRecord.md │ ├── function.getAddressRecord.md │ ├── function.getAvailable.md │ ├── function.getContentHashRecord.md │ ├── function.getExpiry.md │ ├── function.getName.md │ ├── function.getOwner.md │ ├── function.getPrice.md │ ├── function.getRecords.md │ ├── function.getResolver.md │ ├── function.getSupportedInterfaces.md │ ├── function.getTextRecord.md │ └── function.getWrapperData.md ├── subgraph │ ├── README.md │ ├── function.getDecodedName.md │ ├── function.getNameHistory.md │ ├── function.getNamesForAddress.md │ ├── function.getSubgraphRecords.md │ ├── function.getSubgraphRegistrant.md │ └── function.getSubnames.md └── wallet │ ├── README.md │ ├── function.clearRecords.md │ ├── function.commitName.md │ ├── function.createSubname.md │ ├── function.deleteSubname.md │ ├── function.registerName.md │ ├── function.renewNames.md │ ├── function.setAbiRecord.md │ ├── function.setAddressRecord.md │ ├── function.setChildFuses.md │ ├── function.setContentHashRecord.md │ ├── function.setFuses.md │ ├── function.setPrimaryName.md │ ├── function.setRecords.md │ ├── function.setResolver.md │ ├── function.setTextRecord.md │ ├── function.transferName.md │ ├── function.unwrapName.md │ └── function.wrapName.md ├── examples ├── basic-esm │ ├── package.json │ └── src │ │ └── index.js └── basic-tsnode-esm │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── package.json ├── packages ├── ensjs │ ├── .env.example │ ├── .gitignore │ ├── CHANGELOG.md │ ├── contracts │ │ ├── NoMulticallResolver.json │ │ └── OldResolver.json │ ├── deploy │ │ ├── 00_deploy_bulk_renewal.ts │ │ ├── 00_deploy_multicall.ts │ │ ├── 00_legacy_registry.ts │ │ ├── 00_register_concurrently.ts │ │ ├── 00_register_legacy.ts │ │ ├── 00_register_wrapped.ts │ │ ├── 01_delete_names.ts │ │ ├── 01_set_legacy_resolver.ts │ │ ├── 01_set_oldest_resolver.ts │ │ ├── 01_set_primary.ts │ │ └── 02_get_contract_addresses.ts │ ├── ens-test-env.config.js │ ├── hardhat.config.cts │ ├── package.json │ ├── scripts │ │ ├── generateDocs.ts │ │ ├── prepack.ts │ │ ├── rcBranchVersion.ts │ │ └── updateVersion.ts │ ├── src │ │ ├── @types │ │ │ ├── dns-packet.d.ts │ │ │ ├── dns-packet │ │ │ │ ├── index.d.ts │ │ │ │ └── types.d.ts │ │ │ └── pako.d.ts │ │ ├── clients │ │ │ ├── decorators │ │ │ │ ├── public.ts │ │ │ │ ├── subgraph.ts │ │ │ │ └── wallet.ts │ │ │ ├── public.ts │ │ │ ├── subgraph.ts │ │ │ └── wallet.ts │ │ ├── contracts │ │ │ ├── addEnsContracts.ts │ │ │ ├── baseRegistrar.ts │ │ │ ├── bulkRenewal.ts │ │ │ ├── consts.ts │ │ │ ├── dnsRegistrar.ts │ │ │ ├── dnssecImpl.ts │ │ │ ├── erc1155.ts │ │ │ ├── erc165.ts │ │ │ ├── erc721.ts │ │ │ ├── ethRegistrarController.ts │ │ │ ├── getChainContractAddress.ts │ │ │ ├── index.ts │ │ │ ├── multicall.ts │ │ │ ├── nameWrapper.ts │ │ │ ├── publicResolver.ts │ │ │ ├── registry.ts │ │ │ ├── reverseRegistrar.ts │ │ │ └── universalResolver.ts │ │ ├── dns.ts │ │ ├── errors │ │ │ ├── base.ts │ │ │ ├── contracts.ts │ │ │ ├── dns.ts │ │ │ ├── error-utils.ts │ │ │ ├── general.ts │ │ │ ├── public.ts │ │ │ ├── subgraph.ts │ │ │ ├── utils.ts │ │ │ ├── version.ts │ │ │ └── wallet.ts │ │ ├── functions │ │ │ ├── dns │ │ │ │ ├── getDnsImportData.test.ts │ │ │ │ ├── getDnsImportData.ts │ │ │ │ ├── getDnsOffchainData.test.ts │ │ │ │ ├── getDnsOffchainData.ts │ │ │ │ ├── getDnsOwner.test.ts │ │ │ │ ├── getDnsOwner.ts │ │ │ │ ├── importDnsName.test.ts │ │ │ │ ├── importDnsName.ts │ │ │ │ └── types.ts │ │ │ ├── public │ │ │ │ ├── _getAbi.test.ts │ │ │ │ ├── _getAbi.ts │ │ │ │ ├── _getAddr.test.ts │ │ │ │ ├── _getAddr.ts │ │ │ │ ├── _getContentHash.test.ts │ │ │ │ ├── _getContentHash.ts │ │ │ │ ├── _getText.test.ts │ │ │ │ ├── _getText.ts │ │ │ │ ├── batch.test.ts │ │ │ │ ├── batch.ts │ │ │ │ ├── ccip.test.ts │ │ │ │ ├── getAbiRecord.test.ts │ │ │ │ ├── getAbiRecord.ts │ │ │ │ ├── getAddressRecord.test.ts │ │ │ │ ├── getAddressRecord.ts │ │ │ │ ├── getAvailable.test.ts │ │ │ │ ├── getAvailable.ts │ │ │ │ ├── getContentHashRecord.test.ts │ │ │ │ ├── getContentHashRecord.ts │ │ │ │ ├── getExpiry.test.ts │ │ │ │ ├── getExpiry.ts │ │ │ │ ├── getName.test.ts │ │ │ │ ├── getName.ts │ │ │ │ ├── getOwner.test.ts │ │ │ │ ├── getOwner.ts │ │ │ │ ├── getPrice.test.ts │ │ │ │ ├── getPrice.ts │ │ │ │ ├── getRecords.test.ts │ │ │ │ ├── getRecords.ts │ │ │ │ ├── getResolver.test.ts │ │ │ │ ├── getResolver.ts │ │ │ │ ├── getSupportedInterfaces.test.ts │ │ │ │ ├── getSupportedInterfaces.ts │ │ │ │ ├── getTextRecord.test.ts │ │ │ │ ├── getTextRecord.ts │ │ │ │ ├── getWrapperData.test.ts │ │ │ │ ├── getWrapperData.ts │ │ │ │ ├── getWrapperName.test.ts │ │ │ │ ├── getWrapperName.ts │ │ │ │ ├── multicallWrapper.ts │ │ │ │ ├── universalWrapper.test.ts │ │ │ │ └── universalWrapper.ts │ │ │ ├── subgraph │ │ │ │ ├── client.test.ts │ │ │ │ ├── client.ts │ │ │ │ ├── events.ts │ │ │ │ ├── filters.ts │ │ │ │ ├── fragments.ts │ │ │ │ ├── getDecodedName.test.ts │ │ │ │ ├── getDecodedName.ts │ │ │ │ ├── getNameHistory.test.ts │ │ │ │ ├── getNameHistory.ts │ │ │ │ ├── getNamesForAddress.test.ts │ │ │ │ ├── getNamesForAddress.ts │ │ │ │ ├── getSubgraphRecords.test.ts │ │ │ │ ├── getSubgraphRecords.ts │ │ │ │ ├── getSubgraphRegistrant.test.ts │ │ │ │ ├── getSubgraphRegistrant.ts │ │ │ │ ├── getSubnames.test.ts │ │ │ │ ├── getSubnames.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ └── wallet │ │ │ │ ├── clearRecords.test.ts │ │ │ │ ├── clearRecords.ts │ │ │ │ ├── commitName.test.ts │ │ │ │ ├── commitName.ts │ │ │ │ ├── createSubname.test.ts │ │ │ │ ├── createSubname.ts │ │ │ │ ├── deleteSubname.test.ts │ │ │ │ ├── deleteSubname.ts │ │ │ │ ├── registerName.test.ts │ │ │ │ ├── registerName.ts │ │ │ │ ├── renewNames.test.ts │ │ │ │ ├── renewNames.ts │ │ │ │ ├── setAbiRecord.test.ts │ │ │ │ ├── setAbiRecord.ts │ │ │ │ ├── setAddressRecord.test.ts │ │ │ │ ├── setAddressRecord.ts │ │ │ │ ├── setChildFuses.test.ts │ │ │ │ ├── setChildFuses.ts │ │ │ │ ├── setContentHashRecord.test.ts │ │ │ │ ├── setContentHashRecord.ts │ │ │ │ ├── setFuses.test.ts │ │ │ │ ├── setFuses.ts │ │ │ │ ├── setPrimaryName.test.ts │ │ │ │ ├── setPrimaryName.ts │ │ │ │ ├── setRecords.test.ts │ │ │ │ ├── setRecords.ts │ │ │ │ ├── setResolver.test.ts │ │ │ │ ├── setResolver.ts │ │ │ │ ├── setTextRecord.test.ts │ │ │ │ ├── setTextRecord.ts │ │ │ │ ├── transferName.test.ts │ │ │ │ ├── transferName.ts │ │ │ │ ├── unwrapName.test.ts │ │ │ │ ├── unwrapName.ts │ │ │ │ ├── wrapName.test.ts │ │ │ │ └── wrapName.ts │ │ ├── index.ts │ │ ├── public.ts │ │ ├── subgraph.ts │ │ ├── test │ │ │ ├── addTestContracts.ts │ │ │ ├── createHttpServer.ts │ │ │ ├── dns.ts │ │ │ └── setup.ts │ │ ├── types.ts │ │ ├── utils │ │ │ ├── ccipBatchRequest.test.ts │ │ │ ├── ccipBatchRequest.ts │ │ │ ├── ccipRequest.test.ts │ │ │ ├── ccipRequest.ts │ │ │ ├── checkSafeUniversalResolverData.test.ts │ │ │ ├── checkSafeUniversalResolverData.ts │ │ │ ├── consts.ts │ │ │ ├── contentHash.test.ts │ │ │ ├── contentHash.ts │ │ │ ├── dns │ │ │ │ ├── getDnsTxtRecords.ts │ │ │ │ └── misc.ts │ │ │ ├── encoders │ │ │ │ ├── encodeAbi.test.ts │ │ │ │ ├── encodeAbi.ts │ │ │ │ ├── encodeClearRecords.ts │ │ │ │ ├── encodeSetAbi.test.ts │ │ │ │ ├── encodeSetAbi.ts │ │ │ │ ├── encodeSetAddr.test.ts │ │ │ │ ├── encodeSetAddr.ts │ │ │ │ ├── encodeSetContentHash.test.ts │ │ │ │ ├── encodeSetContentHash.ts │ │ │ │ ├── encodeSetText.test.ts │ │ │ │ └── encodeSetText.ts │ │ │ ├── format.test.ts │ │ │ ├── format.ts │ │ │ ├── fuses.test.ts │ │ │ ├── fuses.ts │ │ │ ├── generateFunction.ts │ │ │ ├── generateRecordCallArray.test.ts │ │ │ ├── generateRecordCallArray.ts │ │ │ ├── generateSupportedContentTypes.test.ts │ │ │ ├── generateSupportedContentTypes.ts │ │ │ ├── getNameType.ts │ │ │ ├── getRevertErrorData.ts │ │ │ ├── hexEncodedName.ts │ │ │ ├── index.ts │ │ │ ├── labels.test.ts │ │ │ ├── labels.ts │ │ │ ├── makeLabelNodeAndParent.test.ts │ │ │ ├── makeLabelNodeAndParent.ts │ │ │ ├── makeSafeSecondsDate.test.ts │ │ │ ├── makeSafeSecondsDate.ts │ │ │ ├── normalise.test.ts │ │ │ ├── normalise.ts │ │ │ ├── normaliseCoinId.ts │ │ │ ├── ownerFromContract.test.ts │ │ │ ├── ownerFromContract.ts │ │ │ ├── registerHelpers.test.ts │ │ │ ├── registerHelpers.ts │ │ │ ├── validation.test.ts │ │ │ ├── validation.ts │ │ │ ├── wrapper.test.ts │ │ │ └── wrapper.ts │ │ └── wallet.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── utils │ │ ├── legacyNameGenerator.ts │ │ ├── nonceManager.ts │ │ └── wrappedNameGenerator.ts │ └── vitest.config.ts └── react │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── client.ts │ ├── hooks.ts │ ├── hooks │ │ ├── useDecodedName.ts │ │ ├── useEnsAvailable.ts │ │ ├── useEnsCredentials.ts │ │ ├── useEnsExpiry.ts │ │ ├── useEnsRecordsWrite.ts │ │ ├── useEnsResolverInterfaces.ts │ │ ├── useNamesForAddress.ts │ │ └── useQuery.ts │ ├── index.ts │ ├── interfaces.ts │ ├── query.ts │ └── utils │ │ └── types.ts │ └── tsconfig.build.json ├── patches └── hardhat-deploy.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── tsconfig.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.3/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "ensdomains/ensjs-v3" } 6 | ], 7 | "commit": false, 8 | "fixed": [], 9 | "linked": [], 10 | "access": "public", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch" 13 | } 14 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/universal:2", 3 | "features": {} 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | defaults: 9 | run: 10 | shell: bash 11 | 12 | env: 13 | FORCE_COLOR: true 14 | 15 | jobs: 16 | changelog: 17 | name: PR or Release 18 | if: ${{ github.repository_owner == 'ensdomains' }} 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: write 22 | id-token: write 23 | pull-requests: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: actions/checkout@v4 28 | - name: Enable corepack 29 | run: corepack enable pnpm 30 | 31 | - uses: actions/setup-node@v4 32 | with: 33 | cache: 'pnpm' 34 | node-version: 22 35 | registry-url: 'https://registry.npmjs.org' 36 | 37 | - name: Install dependencies 38 | run: pnpm install 39 | 40 | - name: Build Packages 41 | run: pnpm -r build 42 | 43 | - name: Fix npmrc 44 | run: npm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN" 45 | env: 46 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 47 | 48 | - name: Create Release Pull Request or Publish 49 | id: changesets 50 | uses: changesets/action@v1 51 | with: 52 | # Note: pnpm install after versioning is necessary to refresh lockfile 53 | version: pnpm chgset:version 54 | publish: pnpm release 55 | commit: "chore: release" 56 | title: "[ci] release" 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | # Needs access to publish to npm 60 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 61 | NPM_CONFIG_PROVENANCE: true 62 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node-version: [22] 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - uses: pnpm/action-setup@v4 16 | 17 | - name: Install Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: "pnpm" 22 | 23 | - run: pnpm install --frozen-lockfile 24 | 25 | - name: Build ensjs 26 | run: pnpm -F @ensdomains/ensjs build 27 | 28 | - name: Run tests 29 | run: pnpm -F @ensdomains/ensjs tenv start --extra-time 11368000 30 | lint: 31 | name: Lint 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - uses: pnpm/action-setup@v4 37 | 38 | - name: Install Node.js 39 | uses: actions/setup-node@v4 40 | with: 41 | node-version: 22 42 | cache: "pnpm" 43 | 44 | - run: pnpm install --frozen-lockfile 45 | 46 | - name: Lint 47 | run: pnpm lint 48 | 49 | - name: Type check 50 | run: pnpm -F @ensdomains/ensjs build 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .yarn/* 3 | node_modules/ 4 | 5 | .env 6 | 7 | dist/ 8 | archives/ 9 | data/ 10 | 11 | *.tsbuildinfo -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | link-workspace-packages=deep 3 | provenance=true 4 | auto-install-peers=false -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["arcanis.vscode-zipfs", "biomejs.biome"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "npm.packageManager": "pnpm", 3 | "editor.formatOnSave": true, 4 | "biome.enabled": true, 5 | "eslint.enable": false, 6 | "prettier.enable": false, 7 | "[typescript]": { 8 | "editor.defaultFormatter": "biomejs.biome" 9 | }, 10 | "editor.codeActionsOnSave": { 11 | "source.organizeImports.biome": "explicit", 12 | "quickfix.biome": "explicit" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ENS Labs Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![ENSjs](https://user-images.githubusercontent.com/11844316/161689061-98ea01ee-b119-40ac-a512-5370eb8b4107.svg) 2 | 3 | The ultimate ENS javascript library, with [viem](https://github.com/wagmi-dev/viem) under the hood. 4 | 5 | ## Features 6 | 7 | - Super fast response times 8 | - Easy call batchability 9 | - Written in TypeScript 10 | - Supports the most cutting edge ENS features 11 | - Full tree-shaking support 12 | 13 | ## Installation 14 | 15 | Install @ensdomains/ensjs, alongside [viem](https://github.com/wagmi-dev/viem). 16 | 17 | ```sh 18 | npm install @ensdomains/ensjs viem 19 | ``` 20 | 21 | ## Getting Started 22 | 23 | The most simple way to get started is to create a public ENS client, with a supported 24 | chain and transport imported from viem. The public client has all the read functions available on it, 25 | as well as all subgraph functions. 26 | 27 | ```ts 28 | // Import viem transport, viem chain, and ENSjs 29 | import { http } from 'viem' 30 | import { mainnet } from 'viem/chains' 31 | import { createEnsPublicClient } from '@ensdomains/ensjs' 32 | 33 | // Create the client 34 | const client = createEnsPublicClient({ 35 | chain: mainnet, 36 | transport: http(), 37 | }) 38 | 39 | // Use the client 40 | const ethAddress = client.getAddressRecord({ name: 'ens.eth' }) 41 | ``` 42 | 43 | ## Docs 44 | 45 | Docs can be found [here](https://github.com/ensdomains/ensjs-v3/tree/main/docs). Full docs site coming soon. 46 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "files": { 4 | "ignore": [ 5 | "node_modules", 6 | "package.json", 7 | "dist", 8 | "coverage", 9 | "data", 10 | "cache", 11 | "tsconfig.*.json", 12 | "packages/ensjs/contracts" 13 | ] 14 | }, 15 | "organizeImports": { 16 | "enabled": true 17 | }, 18 | "linter": { 19 | "enabled": true, 20 | "rules": { 21 | "recommended": true, 22 | "complexity": { 23 | "noForEach": "off", 24 | "noBannedTypes": "warn" 25 | }, 26 | "style": { 27 | "noNonNullAssertion": "off" 28 | }, 29 | "correctness": { 30 | "noUnusedImports": "error", 31 | "noUnusedVariables": "error", 32 | "noUnusedPrivateClassMembers": "error" 33 | }, 34 | "performance": { 35 | "noAccumulatingSpread": "off" 36 | }, 37 | "suspicious": { 38 | "noAssignInExpressions": "off", 39 | "noExplicitAny": "warn" 40 | } 41 | } 42 | }, 43 | "formatter": { 44 | "enabled": true 45 | }, 46 | "javascript": { 47 | "formatter": { 48 | "enabled": true, 49 | "semicolons": "asNeeded", 50 | "quoteStyle": "single", 51 | "indentWidth": 2, 52 | "indentStyle": "space" 53 | }, 54 | "linter": { 55 | "enabled": true 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # ENSjs Documentation 2 | 3 | ## Sections 4 | 5 | - [Basics](basics) 6 | - [Main Exports](index) 7 | - [Public Functions](public) 8 | - [Subgraph Functions](subgraph) 9 | - [Wallet Functions](wallet) 10 | - [DNS Functions](dns) 11 | -------------------------------------------------------------------------------- /docs/basics/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > basics 6 | 7 | # Basics 8 | 9 | ## Index 10 | 11 | - [Batching Calls](batching-calls.md) 12 | - [Custom Subgraph URIs](custom-subgraph-uris.md) 13 | - [Extending the Viem Client](extending-the-viem-client.md) 14 | - [Fetching a Profile](fetching-a-profile.md) 15 | - [Using the Viem Client](using-the-viem-client.md) 16 | -------------------------------------------------------------------------------- /docs/basics/batching-calls.md: -------------------------------------------------------------------------------- 1 | # Batching Calls 2 | 3 | Batching is built-in to viem for most situations, but ENSjs also has native batching if you want to be sure that calls are batched. 4 | Only public methods support call batching at this point. On the `EnsPublicClient`, batching can be accessed via `ensBatch` to avoid 5 | colliding with viem's native batching. If using batch outside of the client though, it can be accessed with `batch`. 6 | 7 | ## Using `EnsPublicClient` 8 | 9 | ```ts 10 | import { http } from 'viem' 11 | import { mainnet } from 'viem/chains' 12 | import { createEnsPublicClient } from '@ensdomains/ensjs' 13 | import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public' 14 | 15 | const client = createEnsPublicClient({ 16 | chain: mainnet, 17 | transport: http(), 18 | }) 19 | 20 | const [ethAddress, twitterUsername] = client.ensBatch( 21 | getAddressRecord.batch({ name: 'ens.eth' }), 22 | getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), 23 | ) 24 | /* 25 | [ 26 | { 27 | id: 60, 28 | name: 'ETH', 29 | value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' 30 | }, 31 | 'ensdomains' 32 | ] 33 | */ 34 | ``` 35 | 36 | ## Using Viem Client 37 | 38 | ```ts 39 | import { http, createClient } from 'viem' 40 | import { mainnet } from 'viem/chains' 41 | import { addEnsContracts } from '@ensdomains/ensjs' 42 | import { 43 | batch, 44 | getAddressRecord, 45 | getTextRecord, 46 | } from '@ensdomains/ensjs/public' 47 | 48 | const client = createClient({ 49 | chain: addEnsContracts(mainnet), 50 | transport: http(), 51 | }) 52 | 53 | const [ethAddress, twitterUsername] = batch( 54 | client, 55 | getAddressRecord.batch({ name: 'ens.eth' }), 56 | getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), 57 | ) 58 | /* 59 | [ 60 | { 61 | id: 60, 62 | name: 'ETH', 63 | value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' 64 | }, 65 | 'ensdomains' 66 | ] 67 | */ 68 | ``` 69 | -------------------------------------------------------------------------------- /docs/basics/custom-subgraph-uris.md: -------------------------------------------------------------------------------- 1 | # Custom Subgraph URIs 2 | 3 | If you want to use a custom subgraph endpoint for the chain you are using, such as a local hosted graph-node, you can easily do so by editing the output of `addEnsContracts()`. 4 | Keep in mind though that you can only use custom URIs if not using the default exported ENS clients. 5 | 6 | ```ts 7 | import { http, createClient } from "viem"; 8 | import { mainnet } from "viem/chains"; 9 | import { addEnsContracts } from "@ensdomains/ensjs"; 10 | import { getSubgraphRecords } from "@ensdomains/ensjs/subgraph"; 11 | 12 | const mainnetWithEns = addEnsContracts(mainnet); 13 | 14 | const chain = { 15 | ...mainnetWithEns, 16 | subgraphs: { 17 | ens: { 18 | url: "http://localhost:42069/subgraph", 19 | }, 20 | }, 21 | }; 22 | 23 | const client = createClient({ 24 | chain, 25 | transport: http(), 26 | }); 27 | 28 | const subgraphRecords = await getSubgraphRecords(client, { name: "ens.eth" }); 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/basics/extending-the-viem-client.md: -------------------------------------------------------------------------------- 1 | # Extending the Viem Client 2 | 3 | If you want to customise the set of methods that are added to the Viem client instead of using an ENS client, you can extend the Viem client with the provided actions functions. 4 | 5 | ## Available Actions Functions 6 | 7 | - `ensPublicActions` 8 | - `ensWalletActions` 9 | - `ensSubgraphActions` 10 | 11 | ## Example: Subgraph Only Client 12 | 13 | ```ts 14 | import { http, createClient } from 'viem' 15 | import { mainnet } from 'viem/chains' 16 | import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' 17 | 18 | const client = createClient({ 19 | chain: addEnsContracts(mainnet), 20 | transport: http(), 21 | }).extend(ensSubgraphActions) 22 | 23 | const subgraphRecords = await client.getSubgraphRecords({ name: 'ens.eth' }) 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/basics/fetching-a-profile.md: -------------------------------------------------------------------------------- 1 | # Fetching a Profile 2 | 3 | An ENS profile, meaning all associated records for an ENS name, can be easily fetched using subgraph data with `getSubgraphRecords()` and `getRecords()`. 4 | When using subgraph data, it's also recommended to provide fallback records for wildcard/CCIP names, and any other situations which aren't indexed by the subgraph. 5 | 6 | ```ts 7 | import { http } from 'viem' 8 | import { mainnet } from 'viem/chains' 9 | import { createEnsPublicClient } from '@ensdomains/ensjs' 10 | 11 | const client = createEnsPublicClient({ 12 | chain: mainnet, 13 | transport: http(), 14 | }) 15 | 16 | const subgraphRecords = client.getSubgraphRecords({ name: 'ens.eth' }) 17 | 18 | const records = client.getRecords({ 19 | name: 'ens.eth', 20 | records: { 21 | coins: [...(subgraphRecords?.coins || []), 'BTC', 'ETH', 'ETC', 'SOL'], 22 | texts: [ 23 | ...(subgraphRecords?.texts || []), 24 | 'avatar', 25 | 'email', 26 | 'description', 27 | ], 28 | contentHash: true, 29 | abi: true, 30 | }, 31 | }) 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/basics/using-the-viem-client.md: -------------------------------------------------------------------------------- 1 | # Using the Viem Client 2 | 3 | If you're only using parts of ENSjs, or want to control exactly what functions are being imported, you can use ENSjs methods with the viem `Client`. 4 | Just wrap the viem `Chain` in the `addEnsContracts()` function, which adds all the required addresses to the chain. 5 | 6 | ```ts 7 | import { http, createClient } from 'viem' 8 | import { mainnet } from 'viem/chains' 9 | import { addEnsContracts } from '@ensdomains/ensjs' 10 | import { getAddressRecord } from '@ensdomains/ensjs/public' 11 | 12 | const client = createClient({ 13 | chain: addEnsContracts(mainnet), 14 | transport: http(), 15 | }) 16 | 17 | const ethAddress = getAddressRecord(client, { name: 'ens.eth' }) 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/dns/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > dns 6 | 7 | # Module: dns 8 | 9 | ## Index 10 | 11 | - [getDnsImportData](function.getDnsImportData.md) 12 | - [getDnsOwner](function.getDnsOwner.md) 13 | - [importDnsName](function.importDnsName.md) 14 | 15 | --- 16 | 17 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 18 | -------------------------------------------------------------------------------- /docs/dns/function.getDnsImportData.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [dns](README.md) > getDnsImportData 6 | 7 | # Function: getDnsImportData() 8 | 9 | > **getDnsImportData**(`client`, `parameters`): `Promise`\< `GetDnsImportDataReturnType` \> 10 | 11 | Gets DNS import data, used for `importDnsName()` 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getDnsImportData } from '@ensdomains/ensjs/dns' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const data = await getDnsImportData(client, { 26 | name: 'example.eth', 27 | }) 28 | ``` 29 | 30 | ## Parameters 31 | 32 | | Parameter | Type | Description | 33 | | :----------- | :--------------------------- | :------------------------- | 34 | | `client` | `ClientWithEns` | ClientWithEns | 35 | | `parameters` | `GetDnsImportDataParameters` | GetDnsImportDataParameters | 36 | 37 | ## Returns 38 | 39 | `Promise`\< `GetDnsImportDataReturnType` \> 40 | 41 | DNS import data object, used for proving the value of the `_ens` TXT record 42 | 43 | ## Source 44 | 45 | [packages/ensjs/src/functions/dns/getDnsImportData.ts:65](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/dns/getDnsImportData.ts#L65) 46 | 47 | --- 48 | 49 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 50 | -------------------------------------------------------------------------------- /docs/dns/function.getDnsOwner.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [dns](README.md) > getDnsOwner 6 | 7 | # Function: getDnsOwner() 8 | 9 | > **getDnsOwner**(`parameters`): `Promise`\< \`0x$\{string}\` \> 10 | 11 | Gets the DNS owner of a name, via DNS record lookup 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { getDnsOwner } from '@ensdomains/ensjs/dns' 17 | 18 | const owner = await getDnsOwner({ name: 'ens.domains' }) 19 | // '0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5' 20 | ``` 21 | 22 | ## Parameters 23 | 24 | | Parameter | Type | Description | 25 | | :----------- | :---------------------- | :-------------------- | 26 | | `parameters` | `GetDnsOwnerParameters` | GetDnsOwnerParameters | 27 | 28 | ## Returns 29 | 30 | `Promise`\< \`0x$\{string}\` \> 31 | 32 | Address of DNS owner. GetDnsOwnerReturnType 33 | 34 | ## Source 35 | 36 | [packages/ensjs/src/functions/dns/getDnsOwner.ts:90](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/dns/getDnsOwner.ts#L90) 37 | 38 | --- 39 | 40 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 41 | -------------------------------------------------------------------------------- /docs/index/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > index 6 | 7 | # Module: index 8 | 9 | ## Index 10 | 11 | - [addEnsContracts](function.addEnsContracts.md) 12 | - [createEnsPublicClient](function.createEnsPublicClient.md) 13 | - [createEnsSubgraphClient](function.createEnsSubgraphClient.md) 14 | - [createEnsWalletClient](function.createEnsWalletClient.md) 15 | - [ensPublicActions](function.ensPublicActions.md) 16 | - [ensSubgraphActions](function.ensSubgraphActions.md) 17 | - [ensWalletActions](function.ensWalletActions.md) 18 | 19 | --- 20 | 21 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 22 | -------------------------------------------------------------------------------- /docs/index/function.addEnsContracts.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [index](README.md) > addEnsContracts 6 | 7 | # Function: addEnsContracts() 8 | 9 | > **addEnsContracts**\<`TChain`\>(`chain`): `ChainWithEns`\< `TChain` \> 10 | 11 | Adds ENS contract addresses to the viem chain 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | 20 | const clientWithEns = createPublicClient({ 21 | chain: addEnsContracts(mainnet), 22 | transport: http(), 23 | }) 24 | ``` 25 | 26 | ## Type parameters 27 | 28 | | Parameter | 29 | | :------------------------- | 30 | | `TChain` _extends_ `Chain` | 31 | 32 | ## Parameters 33 | 34 | | Parameter | Type | Description | 35 | | :-------- | :------- | :------------------------------------------------ | 36 | | `chain` | `TChain` | The viem Chain object to add the ENS contracts to | 37 | 38 | ## Returns 39 | 40 | `ChainWithEns`\< `TChain` \> 41 | 42 | ## Source 43 | 44 | [packages/ensjs/src/contracts/addEnsContracts.ts:25](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/contracts/addEnsContracts.ts#L25) 45 | 46 | --- 47 | 48 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 49 | -------------------------------------------------------------------------------- /docs/index/function.ensPublicActions.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [index](README.md) > ensPublicActions 6 | 7 | # Function: ensPublicActions() 8 | 9 | > **ensPublicActions**\<`TTransport`, `TChain`, `TAccount`\>(`client`): `EnsPublicActions` 10 | 11 | Extends the viem client with ENS public actions 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts, ensPublicActions } from '@ensdomains/ensjs' 19 | 20 | const clientWithEns = createPublicClient({ 21 | chain: addEnsContracts(mainnet), 22 | transport: http(), 23 | }).extend(ensPublicActions) 24 | ``` 25 | 26 | ## Type parameters 27 | 28 | | Parameter | Default | 29 | | :-------------------------------------------- | :----------------------- | 30 | | `TTransport` _extends_ `Transport` | `Transport` | 31 | | `TChain` _extends_ `ChainWithEns` | `ChainWithEns` | 32 | | `TAccount` _extends_ `undefined` \| `Account` | `undefined` \| `Account` | 33 | 34 | ## Parameters 35 | 36 | | Parameter | Type | Description | 37 | | :-------- | :----------------------------------------------- | :------------------------------------------------------ | 38 | | `client` | `Client`\< `TTransport`, `TChain`, `TAccount` \> | The viem Client object to add the ENS public actions to | 39 | 40 | ## Returns 41 | 42 | `EnsPublicActions` 43 | 44 | ## Source 45 | 46 | [packages/ensjs/src/clients/decorators/public.ts:372](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/clients/decorators/public.ts#L372) 47 | 48 | --- 49 | 50 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 51 | -------------------------------------------------------------------------------- /docs/index/function.ensSubgraphActions.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [index](README.md) > ensSubgraphActions 6 | 7 | # Function: ensSubgraphActions() 8 | 9 | > **ensSubgraphActions**\<`TTransport`, `TChain`, `TAccount`\>(`client`): `EnsSubgraphActions` 10 | 11 | Extends the viem client with ENS subgraph actions 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs' 19 | 20 | const clientWithEns = createPublicClient({ 21 | chain: addEnsContracts(mainnet), 22 | transport: http(), 23 | }).extend(ensSubgraphActions) 24 | ``` 25 | 26 | ## Type parameters 27 | 28 | | Parameter | Default | 29 | | :-------------------------------------------- | :----------------------- | 30 | | `TTransport` _extends_ `Transport` | `Transport` | 31 | | `TChain` _extends_ `ChainWithEns` | `ChainWithEns` | 32 | | `TAccount` _extends_ `undefined` \| `Account` | `undefined` \| `Account` | 33 | 34 | ## Parameters 35 | 36 | | Parameter | Type | Description | 37 | | :-------- | :----------------------------------------------- | :-------------------------------------------------------- | 38 | | `client` | `Client`\< `TTransport`, `TChain`, `TAccount` \> | The viem Client object to add the ENS subgraph actions to | 39 | 40 | ## Returns 41 | 42 | `EnsSubgraphActions` 43 | 44 | ## Source 45 | 46 | [packages/ensjs/src/clients/decorators/subgraph.ts:181](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/clients/decorators/subgraph.ts#L181) 47 | 48 | --- 49 | 50 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 51 | -------------------------------------------------------------------------------- /docs/index/function.ensWalletActions.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [index](README.md) > ensWalletActions 6 | 7 | # Function: ensWalletActions() 8 | 9 | > **ensWalletActions**\<`TTransport`, `TChain`, `TAccount`\>(`client`): `EnsWalletActions`\< `TChain`, `TAccount` \> 10 | 11 | Extends the viem client with ENS wallet actions 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts, ensWalletActions } from '@ensdomains/ensjs' 19 | 20 | const clientWithEns = createWalletClient({ 21 | chain: addEnsContracts(mainnet), 22 | transport: custom(window.ethereum), 23 | }).extend(ensWalletActions) 24 | ``` 25 | 26 | ## Type parameters 27 | 28 | | Parameter | Default | 29 | | :-------------------------------------------- | :----------------------- | 30 | | `TTransport` _extends_ `Transport` | `Transport` | 31 | | `TChain` _extends_ `ChainWithEns` | `ChainWithEns` | 32 | | `TAccount` _extends_ `undefined` \| `Account` | `undefined` \| `Account` | 33 | 34 | ## Parameters 35 | 36 | | Parameter | Type | Description | 37 | | :-------- | :------- | :------------------------------------------------------------ | 38 | | `client` | `object` | The viem WalletClient object to add the ENS wallet actions to | 39 | 40 | ## Returns 41 | 42 | `EnsWalletActions`\< `TChain`, `TAccount` \> 43 | 44 | ## Source 45 | 46 | [packages/ensjs/src/clients/decorators/wallet.ts:720](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/clients/decorators/wallet.ts#L720) 47 | 48 | --- 49 | 50 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 51 | -------------------------------------------------------------------------------- /docs/public/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > public 6 | 7 | # Module: public 8 | 9 | ## Index 10 | 11 | - [batch](function.batch.md) 12 | - [getAbiRecord](function.getAbiRecord.md) 13 | - [getAddressRecord](function.getAddressRecord.md) 14 | - [getAvailable](function.getAvailable.md) 15 | - [getContentHashRecord](function.getContentHashRecord.md) 16 | - [getExpiry](function.getExpiry.md) 17 | - [getName](function.getName.md) 18 | - [getOwner](function.getOwner.md) 19 | - [getPrice](function.getPrice.md) 20 | - [getRecords](function.getRecords.md) 21 | - [getResolver](function.getResolver.md) 22 | - [getSupportedInterfaces](function.getSupportedInterfaces.md) 23 | - [getTextRecord](function.getTextRecord.md) 24 | - [getWrapperData](function.getWrapperData.md) 25 | 26 | --- 27 | 28 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 29 | -------------------------------------------------------------------------------- /docs/public/function.getAbiRecord.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getAbiRecord 6 | 7 | # Function: getAbiRecord() 8 | 9 | > **getAbiRecord**(`client`, `parameters`): `Promise`\< `GetAbiRecordReturnType` \> 10 | 11 | Gets the ABI record for a name 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getAbiRecord } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getAbiRecord(client, { name: 'ens.eth' }) 26 | // TODO: real example 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :---------------- | :-------------- | :------------------------- | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `object` | GetAbiRecordParameters | 35 | | `parameters.name` | `string` | Name to get ABI record for | 36 | 37 | ## Returns 38 | 39 | `Promise`\< `GetAbiRecordReturnType` \> 40 | 41 | ABI record for the name, or `null` if not found. GetAbiRecordReturnType 42 | 43 | ## Source 44 | 45 | [packages/ensjs/src/functions/public/getAbiRecord.ts:60](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getAbiRecord.ts#L60) 46 | 47 | --- 48 | 49 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 50 | -------------------------------------------------------------------------------- /docs/public/function.getContentHashRecord.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getContentHashRecord 6 | 7 | # Function: getContentHashRecord() 8 | 9 | > **getContentHashRecord**(`client`, `parameters`): `Promise`\< `GetContentHashRecordReturnType` \> 10 | 11 | Gets the content hash record for a name 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getContentHashRecord } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getContentHashRecord(client, { name: 'ens.eth' }) 26 | // { protocolType: 'ipfs', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :---------------- | :-------------- | :---------------------------------- | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `object` | GetContentHashRecordParameters | 35 | | `parameters.name` | `string` | Name to get content hash record for | 36 | 37 | ## Returns 38 | 39 | `Promise`\< `GetContentHashRecordReturnType` \> 40 | 41 | Content hash object, or `null` if not found. GetContentHashRecordReturnType 42 | 43 | ## Source 44 | 45 | [packages/ensjs/src/functions/public/getContentHashRecord.ts:62](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getContentHashRecord.ts#L62) 46 | 47 | --- 48 | 49 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 50 | -------------------------------------------------------------------------------- /docs/public/function.getExpiry.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getExpiry 6 | 7 | # Function: getExpiry() 8 | 9 | > **getExpiry**(`client`, `parameters`): `Promise`\< `GetExpiryReturnType` \> 10 | 11 | Gets the expiry for a name 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getExpiry } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getExpiry(client, { name: 'ens.eth' }) 26 | // { expiry: { date: Date, value: 1913933217n }, gracePeriod: 7776000, status: 'active' } 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :--------------------- | :--------------- | :---------------------------------------------- | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `object` | GetExpiryParameters | 35 | | `parameters.contract`? | `ContractOption` | Optional specific contract to use to get expiry | 36 | | `parameters.name` | `string` | Name to get expiry for | 37 | 38 | ## Returns 39 | 40 | `Promise`\< `GetExpiryReturnType` \> 41 | 42 | Expiry object, or `null` if no expiry. GetExpiryReturnType 43 | 44 | ## Source 45 | 46 | [packages/ensjs/src/functions/public/getExpiry.ts:194](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getExpiry.ts#L194) 47 | 48 | --- 49 | 50 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 51 | -------------------------------------------------------------------------------- /docs/public/function.getOwner.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getOwner 6 | 7 | # Function: getOwner() 8 | 9 | > **getOwner**(`client`, `parameters`): `Promise`\< `GetOwnerReturnType` \> 10 | 11 | Gets the owner(s) of a name. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getOwner } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getOwner(client, { name: 'ens.eth' }) 26 | // { owner: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', registrant: '0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9', ownershipLevel: 'registrar } 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :----------- | :------------------- | :----------------- | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `GetOwnerParameters` | GetOwnerParameters | 35 | 36 | ## Returns 37 | 38 | `Promise`\< `GetOwnerReturnType` \> 39 | 40 | Owner data object, or `null` if no owners exist. GetOwnerReturnType 41 | 42 | ## Source 43 | 44 | [packages/ensjs/src/functions/public/getOwner.ts:245](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getOwner.ts#L245) 45 | 46 | --- 47 | 48 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 49 | -------------------------------------------------------------------------------- /docs/public/function.getRecords.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getRecords 6 | 7 | # Function: getRecords() 8 | 9 | > **getRecords**\<`TParams`\>(`client`, `parameters`): `Promise`\< `GetRecordsReturnType`\< `TParams` \> \> 10 | 11 | Gets arbitrary records for a name 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getRecords } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getRecords(client, { 26 | name: 'ens.eth', 27 | texts: ['com.twitter', 'com.github'], 28 | coins: ['ETH'], 29 | contentHash: true, 30 | }) 31 | // { texts: [{ key: 'com.twitter', value: 'ensdomains' }, { key: 'com.github', value: 'ensdomains' }], coins: [{ id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }], contentHash: { protocolType: 'ipns', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } } 32 | ``` 33 | 34 | ## Type parameters 35 | 36 | | Parameter | 37 | | :----------------------------------------- | 38 | | `TParams` _extends_ `GetRecordsParameters` | 39 | 40 | ## Parameters 41 | 42 | | Parameter | Type | Description | 43 | | :----------- | :-------------- | :------------------- | 44 | | `client` | `ClientWithEns` | ClientWithEns | 45 | | `parameters` | `TParams` | GetRecordsParameters | 46 | 47 | ## Returns 48 | 49 | `Promise`\< `GetRecordsReturnType`\< `TParams` \> \> 50 | 51 | Records data object. GetRecordsReturnType 52 | 53 | ## Source 54 | 55 | [packages/ensjs/src/functions/public/getRecords.ts:376](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getRecords.ts#L376) 56 | 57 | --- 58 | 59 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 60 | -------------------------------------------------------------------------------- /docs/public/function.getResolver.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getResolver 6 | 7 | # Function: getResolver() 8 | 9 | > **getResolver**(`client`, `parameters`): `Promise`\< `GetResolverReturnType` \> 10 | 11 | Gets the resolver address for a name. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getResolver } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getResolver(client, { name: 'ens.eth' }) 26 | // 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :----------- | :---------------------- | :-------------------- | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `GetResolverParameters` | GetResolverParameters | 35 | 36 | ## Returns 37 | 38 | `Promise`\< `GetResolverReturnType` \> 39 | 40 | Resolver address, or null if none is found. GetResolverReturnType 41 | 42 | ## Source 43 | 44 | [packages/ensjs/src/functions/public/getResolver.ts:95](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getResolver.ts#L95) 45 | 46 | --- 47 | 48 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 49 | -------------------------------------------------------------------------------- /docs/public/function.getSupportedInterfaces.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getSupportedInterfaces 6 | 7 | # Function: getSupportedInterfaces() 8 | 9 | > **getSupportedInterfaces**\<`TInterfaces`\>(`client`, `parameters`): `Promise`\< `GetSupportedInterfacesReturnType`\< `TInterfaces` \> \> 10 | 11 | Gets the supported interfaces for any contract address. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getSupportedInterfaces } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getSupportedInterfaces(client, { 26 | address: '0x58774Bb8acD458A640aF0B88238369A167546ef2', 27 | interfaces: ['0x2f435428', '0x23b872dd'], 28 | }) 29 | // [true, false] 30 | ``` 31 | 32 | ## Type parameters 33 | 34 | | Parameter | 35 | | :---------------------------------------------------- | 36 | | `TInterfaces` _extends_ _readonly_ \`0x$\{string}\`[] | 37 | 38 | ## Parameters 39 | 40 | | Parameter | Type | Description | 41 | | :----------- | :---------------------------------------------------- | :------------------------------- | 42 | | `client` | `ClientWithEns` | ClientWithEns | 43 | | `parameters` | `GetSupportedInterfacesParameters`\< `TInterfaces` \> | GetSupportedInterfacesParameters | 44 | 45 | ## Returns 46 | 47 | `Promise`\< `GetSupportedInterfacesReturnType`\< `TInterfaces` \> \> 48 | 49 | Array of booleans matching the input array GetSupportedInterfacesReturnType 50 | 51 | ## Source 52 | 53 | [packages/ensjs/src/functions/public/getSupportedInterfaces.ts:106](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getSupportedInterfaces.ts#L106) 54 | 55 | --- 56 | 57 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 58 | -------------------------------------------------------------------------------- /docs/public/function.getTextRecord.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getTextRecord 6 | 7 | # Function: getTextRecord() 8 | 9 | > **getTextRecord**(`client`, `parameters`): `Promise`\< `GetTextRecordReturnType` \> 10 | 11 | Gets a text record for a name. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getTextRecord } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getTextRecord(client, { 26 | name: 'ens.eth', 27 | key: 'com.twitter', 28 | }) 29 | // ensdomains 30 | ``` 31 | 32 | ## Parameters 33 | 34 | | Parameter | Type | Description | 35 | | :---------------- | :-------------- | :-------------------------- | 36 | | `client` | `ClientWithEns` | ClientWithEns | 37 | | `parameters` | `object` | GetTextRecordParameters | 38 | | `parameters.key` | `string` | Text record key to get | 39 | | `parameters.name` | `string` | Name to get text record for | 40 | 41 | ## Returns 42 | 43 | `Promise`\< `GetTextRecordReturnType` \> 44 | 45 | Text record string, or null if none is found. GetTextRecordReturnType 46 | 47 | ## Source 48 | 49 | [packages/ensjs/src/functions/public/getTextRecord.ts:60](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getTextRecord.ts#L60) 50 | 51 | --- 52 | 53 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 54 | -------------------------------------------------------------------------------- /docs/public/function.getWrapperData.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [public](README.md) > getWrapperData 6 | 7 | # Function: getWrapperData() 8 | 9 | > **getWrapperData**(`client`, `parameters`): `Promise`\< `GetWrapperDataReturnType` \> 10 | 11 | Gets the wrapper data for a name. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getWrapperData } from '@ensdomains/ensjs/public' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getWrapperData(client, { name: 'ilikelasagna.eth' }) 26 | ``` 27 | 28 | ## Parameters 29 | 30 | | Parameter | Type | Description | 31 | | :----------- | :------------------------- | :----------------------- | 32 | | `client` | `ClientWithEns` | ClientWithEns | 33 | | `parameters` | `GetWrapperDataParameters` | GetWrapperDataParameters | 34 | 35 | ## Returns 36 | 37 | `Promise`\< `GetWrapperDataReturnType` \> 38 | 39 | Wrapper data object, or null if name is not wrapped. GetWrapperDataReturnType 40 | 41 | ## Source 42 | 43 | [packages/ensjs/src/functions/public/getWrapperData.ts:123](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/public/getWrapperData.ts#L123) 44 | 45 | --- 46 | 47 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 48 | -------------------------------------------------------------------------------- /docs/subgraph/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > subgraph 6 | 7 | # Module: subgraph 8 | 9 | ## Index 10 | 11 | - [getDecodedName](function.getDecodedName.md) 12 | - [getNameHistory](function.getNameHistory.md) 13 | - [getNamesForAddress](function.getNamesForAddress.md) 14 | - [getSubgraphRecords](function.getSubgraphRecords.md) 15 | - [getSubgraphRegistrant](function.getSubgraphRegistrant.md) 16 | - [getSubnames](function.getSubnames.md) 17 | 18 | --- 19 | 20 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 21 | -------------------------------------------------------------------------------- /docs/subgraph/function.getDecodedName.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getDecodedName 6 | 7 | # Function: getDecodedName() 8 | 9 | > **getDecodedName**(`client`, `parameters`): `Promise`\< `GetDecodedNameReturnType` \> 10 | 11 | Gets the full name for a name with unknown labels from the subgraph. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getDecodedName } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getDecodedName(client, { 26 | name: '[5cee339e13375638553bdf5a6e36ba80fb9f6a4f0783680884d92b558aa471da].eth', 27 | }) 28 | // ens.eth 29 | ``` 30 | 31 | ## Parameters 32 | 33 | | Parameter | Type | Description | 34 | | :----------- | :------------------------- | :----------------------- | 35 | | `client` | `ClientWithEns` | ClientWithEns | 36 | | `parameters` | `GetDecodedNameParameters` | GetDecodedNameParameters | 37 | 38 | ## Returns 39 | 40 | `Promise`\< `GetDecodedNameReturnType` \> 41 | 42 | Full name, or null if name was could not be filled. GetDecodedNameReturnType 43 | 44 | ## Source 45 | 46 | [packages/ensjs/src/functions/subgraph/getDecodedName.ts:45](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getDecodedName.ts#L45) 47 | 48 | --- 49 | 50 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 51 | -------------------------------------------------------------------------------- /docs/subgraph/function.getNameHistory.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getNameHistory 6 | 7 | # Function: getNameHistory() 8 | 9 | > **getNameHistory**(`client`, `parameters`): `Promise`\< `GetNameHistoryReturnType` \> 10 | 11 | Gets the history of a name from the subgraph. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getNameHistory } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getNameHistory(client, { name: 'ens.eth' }) 26 | ``` 27 | 28 | ## Parameters 29 | 30 | | Parameter | Type | Description | 31 | | :----------- | :------------------------- | :----------------------- | 32 | | `client` | `ClientWithEns` | ClientWithEns | 33 | | `parameters` | `GetNameHistoryParameters` | GetNameHistoryParameters | 34 | 35 | ## Returns 36 | 37 | `Promise`\< `GetNameHistoryReturnType` \> 38 | 39 | History object, or null if name could not be found. GetNameHistoryReturnType 40 | 41 | ## Source 42 | 43 | [packages/ensjs/src/functions/subgraph/getNameHistory.ts:83](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getNameHistory.ts#L83) 44 | 45 | --- 46 | 47 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 48 | -------------------------------------------------------------------------------- /docs/subgraph/function.getNamesForAddress.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getNamesForAddress 6 | 7 | # Function: getNamesForAddress() 8 | 9 | > **getNamesForAddress**(`client`, `parameters`): `Promise`\< `GetNamesForAddressReturnType` \> 10 | 11 | Gets the names for an address from the subgraph. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getNamesForAddress } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getNamesForAddress(client, { 26 | address: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', 27 | }) 28 | ``` 29 | 30 | ## Parameters 31 | 32 | | Parameter | Type | Description | 33 | | :----------- | :----------------------------- | :--------------------------- | 34 | | `client` | `ClientWithEns` | ClientWithEns | 35 | | `parameters` | `GetNamesForAddressParameters` | GetNamesForAddressParameters | 36 | 37 | ## Returns 38 | 39 | `Promise`\< `GetNamesForAddressReturnType` \> 40 | 41 | Name array. GetNamesForAddressReturnType 42 | 43 | ## Source 44 | 45 | [packages/ensjs/src/functions/subgraph/getNamesForAddress.ts:161](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getNamesForAddress.ts#L161) 46 | 47 | --- 48 | 49 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 50 | -------------------------------------------------------------------------------- /docs/subgraph/function.getSubgraphRecords.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getSubgraphRecords 6 | 7 | # Function: getSubgraphRecords() 8 | 9 | > **getSubgraphRecords**(`client`, `parameters`): `Promise`\< `GetSubgraphRecordsReturnType` \> 10 | 11 | Gets the records for a name from the subgraph 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getSubgraphRecords } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getSubgraphRecords(client, { name: 'ens.eth' }) 26 | // { 27 | // isMigrated: true, 28 | // createdAt: { date: 2019-08-26T05:09:01.000Z, value: 1566796141000 }, 29 | // texts: [ 'snapshot', 'url', 'avatar', 'com.twitter', 'com.github' ], 30 | // coins: [ '60' ] 31 | // } 32 | ``` 33 | 34 | ## Parameters 35 | 36 | | Parameter | Type | Description | 37 | | :----------- | :----------------------------- | :--------------------------- | 38 | | `client` | `ClientWithEns` | ClientWithEns | 39 | | `parameters` | `GetSubgraphRecordsParameters` | GetSubgraphRecordsParameters | 40 | 41 | ## Returns 42 | 43 | `Promise`\< `GetSubgraphRecordsReturnType` \> 44 | 45 | Record object, or null if name was not found. GetSubgraphRecordsReturnType 46 | 47 | ## Source 48 | 49 | [packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts:154](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getSubgraphRecords.ts#L154) 50 | 51 | --- 52 | 53 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 54 | -------------------------------------------------------------------------------- /docs/subgraph/function.getSubgraphRegistrant.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getSubgraphRegistrant 6 | 7 | # Function: getSubgraphRegistrant() 8 | 9 | > **getSubgraphRegistrant**(`client`, `parameters`): `Promise`\< `GetSubgraphRegistrantReturnType` \> 10 | 11 | Gets the name registrant from the subgraph. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getSubgraphRegistrant } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getSubgraphRegistrant(client, { name: 'ens.eth' }) 26 | // 0xb6E040C9ECAaE172a89bD561c5F73e1C48d28cd9 27 | ``` 28 | 29 | ## Parameters 30 | 31 | | Parameter | Type | Description | 32 | | :----------- | :-------------------------------- | :------------------------------ | 33 | | `client` | `ClientWithEns` | ClientWithEns | 34 | | `parameters` | `GetSubgraphRegistrantParameters` | GetSubgraphRegistrantParameters | 35 | 36 | ## Returns 37 | 38 | `Promise`\< `GetSubgraphRegistrantReturnType` \> 39 | 40 | Registrant address, or null if name was not found. GetSubgraphRegistrantReturnType 41 | 42 | ## Source 43 | 44 | [packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts:52](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.ts#L52) 45 | 46 | --- 47 | 48 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 49 | -------------------------------------------------------------------------------- /docs/subgraph/function.getSubnames.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [subgraph](README.md) > getSubnames 6 | 7 | # Function: getSubnames() 8 | 9 | > **getSubnames**(`client`, `parameters`): `Promise`\< `GetSubnamesReturnType` \> 10 | 11 | Gets the subnames for a name from the subgraph. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createPublicClient, http } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { getSubnames } from '@ensdomains/ensjs/subgraph' 20 | 21 | const client = createPublicClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: http(), 24 | }) 25 | const result = await getSubnames(client, { name: 'ens.eth' }) 26 | ``` 27 | 28 | ## Parameters 29 | 30 | | Parameter | Type | Description | 31 | | :----------- | :---------------------- | :-------------------- | 32 | | `client` | `ClientWithEns` | ClientWithEns | 33 | | `parameters` | `GetSubnamesParameters` | GetSubnamesParameters | 34 | 35 | ## Returns 36 | 37 | `Promise`\< `GetSubnamesReturnType` \> 38 | 39 | Subname array. GetSubnamesReturnType 40 | 41 | ## Source 42 | 43 | [packages/ensjs/src/functions/subgraph/getSubnames.ts:129](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/subgraph/getSubnames.ts#L129) 44 | 45 | --- 46 | 47 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 48 | -------------------------------------------------------------------------------- /docs/wallet/README.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > wallet 6 | 7 | # Module: wallet 8 | 9 | ## Index 10 | 11 | - [clearRecords](function.clearRecords.md) 12 | - [commitName](function.commitName.md) 13 | - [createSubname](function.createSubname.md) 14 | - [deleteSubname](function.deleteSubname.md) 15 | - [registerName](function.registerName.md) 16 | - [renewNames](function.renewNames.md) 17 | - [setAbiRecord](function.setAbiRecord.md) 18 | - [setAddressRecord](function.setAddressRecord.md) 19 | - [setChildFuses](function.setChildFuses.md) 20 | - [setContentHashRecord](function.setContentHashRecord.md) 21 | - [setFuses](function.setFuses.md) 22 | - [setPrimaryName](function.setPrimaryName.md) 23 | - [setRecords](function.setRecords.md) 24 | - [setResolver](function.setResolver.md) 25 | - [setTextRecord](function.setTextRecord.md) 26 | - [transferName](function.transferName.md) 27 | - [unwrapName](function.unwrapName.md) 28 | - [wrapName](function.wrapName.md) 29 | 30 | --- 31 | 32 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 33 | -------------------------------------------------------------------------------- /docs/wallet/function.clearRecords.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [wallet](README.md) > clearRecords 6 | 7 | # Function: clearRecords() 8 | 9 | > **clearRecords**\<`TChain`, `TAccount`, `TChainOverride`\>(`wallet`, `parameters`): `Promise`\< `ClearRecordsReturnType` \> 10 | 11 | Clears the records for a name on a resolver. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { clearRecords } from '@ensdomains/ensjs/wallet' 20 | 21 | const wallet = createWalletClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: custom(window.ethereum), 24 | }) 25 | const hash = await clearRecords(wallet, { 26 | name: 'ens.eth', 27 | resolverAddress: '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', 28 | }) 29 | // 0x... 30 | ``` 31 | 32 | ## Type parameters 33 | 34 | | Parameter | Default | 35 | | :------------------------------------------------------- | :------------- | 36 | | `TChain` _extends_ `ChainWithEns` | - | 37 | | `TAccount` _extends_ `undefined` \| `Account` | - | 38 | | `TChainOverride` _extends_ `undefined` \| `ChainWithEns` | `ChainWithEns` | 39 | 40 | ## Parameters 41 | 42 | | Parameter | Type | Description | 43 | | :--------------------------- | :--------------- | :---------------------------- | 44 | | `wallet` | `object` | ClientWithAccount | 45 | | `parameters` | `object` | ClearRecordsParameters | 46 | | `parameters.name` | `string` | The name to clear records for | 47 | | `parameters.resolverAddress` | \`0x$\{string}\` | The resolver address to use | 48 | 49 | ## Returns 50 | 51 | `Promise`\< `ClearRecordsReturnType` \> 52 | 53 | Transaction hash. ClearRecordsReturnType 54 | 55 | ## Source 56 | 57 | [packages/ensjs/src/functions/wallet/clearRecords.ts:72](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/wallet/clearRecords.ts#L72) 58 | 59 | --- 60 | 61 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 62 | -------------------------------------------------------------------------------- /docs/wallet/function.createSubname.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [wallet](README.md) > createSubname 6 | 7 | # Function: createSubname() 8 | 9 | > **createSubname**\<`TChain`, `TAccount`, `TChainOverride`\>(`wallet`, `parameters`): `Promise`\< `CreateSubnameReturnType` \> 10 | 11 | Creates a subname 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { createSubname } from '@ensdomains/ensjs/wallet' 20 | 21 | const wallet = createWalletClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: custom(window.ethereum), 24 | }) 25 | const hash = await createSubname(wallet, { 26 | name: 'sub.ens.eth', 27 | owner: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', 28 | contract: 'registry', 29 | }) 30 | // 0x... 31 | ``` 32 | 33 | ## Type parameters 34 | 35 | | Parameter | Default | 36 | | :------------------------------------------------------- | :------------- | 37 | | `TChain` _extends_ `ChainWithEns` | - | 38 | | `TAccount` _extends_ `undefined` \| `Account` | - | 39 | | `TChainOverride` _extends_ `undefined` \| `ChainWithEns` | `ChainWithEns` | 40 | 41 | ## Parameters 42 | 43 | | Parameter | Type | Description | 44 | | :----------- | :-------------------------------------------------------------------- | :---------------------- | 45 | | `wallet` | `object` | ClientWithAccount | 46 | | `parameters` | `CreateSubnameParameters`\< `TChain`, `TAccount`, `TChainOverride` \> | CreateSubnameParameters | 47 | 48 | ## Returns 49 | 50 | `Promise`\< `CreateSubnameReturnType` \> 51 | 52 | Transaction hash. CreateSubnameReturnType 53 | 54 | ## Source 55 | 56 | [packages/ensjs/src/functions/wallet/createSubname.ts:175](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/wallet/createSubname.ts#L175) 57 | 58 | --- 59 | 60 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 61 | -------------------------------------------------------------------------------- /docs/wallet/function.setFuses.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [wallet](README.md) > setFuses 6 | 7 | # Function: setFuses() 8 | 9 | > **setFuses**\<`TChain`, `TAccount`, `TChainOverride`\>(`wallet`, `parameters`): `Promise`\< `SetFusesReturnType` \> 10 | 11 | Sets the fuses for a name. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { setFuses } from '@ensdomains/ensjs/wallet' 20 | 21 | const wallet = createWalletClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: custom(window.ethereum), 24 | }) 25 | const hash = await setFuses(wallet, { 26 | name: 'sub.ens.eth', 27 | fuses: { 28 | named: ['CANNOT_TRANSFER'], 29 | }, 30 | }) 31 | // 0x... 32 | ``` 33 | 34 | ## Type parameters 35 | 36 | | Parameter | Default | 37 | | :------------------------------------------------------- | :------------- | 38 | | `TChain` _extends_ `ChainWithEns` | - | 39 | | `TAccount` _extends_ `undefined` \| `Account` | - | 40 | | `TChainOverride` _extends_ `undefined` \| `ChainWithEns` | `ChainWithEns` | 41 | 42 | ## Parameters 43 | 44 | | Parameter | Type | Description | 45 | | :----------------- | :---------------------------- | :-------------------- | 46 | | `wallet` | `object` | ClientWithAccount | 47 | | `parameters` | `object` | SetFusesParameters | 48 | | `parameters.fuses` | `EncodeChildFusesInputObject` | Fuse object to set to | 49 | | `parameters.name` | `string` | Name to set fuses for | 50 | 51 | ## Returns 52 | 53 | `Promise`\< `SetFusesReturnType` \> 54 | 55 | Transaction hash. SetFusesReturnType 56 | 57 | ## Source 58 | 59 | [packages/ensjs/src/functions/wallet/setFuses.ts:84](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/wallet/setFuses.ts#L84) 60 | 61 | --- 62 | 63 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 64 | -------------------------------------------------------------------------------- /docs/wallet/function.setPrimaryName.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [wallet](README.md) > setPrimaryName 6 | 7 | # Function: setPrimaryName() 8 | 9 | > **setPrimaryName**\<`TChain`, `TAccount`, `TChainOverride`\>(`wallet`, `parameters`): `Promise`\< `SetPrimaryNameReturnType` \> 10 | 11 | Sets a primary name for an address. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { setPrimaryName } from '@ensdomains/ensjs/wallet' 20 | 21 | const wallet = createWalletClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: custom(window.ethereum), 24 | }) 25 | const hash = await setPrimaryName(wallet, { 26 | name: 'ens.eth', 27 | }) 28 | // 0x... 29 | ``` 30 | 31 | ## Type parameters 32 | 33 | | Parameter | Default | 34 | | :------------------------------------------------------- | :------------- | 35 | | `TChain` _extends_ `ChainWithEns` | - | 36 | | `TAccount` _extends_ `undefined` \| `Account` | - | 37 | | `TChainOverride` _extends_ `undefined` \| `ChainWithEns` | `ChainWithEns` | 38 | 39 | ## Parameters 40 | 41 | | Parameter | Type | Description | 42 | | :----------- | :--------------------------------------------------------------------- | :----------------------- | 43 | | `wallet` | `object` | ClientWithAccount | 44 | | `parameters` | `SetPrimaryNameParameters`\< `TChain`, `TAccount`, `TChainOverride` \> | SetPrimaryNameParameters | 45 | 46 | ## Returns 47 | 48 | `Promise`\< `SetPrimaryNameReturnType` \> 49 | 50 | Transaction hash. SetPrimaryNameReturnType 51 | 52 | ## Source 53 | 54 | [packages/ensjs/src/functions/wallet/setPrimaryName.ts:126](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/wallet/setPrimaryName.ts#L126) 55 | 56 | --- 57 | 58 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 59 | -------------------------------------------------------------------------------- /docs/wallet/function.transferName.md: -------------------------------------------------------------------------------- 1 | [**@ensdomains/ensjs**](../README.md) 2 | 3 | --- 4 | 5 | > [wallet](README.md) > transferName 6 | 7 | # Function: transferName() 8 | 9 | > **transferName**\<`TChain`, `TAccount`, `TChainOverride`\>(`wallet`, `parameters`): `Promise`\< `TransferNameReturnType` \> 10 | 11 | Transfers a name to a new owner. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import { createWalletClient, custom } from 'viem' 17 | import { mainnet } from 'viem/chains' 18 | import { addEnsContracts } from '@ensdomains/ensjs' 19 | import { transferName } from '@ensdomains/ensjs/wallet' 20 | 21 | const wallet = createWalletClient({ 22 | chain: addEnsContracts(mainnet), 23 | transport: custom(window.ethereum), 24 | }) 25 | const hash = await transferName(wallet, { 26 | name: 'ens.eth', 27 | newOwnerAddress: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7', 28 | contract: 'registry', 29 | }) 30 | // 0x... 31 | ``` 32 | 33 | ## Type parameters 34 | 35 | | Parameter | Default | 36 | | :------------------------------------------------------- | :------------- | 37 | | `TChain` _extends_ `ChainWithEns` | - | 38 | | `TAccount` _extends_ `undefined` \| `Account` | - | 39 | | `TChainOverride` _extends_ `undefined` \| `ChainWithEns` | `ChainWithEns` | 40 | 41 | ## Parameters 42 | 43 | | Parameter | Type | Description | 44 | | :----------- | :------------------------------------------------------------------- | :--------------------- | 45 | | `wallet` | `object` | ClientWithAccount | 46 | | `parameters` | `TransferNameParameters`\< `TChain`, `TAccount`, `TChainOverride` \> | TransferNameParameters | 47 | 48 | ## Returns 49 | 50 | `Promise`\< `TransferNameReturnType` \> 51 | 52 | Transaction hash. TransferNameReturnType 53 | 54 | ## Source 55 | 56 | [packages/ensjs/src/functions/wallet/transferName.ts:226](https://github.com/ensdomains/ensjs-v3/blob/1b90b888/packages/ensjs/src/functions/wallet/transferName.ts#L226) 57 | 58 | --- 59 | 60 | Generated using [TypeDoc](https://typedoc.org/) and [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) 61 | -------------------------------------------------------------------------------- /examples/basic-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-esm", 3 | "private": true, 4 | "type": "module", 5 | "version": "0.0.1", 6 | "scripts": { 7 | "start": "node src/index.js" 8 | }, 9 | "dependencies": { 10 | "@ensdomains/ensjs": "workspace:*", 11 | "viem": "^2.30.6" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/basic-esm/src/index.js: -------------------------------------------------------------------------------- 1 | import { createEnsPublicClient } from '@ensdomains/ensjs' 2 | import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public' 3 | import { http } from 'viem' 4 | import { mainnet } from 'viem/chains' 5 | 6 | const client = createEnsPublicClient({ 7 | chain: mainnet, 8 | transport: http('https://web3.euc.li/v1/mainnet'), 9 | }) 10 | 11 | const main = async () => { 12 | const records = await client.getSubgraphRecords({ name: 'ens.eth' }) 13 | const recordData = await client.getRecords({ 14 | name: 'ens.eth', 15 | coins: [...(records?.coins || []), 'BTC', 'ETH', 'ETC', 'SOL'], 16 | texts: [...(records?.texts || []), 'avatar', 'email', 'description'], 17 | contentHash: true, 18 | abi: true, 19 | }) 20 | console.log(recordData) 21 | 22 | const batchData = await client.ensBatch( 23 | getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), 24 | getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), 25 | ) 26 | console.log(batchData) 27 | } 28 | 29 | main() 30 | -------------------------------------------------------------------------------- /examples/basic-tsnode-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-tsnode-esm", 3 | "private": true, 4 | "type": "module", 5 | "version": "0.0.1", 6 | "scripts": { 7 | "start": "ts-node-esm src/index.ts" 8 | }, 9 | "dependencies": { 10 | "@ensdomains/ensjs": "workspace:*", 11 | "ts-node": "^10.9.2", 12 | "typescript": "^5.6.2", 13 | "viem": "^2.30.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/basic-tsnode-esm/src/index.ts: -------------------------------------------------------------------------------- 1 | import { createEnsPublicClient } from '@ensdomains/ensjs' 2 | import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public' 3 | import { http } from 'viem' 4 | import { mainnet } from 'viem/chains' 5 | 6 | const client = createEnsPublicClient({ 7 | chain: mainnet, 8 | transport: http('https://web3.euc.li/v1/mainnet'), 9 | }) 10 | 11 | const main = async () => { 12 | const records = await client.getSubgraphRecords({ name: 'ens.eth' }) 13 | const recordData = await client.getRecords({ 14 | name: 'ens.eth', 15 | abi: true, 16 | contentHash: true, 17 | ...records, 18 | }) 19 | console.log(recordData) 20 | 21 | const batchData = await client.ensBatch( 22 | getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }), 23 | getAddressRecord.batch({ name: 'ens.eth', coin: 'ETH' }), 24 | ) 25 | console.log(batchData) 26 | } 27 | 28 | main() 29 | -------------------------------------------------------------------------------- /examples/basic-tsnode-esm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "ts-node": { 4 | "compilerOptions": { 5 | "target": "es2020", 6 | "esModuleInterop": true 7 | } 8 | }, 9 | "include": ["src/**/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ensjs-monorepo", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "scripts": { 8 | "publish:local:ens-test-env": "yalc publish packages/ens-test-env --push --up", 9 | "publish:local:ensjs": "yalc publish packages/ensjs --push --up", 10 | "chgset:version": "changeset version && pnpm install", 11 | "chgset:run": "changeset", 12 | "release": "pnpm publish -r --access public && changeset tag", 13 | "chgset": "pnpm chgset:run && pnpm chgset:version", 14 | "lint": "biome check" 15 | }, 16 | "devDependencies": { 17 | "@biomejs/biome": "^1.9.4", 18 | "@changesets/changelog-github": "^0.5.0", 19 | "@changesets/cli": "^2.27.8", 20 | "ts-node": "^10.7.0", 21 | "typescript": "^5.8.3" 22 | }, 23 | "resolutions": { 24 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@0.3.0-beta.13" 25 | }, 26 | "dependenciesMeta": { 27 | "ens-contracts": { 28 | "built": false, 29 | "unplugged": true 30 | } 31 | }, 32 | "packageManager": "pnpm@10.10.0" 33 | } 34 | -------------------------------------------------------------------------------- /packages/ensjs/.env.example: -------------------------------------------------------------------------------- 1 | FORK_RPC_URL= -------------------------------------------------------------------------------- /packages/ensjs/.gitignore: -------------------------------------------------------------------------------- 1 | # hardhat 2 | /cache 3 | /artifacts 4 | /coverage 5 | README.md 6 | LICENSE 7 | 8 | .env.local -------------------------------------------------------------------------------- /packages/ensjs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ensdomains/ensjs 2 | 3 | ## 4.0.2 4 | 5 | ### Patch Changes 6 | 7 | - Fix pagination bug for names with identical createdAt and expiryDate in getNamesForAddress 8 | 9 | ## 4.0.1 10 | 11 | ### Patch Changes 12 | 13 | - [#199](https://github.com/ensdomains/ensjs/pull/199) [`1c2aa83`](https://github.com/ensdomains/ensjs/commit/1c2aa83681a1be98f920e6eac57391c138712df7) Thanks [@lucemans](https://github.com/lucemans)! - Introduce @ensdomains/ensjs-react 14 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/00_deploy_multicall.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, mkdirSync } from 'node:fs' 2 | import { readFile, writeFile } from 'node:fs/promises' 3 | import { resolve } from 'node:path' 4 | import type { ArtifactData, DeployFunction } from 'hardhat-deploy/dist/types.js' 5 | 6 | const func: DeployFunction = async (hre) => { 7 | const { getNamedAccounts } = hre 8 | const { deployer } = await getNamedAccounts() 9 | 10 | let contractJson: ArtifactData 11 | 12 | const jsonPath = resolve(import.meta.dirname, '../cache/multicall.json') 13 | 14 | if (!existsSync(resolve(jsonPath, '../'))) mkdirSync(resolve(jsonPath, '../')) 15 | 16 | if (existsSync(jsonPath)) { 17 | console.log('Multicall JSON file found, using it...') 18 | contractJson = JSON.parse(await readFile(jsonPath, { encoding: 'utf8' })) 19 | } else { 20 | console.log('Downloading Multicall JSON file...') 21 | contractJson = await fetch( 22 | 'https://github.com/mds1/multicall/releases/latest/download/Multicall3.json', 23 | ).then((res) => res.json()) 24 | await writeFile(jsonPath, JSON.stringify(contractJson)) 25 | console.log('Wrote Multicall JSON file to', jsonPath) 26 | } 27 | 28 | await hre.deployments.deploy('Multicall', { 29 | from: deployer, 30 | contract: contractJson, 31 | }) 32 | } 33 | 34 | func.id = 'multicall' 35 | 36 | export default func 37 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/00_legacy_registry.ts: -------------------------------------------------------------------------------- 1 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 2 | import { labelhash, namehash } from 'viem/ens' 3 | 4 | const ZERO_HASH = 5 | '0x0000000000000000000000000000000000000000000000000000000000000000' 6 | 7 | const names = ['legacy'] 8 | 9 | const func: DeployFunction = async (hre) => { 10 | const { viem } = hre 11 | const { owner } = await viem.getNamedClients() 12 | 13 | const registry = await viem.getContract('LegacyENSRegistry', owner) 14 | 15 | const tldTx = await registry.write.setSubnodeOwner( 16 | [ZERO_HASH, labelhash('test'), owner.address], 17 | owner, 18 | ) 19 | console.log(`Creating .test TLD (tx: ${tldTx.hash})...`) 20 | 21 | await viem.waitForTransactionSuccess(tldTx) 22 | 23 | await Promise.all( 24 | names.map(async (name) => { 25 | const nameTx = await registry.write.setSubnodeOwner( 26 | [namehash('test'), labelhash(name), owner.address], 27 | owner, 28 | ) 29 | console.log(`Creating ${name}.test (tx: ${nameTx.hash})...`) 30 | await viem.waitForTransactionSuccess(nameTx) 31 | }), 32 | ) 33 | 34 | return true 35 | } 36 | 37 | func.id = 'legacy-registry-names' 38 | func.tags = ['legacy-registry-names'] 39 | func.dependencies = ['ENSRegistry'] 40 | func.skip = async (hre) => { 41 | const { getNamedAccounts, viem } = hre 42 | const { owner } = await getNamedAccounts() 43 | 44 | const registry = await viem.getContract('LegacyENSRegistry') 45 | 46 | const ownerOfTestTld = await registry.read.owner([namehash('test')]) 47 | if (ownerOfTestTld !== owner) { 48 | return false 49 | } 50 | return true 51 | } 52 | func.runAtTheEnd = true 53 | 54 | export default func 55 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/01_delete_names.ts: -------------------------------------------------------------------------------- 1 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 2 | import { labelhash, namehash, packetToBytes } from 'viem/ens' 3 | import { toHex } from 'viem/utils' 4 | import { EMPTY_ADDRESS } from '../dist/utils/consts.js' 5 | 6 | const func: DeployFunction = async (hre) => { 7 | const { getNamedAccounts, viem } = hre 8 | const allNamedAccts = await getNamedAccounts() 9 | const clients = await viem.getNamedClients() 10 | 11 | const nameWrapper = await viem.getContract('NameWrapper', clients.owner) 12 | const registry = await viem.getContract('ENSRegistry', clients.owner) 13 | 14 | const deleteName = async (name: string) => { 15 | const labels = name.split('.') 16 | const label = labelhash(labels.shift()!) 17 | const node = namehash(labels.join('.')) 18 | 19 | const tx = await registry.write.setSubnodeRecord([ 20 | node, 21 | label, 22 | EMPTY_ADDRESS, 23 | EMPTY_ADDRESS, 24 | 0, 25 | ]) 26 | await viem.waitForTransactionSuccess(tx) 27 | } 28 | 29 | const name1 = 'wrapped-deleted.deletable.eth' 30 | const name2 = 'unwrapped-deleted.deletable.eth' 31 | 32 | // wrap wrapped-deleted.deletable.eth 33 | const approveTx = await registry.write.setApprovalForAll([ 34 | nameWrapper.address, 35 | true, 36 | ]) 37 | await viem.waitForTransactionSuccess(approveTx) 38 | const wrapTx = await nameWrapper.write.wrap([ 39 | toHex(packetToBytes(name1)), 40 | allNamedAccts.owner, 41 | EMPTY_ADDRESS, 42 | ]) 43 | await viem.waitForTransactionSuccess(wrapTx) 44 | 45 | await deleteName(name1) 46 | await deleteName(name2) 47 | 48 | for (const name of [name1, name2]) { 49 | const owner = await registry.read.owner([namehash(name)]) 50 | if (owner !== EMPTY_ADDRESS) { 51 | throw new Error(`Failed to delete name ${name}`) 52 | } 53 | } 54 | 55 | return true 56 | } 57 | 58 | func.id = 'delete-names' 59 | func.tags = ['delete-names'] 60 | func.dependencies = ['register-unwrapped-names'] 61 | func.runAtTheEnd = true 62 | 63 | export default func 64 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/01_set_legacy_resolver.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises' 2 | import { resolve } from 'node:path' 3 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 4 | import { namehash } from 'viem/ens' 5 | 6 | const names = [ 7 | { 8 | namedOwner: 'owner', 9 | name: 'with-legacy-resolver.eth', 10 | addr: [{ key: 60, value: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' }], 11 | }, 12 | ] 13 | 14 | const func: DeployFunction = async (hre) => { 15 | const { getNamedAccounts, deployments, viem } = hre 16 | const allNamedAccts = await getNamedAccounts() 17 | 18 | const registry = await viem.getContract('ENSRegistry') 19 | 20 | console.log('deploying NoMulticallResolver') 21 | await deployments.deploy('NoMulticallResolver', { 22 | from: allNamedAccts.deployer, 23 | contract: JSON.parse( 24 | await fs.readFile( 25 | resolve(import.meta.dirname, '../contracts/NoMulticallResolver.json'), 26 | { 27 | encoding: 'utf8', 28 | }, 29 | ), 30 | ), 31 | args: [registry.address], 32 | }) 33 | 34 | const resolver = await viem.getContract('NoMulticallResolver') 35 | for (const { namedOwner, name, addr } of names) { 36 | const owner = (await viem.getNamedClients())[namedOwner] 37 | 38 | const tx = await registry.write.setResolver( 39 | [namehash(name), resolver.address], 40 | { 41 | account: owner.address, 42 | }, 43 | ) 44 | console.log( 45 | `Setting resolver for ${name} to ${resolver.address} (tx: ${tx})...`, 46 | ) 47 | await viem.waitForTransactionSuccess(tx) 48 | 49 | for (const { key, value } of addr) { 50 | const tx2 = await resolver.write.setAddr([namehash(name), value], { 51 | account: owner.address, 52 | gasLimit: 100000, 53 | }) 54 | console.log(`Setting address for ${key} to ${value} (tx: ${tx})...`) 55 | await viem.waitForTransactionSuccess(tx2) 56 | } 57 | } 58 | 59 | return true 60 | } 61 | 62 | func.id = 'set-legacy-resolver' 63 | func.tags = ['set-legacy-resolver'] 64 | func.runAtTheEnd = true 65 | 66 | export default func 67 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/01_set_oldest_resolver.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises' 2 | import { resolve } from 'node:path' 3 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 4 | import { namehash } from 'viem' 5 | 6 | const names = [ 7 | { 8 | namedOwner: 'owner', 9 | name: 'with-oldest-resolver.eth', 10 | addr: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', 11 | }, 12 | ] 13 | 14 | const func: DeployFunction = async (hre) => { 15 | const { getNamedAccounts, deployments, viem } = hre 16 | const allNamedAccts = await getNamedAccounts() 17 | 18 | const registry = await viem.getContract('ENSRegistry') 19 | 20 | await deployments.deploy('OldestResolver', { 21 | from: allNamedAccts.deployer, 22 | contract: JSON.parse( 23 | await fs.readFile( 24 | resolve(import.meta.dirname, '../contracts/OldResolver.json'), 25 | { 26 | encoding: 'utf8', 27 | }, 28 | ), 29 | ), 30 | args: [registry.address], 31 | }) 32 | 33 | const resolver = await viem.getContract('OldestResolver') 34 | 35 | for (const { namedOwner, name, addr } of names) { 36 | const owner = allNamedAccts[namedOwner] 37 | 38 | const tx = await registry.write.setResolver( 39 | [namehash(name), resolver.address], 40 | { 41 | account: owner, 42 | }, 43 | ) 44 | console.log( 45 | `Setting resolver for ${name} to ${resolver.address} (tx: ${tx})...`, 46 | ) 47 | await viem.waitForTransactionSuccess(tx) 48 | 49 | const tx2 = await resolver.write.setAddr([namehash(name), addr], { 50 | account: owner, 51 | gasLimit: 100000, 52 | }) 53 | console.log(`Setting address for 60 to ${addr} (tx: ${tx})...`) 54 | await viem.waitForTransactionSuccess(tx2) 55 | } 56 | 57 | return true 58 | } 59 | 60 | func.id = 'set-oldest-resolver' 61 | func.tags = ['set-oldest-resolver'] 62 | func.runAtTheEnd = true 63 | 64 | export default func 65 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/01_set_primary.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | 3 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 4 | 5 | const names = [ 6 | { 7 | namedOwner: 'owner2', 8 | primaryName: 'with-profile.eth', 9 | }, 10 | ] 11 | 12 | const func: DeployFunction = async (hre) => { 13 | const { viem } = hre 14 | 15 | for (const { namedOwner, primaryName } of names) { 16 | const owner = (await viem.getNamedClients())[namedOwner] 17 | const reverseRegistrar = await viem.getContract('ReverseRegistrar', owner) 18 | const setPrimaryTx = await reverseRegistrar.write.setName([primaryName]) 19 | console.log( 20 | `Setting primary name for ${owner.address} to ${primaryName} (tx: ${setPrimaryTx})...`, 21 | ) 22 | await viem.waitForTransactionSuccess(setPrimaryTx) 23 | } 24 | 25 | return true 26 | } 27 | 28 | func.id = 'set-primary' 29 | func.tags = ['set-primary'] 30 | func.runAtTheEnd = true 31 | 32 | export default func 33 | -------------------------------------------------------------------------------- /packages/ensjs/deploy/02_get_contract_addresses.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from 'node:fs/promises' 2 | import { resolve } from 'node:path' 3 | import type { DeployFunction } from 'hardhat-deploy/dist/types.js' 4 | import { getAddress } from 'viem' 5 | 6 | const func: DeployFunction = async (hre) => { 7 | const allDeployments = await hre.deployments.all() 8 | const deploymentAddressMap = Object.fromEntries( 9 | Object.keys(allDeployments).map((dkey) => [ 10 | dkey, 11 | getAddress(allDeployments[dkey].address), 12 | ]), 13 | ) 14 | 15 | await writeFile( 16 | resolve(import.meta.dirname, '../.env.local'), 17 | `DEPLOYMENT_ADDRESSES='${JSON.stringify(deploymentAddressMap)}'`, 18 | ) 19 | console.log('Wrote contract addresses to .env.local') 20 | 21 | await (await hre.viem.getTestClient()).request({ method: 'evm_snapshot' }) 22 | } 23 | 24 | func.runAtTheEnd = true 25 | func.dependencies = ['set-legacy-resolver'] 26 | 27 | export default func 28 | -------------------------------------------------------------------------------- /packages/ensjs/ens-test-env.config.js: -------------------------------------------------------------------------------- 1 | import { config } from 'dotenv' 2 | 3 | config({ path: `${process.env.INIT_CWD}/.env.local` }) 4 | config({ 5 | path: `${process.env.INIT_CWD}/.env`, 6 | override: true, 7 | }) 8 | 9 | process.env.ADDRESS_ETH_REGISTRAR = '0xc5a5C42992dECbae36851359345FE25997F5C42d' 10 | process.env.ADDRESS_NAME_WRAPPER = '0x9E545E3C0baAB3E08CdfD552C960A1050f373042' 11 | process.env.BATCH_GATEWAY_URLS = JSON.stringify([ 12 | 'https://universal-offchain-unwrapper.ens-cf.workers.dev/', 13 | ]) 14 | 15 | /** 16 | * @type {import('@ensdomains/ens-test-env').ENSTestEnvConfig} 17 | * */ 18 | export default { 19 | deployCommand: 'pnpm hh deploy --no-compile', 20 | scripts: [ 21 | { 22 | command: 23 | process.env.STATIC_ENS === 'true' ? 'pnpm test:static' : 'pnpm test', 24 | name: 'vitest', 25 | prefixColor: 'yellow.bold', 26 | finishOnExit: true, 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /packages/ensjs/scripts/generateDocs.ts: -------------------------------------------------------------------------------- 1 | import TypeDoc from 'typedoc' 2 | import { load } from 'typedoc-plugin-markdown' 3 | 4 | const main = async () => { 5 | const app = new TypeDoc.Application() 6 | load(app) 7 | 8 | app.options.addReader(new TypeDoc.TSConfigReader()) 9 | app.options.addReader(new TypeDoc.TypeDocReader()) 10 | 11 | app.bootstrap({ 12 | entryPoints: [ 13 | 'src/index.ts', 14 | 'src/dns.ts', 15 | 'src/public.ts', 16 | 'src/subgraph.ts', 17 | 'src/wallet.ts', 18 | ], 19 | githubPages: false, 20 | cleanOutputDir: false, 21 | readme: 'none', 22 | excludeExternals: true, 23 | excludeNotDocumented: true, 24 | excludeNotDocumentedKinds: [ 25 | 'Module', 26 | 'Namespace', 27 | 'Enum', 28 | 'EnumMember', 29 | 'Variable', 30 | 'Function', 31 | 'Class', 32 | 'Interface', 33 | 'Constructor', 34 | 'Property', 35 | 'Method', 36 | 'CallSignature', 37 | 'IndexSignature', 38 | 'ConstructorSignature', 39 | 'Accessor', 40 | 'GetSignature', 41 | 'SetSignature', 42 | 'TypeAlias', 43 | 'Reference', 44 | ], 45 | useTsLinkResolution: true, 46 | }) 47 | 48 | const project = app.convert() 49 | 50 | if (!project) throw new Error('Project failed') 51 | 52 | const outputDir = '../../docs' 53 | 54 | await app.generateDocs(project, outputDir) 55 | } 56 | 57 | main().catch(console.error) 58 | -------------------------------------------------------------------------------- /packages/ensjs/scripts/rcBranchVersion.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process' 2 | import fs from 'node:fs' 3 | import path from 'node:path' 4 | 5 | // get branch name from local git command 6 | const branchName = execSync('git rev-parse --abbrev-ref HEAD') 7 | .toString('utf-8') 8 | .replace(/\//g, '-') 9 | .trim() 10 | 11 | // timestamp 12 | const timestamp = new Date().toISOString().replace(/[-:.]/g, '').slice(0, -1) 13 | 14 | const newVersion = `0.0.0-${branchName}.${timestamp}` 15 | 16 | // // change version in package.json 17 | execSync(`pnpm version --no-workspaces-update ${newVersion}`, { 18 | stdio: 'inherit', 19 | }) 20 | // // Writes the new version to `./src/errors/version.ts`. 21 | const versionFilePath = path.join( 22 | import.meta.dirname, 23 | '../src/errors/version.ts', 24 | ) 25 | 26 | fs.writeFileSync(versionFilePath, `export const version = '${newVersion}'\n`) 27 | -------------------------------------------------------------------------------- /packages/ensjs/scripts/updateVersion.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process' 2 | import fs from 'node:fs' 3 | import path from 'node:path' 4 | 5 | const newVersion = process.argv[2] 6 | 7 | if (!newVersion) throw new Error('No version specified') 8 | 9 | // change version in package.json 10 | execSync(`pnpm version --no-workspaces-update ${newVersion}`, { 11 | stdio: 'inherit', 12 | }) 13 | 14 | // Writes the new version to `./src/errors/version.ts`. 15 | const versionFilePath = path.join( 16 | import.meta.dirname, 17 | '../src/errors/version.ts', 18 | ) 19 | 20 | fs.writeFileSync(versionFilePath, `export const version = '${newVersion}'\n`) 21 | -------------------------------------------------------------------------------- /packages/ensjs/src/@types/dns-packet.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'dns-packet' 2 | -------------------------------------------------------------------------------- /packages/ensjs/src/@types/dns-packet/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'dns-packet/types.js' { 2 | function toType(type: string): number 3 | // biome-ignore lint/suspicious/noShadowRestrictedNames: type defs 4 | function toString(type: number): string 5 | } 6 | -------------------------------------------------------------------------------- /packages/ensjs/src/clients/subgraph.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Client, 3 | type ClientConfig, 4 | type PublicRpcSchema, 5 | type Transport, 6 | createClient, 7 | } from 'viem' 8 | import { addEnsContracts } from '../contracts/addEnsContracts.js' 9 | import type { 10 | ChainWithBaseContracts, 11 | ChainWithEns, 12 | } from '../contracts/consts.js' 13 | import type { Prettify } from '../types.js' 14 | import { 15 | type EnsSubgraphActions, 16 | ensSubgraphActions, 17 | } from './decorators/subgraph.js' 18 | 19 | export type EnsSubgraphClientConfig< 20 | TTransport extends Transport = Transport, 21 | TChain extends ChainWithBaseContracts = ChainWithBaseContracts, 22 | > = Pick< 23 | ClientConfig, 24 | 'batch' | 'key' | 'name' | 'pollingInterval' | 'transport' 25 | > & { 26 | chain: TChain 27 | } 28 | 29 | export type EnsSubgraphClient< 30 | TTransport extends Transport = Transport, 31 | TChain extends ChainWithEns = ChainWithEns, 32 | > = Prettify< 33 | Client 34 | > 35 | 36 | /** 37 | * 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). 38 | * 39 | * @param config - {@link EnsSubgraphClientConfig} 40 | * @returns An ENS Subgraph Client. {@link EnsSubgraphClient} 41 | * 42 | * @example 43 | * import { http } from 'viem' 44 | * import { mainnet } from 'viem/chains' 45 | * import { createEnsSubgraphClient } from '@ensdomains/ensjs' 46 | * 47 | * const client = createEnsSubgraphClient({ 48 | * chain: mainnet, 49 | * transport: http(), 50 | * }) 51 | */ 52 | export const createEnsSubgraphClient = < 53 | TTransport extends Transport, 54 | TChain extends ChainWithBaseContracts, 55 | >({ 56 | batch, 57 | chain, 58 | key = 'ensSubgraph', 59 | name = 'ENS Subgraph Client', 60 | transport, 61 | pollingInterval, 62 | }: EnsSubgraphClientConfig): EnsSubgraphClient< 63 | TTransport, 64 | ChainWithEns 65 | > => { 66 | return createClient({ 67 | batch, 68 | chain: addEnsContracts(chain), 69 | key, 70 | name, 71 | pollingInterval, 72 | transport, 73 | type: 'ensSubgraphClient', 74 | }).extend(ensSubgraphActions) 75 | } 76 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/addEnsContracts.ts: -------------------------------------------------------------------------------- 1 | import type { Chain } from 'viem' 2 | import { NoChainError, UnsupportedChainError } from '../errors/contracts.js' 3 | import { 4 | type CheckedChainWithEns, 5 | type SupportedChain, 6 | addresses, 7 | subgraphs, 8 | supportedChains, 9 | } from './consts.js' 10 | 11 | /** 12 | * Adds ENS contract addresses to the viem chain 13 | * @param chain - The viem {@link Chain} object to add the ENS contracts to 14 | * 15 | * @example 16 | * import { createPublicClient, http } from 'viem' 17 | * import { mainnet } from 'viem/chains' 18 | * import { addEnsContracts } from '@ensdomains/ensjs' 19 | * 20 | * const clientWithEns = createPublicClient({ 21 | * chain: addEnsContracts(mainnet), 22 | * transport: http(), 23 | * }) 24 | */ 25 | export const addEnsContracts = (chain: TChain) => { 26 | if (!chain) throw new NoChainError() 27 | if (!supportedChains.includes(chain.id as SupportedChain)) 28 | throw new UnsupportedChainError({ 29 | chainId: chain.id, 30 | supportedChains, 31 | }) 32 | return { 33 | ...chain, 34 | contracts: { 35 | ...chain.contracts, 36 | ...addresses[chain.id as SupportedChain], 37 | }, 38 | subgraphs: { 39 | ...subgraphs[chain.id as SupportedChain], 40 | }, 41 | } as unknown as CheckedChainWithEns 42 | } 43 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/baseRegistrar.ts: -------------------------------------------------------------------------------- 1 | import { 2 | erc721OwnerOfSnippet, 3 | erc721SafeTransferFromSnippet, 4 | erc721SafeTransferFromWithDataSnippet, 5 | } from './erc721.js' 6 | 7 | export const baseRegistrarAvailableSnippet = [ 8 | { 9 | inputs: [ 10 | { 11 | name: 'id', 12 | type: 'uint256', 13 | }, 14 | ], 15 | name: 'available', 16 | outputs: [ 17 | { 18 | name: 'available', 19 | type: 'bool', 20 | }, 21 | ], 22 | stateMutability: 'view', 23 | type: 'function', 24 | }, 25 | ] as const 26 | 27 | export const baseRegistrarNameExpiresSnippet = [ 28 | { 29 | inputs: [ 30 | { 31 | name: 'id', 32 | type: 'uint256', 33 | }, 34 | ], 35 | name: 'nameExpires', 36 | outputs: [ 37 | { 38 | name: '', 39 | type: 'uint256', 40 | }, 41 | ], 42 | stateMutability: 'view', 43 | type: 'function', 44 | }, 45 | ] as const 46 | 47 | export const baseRegistrarGracePeriodSnippet = [ 48 | { 49 | inputs: [], 50 | name: 'GRACE_PERIOD', 51 | outputs: [ 52 | { 53 | name: '', 54 | type: 'uint256', 55 | }, 56 | ], 57 | stateMutability: 'view', 58 | type: 'function', 59 | }, 60 | ] as const 61 | 62 | export const baseRegistrarReclaimSnippet = [ 63 | { 64 | inputs: [ 65 | { 66 | name: 'id', 67 | type: 'uint256', 68 | }, 69 | { 70 | name: 'owner', 71 | type: 'address', 72 | }, 73 | ], 74 | name: 'reclaim', 75 | outputs: [], 76 | stateMutability: 'nonpayable', 77 | type: 'function', 78 | }, 79 | ] as const 80 | 81 | export const baseRegistrarSafeTransferFromSnippet = [ 82 | ...erc721SafeTransferFromSnippet, 83 | ] as const 84 | 85 | export const baseRegistrarSafeTransferFromWithDataSnippet = [ 86 | ...erc721SafeTransferFromWithDataSnippet, 87 | ] as const 88 | 89 | export const baseRegistrarOwnerOfSnippet = [...erc721OwnerOfSnippet] as const 90 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/bulkRenewal.ts: -------------------------------------------------------------------------------- 1 | export const bulkRenewalRentPriceSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'names', 6 | type: 'string[]', 7 | }, 8 | { 9 | name: 'duration', 10 | type: 'uint256', 11 | }, 12 | ], 13 | name: 'rentPrice', 14 | outputs: [ 15 | { 16 | name: 'total', 17 | type: 'uint256', 18 | }, 19 | ], 20 | stateMutability: 'view', 21 | type: 'function', 22 | }, 23 | ] as const 24 | 25 | export const bulkRenewalRenewAllSnippet = [ 26 | { 27 | inputs: [ 28 | { 29 | name: 'names', 30 | type: 'string[]', 31 | }, 32 | { 33 | name: 'duration', 34 | type: 'uint256', 35 | }, 36 | ], 37 | name: 'renewAll', 38 | outputs: [], 39 | stateMutability: 'payable', 40 | type: 'function', 41 | }, 42 | ] as const 43 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/dnssecImpl.ts: -------------------------------------------------------------------------------- 1 | export const dnssecImplVerifyRrSetSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | components: [ 6 | { 7 | name: 'rrset', 8 | type: 'bytes', 9 | }, 10 | { 11 | name: 'sig', 12 | type: 'bytes', 13 | }, 14 | ], 15 | name: 'input', 16 | type: 'tuple[]', 17 | }, 18 | ], 19 | name: 'verifyRRSet', 20 | outputs: [ 21 | { 22 | name: 'rrs', 23 | type: 'bytes', 24 | }, 25 | { 26 | name: 'inception', 27 | type: 'uint32', 28 | }, 29 | ], 30 | stateMutability: 'view', 31 | type: 'function', 32 | }, 33 | ] as const 34 | 35 | export const dnssecImplAnchorsSnippet = [ 36 | { 37 | inputs: [], 38 | name: 'anchors', 39 | outputs: [ 40 | { 41 | name: '', 42 | type: 'bytes', 43 | }, 44 | ], 45 | stateMutability: 'view', 46 | type: 'function', 47 | }, 48 | ] as const 49 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/erc1155.ts: -------------------------------------------------------------------------------- 1 | export const erc1155SafeTransferFromSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'from', 6 | type: 'address', 7 | }, 8 | { 9 | name: 'to', 10 | type: 'address', 11 | }, 12 | { 13 | name: 'id', 14 | type: 'uint256', 15 | }, 16 | { 17 | name: 'amount', 18 | type: 'uint256', 19 | }, 20 | { 21 | name: 'data', 22 | type: 'bytes', 23 | }, 24 | ], 25 | name: 'safeTransferFrom', 26 | outputs: [], 27 | stateMutability: 'nonpayable', 28 | type: 'function', 29 | }, 30 | ] as const 31 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/erc165.ts: -------------------------------------------------------------------------------- 1 | export const erc165SupportsInterfaceSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'interfaceID', 6 | type: 'bytes4', 7 | }, 8 | ], 9 | name: 'supportsInterface', 10 | outputs: [ 11 | { 12 | name: '', 13 | type: 'bool', 14 | }, 15 | ], 16 | stateMutability: 'pure', 17 | type: 'function', 18 | }, 19 | ] as const 20 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/erc721.ts: -------------------------------------------------------------------------------- 1 | export const erc721OwnerOfSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'id', 6 | type: 'uint256', 7 | }, 8 | ], 9 | name: 'ownerOf', 10 | outputs: [ 11 | { 12 | name: 'owner', 13 | type: 'address', 14 | }, 15 | ], 16 | stateMutability: 'view', 17 | type: 'function', 18 | }, 19 | ] as const 20 | 21 | export const erc721SafeTransferFromSnippet = [ 22 | { 23 | inputs: [ 24 | { 25 | name: 'from', 26 | type: 'address', 27 | }, 28 | { 29 | name: 'to', 30 | type: 'address', 31 | }, 32 | { 33 | name: 'tokenId', 34 | type: 'uint256', 35 | }, 36 | ], 37 | name: 'safeTransferFrom', 38 | outputs: [], 39 | stateMutability: 'nonpayable', 40 | type: 'function', 41 | }, 42 | ] as const 43 | 44 | export const erc721SafeTransferFromWithDataSnippet = [ 45 | { 46 | inputs: [ 47 | { 48 | name: 'from', 49 | type: 'address', 50 | }, 51 | { 52 | name: 'to', 53 | type: 'address', 54 | }, 55 | { 56 | name: 'tokenId', 57 | type: 'uint256', 58 | }, 59 | { 60 | name: '_data', 61 | type: 'bytes', 62 | }, 63 | ], 64 | name: 'safeTransferFrom', 65 | outputs: [], 66 | stateMutability: 'nonpayable', 67 | type: 'function', 68 | }, 69 | ] as const 70 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/getChainContractAddress.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Chain } from 'viem' 2 | import { getChainContractAddress as _getChainContractAddress } from 'viem/utils' 3 | 4 | type ExtractContract = TClient extends { 5 | chain: { contracts: infer C } 6 | } 7 | ? C extends Record 8 | ? C 9 | : never 10 | : never 11 | 12 | export const getChainContractAddress = < 13 | const TClient extends { chain: Chain }, 14 | TContracts extends ExtractContract = ExtractContract, 15 | TContractName extends keyof TContracts = keyof TContracts, 16 | TContract extends TContracts[TContractName] = TContracts[TContractName], 17 | >({ 18 | blockNumber, 19 | client, 20 | contract, 21 | }: { 22 | blockNumber?: bigint 23 | client: TClient 24 | contract: TContractName 25 | }) => 26 | _getChainContractAddress({ 27 | blockNumber, 28 | chain: client.chain, 29 | contract: contract as string, 30 | }) as TContract extends { address: infer A } 31 | ? A extends Address 32 | ? A 33 | : never 34 | : Address 35 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/multicall.ts: -------------------------------------------------------------------------------- 1 | export const multicallTryAggregateSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'requireSuccess', 6 | type: 'bool', 7 | }, 8 | { 9 | components: [ 10 | { 11 | name: 'target', 12 | type: 'address', 13 | }, 14 | { 15 | name: 'callData', 16 | type: 'bytes', 17 | }, 18 | ], 19 | name: 'calls', 20 | type: 'tuple[]', 21 | }, 22 | ], 23 | name: 'tryAggregate', 24 | outputs: [ 25 | { 26 | components: [ 27 | { 28 | name: 'success', 29 | type: 'bool', 30 | }, 31 | { 32 | name: 'returnData', 33 | type: 'bytes', 34 | }, 35 | ], 36 | name: 'returnData', 37 | type: 'tuple[]', 38 | }, 39 | ], 40 | stateMutability: 'payable', 41 | type: 'function', 42 | }, 43 | ] as const 44 | 45 | export const multicallGetCurrentBlockTimestampSnippet = [ 46 | { 47 | inputs: [], 48 | name: 'getCurrentBlockTimestamp', 49 | outputs: [ 50 | { 51 | name: 'timestamp', 52 | type: 'uint256', 53 | }, 54 | ], 55 | stateMutability: 'view', 56 | type: 'function', 57 | }, 58 | ] as const 59 | -------------------------------------------------------------------------------- /packages/ensjs/src/contracts/reverseRegistrar.ts: -------------------------------------------------------------------------------- 1 | export const reverseRegistrarSetNameForAddrSnippet = [ 2 | { 3 | inputs: [ 4 | { 5 | name: 'addr', 6 | type: 'address', 7 | }, 8 | { 9 | name: 'owner', 10 | type: 'address', 11 | }, 12 | { 13 | name: 'resolver', 14 | type: 'address', 15 | }, 16 | { 17 | name: 'name', 18 | type: 'string', 19 | }, 20 | ], 21 | name: 'setNameForAddr', 22 | outputs: [ 23 | { 24 | name: '', 25 | type: 'bytes32', 26 | }, 27 | ], 28 | stateMutability: 'nonpayable', 29 | type: 'function', 30 | }, 31 | ] as const 32 | 33 | export const reverseRegistrarSetNameSnippet = [ 34 | { 35 | inputs: [ 36 | { 37 | name: 'name', 38 | type: 'string', 39 | }, 40 | ], 41 | name: 'setName', 42 | outputs: [ 43 | { 44 | name: '', 45 | type: 'bytes32', 46 | }, 47 | ], 48 | stateMutability: 'nonpayable', 49 | type: 'function', 50 | }, 51 | ] as const 52 | -------------------------------------------------------------------------------- /packages/ensjs/src/dns.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default as getDnsImportData, 3 | type GetDnsImportDataParameters, 4 | type GetDnsImportDataReturnType, 5 | } from './functions/dns/getDnsImportData.js' 6 | export { 7 | default as getDnsOffchainData, 8 | type GetDnsOffchainDataParameters, 9 | type GetDnsOffchainDataReturnType, 10 | } from './functions/dns/getDnsOffchainData.js' 11 | export { 12 | default as getDnsOwner, 13 | type GetDnsOwnerParameters, 14 | type GetDnsOwnerReturnType, 15 | } from './functions/dns/getDnsOwner.js' 16 | export { 17 | default as importDnsName, 18 | type ImportDnsNameDataParameters, 19 | type ImportDnsNameDataReturnType, 20 | type ImportDnsNameParameters, 21 | type ImportDnsNameReturnType, 22 | } from './functions/dns/importDnsName.js' 23 | export type { Endpoint } from './functions/dns/types.js' 24 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/base.ts: -------------------------------------------------------------------------------- 1 | import { getVersion } from './error-utils.js' 2 | 3 | type BaseErrorParameters = { 4 | metaMessages?: string[] 5 | } & ( 6 | | { 7 | cause?: never 8 | details?: string 9 | } 10 | | { 11 | cause: BaseError | Error 12 | details?: never 13 | } 14 | ) 15 | 16 | export class BaseError extends Error { 17 | details: string 18 | 19 | metaMessages?: string[] 20 | 21 | shortMessage: string 22 | 23 | override name = 'EnsJsError' 24 | 25 | version = getVersion() 26 | 27 | cause?: BaseError | Error 28 | 29 | constructor(shortMesage: string, args: BaseErrorParameters = {}) { 30 | super() 31 | 32 | const details = 33 | args.cause instanceof BaseError 34 | ? args.cause.details 35 | : (args.cause?.message ?? args.details!) 36 | 37 | this.message = [ 38 | shortMesage || 'An error occurred', 39 | '', 40 | ...(args.metaMessages ? [...args.metaMessages, ''] : []), 41 | ...(details ? [`Details: ${details}`, ''] : []), 42 | `Version: ${this.version}`, 43 | ].join('\n') 44 | 45 | if (args.cause) this.cause = args.cause 46 | this.details = details 47 | this.metaMessages = args.metaMessages 48 | this.shortMessage = shortMesage 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/contracts.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from './base.js' 2 | 3 | export class UnsupportedChainError extends BaseError { 4 | chainId: number 5 | 6 | supportedChains: readonly number[] 7 | 8 | override name = 'UnsupportedChainError' 9 | 10 | constructor({ 11 | chainId, 12 | supportedChains, 13 | details, 14 | }: { 15 | chainId: number 16 | supportedChains: readonly number[] 17 | details?: string 18 | }) { 19 | super(`Unsupported chain: ${chainId}`, { 20 | metaMessages: [`- Supported chains: ${supportedChains.join(', ')}`], 21 | details, 22 | }) 23 | this.chainId = chainId 24 | this.supportedChains = supportedChains 25 | } 26 | } 27 | 28 | export class NoChainError extends BaseError { 29 | override name = 'NoChainError' 30 | 31 | constructor() { 32 | super('No chain provided') 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/error-utils.ts: -------------------------------------------------------------------------------- 1 | import { version } from './version.js' 2 | 3 | export const getVersion = () => `@ensdomains/ensjs@${version}` 4 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/public.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from './base.js' 2 | 3 | export class CoinFormatterNotFoundError extends BaseError { 4 | coinType: string | number 5 | 6 | override name = 'CoinFormatterNotFoundError' 7 | 8 | constructor({ coinType }: { coinType: string | number }) { 9 | super(`Coin formatter not found for ${coinType}`) 10 | this.coinType = coinType 11 | } 12 | } 13 | 14 | export class FunctionNotBatchableError extends BaseError { 15 | functionIndex: number 16 | 17 | override name = 'FunctionNotBatchableError' 18 | 19 | constructor({ functionIndex }: { functionIndex: number }) { 20 | super(`Function at index ${functionIndex} is not batchable`) 21 | this.functionIndex = functionIndex 22 | } 23 | } 24 | 25 | export class NoRecordsSpecifiedError extends BaseError { 26 | override name = 'NoRecordsSpecifiedError' 27 | 28 | constructor() { 29 | super('No records specified') 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/subgraph.ts: -------------------------------------------------------------------------------- 1 | import { BaseError } from './base.js' 2 | 3 | export class InvalidFilterKeyError extends BaseError { 4 | filterKey: string 5 | 6 | supportedFilterKeys: readonly string[] 7 | 8 | override name = 'InvalidFilterKeyError' 9 | 10 | constructor({ 11 | filterKey, 12 | supportedFilterKeys, 13 | }: { 14 | filterKey: string 15 | supportedFilterKeys: readonly string[] 16 | }) { 17 | super(`Invalid filter key: ${filterKey}`, { 18 | metaMessages: [ 19 | `- Supported filter keys: ${supportedFilterKeys.join(', ')}`, 20 | ], 21 | }) 22 | this.filterKey = filterKey 23 | this.supportedFilterKeys = supportedFilterKeys 24 | } 25 | } 26 | 27 | export class FilterKeyRequiredError extends BaseError { 28 | supportedFilterKeys: readonly string[] 29 | 30 | override name = 'FilterKeyRequiredError' 31 | 32 | constructor({ 33 | supportedFilterKeys, 34 | details, 35 | }: { 36 | supportedFilterKeys: readonly string[] 37 | details?: string 38 | }) { 39 | super('At least one filter key is required', { 40 | metaMessages: [ 41 | `- Supported filter keys: ${supportedFilterKeys.join(', ')}`, 42 | ], 43 | details, 44 | }) 45 | this.supportedFilterKeys = supportedFilterKeys 46 | } 47 | } 48 | 49 | export class InvalidOrderByError extends BaseError { 50 | orderBy: string 51 | 52 | supportedOrderBys: string[] 53 | 54 | override name = 'InvalidOrderByError' 55 | 56 | constructor({ 57 | orderBy, 58 | supportedOrderBys, 59 | }: { 60 | orderBy: string 61 | supportedOrderBys: string[] 62 | }) { 63 | super(`Invalid orderBy: ${orderBy}`, { 64 | metaMessages: [ 65 | `- Supported orderBy keys: ${supportedOrderBys.join(', ')}`, 66 | ], 67 | }) 68 | this.orderBy = orderBy 69 | this.supportedOrderBys = supportedOrderBys 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/version.ts: -------------------------------------------------------------------------------- 1 | export const version = 'v4.0.2-alpha.5' 2 | -------------------------------------------------------------------------------- /packages/ensjs/src/errors/wallet.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ensdomains/ensjs/cde41751a71617c30b85a2314d093ba63b0d635e/packages/ensjs/src/errors/wallet.ts -------------------------------------------------------------------------------- /packages/ensjs/src/functions/dns/getDnsImportData.test.ts: -------------------------------------------------------------------------------- 1 | import { SignedSet } from '@ensdomains/dnsprovejs' 2 | import { toBytes } from 'viem' 3 | import { expect, it, vi } from 'vitest' 4 | import { publicClient } from '../../test/addTestContracts.js' 5 | import getDnsImportData, { type RrSetWithSig } from './getDnsImportData.js' 6 | 7 | vi.setConfig({ 8 | testTimeout: 10000, 9 | }) 10 | 11 | const decodeProofs = (proofs: RrSetWithSig[]) => 12 | proofs.map((proof) => 13 | SignedSet.fromWire( 14 | Buffer.from(toBytes(proof.rrset)), 15 | Buffer.from(toBytes(proof.sig)), 16 | ), 17 | ) 18 | 19 | it('returns all rrsets', async () => { 20 | const result = await getDnsImportData(publicClient, { 21 | name: 'taytems.xyz', 22 | }) 23 | expect(result.length).toBeGreaterThan(0) 24 | const decodedProofs = decodeProofs(result) 25 | const rootProofs = decodedProofs.filter((x) => x.signature.name === '.') 26 | const tldProofs = decodedProofs.filter((x) => x.signature.name === 'xyz') 27 | const twoLDProofs = decodedProofs.filter( 28 | (x) => x.signature.name === 'taytems.xyz', 29 | ) 30 | const threeLDProofs = decodedProofs.filter( 31 | (x) => x.signature.name === '_ens.taytems.xyz', 32 | ) 33 | expect(rootProofs.length).toBeGreaterThan(0) 34 | expect(tldProofs.length).toBeGreaterThan(0) 35 | expect(twoLDProofs.length).toBeGreaterThan(0) 36 | expect(threeLDProofs.length).toBeGreaterThan(0) 37 | }) 38 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/dns/types.ts: -------------------------------------------------------------------------------- 1 | export type Endpoint = `https://${string}` | `http://${string}` 2 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getAbi.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import _getAbi from './_getAbi.js' 4 | 5 | it('does not propagate error when strict is false', async () => { 6 | const result = await _getAbi.decode({} as ClientWithEns, '0x1234', { 7 | strict: false, 8 | }) 9 | expect(result).toBeNull() 10 | }) 11 | 12 | it('propagates error when strict is true', async () => { 13 | await expect( 14 | _getAbi.decode({} as ClientWithEns, '0x1234', { strict: true }), 15 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 16 | [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. 17 | 18 | Params: (uint256, bytes) 19 | Data: 0x1234 (2 bytes) 20 | 21 | Version: viem@2.30.6] 22 | `) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getAddr.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import _getAddr from './_getAddr.js' 4 | 5 | it('does not propagate error when strict is false', async () => { 6 | const result = await _getAddr.decode({} as ClientWithEns, '0x1234', { 7 | strict: false, 8 | }) 9 | expect(result).toBeNull() 10 | }) 11 | 12 | it('propagates error when strict is true', async () => { 13 | await expect( 14 | _getAddr.decode({} as ClientWithEns, '0x1234', { strict: true }), 15 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 16 | [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. 17 | 18 | Params: (address) 19 | Data: 0x1234 (2 bytes) 20 | 21 | Version: viem@2.30.6] 22 | `) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getContentHash.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import _getContentHash from './_getContentHash.js' 4 | 5 | it('does not propagate error when strict is false', async () => { 6 | const result = await _getContentHash.decode({} as ClientWithEns, '0x1234', { 7 | strict: false, 8 | }) 9 | expect(result).toBeNull() 10 | }) 11 | 12 | it('propagates error when strict is true', async () => { 13 | await expect( 14 | _getContentHash.decode({} as ClientWithEns, '0x1234', { strict: true }), 15 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 16 | [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. 17 | 18 | Params: (bytes) 19 | Data: 0x1234 (2 bytes) 20 | 21 | Version: viem@2.30.6] 22 | `) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getContentHash.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, decodeFunctionResult, encodeFunctionData } from 'viem' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import { publicResolverContenthashSnippet } from '../../contracts/publicResolver.js' 4 | import type { Prettify, SimpleTransactionRequest } from '../../types.js' 5 | import { EMPTY_ADDRESS } from '../../utils/consts.js' 6 | import { 7 | type DecodedContentHash, 8 | decodeContentHash, 9 | } from '../../utils/contentHash.js' 10 | import { generateFunction } from '../../utils/generateFunction.js' 11 | import { namehash } from '../../utils/normalise.js' 12 | 13 | export type InternalGetContentHashParameters = { 14 | /** Name to get content hash record for */ 15 | name: string 16 | /** Whether or not to throw decoding errors */ 17 | strict?: boolean 18 | } 19 | 20 | export type InternalGetContentHashReturnType = 21 | Prettify 22 | 23 | const encode = ( 24 | _client: ClientWithEns, 25 | { name }: Omit, 26 | ): SimpleTransactionRequest => { 27 | return { 28 | to: EMPTY_ADDRESS, 29 | data: encodeFunctionData({ 30 | abi: publicResolverContenthashSnippet, 31 | functionName: 'contenthash', 32 | args: [namehash(name)], 33 | }), 34 | } 35 | } 36 | 37 | const decode = async ( 38 | _client: ClientWithEns, 39 | data: Hex, 40 | { strict }: Pick, 41 | ): Promise => { 42 | if (data === '0x') return null 43 | 44 | try { 45 | const response = decodeFunctionResult({ 46 | abi: publicResolverContenthashSnippet, 47 | functionName: 'contenthash', 48 | data, 49 | }) 50 | 51 | return decodeContentHash(response) 52 | } catch (error) { 53 | if (strict) throw error 54 | return null 55 | } 56 | } 57 | 58 | const _getContentHash = generateFunction({ encode, decode }) 59 | 60 | export default _getContentHash 61 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getText.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import _getText from './_getText.js' 4 | 5 | it('does not propagate error when strict is false', async () => { 6 | const result = await _getText.decode({} as ClientWithEns, '0x1234', { 7 | strict: false, 8 | }) 9 | expect(result).toBeNull() 10 | }) 11 | 12 | it('propagates error when strict is true', async () => { 13 | await expect( 14 | _getText.decode({} as ClientWithEns, '0x1234', { strict: true }), 15 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 16 | [AbiDecodingDataSizeTooSmallError: Data size of 2 bytes is too small for given parameters. 17 | 18 | Params: (string) 19 | Data: 0x1234 (2 bytes) 20 | 21 | Version: viem@2.30.6] 22 | `) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/_getText.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, decodeFunctionResult, encodeFunctionData } from 'viem' 2 | import type { ClientWithEns } from '../../contracts/consts.js' 3 | import { publicResolverTextSnippet } from '../../contracts/publicResolver.js' 4 | import type { SimpleTransactionRequest } from '../../types.js' 5 | import { EMPTY_ADDRESS } from '../../utils/consts.js' 6 | import { generateFunction } from '../../utils/generateFunction.js' 7 | import { namehash } from '../../utils/normalise.js' 8 | 9 | export type InternalGetTextParameters = { 10 | /** Name to get text record for */ 11 | name: string 12 | /** Text record key to get */ 13 | key: string 14 | /** Whether or not to throw decoding errors */ 15 | strict?: boolean 16 | } 17 | 18 | export type InternalGetTextReturnType = string | null 19 | 20 | const encode = ( 21 | _client: ClientWithEns, 22 | { name, key }: Omit, 23 | ): SimpleTransactionRequest => { 24 | return { 25 | to: EMPTY_ADDRESS, 26 | data: encodeFunctionData({ 27 | abi: publicResolverTextSnippet, 28 | functionName: 'text', 29 | args: [namehash(name), key], 30 | }), 31 | } 32 | } 33 | 34 | const decode = async ( 35 | _client: ClientWithEns, 36 | data: Hex, 37 | { strict }: Pick, 38 | ): Promise => { 39 | if (data === '0x') return null 40 | 41 | try { 42 | const response = decodeFunctionResult({ 43 | abi: publicResolverTextSnippet, 44 | functionName: 'text', 45 | data, 46 | }) 47 | 48 | if (!response) return null 49 | 50 | return response 51 | } catch (error) { 52 | if (strict) throw error 53 | return null 54 | } 55 | } 56 | 57 | const _getText = generateFunction({ encode, decode }) 58 | 59 | export default _getText 60 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/batch.test.ts: -------------------------------------------------------------------------------- 1 | import { http, createPublicClient } from 'viem' 2 | import { mainnet } from 'viem/chains' 3 | import { describe, expect, it } from 'vitest' 4 | import { addEnsContracts } from '../../contracts/addEnsContracts.js' 5 | import { 6 | deploymentAddresses, 7 | publicClient, 8 | } from '../../test/addTestContracts.js' 9 | import batch from './batch.js' 10 | import getAddressRecord from './getAddressRecord.js' 11 | import getName from './getName.js' 12 | import getText from './getTextRecord.js' 13 | 14 | const mainnetPublicClient = createPublicClient({ 15 | chain: addEnsContracts(mainnet), 16 | transport: http('https://mainnet.gateway.tenderly.co/4imxc4hQfRjxrVB2kWKvTo'), 17 | }) 18 | 19 | describe('batch', () => { 20 | it('should batch calls together', async () => { 21 | const result = await batch( 22 | publicClient, 23 | getText.batch({ name: 'with-profile.eth', key: 'description' }), 24 | getAddressRecord.batch({ name: 'with-profile.eth' }), 25 | getName.batch({ address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' }), 26 | ) 27 | expect(result).toMatchInlineSnapshot(` 28 | [ 29 | "Hello2", 30 | { 31 | "id": 60, 32 | "name": "eth", 33 | "value": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", 34 | }, 35 | { 36 | "match": true, 37 | "name": "with-profile.eth", 38 | "resolverAddress": "${deploymentAddresses.LegacyPublicResolver}", 39 | "reverseResolverAddress": "${deploymentAddresses.PublicResolver}", 40 | }, 41 | ] 42 | `) 43 | }) 44 | it('should batch a single call', async () => { 45 | const result = await batch( 46 | publicClient, 47 | getText.batch({ name: 'with-profile.eth', key: 'description' }), 48 | ) 49 | expect(result).toMatchInlineSnapshot(` 50 | [ 51 | "Hello2", 52 | ] 53 | `) 54 | }) 55 | it('should batch ccip', async () => { 56 | const result = await batch( 57 | mainnetPublicClient, 58 | getText.batch({ name: '1.offchainexample.eth', key: 'email' }), 59 | getText.batch({ name: '2.offchainexample.eth', key: 'email' }), 60 | ) 61 | expect(result).toMatchInlineSnapshot(` 62 | [ 63 | "nick@ens.domains", 64 | "nick@ens.domains", 65 | ] 66 | `) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getAvailable.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getAvailable from './getAvailable.js' 4 | 5 | describe('getAvailable', () => { 6 | it('should return false for a name that is unavailable', async () => { 7 | const result = await getAvailable(publicClient, { name: 'test123.eth' }) 8 | expect(typeof result).toBe('boolean') 9 | expect(result).toBe(false) 10 | }) 11 | it('should return true for a name that is available', async () => { 12 | const result = await getAvailable(publicClient, { 13 | name: 'available-name.eth', 14 | }) 15 | expect(typeof result).toBe('boolean') 16 | expect(result).toBe(true) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getExpiry.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getExpiry from './getExpiry.js' 4 | 5 | describe('getExpiry', () => { 6 | it('should get the expiry for a .eth name with no other args', async () => { 7 | const result = await getExpiry(publicClient, { name: 'with-profile.eth' }) 8 | expect(result).toBeTruthy() 9 | if (result) { 10 | const { expiry, gracePeriod, status } = result 11 | expect(expiry.date).toBeInstanceOf(Date) 12 | expect(typeof expiry.value).toBe('bigint') 13 | expect(gracePeriod).toBe(7776000) 14 | expect(status).toBe('active') 15 | } 16 | }) 17 | it('should get the expiry for a wrapped name', async () => { 18 | const result = await getExpiry(publicClient, { 19 | name: 'wrapped.eth', 20 | contract: 'nameWrapper', 21 | }) 22 | 23 | expect(result).toBeTruthy() 24 | if (result) { 25 | const { expiry, gracePeriod, status } = result 26 | expect(expiry.date).toBeInstanceOf(Date) 27 | expect(typeof expiry.value).toBe('bigint') 28 | expect(gracePeriod).toBe(0) 29 | expect(status).toBe('active') 30 | } 31 | }) 32 | it('should return null for a non .eth name if not wrapped', async () => { 33 | const result = await getExpiry(publicClient, { 34 | name: 'sub.with-profile.eth', 35 | }) 36 | expect(result).toBeNull() 37 | }) 38 | it('should throw an error for a non .eth name if registrar is specified', async () => { 39 | try { 40 | await getExpiry(publicClient, { 41 | name: 'sub.with-profile.eth', 42 | contract: 'registrar', 43 | }) 44 | expect(false).toBeTruthy() 45 | } catch { 46 | expect(true).toBeTruthy() 47 | } 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getPrice.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getPrice from './getPrice.js' 4 | 5 | const yearCost = BigInt('8561643835626') 6 | 7 | describe('getPrice', () => { 8 | it('should return a base and premium price for a name', async () => { 9 | const result = await getPrice(publicClient, { 10 | nameOrNames: 'test123.eth', 11 | duration: 86400, 12 | }) 13 | expect(result).toBeTruthy() 14 | if (result) { 15 | const { base, premium } = result 16 | expect(base).toBe(yearCost) 17 | expect(premium).toBe(0n) 18 | } 19 | }) 20 | 21 | it('should return a base and premium price for an array of names', async () => { 22 | const result = await getPrice(publicClient, { 23 | nameOrNames: ['test123.eth', 'to-be-renewed.eth'], 24 | duration: 86400, 25 | }) 26 | expect(result).toBeTruthy() 27 | if (result) { 28 | const { base, premium } = result 29 | expect(base).toBe(yearCost * 2n) 30 | expect(premium).toBe(0n) 31 | } 32 | }) 33 | 34 | it('should allow labels as inputs', async () => { 35 | const result = await getPrice(publicClient, { 36 | nameOrNames: 'test123', 37 | duration: 86400, 38 | }) 39 | expect(result).toBeTruthy() 40 | if (result) { 41 | const { base, premium } = result 42 | expect(base).toBe(yearCost) 43 | expect(premium).toBe(0n) 44 | } 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getResolver.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { 3 | deploymentAddresses, 4 | publicClient, 5 | } from '../../test/addTestContracts.js' 6 | import getResolver from './getResolver.js' 7 | 8 | describe('getResolver', () => { 9 | it('should find the resolver for a name with a resolver', async () => { 10 | const result = await getResolver(publicClient, { name: 'with-profile.eth' }) 11 | expect(result).toBe(deploymentAddresses.LegacyPublicResolver) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getSupportedInterfaces.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { 3 | deploymentAddresses, 4 | publicClient, 5 | } from '../../test/addTestContracts.js' 6 | import getSupportedInterfaces from './getSupportedInterfaces.js' 7 | 8 | it('returns true for a single supported interface', async () => { 9 | const result = await getSupportedInterfaces(publicClient, { 10 | address: deploymentAddresses.DNSRegistrar, 11 | interfaces: ['0x2f435428'], 12 | }) 13 | 14 | expect(result).toEqual([true]) 15 | }) 16 | 17 | it('returns false for a single unsupported interface', async () => { 18 | const result = await getSupportedInterfaces(publicClient, { 19 | address: deploymentAddresses.DNSRegistrar, 20 | interfaces: ['0x23b872dd'], 21 | }) 22 | 23 | expect(result).toEqual([false]) 24 | }) 25 | 26 | it('returns correct values for multiple supported interfaces', async () => { 27 | const result = await getSupportedInterfaces(publicClient, { 28 | address: deploymentAddresses.DNSRegistrar, 29 | interfaces: ['0x2f435428', '0x01ffc9a7'], 30 | }) 31 | 32 | expect(result).toEqual([true, true]) 33 | }) 34 | 35 | it('returns correct values for multiple unsupported interfaces', async () => { 36 | const result = await getSupportedInterfaces(publicClient, { 37 | address: deploymentAddresses.DNSRegistrar, 38 | interfaces: ['0x23b872dd', '0x28ed4f6c'], 39 | }) 40 | 41 | expect(result).toEqual([false, false]) 42 | }) 43 | 44 | it('returns correct values for mixed supported and unsupported interfaces', async () => { 45 | const result = await getSupportedInterfaces(publicClient, { 46 | address: deploymentAddresses.DNSRegistrar, 47 | interfaces: ['0x2f435428', '0x01ffc9a7', '0x23b872dd', '0x28ed4f6c'], 48 | }) 49 | 50 | expect(result).toEqual([true, true, false, false]) 51 | }) 52 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getTextRecord.test.ts: -------------------------------------------------------------------------------- 1 | import { RawContractError } from 'viem' 2 | import { describe, expect, it } from 'vitest' 3 | import type { ClientWithEns } from '../../contracts/consts.js' 4 | import { publicClient } from '../../test/addTestContracts.js' 5 | import getTextRecord from './getTextRecord.js' 6 | 7 | describe('getTextRecord()', () => { 8 | it('should return a record from a key', async () => { 9 | const result = await getTextRecord(publicClient, { 10 | name: 'with-profile.eth', 11 | key: 'description', 12 | }) 13 | expect(result).toBe('Hello2') 14 | }) 15 | 16 | it('should return null for a non-existent key', async () => { 17 | const result = await getTextRecord(publicClient, { 18 | name: 'with-profile.eth', 19 | key: 'thiskeydoesntexist', 20 | }) 21 | expect(result).toBeNull() 22 | }) 23 | it('should return null on error when strict is false', async () => { 24 | await expect( 25 | getTextRecord.decode( 26 | {} as ClientWithEns, 27 | new RawContractError({ 28 | data: '0x7199966d', // ResolverNotFound() 29 | }), 30 | { 31 | address: '0x1234567890abcdef', 32 | args: ['0x', '0x'], 33 | }, 34 | { strict: false }, 35 | ), 36 | ).resolves.toBeNull() 37 | }) 38 | it('should throw on error when strict is true', async () => { 39 | await expect( 40 | getTextRecord.decode( 41 | {} as ClientWithEns, 42 | new RawContractError({ 43 | data: '0x7199966d', // ResolverNotFound() 44 | }), 45 | { 46 | address: '0x1234567890abcdef', 47 | args: ['0x', '0x'], 48 | }, 49 | { strict: true }, 50 | ), 51 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 52 | [ContractFunctionExecutionError: The contract function "resolve" reverted. 53 | 54 | Error: ResolverNotFound() 55 | 56 | Contract Call: 57 | address: 0x1234567890abcdef 58 | function: resolve(bytes name, bytes data) 59 | args: (0x, 0x) 60 | 61 | Version: viem@2.30.6] 62 | `) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/public/getWrapperName.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getWrapperName from './getWrapperName.js' 4 | 5 | describe('getWrapperName()', () => { 6 | it('should return name for existing name', async () => { 7 | const result = await getWrapperName(publicClient, { 8 | // wrapped.eth 9 | name: '[4ca938ec1b323ca71c4fb47a712abb68cce1cabf39ea4d6789e42fbc1f95459b].eth', 10 | }) 11 | expect(result).toBe('wrapped.eth') 12 | }) 13 | it('should return null for non-existent name', async () => { 14 | const result = await getWrapperName(publicClient, { 15 | // test123.eth 16 | name: '[f81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad].eth', 17 | }) 18 | expect(result).toBeNull() 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/fragments.ts: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import type { Address, Hex } from 'viem' 3 | 4 | export const domainDetailsWithoutParentFragment = gql` 5 | fragment DomainDetailsWithoutParent on Domain { 6 | id 7 | labelName 8 | labelhash 9 | name 10 | isMigrated 11 | createdAt 12 | resolvedAddress { 13 | id 14 | } 15 | owner { 16 | id 17 | } 18 | registrant { 19 | id 20 | } 21 | wrappedOwner { 22 | id 23 | } 24 | } 25 | ` 26 | 27 | export const domainDetailsFragment = gql` 28 | fragment DomainDetails on Domain { 29 | ...DomainDetailsWithoutParent 30 | parent { 31 | name 32 | } 33 | } 34 | ${domainDetailsWithoutParentFragment} 35 | ` 36 | 37 | export type SubgraphDomainFragment = { 38 | id: Hex 39 | labelName: string | null 40 | labelhash: Hex 41 | name: string 42 | isMigrated: boolean 43 | parent?: { 44 | name: string 45 | } 46 | createdAt: string 47 | resolvedAddress?: { 48 | id: Address 49 | } 50 | owner: { 51 | id: Address 52 | } 53 | registrant?: { 54 | id: Address 55 | } 56 | wrappedOwner?: { 57 | id: Address 58 | } 59 | } 60 | 61 | export const registrationDetailsFragment = gql` 62 | fragment RegistrationDetails on Registration { 63 | registrationDate 64 | expiryDate 65 | } 66 | ` 67 | 68 | export type SubgraphRegistrationFragment = { 69 | registrationDate: string 70 | expiryDate: string 71 | } 72 | 73 | export const wrappedDomainDetailsFragment = gql` 74 | fragment WrappedDomainDetails on WrappedDomain { 75 | expiryDate 76 | fuses 77 | } 78 | ` 79 | 80 | export type SubgraphWrappedDomainFragment = { 81 | expiryDate: string 82 | fuses: string 83 | } 84 | 85 | export type SubgraphDomain = SubgraphDomainFragment & { 86 | registration?: SubgraphRegistrationFragment 87 | wrappedDomain?: SubgraphWrappedDomainFragment 88 | } 89 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/getDecodedName.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getDecodedName from './getDecodedName.js' 4 | 5 | it('should decode a name via namehash lookup', async () => { 6 | const result = await getDecodedName(publicClient, { 7 | name: '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].wrapped-with-subnames.eth', 8 | }) 9 | expect(result).toBe('test.wrapped-with-subnames.eth') 10 | }) 11 | it('should decode a name via labelhash lookup', async () => { 12 | const result = await getDecodedName(publicClient, { 13 | name: '[f81b517a242b218999ec8eec0ea6e2ddbef2a367a14e93f4a32a39e260f686ad].eth', 14 | }) 15 | expect(result).toBe('test123.eth') 16 | }) 17 | it('should partially decode a name when allowIncomplete is true', async () => { 18 | const result = await getDecodedName(publicClient, { 19 | name: '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].[6c14e1739568670447af1d5af8a571008f7a582068af18bcd7ac2dbc13bb37c1].eth', 20 | allowIncomplete: true, 21 | }) 22 | expect(result).toBe( 23 | '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].with-unknown-subnames.eth', 24 | ) 25 | }) 26 | it('should not partially decode a name when allowIncomplete is false', async () => { 27 | const result = await getDecodedName(publicClient, { 28 | name: '[7bffb6e3ebf801bbc438fea5c11d957ba49978bdc8d52b71cba974139d22edea].[6c14e1739568670447af1d5af8a571008f7a582068af18bcd7ac2dbc13bb37c1].eth', 29 | allowIncomplete: false, 30 | }) 31 | expect(result).toBeNull() 32 | }) 33 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/getNameHistory.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { publicClient } from '../../test/addTestContracts.js' 3 | import getNameHistory from './getNameHistory.js' 4 | 5 | it('returns null for a non-existent name', async () => { 6 | const result = await getNameHistory(publicClient, { 7 | name: 'test123123cool.eth', 8 | }) 9 | expect(result).toBeNull() 10 | }) 11 | 12 | it('returns the history of a name', async () => { 13 | const result = await getNameHistory(publicClient, { 14 | name: 'with-profile.eth', 15 | }) 16 | if (!result) throw new Error('No result') 17 | expect(result.domainEvents.length).toBeGreaterThan(0) 18 | expect(result.registrationEvents!.length).toBeGreaterThan(0) 19 | expect(result.resolverEvents!.length).toBeGreaterThan(0) 20 | }) 21 | 22 | it('returns the history of a wrapped name', async () => { 23 | const result = await getNameHistory(publicClient, { 24 | name: 'wrapped.eth', 25 | }) 26 | if (!result) throw new Error('No result') 27 | expect(result.domainEvents.length).toBeGreaterThan(0) 28 | expect(result.registrationEvents!.length).toBeGreaterThan(0) 29 | expect(result.resolverEvents).not.toBeNull() 30 | }) 31 | 32 | it('returns the history of a subname', async () => { 33 | const result = await getNameHistory(publicClient, { 34 | name: 'test.wrapped-with-subnames.eth', 35 | }) 36 | if (!result) throw new Error('No result') 37 | expect(result.domainEvents.length).toBeGreaterThan(0) 38 | expect(result.registrationEvents).toBeNull() 39 | expect(result.resolverEvents).not.toBeNull() 40 | }) 41 | 42 | it('returns the history of a name with a null addr record', async () => { 43 | const result = await getNameHistory(publicClient, { 44 | name: 'with-empty-addr.eth', 45 | }) 46 | if (!result) throw new Error('No result') 47 | expect(result.domainEvents.length).toBeGreaterThan(0) 48 | expect(result.registrationEvents!.length).toBeGreaterThan(0) 49 | expect(result.resolverEvents).not.toBeNull() 50 | expect( 51 | result.resolverEvents!.some( 52 | (event) => event.type === 'MulticoinAddrChanged' && event.addr === null, 53 | ), 54 | ).toBe(true) 55 | }) 56 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/getSubgraphRecords.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { 3 | deploymentAddresses, 4 | publicClient, 5 | } from '../../test/addTestContracts.js' 6 | import getSubgraphRecords from './getSubgraphRecords.js' 7 | 8 | it('gets basic records', async () => { 9 | const result = await getSubgraphRecords(publicClient, { 10 | name: 'with-profile.eth', 11 | }) 12 | expect(result).toBeTruthy() 13 | const { createdAt, ...snapshot } = result! 14 | expect(createdAt.date).toBeInstanceOf(Date) 15 | expect(createdAt.value).toBe(createdAt.date.getTime()) 16 | expect(snapshot).toMatchInlineSnapshot(` 17 | { 18 | "coins": [ 19 | "60", 20 | "61", 21 | "0", 22 | "2", 23 | ], 24 | "isMigrated": true, 25 | "texts": [ 26 | "description", 27 | "url", 28 | "blankrecord", 29 | "email", 30 | ], 31 | } 32 | `) 33 | }) 34 | it('gets records for custom resolver address', async () => { 35 | const result = await getSubgraphRecords(publicClient, { 36 | name: 'with-legacy-resolver.eth', 37 | resolverAddress: deploymentAddresses.LegacyPublicResolver, 38 | }) 39 | expect(result).toBeTruthy() 40 | const { createdAt, ...snapshot } = result! 41 | expect(createdAt.date).toBeInstanceOf(Date) 42 | expect(createdAt.value).toBe(createdAt.date.getTime()) 43 | expect(snapshot).toMatchInlineSnapshot(` 44 | { 45 | "coins": [ 46 | "60", 47 | ], 48 | "isMigrated": true, 49 | "texts": [], 50 | } 51 | `) 52 | }) 53 | it('returns null for nonexistent name', async () => { 54 | const result = await getSubgraphRecords(publicClient, { 55 | name: 'nonexistent.eth', 56 | }) 57 | expect(result).toBeNull() 58 | }) 59 | it('returns empty texts/coins for nonexistent resolver', async () => { 60 | const result = await getSubgraphRecords(publicClient, { 61 | name: 'with-profile.eth', 62 | resolverAddress: '0xb794F5eA0ba39494cE839613fffBA74279579268', 63 | }) 64 | expect(result).toBeTruthy() 65 | const { createdAt, ...snapshot } = result! 66 | expect(createdAt.date).toBeInstanceOf(Date) 67 | expect(createdAt.value).toBe(createdAt.date.getTime()) 68 | expect(snapshot).toMatchInlineSnapshot(` 69 | { 70 | "coins": [], 71 | "isMigrated": true, 72 | "texts": [], 73 | } 74 | `) 75 | }) 76 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/getSubgraphRegistrant.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { 3 | deploymentAddresses, 4 | publicClient, 5 | } from '../../test/addTestContracts.js' 6 | import getSubgraphRegistrant from './getSubgraphRegistrant.js' 7 | 8 | it('gets the registrant for a 2ld .eth name', async () => { 9 | const result = await getSubgraphRegistrant(publicClient, { 10 | name: 'wrapped.eth', 11 | }) 12 | expect(result).toBe(deploymentAddresses.NameWrapper) 13 | }) 14 | it('returns null for nonexistent name', async () => { 15 | const result = await getSubgraphRegistrant(publicClient, { 16 | name: 'unregistered.eth', 17 | }) 18 | expect(result).toBeNull() 19 | }) 20 | it('throws an error for other name', async () => { 21 | await expect( 22 | getSubgraphRegistrant(publicClient, { 23 | name: 'test123.com', 24 | }), 25 | ).rejects.toThrowErrorMatchingInlineSnapshot(` 26 | [UnsupportedNameTypeError: Unsupported name type: other-2ld 27 | 28 | - Supported name types: eth-2ld 29 | 30 | Details: Registrant can only be fetched for eth-2ld names 31 | 32 | Version: @ensdomains/ensjs@1.0.0-mock.0] 33 | `) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/subgraph/types.ts: -------------------------------------------------------------------------------- 1 | import type { Hex } from 'viem' 2 | 3 | export type InputMaybe = T | null 4 | /** All built-in and custom scalars, mapped to their actual values */ 5 | export type Scalars = { 6 | ID: string 7 | String: string 8 | Boolean: boolean 9 | Int: number 10 | Float: number 11 | Bytes: Hex 12 | BigInt: string 13 | } 14 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/wallet/clearRecords.test.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Hex } from 'viem' 2 | import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' 3 | import { 4 | publicClient, 5 | testClient, 6 | waitForTransaction, 7 | walletClient, 8 | } from '../../test/addTestContracts.js' 9 | import getResolver from '../public/getResolver.js' 10 | import getText from '../public/getTextRecord.js' 11 | import clearRecords from './clearRecords.js' 12 | import setTextRecord from './setTextRecord.js' 13 | 14 | let snapshot: Hex 15 | let accounts: Address[] 16 | 17 | beforeAll(async () => { 18 | accounts = await walletClient.getAddresses() 19 | }) 20 | 21 | beforeEach(async () => { 22 | snapshot = await testClient.snapshot() 23 | }) 24 | 25 | afterEach(async () => { 26 | await testClient.revert({ id: snapshot }) 27 | }) 28 | 29 | it('should allow a name to be cleared', async () => { 30 | const resolverAddress = (await getResolver(publicClient, { 31 | name: 'wrapped.eth', 32 | }))! 33 | 34 | const setTextTx = await setTextRecord(walletClient, { 35 | name: 'wrapped.eth', 36 | key: 'description', 37 | value: 'test', 38 | resolverAddress, 39 | account: accounts[1], 40 | }) 41 | 42 | expect(setTextTx).toBeTruthy() 43 | const setTextReceipt = await waitForTransaction(setTextTx) 44 | expect(setTextReceipt.status).toBe('success') 45 | 46 | const priorResponse = await getText(publicClient, { 47 | name: 'wrapped.eth', 48 | key: 'description', 49 | }) 50 | expect(priorResponse).toBe('test') 51 | 52 | const tx = await clearRecords(walletClient, { 53 | name: 'wrapped.eth', 54 | resolverAddress, 55 | account: accounts[1], 56 | }) 57 | expect(tx).toBeTruthy() 58 | const receipt = await waitForTransaction(tx) 59 | expect(receipt.status).toBe('success') 60 | 61 | const response = await getText(publicClient, { 62 | name: 'wrapped.eth', 63 | key: 'description', 64 | }) 65 | expect(response).toBeNull() 66 | }) 67 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/wallet/commitName.test.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Hex } from 'viem' 2 | import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' 3 | import { ethRegistrarControllerCommitmentsSnippet } from '../../contracts/ethRegistrarController.js' 4 | import { getChainContractAddress } from '../../contracts/getChainContractAddress.js' 5 | import { 6 | publicClient, 7 | testClient, 8 | waitForTransaction, 9 | walletClient, 10 | } from '../../test/addTestContracts.js' 11 | import { 12 | type RegistrationParameters, 13 | makeCommitment, 14 | } from '../../utils/registerHelpers.js' 15 | import commitName from './commitName.js' 16 | 17 | let snapshot: Hex 18 | let accounts: Address[] 19 | 20 | beforeAll(async () => { 21 | accounts = await walletClient.getAddresses() 22 | }) 23 | 24 | beforeEach(async () => { 25 | snapshot = await testClient.snapshot() 26 | }) 27 | 28 | afterEach(async () => { 29 | await testClient.revert({ id: snapshot }) 30 | }) 31 | 32 | const secret = `0x${'a'.repeat(64)}` as Hex 33 | 34 | it('should return a commit transaction and succeed', async () => { 35 | const params: RegistrationParameters = { 36 | name: 'wrapped-with-subnames.eth', 37 | duration: 31536000, 38 | owner: accounts[1], 39 | secret, 40 | } 41 | const tx = await commitName(walletClient, { 42 | ...params, 43 | account: accounts[1], 44 | }) 45 | expect(tx).toBeTruthy() 46 | const receipt = await waitForTransaction(tx) 47 | expect(receipt.status).toBe('success') 48 | 49 | const commitment = await publicClient.readContract({ 50 | abi: ethRegistrarControllerCommitmentsSnippet, 51 | functionName: 'commitments', 52 | address: getChainContractAddress({ 53 | client: publicClient, 54 | contract: 'ensEthRegistrarController', 55 | }), 56 | args: [makeCommitment(params)], 57 | }) 58 | expect(commitment).toBeTruthy() 59 | }) 60 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/wallet/setContentHashRecord.test.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Hex } from 'viem' 2 | import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' 3 | import { 4 | publicClient, 5 | testClient, 6 | waitForTransaction, 7 | walletClient, 8 | } from '../../test/addTestContracts.js' 9 | import getContentHashRecord from '../public/getContentHashRecord.js' 10 | import getResolver from '../public/getResolver.js' 11 | import setContentHashRecord from './setContentHashRecord.js' 12 | 13 | let snapshot: Hex 14 | let accounts: Address[] 15 | 16 | beforeAll(async () => { 17 | accounts = await walletClient.getAddresses() 18 | }) 19 | 20 | beforeEach(async () => { 21 | snapshot = await testClient.snapshot() 22 | }) 23 | 24 | afterEach(async () => { 25 | await testClient.revert({ id: snapshot }) 26 | }) 27 | 28 | it('should allow a contenthash record to be set', async () => { 29 | const tx = await setContentHashRecord(walletClient, { 30 | name: 'test123.eth', 31 | contentHash: 32 | 'ipns://k51qzi5uqu5dgox2z23r6e99oqency055a6xt92xzmyvpz8mwz5ycjavm0u150', 33 | resolverAddress: (await getResolver(publicClient, { 34 | name: 'test123.eth', 35 | }))!, 36 | account: accounts[1], 37 | }) 38 | expect(tx).toBeTruthy() 39 | const receipt = await waitForTransaction(tx) 40 | expect(receipt.status).toBe('success') 41 | 42 | const response = await getContentHashRecord(publicClient, { 43 | name: 'test123.eth', 44 | }) 45 | expect(response).toMatchInlineSnapshot(` 46 | { 47 | "decoded": "k51qzi5uqu5dgox2z23r6e99oqency055a6xt92xzmyvpz8mwz5ycjavm0u150", 48 | "protocolType": "ipns", 49 | } 50 | `) 51 | }) 52 | 53 | it('should allow a contenthash record to be set to blank', async () => { 54 | const tx = await setContentHashRecord(walletClient, { 55 | name: 'with-contenthash.eth', 56 | contentHash: null, 57 | resolverAddress: (await getResolver(publicClient, { 58 | name: 'test123.eth', 59 | }))!, 60 | account: accounts[1], 61 | }) 62 | expect(tx).toBeTruthy() 63 | const receipt = await waitForTransaction(tx) 64 | expect(receipt.status).toBe('success') 65 | 66 | const response = await getContentHashRecord(publicClient, { 67 | name: 'test123.eth', 68 | }) 69 | expect(response).toBeNull() 70 | }) 71 | -------------------------------------------------------------------------------- /packages/ensjs/src/functions/wallet/setTextRecord.test.ts: -------------------------------------------------------------------------------- 1 | import type { Address, Hex } from 'viem' 2 | import { afterEach, beforeAll, beforeEach, expect, it } from 'vitest' 3 | import { 4 | publicClient, 5 | testClient, 6 | waitForTransaction, 7 | walletClient, 8 | } from '../../test/addTestContracts.js' 9 | import getResolver from '../public/getResolver.js' 10 | import getText from '../public/getTextRecord.js' 11 | import setTextRecord from './setTextRecord.js' 12 | 13 | let snapshot: Hex 14 | let accounts: Address[] 15 | 16 | beforeAll(async () => { 17 | accounts = await walletClient.getAddresses() 18 | }) 19 | 20 | beforeEach(async () => { 21 | snapshot = await testClient.snapshot() 22 | }) 23 | 24 | afterEach(async () => { 25 | await testClient.revert({ id: snapshot }) 26 | }) 27 | 28 | it('should allow a text record to be set', async () => { 29 | const tx = await setTextRecord(walletClient, { 30 | name: 'test123.eth', 31 | key: 'foo', 32 | value: 'bar', 33 | resolverAddress: (await getResolver(publicClient, { 34 | name: 'test123.eth', 35 | }))!, 36 | account: accounts[1], 37 | }) 38 | expect(tx).toBeTruthy() 39 | const receipt = await waitForTransaction(tx) 40 | expect(receipt.status).toBe('success') 41 | 42 | const response = await getText(publicClient, { 43 | name: 'test123.eth', 44 | key: 'foo', 45 | }) 46 | expect(response).toBe('bar') 47 | }) 48 | 49 | it('should allow a text record to be set to blank', async () => { 50 | const tx = await setTextRecord(walletClient, { 51 | name: 'test123.eth', 52 | key: 'url', 53 | value: null, 54 | resolverAddress: (await getResolver(publicClient, { 55 | name: 'test123.eth', 56 | }))!, 57 | account: accounts[1], 58 | }) 59 | expect(tx).toBeTruthy() 60 | const receipt = await waitForTransaction(tx) 61 | expect(receipt.status).toBe('success') 62 | 63 | const response = await getText(publicClient, { 64 | name: 'test123.eth', 65 | key: 'url', 66 | }) 67 | expect(response).toBeNull() 68 | }) 69 | -------------------------------------------------------------------------------- /packages/ensjs/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ensPublicActions, 3 | type EnsPublicActions, 4 | } from './clients/decorators/public.js' 5 | export { 6 | ensSubgraphActions, 7 | type EnsSubgraphActions, 8 | } from './clients/decorators/subgraph.js' 9 | export { 10 | ensWalletActions, 11 | type EnsWalletActions, 12 | } from './clients/decorators/wallet.js' 13 | export { 14 | createEnsPublicClient, 15 | type EnsPublicClient, 16 | type EnsPublicClientConfig, 17 | } from './clients/public.js' 18 | export { 19 | createEnsSubgraphClient, 20 | type EnsSubgraphClient, 21 | type EnsSubgraphClientConfig, 22 | } from './clients/subgraph.js' 23 | export { 24 | createEnsWalletClient, 25 | type EnsWalletClient, 26 | type EnsWalletClientConfig, 27 | } from './clients/wallet.js' 28 | 29 | export { addEnsContracts } from './contracts/addEnsContracts.js' 30 | export { BaseError } from './errors/base.js' 31 | export { NoChainError, UnsupportedChainError } from './errors/contracts.js' 32 | export { 33 | DnsDnssecVerificationFailedError, 34 | DnsDnssecWildcardExpansionError, 35 | DnsInvalidAddressChecksumError, 36 | DnsInvalidTxtRecordError, 37 | DnsNewerRecordTypeAvailableError, 38 | DnsNoTxtRecordError, 39 | DnsResponseStatusError, 40 | } from './errors/dns.js' 41 | export { 42 | AdditionalParameterSpecifiedError, 43 | InvalidContractTypeError, 44 | RequiredParameterNotSpecifiedError, 45 | UnsupportedNameTypeError, 46 | } from './errors/general.js' 47 | export { 48 | CoinFormatterNotFoundError, 49 | FunctionNotBatchableError, 50 | NoRecordsSpecifiedError, 51 | } from './errors/public.js' 52 | export { 53 | FilterKeyRequiredError, 54 | InvalidFilterKeyError, 55 | InvalidOrderByError, 56 | } from './errors/subgraph.js' 57 | export { 58 | CampaignReferenceTooLargeError, 59 | FusesFuseNotAllowedError, 60 | FusesInvalidFuseObjectError, 61 | FusesInvalidNamedFuseError, 62 | FusesInvalidUnnamedFuseError, 63 | FusesOutOfRangeError, 64 | FusesRestrictionNotAllowedError, 65 | FusesValueRequiredError, 66 | InvalidContentHashError, 67 | InvalidEncodedLabelError, 68 | InvalidLabelhashError, 69 | NameWithEmptyLabelsError, 70 | RootNameIncludesOtherLabelsError, 71 | UnknownContentTypeError, 72 | WrappedLabelTooLargeError, 73 | } from './errors/utils.js' 74 | -------------------------------------------------------------------------------- /packages/ensjs/src/subgraph.ts: -------------------------------------------------------------------------------- 1 | export { createSubgraphClient } from './functions/subgraph/client.js' 2 | export type { 3 | AbiChanged, 4 | AddrChanged, 5 | AuthorisationChanged, 6 | BaseDomainEvent, 7 | BaseRegistrationEvent, 8 | BaseResolverEvent, 9 | ContenthashChanged, 10 | DomainEvent, 11 | DomainEventKey, 12 | ExpiryExtended, 13 | FusesSet, 14 | InterfaceChanged, 15 | MulticoinAddrChanged, 16 | NameChanged, 17 | NameRegistered, 18 | NameRenewed, 19 | NameTransferred, 20 | NameUnwrapped, 21 | NameWrapped, 22 | NewOwner, 23 | NewResolver, 24 | NewTtl, 25 | PubkeyChanged, 26 | RegistrationEvent, 27 | RegistrationEventKey, 28 | ResolverEvent, 29 | ResolverEventKey, 30 | TextChanged, 31 | Transfer, 32 | VersionChanged, 33 | WrappedTransfer, 34 | } from './functions/subgraph/events.js' 35 | export { 36 | default as getDecodedName, 37 | type GetDecodedNameParameters, 38 | type GetDecodedNameReturnType, 39 | } from './functions/subgraph/getDecodedName.js' 40 | export { 41 | default as getNameHistory, 42 | type GetNameHistoryParameters, 43 | type GetNameHistoryReturnType, 44 | } from './functions/subgraph/getNameHistory.js' 45 | export { 46 | default as getNamesForAddress, 47 | type GetNamesForAddressParameters, 48 | type GetNamesForAddressReturnType, 49 | type NameWithRelation, 50 | } from './functions/subgraph/getNamesForAddress.js' 51 | export { 52 | default as getSubgraphRecords, 53 | type GetSubgraphRecordsParameters, 54 | type GetSubgraphRecordsReturnType, 55 | } from './functions/subgraph/getSubgraphRecords.js' 56 | export { 57 | default as getSubgraphRegistrant, 58 | type GetSubgraphRegistrantParameters, 59 | type GetSubgraphRegistrantReturnType, 60 | } from './functions/subgraph/getSubgraphRegistrant.js' 61 | export { 62 | default as getSubnames, 63 | type GetSubnamesParameters, 64 | type GetSubnamesReturnType, 65 | } from './functions/subgraph/getSubnames.js' 66 | export { 67 | getChecksumAddressOrNull, 68 | makeNameObject, 69 | type Name, 70 | } from './functions/subgraph/utils.js' 71 | -------------------------------------------------------------------------------- /packages/ensjs/src/test/createHttpServer.ts: -------------------------------------------------------------------------------- 1 | import { type RequestListener, createServer } from 'node:http' 2 | import type { AddressInfo } from 'node:net' 3 | 4 | export function createHttpServer(handler: RequestListener): Promise<{ 5 | close: () => Promise 6 | url: `http://localhost:${number}` 7 | }> { 8 | const server = createServer(handler) 9 | 10 | const closeServer = () => { 11 | return new Promise((resolve, reject) => { 12 | server.close((err) => { 13 | if (err) { 14 | reject(err) 15 | return 16 | } 17 | resolve() 18 | }) 19 | }) 20 | } 21 | 22 | return new Promise((resolve) => { 23 | server.listen(() => { 24 | const { port } = server.address() as AddressInfo 25 | resolve({ 26 | close: closeServer, 27 | url: `http://localhost:${port}`, 28 | }) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /packages/ensjs/src/test/dns.ts: -------------------------------------------------------------------------------- 1 | import type { RequestListener } from 'node:http' 2 | import type { MockedFunction } from 'vitest' 3 | 4 | type Handler = MockedFunction 5 | 6 | export const createHandlerResponse = (handler: Handler, response: object) => { 7 | handler.mockImplementation((_req, res) => { 8 | res.writeHead(200, { 'Content-Type': 'application/dns-json' }) 9 | res.end(JSON.stringify(response)) 10 | res.destroy() 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /packages/ensjs/src/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, vi } from 'vitest' 2 | 3 | beforeAll(() => { 4 | vi.mock('../errors/error-utils.ts', () => ({ 5 | getVersion: vi.fn().mockReturnValue('@ensdomains/ensjs@1.0.0-mock.0'), 6 | })) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/ccipRequest.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type CcipRequestParameters, 3 | type Chain, 4 | decodeFunctionData, 5 | encodeFunctionResult, 6 | isAddressEqual, 7 | parseAbi, 8 | ccipRequest as viemCcipRequest, 9 | } from 'viem' 10 | import { getChainContractAddress } from '../contracts/getChainContractAddress.js' 11 | import { ccipBatchRequest } from './ccipBatchRequest.js' 12 | 13 | const abi = parseAbi([ 14 | 'function query((address,string[],bytes)[]) returns (bool[],bytes[])', 15 | ]) 16 | 17 | const universalResolverQuerySig = '0xa780bab6' 18 | 19 | export const ccipRequest = 20 | (chain: TChain) => 21 | async ({ 22 | data, 23 | sender, 24 | urls, 25 | }: CcipRequestParameters): ReturnType => { 26 | const universalResolverAddress = getChainContractAddress({ 27 | client: { chain }, 28 | contract: 'ensUniversalResolver', 29 | }) 30 | const isUniversalResolverRequest = isAddressEqual( 31 | sender, 32 | universalResolverAddress, 33 | ) 34 | if ( 35 | isUniversalResolverRequest && 36 | data.slice(0, 10) === universalResolverQuerySig 37 | ) { 38 | const { args } = decodeFunctionData({ abi, data }) 39 | const result = await ccipBatchRequest(args[0]) 40 | return encodeFunctionResult({ abi, result }) 41 | } 42 | 43 | return viemCcipRequest({ data, sender, urls }) 44 | } 45 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/checkSafeUniversalResolverData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type BaseError, 3 | CallExecutionError, 4 | type Hex, 5 | decodeErrorResult, 6 | getContractError, 7 | } from 'viem' 8 | import type { Prettify } from '../types.js' 9 | import { getRevertErrorData } from './getRevertErrorData.js' 10 | 11 | type CheckSafeUniversalResolverDataParameters = Prettify< 12 | { 13 | strict: boolean | undefined 14 | args: any[] | (() => any[]) 15 | } & Omit[1], 'args'> 16 | > 17 | 18 | /** 19 | * Checks if the data returned from a universal resolver is safe to use, or if it is a known revert error 20 | * @param parameters - {@link CheckSafeUniversalResolverDataParameters} 21 | * @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 22 | */ 23 | export const checkSafeUniversalResolverData = ( 24 | data: Hex | BaseError, 25 | { 26 | strict, 27 | abi, 28 | args, 29 | functionName, 30 | address, 31 | docsPath, 32 | sender, 33 | }: CheckSafeUniversalResolverDataParameters, 34 | ): data is Hex => { 35 | if (typeof data === 'object') { 36 | // if not strict, attempt to check if the error is a known revert error before throwing 37 | // if it is a known revert error, return false instead of throwing 38 | // even if strict is false, we want to throw unknown contract errors 39 | if (!strict) { 40 | const errorData = getRevertErrorData(data) 41 | if (errorData) { 42 | try { 43 | decodeErrorResult({ 44 | abi, 45 | data: errorData, 46 | }) 47 | return false 48 | } catch (_error) {} 49 | } 50 | } 51 | if (data instanceof CallExecutionError) throw data 52 | throw getContractError(data, { 53 | abi, 54 | args: typeof args === 'function' ? args() : args, 55 | functionName, 56 | address, 57 | docsPath, 58 | sender, 59 | }) as BaseError 60 | } 61 | return true 62 | } 63 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/consts.ts: -------------------------------------------------------------------------------- 1 | export const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000' 2 | export const MAX_INT_64 = BigInt('18446744073709551615') 3 | export const MAX_DATE_INT = 8640000000000000 4 | export const MINIMUM_DOT_ETH_CHARS = 3 5 | export const GRACE_PERIOD_SECONDS = 7776000 // 90 days 6 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/dns/getDnsTxtRecords.ts: -------------------------------------------------------------------------------- 1 | import type { Endpoint } from '../../functions/dns/types.js' 2 | import type { DnsResponse } from './misc.js' 3 | 4 | export type GetDnsTxtRecordsParameters = { 5 | /** Name to get the txt records for */ 6 | name: string 7 | /** An RFC-1035 compatible DNS endpoint to use (default: `https://cloudflare-dns.com/dns-query`) */ 8 | endpoint?: Endpoint 9 | } 10 | 11 | export type GetDnsTxtRecordsReturnType = DnsResponse 12 | 13 | /** 14 | * Gets the DNS record response of a name, via DNS record lookup 15 | * @param parameters - {@link GetDnsTxtRecordsParameters} 16 | * @returns DNS response. {@link GetDnsTxtRecordsReturnType} 17 | * 18 | * @example 19 | * import { getDnsTxtRecords } from '@ensdomains/ensjs/utils' 20 | * 21 | * const owner = await getDnsTxtRecords({ name: '_ens.ens.domains' }) 22 | */ 23 | export const getDnsTxtRecords = async ({ 24 | name, 25 | endpoint = 'https://cloudflare-dns.com/dns-query', 26 | }: GetDnsTxtRecordsParameters): Promise => { 27 | const response: DnsResponse = await fetch( 28 | `${endpoint}?name=${name}.&type=TXT&do=1`, 29 | { 30 | method: 'GET', 31 | headers: { 32 | accept: 'application/dns-json', 33 | }, 34 | }, 35 | ).then((res) => res.json()) 36 | 37 | return response 38 | } 39 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/dns/misc.ts: -------------------------------------------------------------------------------- 1 | export enum DnsResponseStatus { 2 | NOERROR = 0, 3 | FORMERR = 1, 4 | SERVFAIL = 2, 5 | NXDOMAIN = 3, 6 | NOTIMP = 4, 7 | REFUSED = 5, 8 | YXDOMAIN = 6, 9 | YXRRSET = 7, 10 | NXRRSET = 8, 11 | NOTAUTH = 9, 12 | NOTZONE = 10, 13 | DSOTYPENI = 11, 14 | BADVERS = 16, // also 16 = BADSIG but not important 15 | BADKEY = 17, 16 | BADTIME = 18, 17 | BADMODE = 19, 18 | BADNAME = 20, 19 | BADALG = 21, 20 | BADTRUNC = 22, 21 | BADCOOKIE = 23, 22 | } 23 | 24 | export enum DnsRecordType { 25 | TXT = 16, 26 | DS = 43, 27 | RRSIG = 46, 28 | DNSKEY = 48, 29 | } 30 | 31 | export type DnsQuestionItem = { 32 | name: string 33 | type: DnsRecordType 34 | } 35 | 36 | export type DnsResponseItem = DnsQuestionItem & { 37 | TTL: number 38 | data: string 39 | } 40 | 41 | export type DnsResponse = { 42 | Status: DnsResponseStatus 43 | TC: boolean 44 | RD: boolean 45 | RA: boolean 46 | AD: boolean 47 | CD: boolean 48 | Question: DnsQuestionItem[] 49 | Answer?: DnsResponseItem[] 50 | Authority?: DnsResponseItem[] 51 | Additional?: DnsResponseItem[] 52 | Comment?: string 53 | } 54 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeClearRecords.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, encodeFunctionData } from 'viem' 2 | import { publicResolverClearRecordsSnippet } from '../../contracts/publicResolver.js' 3 | 4 | export const encodeClearRecords = (namehash: Hex) => 5 | encodeFunctionData({ 6 | abi: publicResolverClearRecordsSnippet, 7 | functionName: 'clearRecords', 8 | args: [namehash], 9 | }) 10 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetAbi.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { encodeAbi } from './encodeAbi.js' 3 | import { type EncodeSetAbiParameters, encodeSetAbi } from './encodeSetAbi.js' 4 | 5 | describe('encodeSetAbi', () => { 6 | const namehash = 7 | '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' 8 | const contentType = 1 9 | const encodedData = '0x' 10 | 11 | const parameters: EncodeSetAbiParameters = { 12 | namehash, 13 | contentType, 14 | encodedData, 15 | } 16 | 17 | it('encodes the setAbi function data correctly with null encodedData', async () => { 18 | const expected = 19 | '0x623195b01234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000' 20 | const result = encodeSetAbi(parameters) 21 | expect(result).toEqual(expected) 22 | }) 23 | 24 | it('encodes the setAbi function data correctly with encodedData', async () => { 25 | const result = encodeSetAbi({ 26 | namehash, 27 | ...(await encodeAbi({ encodeAs: 'json', data: { foo: 'bar' } })), 28 | }) 29 | expect(result).toEqual( 30 | '0x623195b01234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d7b22666f6f223a22626172227d00000000000000000000000000000000000000', 31 | ) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetAbi.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, encodeFunctionData } from 'viem' 2 | import { publicResolverSetAbiSnippet } from '../../contracts/publicResolver.js' 3 | import type { EncodedAbi } from './encodeAbi.js' 4 | 5 | export type EncodeSetAbiParameters = { 6 | namehash: Hex 7 | } & EncodedAbi 8 | 9 | export type EncodeSetAbiReturnType = Hex 10 | 11 | export const encodeSetAbi = ({ 12 | namehash, 13 | contentType, 14 | encodedData, 15 | }: EncodeSetAbiParameters): EncodeSetAbiReturnType => { 16 | return encodeFunctionData({ 17 | abi: publicResolverSetAbiSnippet, 18 | functionName: 'setABI', 19 | args: [namehash, BigInt(contentType), encodedData], 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetAddr.ts: -------------------------------------------------------------------------------- 1 | import { type Address, type Hex, bytesToHex, encodeFunctionData } from 'viem' 2 | import { publicResolverSetAddrSnippet } from '../../contracts/publicResolver.js' 3 | import { getCoderFromCoin } from '../normaliseCoinId.js' 4 | 5 | export type EncodeSetAddrParameters = { 6 | namehash: Hex 7 | coin: string | number 8 | value: Address | string | null 9 | } 10 | 11 | export type EncodeSetAddrReturnType = Hex 12 | 13 | export const encodeSetAddr = ({ 14 | namehash, 15 | coin, 16 | value, 17 | }: EncodeSetAddrParameters): EncodeSetAddrReturnType => { 18 | const coder = getCoderFromCoin(coin) 19 | const inputCoinType = coder.coinType 20 | let encodedAddress: Hex | Uint8Array = value ? coder.decode(value) : '0x' 21 | if (inputCoinType === 60 && encodedAddress === '0x') 22 | encodedAddress = coder.decode('0x0000000000000000000000000000000000000000') 23 | if (typeof encodedAddress !== 'string') { 24 | encodedAddress = bytesToHex(encodedAddress) 25 | } 26 | 27 | return encodeFunctionData({ 28 | abi: publicResolverSetAddrSnippet, 29 | functionName: 'setAddr', 30 | args: [namehash, BigInt(inputCoinType), encodedAddress], 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetContentHash.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { 3 | type EncodeSetContentHashParameters, 4 | encodeSetContentHash, 5 | } from './encodeSetContentHash.js' 6 | 7 | describe('encodeSetContentHash', () => { 8 | const namehash = 9 | '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' 10 | const contentHash = 'ipfs://QmXwMFNjzjRvZuPvzJfYJZ1QqX2QJjzj1YJZ1QqX2QJjzj' 11 | 12 | it('encodes the function data correctly when contentHash is not null', () => { 13 | const expectedEncodedData = 14 | '0x304e6ade1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000026e301017012208e9cc47fde7ff64028480ec671a4ddb8f767a71ff71a73247f51a495a6f296340000000000000000000000000000000000000000000000000000' 15 | const params: EncodeSetContentHashParameters = { namehash, contentHash } 16 | expect(encodeSetContentHash(params)).toEqual(expectedEncodedData) 17 | }) 18 | 19 | it('encodes the function data correctly when contentHash is null', () => { 20 | const expectedEncodedData = 21 | '0x304e6ade1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000' 22 | const params: EncodeSetContentHashParameters = { 23 | namehash, 24 | contentHash: null, 25 | } 26 | expect(encodeSetContentHash(params)).toEqual(expectedEncodedData) 27 | }) 28 | 29 | it('throws an error when contentHash is invalid', () => { 30 | const params: EncodeSetContentHashParameters = { 31 | namehash, 32 | contentHash: 'invalid-content-hash', 33 | } 34 | expect(() => 35 | encodeSetContentHash(params), 36 | ).toThrowErrorMatchingInlineSnapshot(` 37 | [InvalidContentHashError: Invalid content hash 38 | 39 | Version: @ensdomains/ensjs@1.0.0-mock.0] 40 | `) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetContentHash.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, encodeFunctionData } from 'viem' 2 | import { publicResolverSetContenthashSnippet } from '../../contracts/publicResolver.js' 3 | import { encodeContentHash } from '../contentHash.js' 4 | 5 | export type EncodeSetContentHashParameters = { 6 | namehash: Hex 7 | contentHash: string | null 8 | } 9 | 10 | export type EncodeSetContentHashReturnType = Hex 11 | 12 | export const encodeSetContentHash = ({ 13 | namehash, 14 | contentHash, 15 | }: EncodeSetContentHashParameters): EncodeSetContentHashReturnType => { 16 | let encodedHash: Hex = '0x' 17 | if (contentHash) { 18 | encodedHash = encodeContentHash(contentHash) 19 | } 20 | return encodeFunctionData({ 21 | abi: publicResolverSetContenthashSnippet, 22 | functionName: 'setContenthash', 23 | args: [namehash, encodedHash], 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetText.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { type EncodeSetTextParameters, encodeSetText } from './encodeSetText.js' 3 | 4 | describe('encodeSetText', () => { 5 | const namehash = 6 | '0x1234567890123456789012345678901234567890123456789012345678901234' 7 | const key = 'email' 8 | const value = 'test@example.com' 9 | 10 | const parameters: EncodeSetTextParameters = { 11 | namehash, 12 | key, 13 | value, 14 | } 15 | 16 | it('encodes the function data correctly', () => { 17 | const expected = 18 | '0x10f13a8c1234567890123456789012345678901234567890123456789012345678901234000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005656d61696c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001074657374406578616d706c652e636f6d00000000000000000000000000000000' 19 | const result = encodeSetText(parameters) 20 | expect(result).toEqual(expected) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/encoders/encodeSetText.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, encodeFunctionData } from 'viem' 2 | import { publicResolverSetTextSnippet } from '../../contracts/publicResolver.js' 3 | 4 | export type EncodeSetTextParameters = { 5 | namehash: Hex 6 | key: string 7 | value: string | null 8 | } 9 | 10 | export type EncodeSetTextReturnType = Hex 11 | 12 | export const encodeSetText = ({ 13 | namehash, 14 | key, 15 | value, 16 | }: EncodeSetTextParameters): EncodeSetTextReturnType => { 17 | return encodeFunctionData({ 18 | abi: publicResolverSetTextSnippet, 19 | functionName: 'setText', 20 | args: [namehash, key, value ?? ''], 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/format.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { truncateFormat } from './format.js' 3 | 4 | describe('truncateFormat', () => { 5 | it('should truncate a long name correctly', () => { 6 | const longName = 7 | '[0x12345678901234567890123456789012345678901234567890123456dfssss]' 8 | const expected = '[0x1...sss]' 9 | const result = truncateFormat(longName) 10 | expect(result).toEqual(expected) 11 | }) 12 | 13 | it('should not modify a short name', () => { 14 | const shortName = 'example.eth' 15 | const result = truncateFormat(shortName) 16 | expect(result).toEqual(shortName) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/format.ts: -------------------------------------------------------------------------------- 1 | export const truncateFormat = (name: string): string => 2 | name.replace(/(\[.{3})(.{58})(.{3}\])/g, '$1...$3') 3 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/generateRecordCallArray.ts: -------------------------------------------------------------------------------- 1 | import type { Hex } from 'viem' 2 | import type { Prettify } from '../types.js' 3 | import type { EncodedAbi } from './encoders/encodeAbi.js' 4 | import { encodeClearRecords } from './encoders/encodeClearRecords.js' 5 | import { 6 | type EncodeSetAbiParameters, 7 | encodeSetAbi, 8 | } from './encoders/encodeSetAbi.js' 9 | import { 10 | type EncodeSetAddrParameters, 11 | encodeSetAddr, 12 | } from './encoders/encodeSetAddr.js' 13 | import { encodeSetContentHash } from './encoders/encodeSetContentHash.js' 14 | import { 15 | type EncodeSetTextParameters, 16 | encodeSetText, 17 | } from './encoders/encodeSetText.js' 18 | 19 | export type RecordOptions = Prettify<{ 20 | /** Clears all current records */ 21 | clearRecords?: boolean 22 | /** ContentHash value */ 23 | contentHash?: string | null 24 | /** Array of text records */ 25 | texts?: Omit[] 26 | /** Array of coin records */ 27 | coins?: Omit[] 28 | /** ABI value */ 29 | abi?: EncodedAbi | EncodedAbi[] 30 | }> 31 | 32 | export const generateRecordCallArray = ({ 33 | namehash, 34 | clearRecords, 35 | contentHash, 36 | texts, 37 | coins, 38 | abi, 39 | }: { namehash: Hex } & RecordOptions): Hex[] => { 40 | const calls: Hex[] = [] 41 | 42 | if (clearRecords) { 43 | calls.push(encodeClearRecords(namehash)) 44 | } 45 | 46 | if (contentHash !== undefined) { 47 | const data = encodeSetContentHash({ namehash, contentHash }) 48 | if (data) calls.push(data) 49 | } 50 | 51 | if (abi !== undefined) { 52 | const abis = Array.isArray(abi) ? abi : [abi] 53 | for (const abi_ of abis) { 54 | const data = encodeSetAbi({ namehash, ...abi_ } as EncodeSetAbiParameters) 55 | if (data) calls.push(data) 56 | } 57 | } 58 | 59 | if (texts && texts.length > 0) { 60 | const data = texts.map((textItem) => 61 | encodeSetText({ namehash, ...textItem }), 62 | ) 63 | if (data) calls.push(...data) 64 | } 65 | 66 | if (coins && coins.length > 0) { 67 | const data = coins.map((coinItem) => 68 | encodeSetAddr({ namehash, ...coinItem }), 69 | ) 70 | if (data) calls.push(...data) 71 | } 72 | 73 | return calls 74 | } 75 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/generateSupportedContentTypes.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { generateSupportedContentTypes } from './generateSupportedContentTypes.js' 3 | 4 | type FunctionParameters = Parameters 5 | type EncodeAsParameter = FunctionParameters[0] 6 | 7 | describe('generateSupportedContentTypes', () => { 8 | it.each([ 9 | ['json', 1n], 10 | ['zlib', 2n], 11 | ['cbor', 4n], 12 | ['uri', 8n], 13 | ['unknown', 0n], 14 | [['json', 'zlib'], 3n], 15 | [['zlib', 'cbor'], 6n], 16 | [['cbor', 'uri'], 12n], 17 | [['json', 'zlib', 'cbor', 'uri'], 15n], 18 | ] as [EncodeAsParameter, bigint][])( 19 | 'should return the correct bitwise value supportedContentTypes %p (%p)', 20 | (encodedAs, expected) => { 21 | const result = generateSupportedContentTypes(encodedAs) 22 | expect(result).toEqual(expected) 23 | }, 24 | ) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/generateSupportedContentTypes.ts: -------------------------------------------------------------------------------- 1 | import type { AbiEncodeAs } from './encoders/encodeAbi.js' 2 | 3 | const abiEncodeAsMap: { [key in AbiEncodeAs]: bigint } = { 4 | json: 1n, 5 | zlib: 2n, 6 | cbor: 4n, 7 | uri: 8n, 8 | } as const 9 | 10 | export const generateSupportedContentTypes = ( 11 | encodeAsItemOrList: AbiEncodeAs | AbiEncodeAs[], 12 | ): bigint => { 13 | const encodeAsList = Array.isArray(encodeAsItemOrList) 14 | ? encodeAsItemOrList 15 | : [encodeAsItemOrList] 16 | return encodeAsList.reduce((result, encodeAs) => { 17 | const contentType = abiEncodeAsMap[encodeAs] 18 | // biome-ignore lint/style/noParameterAssign: idk what is this 19 | if (contentType) result |= contentType 20 | return result 21 | }, 0n) 22 | } 23 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/getNameType.ts: -------------------------------------------------------------------------------- 1 | import type { NameType } from '../types.js' 2 | 3 | export const getNameType = (name: string): NameType => { 4 | const labels = name.split('.') 5 | const isDotEth = labels[labels.length - 1] === 'eth' 6 | 7 | if (labels.length === 0) return 'root' 8 | if (labels.length === 1) { 9 | if (isDotEth) return 'eth-tld' 10 | return 'tld' 11 | } 12 | if (labels.length === 2) { 13 | if (isDotEth) return 'eth-2ld' 14 | return 'other-2ld' 15 | } 16 | if (isDotEth) return 'eth-subname' 17 | return 'other-subname' 18 | } 19 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/getRevertErrorData.ts: -------------------------------------------------------------------------------- 1 | import { BaseError, type RawContractError } from 'viem' 2 | 3 | export const getRevertErrorData = (err: unknown) => { 4 | if (!(err instanceof BaseError)) return undefined 5 | const error = err.walk() as RawContractError 6 | const hex = typeof error.data === 'object' ? error.data.data : error.data 7 | if (hex === '0x') return undefined 8 | return hex 9 | } 10 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/hexEncodedName.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/mafintosh/dns-packet 2 | import { type ByteArray, bytesToString, labelhash, stringToBytes } from 'viem' 3 | import { encodeLabelhash } from './labels.js' 4 | 5 | /* 6 | * @description Encodes a DNS packet into a ByteArray containing a UDP payload. 7 | */ 8 | export function packetToBytes(packet: string): ByteArray { 9 | // strip leading and trailing `.` 10 | const value = packet.replace(/^\.|\.$/gm, '') 11 | if (value.length === 0) return new Uint8Array(1) 12 | 13 | const bytes = new Uint8Array(stringToBytes(value).byteLength + 2) 14 | 15 | let offset = 0 16 | const list = value.split('.') 17 | for (let i = 0; i < list.length; i += 1) { 18 | let encoded = stringToBytes(list[i]) 19 | // if the length is > 255, make the encoded label value a labelhash 20 | // this is compatible with the universal resolver 21 | if (encoded.byteLength > 255) 22 | encoded = stringToBytes(encodeLabelhash(labelhash(list[i]))) 23 | bytes[offset] = encoded.length 24 | bytes.set(encoded, offset + 1) 25 | offset += encoded.length + 1 26 | } 27 | 28 | if (bytes.byteLength !== offset + 1) return bytes.slice(0, offset + 1) 29 | 30 | return bytes 31 | } 32 | 33 | export function bytesToPacket(bytes: ByteArray): string { 34 | let offset = 0 35 | let result = '' 36 | 37 | while (offset < bytes.length) { 38 | const len = bytes[offset] 39 | if (len === 0) { 40 | offset += 1 41 | break 42 | } 43 | 44 | result += `${bytesToString(bytes.subarray(offset + 1, offset + len + 1))}.` 45 | offset += len + 1 46 | } 47 | 48 | return result.replace(/\.$/, '') 49 | } 50 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/makeLabelNodeAndParent.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { makeLabelNodeAndParent } from './makeLabelNodeAndParent.js' 3 | 4 | it('makes label node and parent', () => { 5 | expect(makeLabelNodeAndParent('test.eth')).toMatchInlineSnapshot(` 6 | { 7 | "label": "test", 8 | "labelhash": "0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658", 9 | "parentNode": "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", 10 | } 11 | `) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/makeLabelNodeAndParent.ts: -------------------------------------------------------------------------------- 1 | import { labelhash } from 'viem' 2 | import { namehash } from './normalise.js' 3 | 4 | export const makeLabelNodeAndParent = (name: string) => { 5 | const labels = name.split('.') 6 | const label = labels.shift() as string 7 | const parentNode = namehash(labels.join('.')) 8 | return { 9 | label, 10 | labelhash: labelhash(label), 11 | parentNode, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/makeSafeSecondsDate.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { MAX_DATE_INT } from './consts.js' 3 | import { makeSafeSecondsDate } from './makeSafeSecondsDate.js' 4 | 5 | it('makes date from seconds', () => { 6 | expect(makeSafeSecondsDate(21000)).toEqual(new Date(21000000)) 7 | }) 8 | it('makes date from seconds with big int', () => { 9 | expect(makeSafeSecondsDate(21000n)).toEqual(new Date(21000000)) 10 | }) 11 | it('makes date from seconds with big int larger than max date int', () => { 12 | expect(makeSafeSecondsDate(MAX_DATE_INT + 28930402)).toEqual( 13 | new Date(MAX_DATE_INT), 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/makeSafeSecondsDate.ts: -------------------------------------------------------------------------------- 1 | import { MAX_DATE_INT } from './consts.js' 2 | 3 | export const makeSafeSecondsDate = (seconds: number | bigint) => { 4 | const milliseconds = BigInt(seconds) * 1000n 5 | if (milliseconds > BigInt(MAX_DATE_INT)) return new Date(MAX_DATE_INT) 6 | return new Date(Number(milliseconds)) 7 | } 8 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/normalise.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { namehash } from './normalise.js' 3 | 4 | describe('namehash()', () => { 5 | it('returns namehash for name', () => { 6 | expect(namehash('test.eth')).toEqual( 7 | '0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1', 8 | ) 9 | }) 10 | it('returns namehash for name with encoded labelhash', () => { 11 | expect( 12 | namehash( 13 | '[9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658].eth', 14 | ), 15 | ).toEqual( 16 | '0xeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1', 17 | ) 18 | }) 19 | }) 20 | 21 | it('exports functions from ens-normalize', async () => { 22 | const modules = await import('./normalise.js') 23 | expect(modules).toMatchInlineSnapshot(` 24 | { 25 | "beautify": [Function], 26 | "emoji": [Function], 27 | "isCombiningMark": [Function], 28 | "namehash": [Function], 29 | "normalise": [Function], 30 | "normaliseFragment": [Function], 31 | "shouldEscape": [Function], 32 | "split": [Function], 33 | "tokenise": [Function], 34 | } 35 | `) 36 | }) 37 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/normalise.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type DisallowedToken, 3 | type EmojiToken, 4 | type IgnoredToken, 5 | type Label, 6 | type MappedToken, 7 | type NFCToken, 8 | type StopToken, 9 | type TextToken, 10 | type Token, 11 | type ValidToken, 12 | ens_beautify, 13 | ens_emoji, 14 | ens_normalize, 15 | ens_normalize_fragment, 16 | ens_split, 17 | ens_tokenize, 18 | is_combining_mark, 19 | should_escape, 20 | } from '@adraffy/ens-normalize' 21 | import { 22 | type ByteArray, 23 | bytesToHex, 24 | concat, 25 | hexToBytes, 26 | keccak256, 27 | stringToBytes, 28 | } from 'viem' 29 | import { decodeLabelhash, isEncodedLabelhash } from './labels.js' 30 | 31 | const zeros = new Uint8Array(32) 32 | zeros.fill(0) 33 | 34 | export const normalise = (name: string) => (name ? ens_normalize(name) : name) 35 | 36 | export function namehash(name: string) { 37 | let result: ByteArray = new Uint8Array(32).fill(0) 38 | if (!name) return bytesToHex(result) 39 | 40 | const labels = name.split('.') 41 | // Iterate in reverse order building up hash 42 | for (let i = labels.length - 1; i >= 0; i -= 1) { 43 | let labelSha: Uint8Array 44 | if (isEncodedLabelhash(labels[i])) { 45 | labelSha = hexToBytes(decodeLabelhash(labels[i])) 46 | } else { 47 | const normalised = normalise(labels[i]) 48 | labelSha = keccak256(stringToBytes(normalised), 'bytes') 49 | } 50 | result = keccak256(concat([result, labelSha]), 'bytes') 51 | } 52 | 53 | return bytesToHex(result) 54 | } 55 | 56 | export const beautify = ens_beautify 57 | export const emoji = ens_emoji 58 | export const normaliseFragment = ens_normalize_fragment 59 | export const split = ens_split 60 | export const tokenise = ens_tokenize 61 | export const isCombiningMark = is_combining_mark 62 | export const shouldEscape = should_escape 63 | 64 | export type { 65 | DisallowedToken, 66 | EmojiToken, 67 | IgnoredToken, 68 | Label, 69 | MappedToken, 70 | NFCToken, 71 | StopToken, 72 | TextToken, 73 | Token, 74 | ValidToken, 75 | } 76 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/normaliseCoinId.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Coin, 3 | getCoderByCoinName, 4 | getCoderByCoinType, 5 | } from '@ensdomains/address-encoder' 6 | import { CoinFormatterNotFoundError } from '../errors/public.js' 7 | 8 | export const normaliseCoinId = (coinId: string | number) => { 9 | const isString = typeof coinId === 'string' 10 | 11 | if (isString && Number.isNaN(Number.parseInt(coinId))) { 12 | return { 13 | type: 'name', 14 | value: coinId.toLowerCase().replace(/legacy$/, 'Legacy'), 15 | } as const 16 | } 17 | return { 18 | type: 'id', 19 | value: isString ? Number.parseInt(coinId as string) : (coinId as number), 20 | } as const 21 | } 22 | 23 | export const getCoderFromCoin = (coinId: string | number): Coin => { 24 | const normalisedCoin = normaliseCoinId(coinId) 25 | let coder: Coin 26 | try { 27 | coder = 28 | normalisedCoin.type === 'id' 29 | ? getCoderByCoinType(normalisedCoin.value) 30 | : getCoderByCoinName(normalisedCoin.value) 31 | } catch { 32 | throw new CoinFormatterNotFoundError({ coinType: coinId }) 33 | } 34 | 35 | return coder 36 | } 37 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/ownerFromContract.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest' 2 | import { deploymentAddresses, publicClient } from '../test/addTestContracts.js' 3 | import { namehash } from './normalise.js' 4 | import { ownerFromContract } from './ownerFromContract.js' 5 | 6 | const baseParams = { 7 | client: publicClient, 8 | namehash: namehash('test.eth'), 9 | } 10 | 11 | it('uses nameWrapper contract when contract is nameWrapper', () => { 12 | expect( 13 | ownerFromContract({ ...baseParams, contract: 'nameWrapper' }), 14 | ).toMatchInlineSnapshot(` 15 | { 16 | "data": "0x6352211eeb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", 17 | "to": "${deploymentAddresses.NameWrapper}", 18 | } 19 | `) 20 | }) 21 | it('uses registry contract when contract is registry', () => { 22 | expect( 23 | ownerFromContract({ ...baseParams, contract: 'registry' }), 24 | ).toMatchInlineSnapshot(` 25 | { 26 | "data": "0x02571be3eb4f647bea6caa36333c816d7b46fdcb05f9466ecacc140ea8c66faf15b3d9f1", 27 | "to": "${deploymentAddresses.ENSRegistry}", 28 | } 29 | `) 30 | }) 31 | it('uses registrar contract when contract is registrar', () => { 32 | expect( 33 | ownerFromContract({ 34 | ...baseParams, 35 | contract: 'registrar', 36 | labels: ['test'], 37 | }), 38 | ).toMatchInlineSnapshot(` 39 | { 40 | "data": "0x6352211e9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658", 41 | "to": "${deploymentAddresses.BaseRegistrarImplementation}", 42 | } 43 | `) 44 | }) 45 | it('throws when contract is not nameWrapper, registry, or registrar', () => { 46 | expect(() => 47 | ownerFromContract({ ...baseParams, contract: 'invalid' as any }), 48 | ).toThrowErrorMatchingInlineSnapshot(` 49 | [InvalidContractTypeError: Invalid contract type: invalid 50 | 51 | - Supported contract types: nameWrapper, registry, registrar 52 | 53 | Version: @ensdomains/ensjs@1.0.0-mock.0] 54 | `) 55 | }) 56 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/ownerFromContract.ts: -------------------------------------------------------------------------------- 1 | import { type Hex, encodeFunctionData, labelhash } from 'viem' 2 | import { baseRegistrarOwnerOfSnippet } from '../contracts/baseRegistrar.js' 3 | import type { ClientWithEns } from '../contracts/consts.js' 4 | import { getChainContractAddress } from '../contracts/getChainContractAddress.js' 5 | import { nameWrapperOwnerOfSnippet } from '../contracts/nameWrapper.js' 6 | import { registryOwnerSnippet } from '../contracts/registry.js' 7 | import { InvalidContractTypeError } from '../errors/general.js' 8 | 9 | export type OwnerContract = 'nameWrapper' | 'registry' | 'registrar' 10 | 11 | export type OwnerFromContractArgs = { 12 | client: ClientWithEns 13 | contract: OwnerContract 14 | namehash?: Hex 15 | labels?: string[] 16 | } & ( 17 | | { 18 | contract: Exclude 19 | namehash: Hex 20 | } 21 | | { 22 | contract: 'registrar' 23 | labels: string[] 24 | } 25 | ) 26 | 27 | export const ownerFromContract = ({ 28 | client, 29 | contract, 30 | namehash, 31 | labels, 32 | }: OwnerFromContractArgs) => { 33 | switch (contract) { 34 | case 'nameWrapper': 35 | return { 36 | to: getChainContractAddress({ client, contract: 'ensNameWrapper' }), 37 | data: encodeFunctionData({ 38 | abi: nameWrapperOwnerOfSnippet, 39 | functionName: 'ownerOf', 40 | args: [BigInt(namehash)], 41 | }), 42 | } 43 | case 'registry': 44 | return { 45 | to: getChainContractAddress({ client, contract: 'ensRegistry' }), 46 | data: encodeFunctionData({ 47 | abi: registryOwnerSnippet, 48 | functionName: 'owner', 49 | args: [namehash], 50 | }), 51 | } 52 | case 'registrar': 53 | return { 54 | to: getChainContractAddress({ 55 | client, 56 | contract: 'ensBaseRegistrarImplementation', 57 | }), 58 | data: encodeFunctionData({ 59 | abi: baseRegistrarOwnerOfSnippet, 60 | functionName: 'ownerOf', 61 | args: [BigInt(labelhash(labels[0]))], 62 | }), 63 | } 64 | default: 65 | throw new InvalidContractTypeError({ 66 | contractType: contract, 67 | supportedContractTypes: ['nameWrapper', 'registry', 'registrar'], 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/validation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NameWithEmptyLabelsError, 3 | RootNameIncludesOtherLabelsError, 4 | } from '../errors/utils.js' 5 | import { MINIMUM_DOT_ETH_CHARS } from './consts.js' 6 | import { checkLabel, isEncodedLabelhash, saveName } from './labels.js' 7 | import { type Label, normalise, split } from './normalise.js' 8 | 9 | export const validateName = (name: string) => { 10 | const nameArray = name.split('.') 11 | const normalisedArray = nameArray.map((label) => { 12 | if (label.length === 0) throw new NameWithEmptyLabelsError({ name }) 13 | if (label === '[root]') { 14 | if (name !== label) throw new RootNameIncludesOtherLabelsError({ name }) 15 | return label 16 | } 17 | return isEncodedLabelhash(label) 18 | ? checkLabel(label) || label 19 | : normalise(label) 20 | }) 21 | const normalisedName = normalisedArray.join('.') 22 | saveName(normalisedName) 23 | return normalisedName 24 | } 25 | 26 | export type ParsedInputResult = { 27 | type: 'name' | 'label' 28 | normalised: string | undefined 29 | isValid: boolean 30 | isShort: boolean 31 | is2LD: boolean 32 | isETH: boolean 33 | labelDataArray: Label[] 34 | } 35 | 36 | export const parseInput = (input: string): ParsedInputResult => { 37 | let nameReference = input 38 | let isValid = false 39 | 40 | try { 41 | nameReference = validateName(input) 42 | isValid = true 43 | } catch {} 44 | 45 | const normalisedName = isValid ? nameReference : undefined 46 | 47 | const labels = nameReference.split('.') 48 | const tld = labels[labels.length - 1] 49 | const isETH = tld === 'eth' 50 | const labelDataArray = split(nameReference) 51 | const isShort = 52 | (labelDataArray[0].output?.length || 0) < MINIMUM_DOT_ETH_CHARS 53 | 54 | if (labels.length === 1) { 55 | return { 56 | type: 'label', 57 | normalised: normalisedName, 58 | isShort, 59 | isValid, 60 | is2LD: false, 61 | isETH, 62 | labelDataArray, 63 | } 64 | } 65 | 66 | const is2LD = labels.length === 2 67 | return { 68 | type: 'name', 69 | normalised: normalisedName, 70 | isShort: isETH && is2LD ? isShort : false, 71 | isValid, 72 | is2LD, 73 | isETH, 74 | labelDataArray, 75 | } 76 | } 77 | 78 | export const checkIsDotEth = (labels: string[]) => 79 | labels.length === 2 && labels[1] === 'eth' 80 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/wrapper.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { expiryToBigInt, wrappedLabelLengthCheck } from './wrapper.js' 3 | 4 | describe('expiryToBigInt', () => { 5 | it('returns default value when expiry is undefined', () => { 6 | expect(expiryToBigInt(undefined)).toEqual(0n) 7 | }) 8 | it('allows custom default value', () => { 9 | expect(expiryToBigInt(undefined, 123n)).toEqual(123n) 10 | }) 11 | it('returns bigint expiry when expiry is bigint', () => { 12 | expect(expiryToBigInt(123n)).toEqual(123n) 13 | }) 14 | it('returns bigint expiry when expiry is string', () => { 15 | expect(expiryToBigInt('123')).toEqual(123n) 16 | }) 17 | it('returns bigint expiry when expiry is number', () => { 18 | expect(expiryToBigInt(123)).toEqual(123n) 19 | }) 20 | it('returns bigint expiry when expiry is Date', () => { 21 | expect(expiryToBigInt(new Date(123000))).toEqual(123n) 22 | }) 23 | it('throws when expiry is not bigint, string, number or Date', () => { 24 | expect(() => expiryToBigInt({} as any)).toThrowErrorMatchingInlineSnapshot( 25 | '[TypeError: Expiry must be a bigint, string, number or Date]', 26 | ) 27 | }) 28 | }) 29 | 30 | describe('wrappedLabelLengthCheck', () => { 31 | it('returns undefined when label is less than 255 bytes', () => { 32 | expect(wrappedLabelLengthCheck('a'.repeat(255))).toBeUndefined() 33 | }) 34 | it('throws when label is more than 255 bytes', () => { 35 | expect(() => 36 | wrappedLabelLengthCheck('a'.repeat(256)), 37 | ).toThrowErrorMatchingInlineSnapshot(` 38 | [WrappedLabelTooLargeError: Supplied label was too long 39 | 40 | - Supplied label: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 41 | - Max byte length: 255 42 | - Actual byte length: 256 43 | 44 | Version: @ensdomains/ensjs@1.0.0-mock.0] 45 | `) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /packages/ensjs/src/utils/wrapper.ts: -------------------------------------------------------------------------------- 1 | import { stringToBytes } from 'viem' 2 | import { WrappedLabelTooLargeError } from '../errors/utils.js' 3 | import type { AnyDate } from '../types.js' 4 | import { ParentFuses } from './fuses.js' 5 | 6 | export const MAX_EXPIRY = 2n ** 64n - 1n 7 | 8 | export const expiryToBigInt = (expiry?: AnyDate, defaultValue = 0n): bigint => { 9 | if (!expiry) return defaultValue 10 | if (typeof expiry === 'bigint') return expiry 11 | if (typeof expiry === 'string' || typeof expiry === 'number') 12 | return BigInt(expiry) 13 | if (expiry instanceof Date) return BigInt(Math.floor(expiry.getTime() / 1000)) 14 | throw new TypeError('Expiry must be a bigint, string, number or Date') 15 | } 16 | 17 | export const wrappedLabelLengthCheck = (label: string) => { 18 | const bytes = stringToBytes(label) 19 | if (bytes.byteLength > 255) 20 | throw new WrappedLabelTooLargeError({ label, byteLength: bytes.byteLength }) 21 | } 22 | 23 | export const makeDefaultExpiry = (fuses?: number): bigint => { 24 | if (fuses && BigInt(fuses) & ParentFuses.PARENT_CANNOT_CONTROL) 25 | return MAX_EXPIRY 26 | return 0n 27 | } 28 | -------------------------------------------------------------------------------- /packages/ensjs/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src"], 3 | "exclude": ["src/**/*.test.ts", "src/test/**/*"], 4 | "compilerOptions": { 5 | "sourceMap": true, 6 | "rootDir": "./src", 7 | "outDir": "./dist", 8 | "declaration": true, 9 | "module":"nodenext", 10 | "target": "esnext", 11 | "moduleResolution": "nodenext", 12 | "skipLibCheck": true, 13 | "strict": true 14 | } 15 | } -------------------------------------------------------------------------------- /packages/ensjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "nodenext", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "moduleResolution": "nodenext", 8 | "forceConsistentCasingInFileNames": true 9 | }, 10 | "include": [ 11 | "hardhat.config.cts", 12 | "./scripts", 13 | "./deploy", 14 | "./test", 15 | "typechain/**/*", 16 | "./utils" 17 | ], 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/ensjs/utils/nonceManager.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatRuntimeEnvironment } from 'hardhat/types/runtime.js' 2 | import { hexToNumber } from 'viem/utils' 3 | 4 | const makeNonceManager = async (href: HardhatRuntimeEnvironment) => { 5 | const { getNamedAccounts, network } = href 6 | const names = ['owner', 'owner2', 'owner3', 'owner4'] 7 | const allNamedAccts = await getNamedAccounts() 8 | const startingNonces = await Promise.all( 9 | names.map((name) => 10 | network.provider 11 | .send('eth_getTransactionCount', [allNamedAccts[name], 'latest']) 12 | .then(hexToNumber), 13 | ), 14 | ) 15 | const nonceMap = Object.fromEntries( 16 | names.map((name, i) => [name, startingNonces[i]]), 17 | ) 18 | 19 | console.log('Nonce manager initialized', nonceMap) 20 | 21 | return { 22 | getNonce: (name: string) => { 23 | const nonce = nonceMap[name] 24 | nonceMap[name]++ 25 | return nonce 26 | }, 27 | } 28 | } 29 | 30 | export { makeNonceManager } 31 | -------------------------------------------------------------------------------- /packages/ensjs/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | coverage: { 6 | enabled: true, 7 | include: ['src/**/*'], 8 | exclude: ['data/**/*'], 9 | }, 10 | environment: 'happy-dom', 11 | setupFiles: ['./src/test/setup.ts'], 12 | include: ['src/**/*.test.ts'], 13 | exclude: ['data/**/*'], 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | # hardhat 2 | /cache 3 | /artifacts 4 | /coverage 5 | LICENSE 6 | 7 | .env.local 8 | -------------------------------------------------------------------------------- /packages/react/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @ensdomains/ensjs-react 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies []: 8 | - @ensdomains/ensjs@4.0.2 9 | 10 | ## 0.0.3 11 | 12 | ### Patch Changes 13 | 14 | - [#209](https://github.com/ensdomains/ensjs/pull/209) [`927ab6e`](https://github.com/ensdomains/ensjs/commit/927ab6e4dc717159a2f670da3727c2ef24dac1fb) Thanks [@lucemans](https://github.com/lucemans)! - Sorted imports 15 | 16 | ## 0.0.2 17 | 18 | ### Patch Changes 19 | 20 | - [#199](https://github.com/ensdomains/ensjs/pull/199) [`1c2aa83`](https://github.com/ensdomains/ensjs/commit/1c2aa83681a1be98f920e6eac57391c138712df7) Thanks [@lucemans](https://github.com/lucemans)! - Introduce @ensdomains/ensjs-react 21 | 22 | - Updated dependencies [[`1c2aa83`](https://github.com/ensdomains/ensjs/commit/1c2aa83681a1be98f920e6eac57391c138712df7)]: 23 | - @ensdomains/ensjs@4.0.1 24 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # @ensdomains/ensjs-react 2 | 3 | React hooks & utilities to interact with the Ethereum Name Service using ENSjs 4 | 5 | ## Installation 6 | 7 | After installing [wagmi](https://wagmi.sh), simply run 8 | 9 | ```sh 10 | pnpm install @ensdomains/ensjs-react 11 | ``` 12 | 13 | ## Hooks 14 | 15 | - `useEnsAvailable` - checks availability (uses [getAvailable](/docs/public/function.getAvailable.md)) 16 | - `useEnsExpiry` - returns expiry of a name (uses [getExpiry](/docs/public/function.getExpiry.md)) 17 | - `useEnsResolverInterfaces` - check supportsInterface on resolver (uses [getSupportedInterfaces](/docs/public/function.getSupportedInterfaces.md)) 18 | - `useNamesForAddress` - lists names from subgraph (uses [getNamesForAddress](/docs/subgraph/function.getNamesForAddress.md)) 19 | - `useDecodedName` - decodes name using subgraph (uses [getDecodedName](/docs/subgraph/function.getDecodedName.md)) 20 | - `useEnsRecordsWrite` - writes records to a name (uses [setRecords](/docs/wallet/function.setRecords.md)) (wip) 21 | - `useEnsCredentials` - returns credentials from a name (uses [getTextRecord](/docs/public/function.getTextRecord.md)) (lite) 22 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ensdomains/ensjs-react", 3 | "version": "0.0.4", 4 | "description": "ENS javascript library for contract interaction", 5 | "type": "module", 6 | "types": "./dist/types/index.d.ts", 7 | "typings": "./dist/types/index.d.ts", 8 | "sideEffects": false, 9 | "exports": { 10 | "types": "./dist/index.d.ts", 11 | "default": "./dist/index.js" 12 | }, 13 | "files": [ 14 | "dist/", 15 | "src/", 16 | "!src/**/*.test.ts", 17 | "!src/test" 18 | ], 19 | "repository": "git@github.com:ensdomains/ensjs.git", 20 | "author": "Lucemans ", 21 | "license": "MIT", 22 | "scripts": { 23 | "clean": "rm -rf ./dist", 24 | "build": "tsc --project tsconfig.build.json" 25 | }, 26 | "dependencies": { 27 | "@ensdomains/ensjs": "workspace:*" 28 | }, 29 | "devDependencies": { 30 | "@ensdomains/buffer": "^0.1.1", 31 | "@ensdomains/ens-test-env": "^1.0.1", 32 | "ts-node": "^10.9.2", 33 | "tslib": "^2.7.0", 34 | "viem": "2.29.0", 35 | "wagmi": "^2" 36 | }, 37 | "peerDependencies": { 38 | "@tanstack/react-query": "^5.54", 39 | "viem": "^2.9.2", 40 | "wagmi": "^2" 41 | }, 42 | "engines": { 43 | "node": ">=22" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/react/src/client.ts: -------------------------------------------------------------------------------- 1 | import type { ClientWithEns } from '@ensdomains/ensjs/contracts' 2 | import type { QueryClient } from '@tanstack/react-query' 3 | import type { UseQueryParameters } from './hooks/useQuery.js' 4 | 5 | export type ParamWithClients = T & { 6 | client: ClientWithEns 7 | } 8 | 9 | export type QueryConfig = { 10 | queryClient?: QueryClient 11 | } & UseQueryParameters 12 | -------------------------------------------------------------------------------- /packages/react/src/hooks.ts: -------------------------------------------------------------------------------- 1 | export { useEnsResolverInterfaces } from './hooks/useEnsResolverInterfaces.js' 2 | export { useNamesForAddress } from './hooks/useNamesForAddress.js' 3 | export { useEnsRecordsWrite } from './hooks/useEnsRecordsWrite.js' 4 | export { useEnsCredentials } from './hooks/useEnsCredentials.js' 5 | export { useEnsAvailable } from './hooks/useEnsAvailable.js' 6 | export { useDecodedName } from './hooks/useDecodedName.js' 7 | export { useEnsExpiry } from './hooks/useEnsExpiry.js' 8 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useDecodedName.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GetDecodedNameParameters, 3 | type GetDecodedNameReturnType, 4 | getDecodedName, 5 | } from '@ensdomains/ensjs/subgraph' 6 | import type { ParamWithClients, QueryConfig } from '../client.js' 7 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 8 | 9 | export type UseDecodedNameParams = ParamWithClients 10 | 11 | export type UseDecodedNameReturnType = GetDecodedNameReturnType 12 | 13 | /** 14 | * Decode names returned using the subgraph 15 | * 16 | * Performs network request only if the name needs to be decoded, otherwise transparent 17 | * 18 | * @param params - {@link UseDecodedNameParams} 19 | * @returns - {@link GetDecodedNameReturnType} 20 | */ 21 | export const useDecodedName = ( 22 | params: UseDecodedNameParams, 23 | query?: QueryConfig, 24 | ): UseQueryReturnType => { 25 | const { client } = params 26 | 27 | return useQuery( 28 | ['ensjs', 'decoded-subgraph-name', params.name], 29 | { 30 | queryFn: async () => { 31 | const result = await getDecodedName(client, params) 32 | 33 | return result 34 | }, 35 | }, 36 | query, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useEnsAvailable.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GetAvailableParameters, 3 | type GetAvailableReturnType, 4 | getAvailable, 5 | } from '@ensdomains/ensjs/public' 6 | import type { ParamWithClients, QueryConfig } from '../client.js' 7 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 8 | 9 | export type UseEnsAvailableParams = ParamWithClients 10 | 11 | export type UseEnsAvailableReturnType = GetAvailableReturnType 12 | 13 | /** 14 | * Returns a list of names for an address 15 | * 16 | * Keep in mind that this function is limited to .eth names 17 | * 18 | * @param params - {@link UseEnsAvailableParams} 19 | * @returns - {@link UseEnsAvailableReturnType} 20 | */ 21 | export const useEnsAvailable = ( 22 | params: UseEnsAvailableParams, 23 | query?: QueryConfig, 24 | ): UseQueryReturnType => { 25 | const { client } = params 26 | 27 | return useQuery( 28 | ['ensjs', 'eth-name-available', params.name], 29 | { 30 | queryKey: [], 31 | queryFn: async () => { 32 | const result = await getAvailable(client, params) 33 | 34 | return result 35 | }, 36 | }, 37 | query, 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useEnsCredentials.ts: -------------------------------------------------------------------------------- 1 | import { getTextRecord } from '@ensdomains/ensjs/public' 2 | import type { ParamWithClients, QueryConfig } from '../client.js' 3 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 4 | 5 | export type UseEnsCredentialsParams = ParamWithClients<{ name: string }> 6 | 7 | export type ExternalCredential = { 8 | url: string 9 | } 10 | 11 | export type UseEnsCredentialsReturnType = ExternalCredential[] 12 | 13 | /** 14 | * Returns credentials from a name 15 | * 16 | * @param params - {@link UseEnsCredentialsParams} 17 | * @returns - {@link UseEnsCredentialsReturnType} 18 | * 19 | * @beta 20 | */ 21 | export const useEnsCredentials = ( 22 | params: UseEnsCredentialsParams, 23 | query?: QueryConfig, 24 | ): UseQueryReturnType => { 25 | const { name, client } = params 26 | 27 | return useQuery( 28 | ['ensjs', 'credentials', params.name], 29 | { 30 | queryFn: async () => { 31 | const result = await getTextRecord(client, { 32 | name, 33 | key: 'verifications', 34 | }) 35 | 36 | if (!result) return [] 37 | 38 | const credentials = (JSON.parse(result) as string[]) 39 | .filter((url) => new URL(url)) 40 | .map((url) => ({ 41 | url, 42 | })) 43 | 44 | return credentials 45 | }, 46 | }, 47 | query, 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useEnsExpiry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GetExpiryParameters, 3 | type GetExpiryReturnType, 4 | getExpiry, 5 | } from '@ensdomains/ensjs/public' 6 | import type { ParamWithClients, QueryConfig } from '../client.js' 7 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 8 | 9 | export type UseEnsExpiryParams = ParamWithClients 10 | 11 | export type UseEnsExpiryReturnType = GetExpiryReturnType 12 | 13 | /** 14 | * Returns expiry of a name 15 | * 16 | * Keep in mind that this function is limited to second-level .eth names (luc.eth, nick.eth, etc) 17 | * 18 | * @param params - {@link UseEnsExpiryParams} 19 | * @returns - {@link UseEnsExpiryReturnType} 20 | */ 21 | export const useEnsExpiry = ( 22 | params: UseEnsExpiryParams, 23 | query?: QueryConfig, 24 | ): UseQueryReturnType => { 25 | const { client } = params 26 | 27 | return useQuery( 28 | ['ensjs', 'ens-expiry', params.name], 29 | { 30 | queryFn: async () => getExpiry(client, params), 31 | }, 32 | query, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useEnsRecordsWrite.ts: -------------------------------------------------------------------------------- 1 | import { useAccount } from 'wagmi' 2 | import type { ParamWithClients } from '../client.js' 3 | 4 | export type UseEnsRecordsWriteParams = ParamWithClients<{}> 5 | 6 | /** 7 | * Allows you to write records to a name, provided the name supports it and the signed in user has the required permissions 8 | * 9 | * You can use the {@link resolverInterfaces} shorthand, or manually specify a Hex value 10 | * 11 | * @param params - {@link UseEnsResolverInterfacesParams} 12 | * @returns - {@link boolean[]} 13 | * 14 | * @alpha 15 | */ 16 | export const useEnsRecordsWrite = ( 17 | _params: UseEnsRecordsWriteParams, 18 | config?: any, 19 | ) => { 20 | const { address } = useAccount({ config }) 21 | // const client = useWalletClient() 22 | 23 | console.log('Hello ', address) 24 | 25 | return { data: undefined } 26 | // return setRecords(client as any, params) 27 | } 28 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useEnsResolverInterfaces.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GetSupportedInterfacesParameters, 3 | type GetSupportedInterfacesReturnType, 4 | getSupportedInterfaces, 5 | } from '@ensdomains/ensjs/public' 6 | import type { Hex } from 'viem' 7 | import type { ParamWithClients, QueryConfig } from '../client.js' 8 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 9 | 10 | export type UseEnsResolverInterfacesParams< 11 | Interfaces extends readonly Hex[] = [Hex, Hex], 12 | > = ParamWithClients> 13 | 14 | export type UseEnsResolverInterfacesReturnType< 15 | Interfaces extends readonly Hex[], 16 | > = GetSupportedInterfacesReturnType 17 | 18 | /** 19 | * Returns a wether or not the interfaces are supported by the resolver 20 | * You can find a list of interfaces at https://docs.ens.domains/resolvers/interfaces 21 | * 22 | * You can use the {@link resolverInterfaces} shorthand, or manually specify a Hex value 23 | * 24 | * @param params - {@link UseEnsResolverInterfacesParams} 25 | * @returns - {@link boolean[]} 26 | */ 27 | export const useEnsResolverInterfaces = ( 28 | params: UseEnsResolverInterfacesParams, 29 | query?: QueryConfig, 30 | ): UseQueryReturnType> => { 31 | const { client } = params 32 | 33 | return useQuery( 34 | ['ensjs', 'resolver-interfaces', params.address], 35 | { 36 | queryFn: async () => { 37 | const result = await getSupportedInterfaces(client, params) 38 | 39 | return result 40 | }, 41 | }, 42 | query, 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useNamesForAddress.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GetNamesForAddressReturnType, 3 | getNamesForAddress, 4 | } from '@ensdomains/ensjs/subgraph' 5 | import type { Address } from 'viem' 6 | import type { ParamWithClients, QueryConfig } from '../client.js' 7 | import { type UseQueryReturnType, useQuery } from './useQuery.js' 8 | 9 | export type UseNamesForAddressParams = ParamWithClients<{ 10 | address: Address 11 | }> 12 | 13 | export type UseNamesForAddressReturnType = GetNamesForAddressReturnType 14 | 15 | /** 16 | * Returns a list of names for an address 17 | * 18 | * Keep in mind that this list will be loaded from the subgraph, and only include watchable names. 19 | * Read more about enumeration and watchability here: https://docs.ens.domains/web/enumerate 20 | * 21 | * @param params - {@link UseNamesForAddressParams} 22 | * @returns - {@link UseNamesForAddressReturnType} 23 | */ 24 | export const useNamesForAddress = ( 25 | params: UseNamesForAddressParams, 26 | queryConfig?: QueryConfig, 27 | ): UseQueryReturnType => { 28 | const { address, client } = params 29 | 30 | return useQuery( 31 | ['ensjs', 'names-for-address', address], 32 | { 33 | queryFn: async () => { 34 | const result = await getNamesForAddress(client, { 35 | address, 36 | }) 37 | 38 | return result 39 | }, 40 | enabled: !!params.address, 41 | initialData: [], 42 | }, 43 | queryConfig, 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useQuery.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type DefaultError, 3 | type DefinedUseQueryResult, 4 | type QueryKey, 5 | type UseQueryOptions, 6 | useQuery as useTanstackQuery, 7 | } from '@tanstack/react-query' 8 | import type { ExactPartial } from 'viem' 9 | import type { QueryConfig } from '../client.js' 10 | import { fallbackQueryClient } from '../query.js' 11 | import type { Compute } from '../utils/types.js' 12 | 13 | export type UseQueryParameters< 14 | QueryFnData = unknown, 15 | Error = DefaultError, 16 | Data = QueryFnData, 17 | TheQueryKey extends QueryKey = QueryKey, 18 | > = Compute< 19 | ExactPartial< 20 | Omit, 'initialData'> 21 | > & { 22 | // Fix `initialData` type 23 | initialData?: 24 | | UseQueryOptions['initialData'] 25 | | undefined 26 | } 27 | > 28 | 29 | export type UseQueryReturnType< 30 | Data = unknown, 31 | Error = DefaultError, 32 | > = DefinedUseQueryResult 33 | 34 | export const useQuery = < 35 | Parameters extends UseQueryParameters, 36 | Data = unknown, 37 | Error = unknown, 38 | >( 39 | key: QueryKey, 40 | queryParameters: Exclude, 41 | queryConfig?: QueryConfig, 42 | ): UseQueryReturnType => { 43 | const parameters = { 44 | ...queryParameters, 45 | ...queryConfig, 46 | queryKey: key, 47 | } 48 | 49 | return useTanstackQuery( 50 | { ...parameters } as any, 51 | queryConfig?.queryClient ?? fallbackQueryClient, 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hooks.js' 2 | export { resolverInterfaces } from './interfaces.js' 3 | export type { ParamWithClients } from './client.js' 4 | -------------------------------------------------------------------------------- /packages/react/src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export const resolverInterfaces = { 2 | /** 3 | * Addr addr(bytes32 node, uint256 coinType) 4 | * Legacy please use addrMulticoin 5 | */ 6 | addr: '0x3b3b57de', 7 | /** 8 | * Addr Multicoin addr(bytes32 node, uint256 coinType) 9 | */ 10 | addrMulticoin: '0xf1cb7e06', 11 | /** 12 | * Content Hash contenthash(bytes32 node) 13 | */ 14 | contentHash: '0xbc1c58d1', 15 | /** 16 | * Text text(bytes32 node, string key) 17 | */ 18 | text: '0x59d1d43c', 19 | /** 20 | * ABI abi(bytes32 node, uint256 contentType) 21 | */ 22 | abi: '0x2203ab56', 23 | /** 24 | * Public Key pubkey(bytes32 node) 25 | */ 26 | pubkey: '0xc8690233', 27 | /** 28 | * Reverse Name name(bytes32 node) 29 | */ 30 | name: '0x691f3431', 31 | /** 32 | * Wildcard 33 | */ 34 | wildcard: '0x9061b923', 35 | } as const 36 | -------------------------------------------------------------------------------- /packages/react/src/query.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query' 2 | 3 | // TODO: figure out why not taking from provider 4 | export const fallbackQueryClient = new QueryClient() 5 | -------------------------------------------------------------------------------- /packages/react/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type Compute = { [key in keyof Type]: Type[key] } & unknown 2 | -------------------------------------------------------------------------------- /packages/react/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["./src"], 4 | "exclude": ["src/**/*.test.ts", "src/test/**/*"], 5 | "compilerOptions": { 6 | "sourceMap": true, 7 | "rootDir": "./src", 8 | "outDir": "./dist", 9 | "declaration": true, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /patches/hardhat-deploy.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dist/src/DeploymentsManager.js b/dist/src/DeploymentsManager.js 2 | index 9ff5b84084cc6a6ce05e6d41efd2e95e868a0082..4cf3d260350d91dc93289e213163f806ffad165e 100644 3 | --- a/dist/src/DeploymentsManager.js 4 | +++ b/dist/src/DeploymentsManager.js 5 | @@ -733,7 +733,7 @@ class DeploymentsManager { 6 | // console.log("fetching " + scriptFilePath); 7 | try { 8 | delete require.cache[scriptFilePath]; // ensure we reload it every time, so changes are taken in consideration 9 | - deployFunc = require(scriptFilePath); 10 | + deployFunc = await import(scriptFilePath); 11 | if (deployFunc.default) { 12 | deployFunc = deployFunc.default; 13 | } 14 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | - examples/* 4 | patchedDependencies: 5 | hardhat-deploy: patches/hardhat-deploy.patch 6 | --------------------------------------------------------------------------------