IdentityWallet API

Wallet API supports registering a Basename using the arbitrary smart contract invocation feature. Basenames are the Base Layer 2’s human-readable address naming system that makes sending transactions simpler and helps you curate your onchain identity.

This solutions guide explains how you can register a Basename for your API Wallet, allowing anyone to easily send assets to your wallet and engage with your onchain identity.

See the GitHub repo for more information about cloning this quick start.

See the Replit template for more information about running the quick start on Base Sepolia testnet.

Replit for easy deployments

Replit is an AI-powered software development & deployment platform for building, sharing, and shipping software fast. Coinbase has partnered with Replit to create a template that enables developers to register their AI agent onchain in just minutes.

Get started with our Basename registration template. If you plan to deploy this template publicly, read Securing a Wallet to learn how to protect your wallets.

Prerequisites

  • Install the CDP SDK.
  • Have a persisted funded API Wallet on the Base network (minimum of 0.005 Base mainnet ETH). See creating a wallet to quickly spin up a 1-of-1 Developer-Managed wallet, and refer to persisting a wallet for more information on how to save it.

Step-by-Step guide

Step 1. Import Required Modules

register-basename.js
import { Coinbase, Wallet } from "@coinbase/coinbase-sdk";
import { encodeFunctionData, namehash } from "viem";
import { normalize } from "viem/ens";
import os from "os";

Step 2. Define Contract ABIs and Addresses

Set up the ABIs for the L2 Resolver and Registrar contracts, and define the BaseNamesRegistrarControllerAddress.

The Registrar contract is responsible for registering Basenames. By calling the contract alongside an ETH payment, you buy the name and associate it with your address on the Resolver.

The Resolver is what allows wallets to resolve a certain Basename to its proper address.

register-basename.js
// Base Mainnet Registrar Controller Contract Address.
const BaseNamesRegistrarControllerAddress = "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5";

// Base Mainnet L2 Resolver Contract Address.
const L2ResolverAddress = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD";

// The regular expression to validate a Basename on Base Mainnet.
const baseNameRegex = /\.base\.eth$/;

// Relevant ABI for L2 Resolver Contract.
const l2ResolverABI = [
  {
    inputs: [
      { internalType: "bytes32", name: "node", type: "bytes32" },
      { internalType: "address", name: "a", type: "address" },
    ],
    name: "setAddr",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "bytes32", name: "node", type: "bytes32" },
      { internalType: "string", name: "newName", type: "string" },
    ],
    name: "setName",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
];

// Relevant ABI for Basenames Registrar Controller Contract.
const registrarABI = [
  {
    inputs: [
      {
        components: [
          {
            internalType: "string",
            name: "name",
            type: "string",
          },
          {
            internalType: "address",
            name: "owner",
            type: "address",
          },
          {
            internalType: "uint256",
            name: "duration",
            type: "uint256",
          },
          {
            internalType: "address",
            name: "resolver",
            type: "address",
          },
          {
            internalType: "bytes[]",
            name: "data",
            type: "bytes[]",
          },
          {
            internalType: "bool",
            name: "reverseRecord",
            type: "bool",
          },
        ],
        internalType: "struct RegistrarController.RegisterRequest",
        name: "request",
        type: "tuple",
      },
    ],
    name: "register",
    outputs: [],
    stateMutability: "payable",
    type: "function",
  },
];

Step 3. Create Register Contract Method Arguments

Here, we create the arguments for the register contract method using the Resolver and Registrar ABIs.

register-basename.js
// Create register contract method arguments.
function createRegisterContractMethodArgs(baseName, addressId) {
  const addressData = encodeFunctionData({
    abi: l2ResolverABI,
    functionName: "setAddr",
    args: [namehash(normalize(baseName)), addressId],
  });
  const nameData = encodeFunctionData({
    abi: l2ResolverABI,
    functionName: "setName",
    args: [namehash(normalize(baseName)), baseName],
  });

  const registerArgs = {
    request: [
      baseName.replace(baseNameRegex, ""),
      addressId,
      "31557600",
      L2ResolverAddress,
      [addressData, nameData],
      true,
    ],
  };
  console.log(`Register contract method arguments constructed: `, registerArgs);

  return registerArgs;
}

Step 4. Implement Basename Registration Function

This function will handle the registration of the Basename.

register-basename.js
async function registerBaseName(wallet, registerArgs) {
  try {
    const contractInvocation = await wallet.invokeContract({
      contractAddress: BaseNamesRegistrarControllerAddress,
      method: "register",
      abi: registrarABI,
      args: registerArgs,
      amount: 0.002,
      assetId: Coinbase.assets.Eth,
    });

    await contractInvocation.wait();

    console.log(`Successfully registered Basename ${registerArgs[0]} for wallet: `, wallet);
  } catch (error) {
    console.error(`Error registering a Basename for ${wallet}: `, error);
  }
}

Step 5. Main Execution Flow

Here, we’ll be using the information returned when persisting the wallet to import the wallet and register a Basename. Save these values as environment variables by running the following commands:

export BASE_NAME="your-basename.base.eth"
export WALLET_ID="your-wallet-id"
export SEED_FILE_PATH="/path-to-your-seed-file"
register-basename.js
async () => {
  try {
    const { BASE_NAME, WALLET_ID, SEED_FILE_PATH } = process.env;

    // Manage CDP Secret API Key for Coinbase SDK.
    // Configure location to CDP Secret API Key.
    Coinbase.configureFromJson({
      filePath: `${os.homedir()}/Downloads/cdp_api_key.json`,
    });

    // Fetch funded Wallet.
    const wallet = await fetchWalletAndLoadSeed(WALLET_ID, SEED_FILE_PATH);
    const defaultAddress = await wallet.getDefaultAddress();

    // Register Basename.
    const registerArgs = createRegisterContractMethodArgs(BASE_NAME, defaultAddress.getId());
    await registerBaseName(wallet, registerArgs);
  } catch (error) {
    console.error(`Error in registering a Basename for my wallet: `, error);
  }
};

Now that you have registered a Basename for your wallet, reach out to us in the #wallet-api channel of the CDP Discord if you have any questions or would like to see more solutions guides!