@ans-abstract-name-service/sdk
v0.2.6
Published
Official SDK for ANS v2 on Abstract
Readme
@ans-abstract-name-service/sdk
Official TypeScript SDK for ANS v2 on Abstract.
What This SDK Covers
- Contract reads: resolve names, records, reverse lookup, owner domains, pricing, subdomain root config.
- Contract writes: registration (price-update flows only), coupon registration, record updates, text records, transfer, migration.
- Event watchers:
DomainRegistered,DomainTransferred,RecordSet,TextRecordSet. - Pyth helpers: fetch matched latest price + update payloads from Hermes.
- Revert decoding: convert ANS custom errors into readable app messages.
- Prepare-only helper: return calldata, args, and payable value before signing.
- Coupon helpers: validate coupon status and build coupon-aware registration quotes.
Supported Registration Methods
ANS pricing is oracle-driven and requires a price update payload.
registerWithPriceUpdate(name, priceUpdateData)registerWithCouponAndPriceUpdate(name, couponCode, priceUpdateData)
Install
npm install @ans-abstract-name-service/sdk viempnpm add @ans-abstract-name-service/sdk viemyarn add @ans-abstract-name-service/sdk viemRequirements
- Node.js 18+
viem2.38+- Abstract mainnet RPC
- Wallet provider for writes (for example
window.ethereum)
Backend Private-Key Note
- For backend/private-key signing, pass a viem
Accountobject (for exampleprivateKeyToAccount(...)) to write calls, not only an address string. - This forces raw signed transaction flow (
eth_sendRawTransaction) and avoids 403 RPC errors like"rpc method is not whitelisted".
Pyth Oracle Configuration
ANS V2 registration pricing uses Pyth ETH/USD. The SDK ships with the same defaults used in ANS deployment:
- Hermes URL:
https://hermes.pyth.network(DEFAULT_PYTH_HERMES_URL) - ETH/USD Price ID:
0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace(DEFAULT_PYTH_ETH_USD_PRICE_ID)
Example before register:
import {
fetchPythLatestUpdate,
DEFAULT_PYTH_ETH_USD_PRICE_ID,
} from "@ans-abstract-name-service/sdk";
const latestUpdate = await fetchPythLatestUpdate(DEFAULT_PYTH_ETH_USD_PRICE_ID);
const priceUpdateData = latestUpdate.updateData;
const quote = await ans.quoteRegisterValue({
name: "newname.abs",
account,
priceUpdateData,
});If you override priceId, make sure it matches the on-chain oracle feed configured for ANS V2.
Quick Start (Vite)
import { createAnsClient, fetchPythUpdateData } from "@ans-abstract-name-service/sdk";
import {
createPublicClient,
createWalletClient,
custom,
defineChain,
http,
} from "viem";
const abstract = defineChain({
id: 2741,
name: "Abstract",
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://api.mainnet.abs.xyz"] } },
});
const publicClient = createPublicClient({ chain: abstract, transport: http() });
const walletClient = createWalletClient({ chain: abstract, transport: custom(window.ethereum) });
const ans = createAnsClient({ publicClient, walletClient });Read Examples
const owner = await ans.resolve("luca.abs");
const record = await ans.getRecord("luca.abs");
const avatar = await ans.getTextRecord("luca.abs", "avatar");
const reverse = await ans.getNameByAddress("0x1234...abcd");
const domains = await ans.getDomainsByOwner("0x1234...abcd");
const allNames = await ans.getAllNames();
const roots = await ans.getSubdomainRoots();
const available = await ans.isNameAvailable("fresh.abs");Registration Flow (Pyth Update Required)
const [account] = await walletClient.getAddresses();
// 1) Fetch the latest matched oracle price + update payload (Hermes)
const latestUpdate = await fetchPythLatestUpdate();
const priceUpdateData = latestUpdate.updateData;
// 2) Quote total value (registration + required pyth fee)
const quote = await ans.quoteRegisterValue({
name: "newname.abs",
account,
priceUpdateData,
});
// 3) Submit supported registration tx
const txHash = await ans.registerWithPriceUpdate({
name: "newname.abs",
priceUpdateData,
value: quote.total,
account,
});
// 4) Wait for confirmation
await ans.waitForReceipt(txHash);Prepare Transaction Without Signing
const prepared = await ans.prepareRegisterTransaction({
name: "newname.abs",
account,
priceUpdateData,
});
console.log(prepared.functionName);
console.log(prepared.args);
console.log(prepared.data);
console.log(prepared.value.toString());This is useful when your app, agent, or backend wants the SDK to build registration calldata and the exact required value first, while execution still happens elsewhere.
Coupon + Price Update Flow
const quote = await ans.quoteRegisterValue({
name: "newname.abs",
account,
couponCode: "SAVE10",
priceUpdateData,
});
const prepared = await ans.prepareRegisterTransaction({
name: "newname.abs",
account,
couponCode: "SAVE10",
priceUpdateData,
});
const txHash = await ans.registerWithCouponAndPriceUpdate({
name: "newname.abs",
couponCode: "SAVE10",
priceUpdateData,
value: quote.total,
account,
});
await ans.waitForReceipt(txHash);When a valid coupon applies, quoteRegisterValue() and prepareRegisterTransaction() automatically
switch to the coupon-adjusted total and return registerWithCouponAndPriceUpdate calldata.
Records and Profile Data
await ans.setRecord({
name: "luca.abs",
record: "0xabc...def",
account,
});
await ans.setTextRecord({
name: "luca.abs",
key: "twitter",
value: "@handle",
account,
});Allowed text keys are:
twitterdiscordavatarurlemail
Other Write Methods
await ans.transferDomain({ name: "luca.abs", to: "0x...", account });
await ans.migrateFromOldContract({ oldTokenId: 123n, newName: "luca.abs", account });Events / Indexing
const unwatch = ans.watchDomainRegistered((logs) => {
console.log("new domains", logs);
});
// later
unwatch();Also available:
watchDomainTransferredwatchRecordSetwatchTextRecordSet
Error Handling
import { decodeAnsContractError } from "@ans-abstract-name-service/sdk";
try {
// write call...
} catch (error) {
const parsed = decodeAnsContractError(error);
console.error(parsed.code, parsed.message);
}Price and Discount Notes
price(name)is sender-sensitive in ANS (discount logic checks caller holdings).quoteRegisterValue()andprepareRegisterTransaction()no longer rely on a raw on-chainprice(name)preflight. They compute the registration price from the same length/root/discount rules as the contract and then add the live Pyth update fee.- Always pass the real user wallet as
accountwhen quoting viaquoteRegisterValue. couponCodeis optional onquoteRegisterValue()andprepareRegisterTransaction().priceUpdateDatamust be non-empty for SDK registration flows.
Troubleshooting
403 "rpc method is not whitelisted"usually means the call triedeth_sendTransactionon a restricted public RPC. For backend/private-key writes, pass anAccountobject so viem signs locally and sends viaeth_sendRawTransaction.
API Surface
Core methods from createAnsClient:
- Reads:
resolve,getRecord,getTextRecord,getNameByAddress,getDomainsByOwner,getAllNames,isNameAvailable,getPrice,getSubdomainRoot,getSubdomainRoots,getPythUpdateFee,quoteRegisterValue - Writes:
registerWithPriceUpdate,registerWithCouponAndPriceUpdate,setRecord,setTextRecord,transferDomain,migrateFromOldContract - Utilities:
prepareRegisterTransaction,waitForReceipt, event watchers
Top-level exports also include:
fetchPythLatestUpdate,fetchPythUpdateData,fetchPythPriceInfo- coupon helpers:
normalizeCouponCode,assertCouponUsable,applyCouponDiscount decodeAnsContractError- name helpers:
normalizeName,validateName - constants:
ABSTRACT_MAINNET_CHAIN_ID,DEFAULT_ANS_V2_CONTRACT_ADDRESS
