ENSjs

repository·main·Indexed 41 days ago

https://github.com/ensdomains/ensjs

A TypeScript library for interacting with the Ethereum Name Service (ENS) v1 and v2. Built on top of viem, it provides tree-shakeable, composable actions for reading and writing ENS records, batching queries via EnsPublicClient, and importing DNS names. It includes utilities for extending viem clients with public, wallet, and subgraph actions, as well as a standalone @ensdomains/ensjs-abi package for raw ABI snippets.

Tokens
62K
Snippets
164
Records
213
Agent score
99%

What's inside ENSjs

  1. Understand the ENSjs Action pattern

    main

    ENSjs uses an 'Action pattern' where every action is a plain function. The first argument is always the viem Client, and the second argument is an options object containing the necessary parameters.

    Read Actions: Use getAction and readContract or multicall, meaning they automatically respect the batching configuration of your client. Write Actions: Return a transaction hash and accept standard viem write parameters such as account, chain, and gas.

    const result = await actionName(client, {
      /* params */
    })
  2. Extend the Viem Client with ENS Actions

    main

    Instead of using a full ENS client, you can customize a standard Viem client by extending it with specific ENS action functions. This allows you to add only the ENS-related methods you need to your existing Viem client instance using the .extend() method.

    Available action functions to extend the client include:

    • ensPublicActions: For read-only ENS operations.
    • ensWalletActions: For ENS operations requiring a signer/wallet.
    • ensSubgraphActions: For querying ENS data via subgraphs.
    import { http, createClient } from 'viem'
    import { mainnet } from 'viem/chains'
    import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs'
    
    const client = createClient({
      chain: addEnsContracts(mainnet),
      transport: http(),
    }).extend(ensSubgraphActions)
    
    const subgraphRecords = await client.getSubgraphRecords({ name: 'ens.eth' })
  3. Use ENSjs methods with a Viem Client

    main

    If you want to use ENSjs methods while maintaining control over your viem imports or if you are only using specific parts of ENSjs, you can integrate ENSjs with a standard viem Client.

    To do this, wrap your viem Chain object with the addEnsContracts() function provided by @ensdomains/ensjs. This utility injects the necessary ENS contract addresses into the chain object, allowing ENSjs functions to resolve the correct addresses when interacting with the client.

    import { http, createClient } from 'viem'
    import { mainnet } from 'viem/chains'
    import { addEnsContracts } from '@ensdomains/ensjs'
    import { getAddressRecord } from '@ensdomains/ensjs/public'
    
    const client = createClient({
      chain: addEnsContracts(mainnet),
      transport: http(),
    })
    
    const ethAddress = getAddressRecord(client, { name: 'ens.eth' })
  4. Get started with ENSjs and viem clients

    main

    To interact with ENS, you must first extend a viem chain using addEnsL1Contracts. This adds ENS contract addresses and subgraph URLs to the chain object, allowing you to use ENS actions with standard viem clients.

    Supported chains include mainnet and sepolia.

    import { http, createPublicClient } from 'viem'
    import { mainnet } from 'viem/chains'
    import { addEnsL1Contracts } from '@ensdomains/ensjs'
    import { getAddressRecord, getRecords } from '@ensdomains/ensjs/public'
    
    const client = createPublicClient({
      chain: addEnsL1Contracts(mainnet),
      transport: http(),
    })
    
    const eth = await getAddressRecord(client, { name: 'ens.eth' })
    const records = await getRecords(client, {
      name: 'ens.eth',
      texts: ['com.twitter', 'avatar'],
      contentHash: true,
    })
  5. Extend a Viem Public Client with ENS Subgraph Actions

    main

    Instead of using a standalone client, you can extend an existing Viem publicClient with ENS subgraph capabilities. This is done by using addEnsContracts to ensure the chain has the necessary ENS contracts and then calling .extend(ensSubgraphActions). This approach integrates subgraph queries directly into your existing Viem client workflow.

    import { createPublicClient, http } from 'viem'
    import { mainnet } from 'viem/chains'
    import { addEnsContracts, ensSubgraphActions } from '@ensdomains/ensjs'
    
    const client = createPublicClient({
      chain: addEnsContracts(mainnet),
      transport: http(),
    }).extend(ensSubgraphActions)
  6. Fetch an ENS profile using subgraph and fallback records

    main

    You can fetch all associated records for an ENS name by combining subgraph data with manual fallback records. Use getSubgraphRecords() to retrieve indexed data from the subgraph, and pass that data into getRecords() along with any additional records (like wildcard/CCIP names or unindexed fields) that you want to ensure are included in the final profile.

    import { http } from 'viem'
    import { mainnet } from 'viem/chains'
    import { createEnsPublicClient } from '@ensdomains/ensjs'
    
    const client = createEnsPublicClient({
      chain: mainnet,
      transport: http(),
    })
    
    // 1. Fetch indexed data from the subgraph
    const subgraphRecords = client.getSubgraphRecords({ name: 'ens.eth' })
    
    // 2. Combine subgraph data with fallback/additional records
    const records = client.getRecords({
      name: 'ens.eth',
      records: {
        // Spread existing subgraph coins and add fallbacks
        coins: [...(subgraphRecords?.coins || []), 'BTC', 'ETH', 'ETC', 'SOL'],
        // Spread existing subgraph texts and add fallbacks
        texts: [
          ...(subgraphRecords?.texts || []),
          'avatar',
          'email',
          'description',
        ],
        contentHash: true,
        abi: true,
      },
    })
  7. Use custom subgraph URIs for ENS queries

    main

    By default, ensjs uses standard ENS subgraph endpoints. If you need to point to a custom endpoint (for example, a locally hosted graph-node), you can override the subgraph configuration by modifying the object returned by addEnsContracts().

    Note: Custom URIs can only be used if you are manually constructing your chain object; they will not be applied if you use the default exported ENS clients directly.

    import { http, createClient } from "viem";
    import { mainnet } from "viem/chains";
    import { addEnsContracts } from "@ensdomains/ensjs";
    import { getSubgraphRecords } from "@ensdomains/ensjs/subgraph";
    
    // 1. Augment the chain with ENS contract addresses
    const mainnetWithEns = addEnsContracts(mainnet);
    
    // 2. Override the subgraph URL in the chain object
    const chain = {
      ...mainnetWithEns,
      subgraphs: {
        ens: {
          url: "http://localhost:42069/subgraph",
        },
      },
    };
    
    // 3. Initialize the client with the custom chain
    const client = createClient({
      chain,
      transport: http(),
    });
    
    // 4. Query the custom subgraph
    const subgraphRecords = await getSubgraphRecords(client, { name: "ens.eth" });
  8. Batch ENS calls using a Viem Client

    main

    If you are using a standard viem client (configured with addEnsContracts), you can use the standalone batch function imported from @ensdomains/ensjs/public. This function takes the client as its first argument, followed by the batched ENS queries. To prepare queries, use the .batch() method on the ENS public method.

    import { http, createClient } from 'viem'
    import { mainnet } from 'viem/chains'
    import { addEnsContracts } from '@ensdomains/ensjs'
    import {
      batch,
      getAddressRecord,
      getTextRecord,
    } from '@ensdomains/ensjs/public'
    
    const client = createClient({
      chain: addEnsContracts(mainnet),
      transport: http(),
    })
    
    const [ethAddress, twitterUsername] = batch(
      client,
      getAddressRecord.batch({ name: 'ens.eth' }),
      getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }),
    )
  9. Batch ENS calls using EnsPublicClient

    main

    When using an EnsPublicClient created via createEnsPublicClient, you can perform batched ENS queries using the .ensBatch() method. This method is specifically designed to avoid collisions with viem's native batching logic. To prepare queries for batching, call the .batch() method on the specific ENS public method (e.g., getAddressRecord.batch(...)).

    import { http } from 'viem'
    import { mainnet } from 'viem/chains'
    import { createEnsPublicClient } from '@ensdomains/ensjs'
    import { getAddressRecord, getTextRecord } from '@ensdomains/ensjs/public'
    
    const client = createEnsPublicClient({
      chain: mainnet,
      transport: http(),
    })
    
    const [ethAddress, twitterUsername] = client.ensBatch(
      getAddressRecord.batch({ name: 'ens.eth' }),
      getTextRecord.batch({ name: 'ens.eth', key: 'com.twitter' }),
    )
  10. ENS Name Types and Specifiers

    main

    ENSjs provides a set of types to represent and categorize different ENS name structures. These types allow for type-safe handling of ENS names based on their format (e.g., .eth names vs other TLDs) and their depth (TLD, 2LD, subname).

    Name Formats

    • RootName: The empty string ''.
    • EthTldName: The literal 'eth'.
    • Eth2ldName: Names ending in .eth (e.g., name.eth).
    • EthSubname: Nested .eth names (e.g., sub.name.eth).
    • TldName: Generic Top Level Domain names.
    • Other2ldName: Generic two-level names (e.g., name.com).
    • OtherSubname: Generic subnames (e.g., sub.name.com).

    Name Specifiers

    Specifiers are used to identify the category of a name. You can determine the specifier for a given name using the GetNameType<name> utility type. Common specifiers include:

    • root
    • eth-tld
    • eth-2ld
    • eth-subname
    • tld
    • other-2ld
    • other-subname
  11. Import ABI snippets from @ensdomains/ensjs-abi

    main

    If you need raw ABI snippets for ENS contracts (v1 or v2), you can use the standalone @ensdomains/ensjs-abi package.

    import { permissionedRegistryGetStateSnippet } from '@ensdomains/ensjs-abi/v2/permissionedRegistry'
    import { ethRegistrarControllerRegisterSnippet } from '@ensdomains/ensjs-abi/v1/ethRegistrarController