Hawk is identity for the agent economy on Base — ENS-standard
.hawk names for operators, agents, and wallets. It implements
the exact ENS interfaces (ERC-137 registry, standard resolver profiles,
ENSIP namehash, reverse records, a UniversalResolver), so
the ENS tooling you already use works unchanged.
An agent that claims to be yours either resolves under your name — or it doesn’t: bot1.acme.hawk cryptographically hangs off acme.hawk.
acme.hawk → wrap it (one click in the
app, or HawkWrapper.wrapETH2LD).
Wrapping turns your name into a parent that can issue subname tokens.
HawkWrapper.setSubnodeOwner(
namehash("acme.hawk"),
"bot1",
agentAccount, // the agent's smart account
0, // fuses — or PARENT_CANNOT_CONTROL to emancipate
0
)
Then point it at the agent:
PublicResolver.setAddr(node, agentAccount).
| key | value |
|---|---|
url | the agent's API / A2A endpoint |
description | what this agent does |
agent.capabilities | comma-separated verbs: swap,lend,rebalance |
agent.model | model / runtime identifier |
agent.operator | acme.hawk — walk up to the human |
avatar | image URI |
ReverseRegistrar.setName("bot1.acme.hawk") — every explorer
and dapp that renders reverse resolution now shows the agent's name.
import { createPublicClient, http } from "viem";
import { base, namehash } from "hawk-names";
const client = createPublicClient({ chain: base, transport: http() });
async function verifyAgent(claimedName, senderAddress) {
const resolved = await client.getEnsAddress({ name: claimedName });
if (resolved?.toLowerCase() !== senderAddress.toLowerCase()) return null;
const operator = claimedName.split(".").slice(1).join(".");
const endpoint = await client.getEnsText({ name: claimedName, key: "url" });
const capabilities = await client.getEnsText({ name: claimedName, key: "agent.capabilities" });
return { operator, endpoint, capabilities };
}
import { createPublicClient, http } from "viem";
import { base } from "hawk-names"; // ← Base with Hawk wired in as its ENS
const client = createPublicClient({ chain: base, transport: http() });
// reverse: address → name (do this in your UI)
await client.getEnsName({ address: "0x882220CF716aEF2421b6ab283E63427D81497d8c" });
// → "genesis.hawk"
// forward: name → address
await client.getEnsAddress({ name: "genesis.hawk" });
// capability records
await client.getEnsText({ name: "genesis.hawk", key: "url" });
Install: npm i hawk-names viem — source at
packages/sdk,
package at npmjs.com/package/hawk-names.
With wagmi, pass the chain into your config and use the stock hooks.
import { defineChain } from "viem";
const baseWithHawk = defineChain({
id: 8453,
name: "Base",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://mainnet.base.org"] } },
contracts: {
ensRegistry: { address: "0x5DAF4DF48b022Bf0Fb454DBe9CB2592d3A32b0b2" },
ensUniversalResolver: { address: "0x1Ebbe68AfA011ADBE561De6215B1AA1Fe0e4bc6C" },
multicall3: { address: "0xcA11bde05977b3631167028862bE2a173976CA11" },
},
});
ethers v6:
new JsonRpcProvider(rpc, { chainId: 8453, name: "base", ensAddress: "0x5DAF…b0b2" }).
Payment: register and renew in ETH or USDC (Circle's
canonical USDC on Base).
normalize from hawk-names or viem/ens).Names, owners, expiries, subnames, and auctions over GraphQL — CORS open, call it straight from your dapp or agent:
api.dothawk.xyz/graphql
curl -s https://api.dothawk.xyz/graphql \
-H "content-type: application/json" \
-d '{"query":"{ names(orderBy: \"expiresAt\", orderDirection: \"desc\", limit: 20) { items { label owner expiresAt wrapped } totalCount } }"}'
Base mainnet, chainId 8453 — live, sources on
Blockscout. Owned by a 2-of-3 Safe:
0xd0eC82124401A30d8337FEF77899e883bb12Df0b.
| contract | address |
|---|---|
| HawkRegistry | 0x5DAF4DF48b022Bf0Fb454DBe9CB2592d3A32b0b2 |
| UniversalResolver | 0x1Ebbe68AfA011ADBE561De6215B1AA1Fe0e4bc6C |
| PublicResolver | 0xC7cAB8Af20fF52346F4e93c143Cc8e9d6384e26b |
| ReverseRegistrar | 0x4221F2953294ec98Ddf54964DB293E281db71daB |
| DefaultReverseRegistrar | 0x34acC481E26Ee9566d868F455BecD05353Bae257 |
| HawkRegistrarController | 0x65Acb254B2EF5af1FDFf7B8C77427Ca051Ff4F71 |
| HawkBaseRegistrar | 0x83dcABD50E531325C76b9CB07F4C04Aca187722E |
| HawkWrapper | 0x0B922e8B56c778667AbDbBBa522880F98362c6C8 |
| HawkPriceOracle | 0x697b2C75652b6895D507b9C8E2Cb3b3C8EfccA29 |
| HawkReservedList | 0x351c51AA3079e7f7EbeFb5079A92279b22a0AB6c |
| HawkMetadata | 0xA972f32580C2DD8eEf379e27B4B91b205BF3437F |
| GatewayProvider | 0x4Bebe0C9073167d9772E89f272100036EC97F2ff |
Addresses come from the deploy script's own record
(contracts/deployments/hawk-base.json),
so published addresses can never drift from what's on chain. USDC:
0x8335…2913 (Circle, canonical on Base).