GOAT Network
AgentKit

GOAT Name Service (.goat)

The gns plugin provides a complete ENS-style .goat namespace stack:

  • Read paths: check availability, get name details, list names owned by an address, estimate registration price.
  • Two-phase ENS-style registration: commitwaitForCommitment (min/max commitment age verified on-chain) → register (with permit or pre-approve) → renew.
  • Profile management: setAddress, setProfileRecords (text + addr multicall), setPrimaryName (reverse record).
  • Cross-chain x402 registration: pay for a .goat registration in USDC/USDT on Polygon, Base, Arbitrum, Optimism, BSC, or Metis. The GNS backend settles on goat-mainnet after the source-chain transfer is detected.

End users register names via the agentkit-gns CLI; programmatic consumers use the plugin's 15 actions directly.

Quick Start (CLI)

Look up + register a .goat name on GOAT mainnet
# Check availability
npx -p @goatnetwork/agentkit agentkit-gns search alice

# Quote the registration price
npx -p @goatnetwork/agentkit agentkit-gns price alice --years 1 --token USDC

# Register on GOAT mainnet (requires USDC/USDT already on GOAT)
GOAT_PRIVATE_KEY=0x... \
  npx -p @goatnetwork/agentkit agentkit-gns register alice --years 1 --token USDC
Cross-chain register paid from BNB Smart Chain USDT
GOAT_PRIVATE_KEY=0x... \
PAYMENT_CHAIN_RPC_URL=https://bsc-dataseed.bnbchain.org \
  npx -p @goatnetwork/agentkit agentkit-gns x402-register alice \
    --pay-chain 56 \
    --pay-token USDT \
    --pay-token-contract 0x55d398326f99059fF775485246999027B3197955

Actions

Read

ActionNameRiskDescription
checkAvailabilityActiongoat.gns.checkAvailabilityreadIs the .goat name available?
getNameDetailsActiongoat.gns.getNameDetailsreadFull record: owner, expiry, resolver, primary, profile records
getMyNamesActiongoat.gns.getMyNamesreadAll .goat names owned by a given address
estimatePriceActiongoat.gns.estimatePricereadQuote registration price for a name + duration + payment token

Two-phase registration

ActionNameRiskDescription
commitActiongoat.gns.commitmediumGenerate a 32-byte secret, compute commit hash, broadcast on-chain commit
waitForCommitmentActiongoat.gns.waitForCommitmentreadPoll on-chain commit storage until block.timestamp ≥ revealAt (or detect expiry)
registerActiongoat.gns.registerhighReveal-phase register; re-derives the commit hash on-chain and rejects parameter drift
renewActiongoat.gns.renewhighExtend an existing registration

commit returns the 32-byte secret as a top-level sensitiveOutputFields value — redacted by default in hook events; the CLI exposes it under --reveal-secrets only.

Profile

ActionNameRiskDescription
setAddressActiongoat.gns.setAddressmediumSet / update the address that a name resolves to
setProfileRecordsActiongoat.gns.setProfileRecordsmediumBatch set text + addr records via PublicResolver multicall
setPrimaryNameActiongoat.gns.setPrimaryNamemediumSet the reverse (primary) record for the caller

Cross-chain x402-paid registration

ActionNameRiskDescription
x402CreateOrderActiongoat.gns.x402.createOrdermediumCreate a cross-chain x402 order via the GNS backend
x402SubmitSignatureActiongoat.gns.x402.submitSignaturehighEIP-712 sign the calldata authorization (GOAT-adapter chain)
x402PayOrderActiongoat.gns.x402.payOrderhighBroadcast the source-chain ERC-20 transfer that fulfills the order
x402GetOrderStatusActiongoat.gns.x402.getOrderStatusreadPoll lifecycle: CHECKOUT_VERIFIED → PAYMENT_CONFIRMED → INVOICED

Cross-Chain x402 Registration — Orchestration

The CLI (agentkit-gns x402-register) and the examples/gns-x402-register demo both run this 8-step sequence:

x402-register orchestration
1. x402.config preflight     — validate (payChain, payToken, payTokenContract)
                                 against the merchant's authoritative config
2. estimatePrice (GOAT-side) — quote totalWei in the GOAT-side stablecoin
3. commit (GOAT)             — bind (GOAT-side stablecoin, totalWei) into
                                 the on-chain commit hash
4. waitForCommitment         — poll until block.timestamp ≥ revealAt
                                 (minCommitmentAge = 60s; CLI default
                                  timeout 180s)
5. x402.createOrder          — pin commitMaxAmountWei so the backend
                                 re-derives a matching hash
6. x402.submitSignature      — user signs the calldata typed data
7. x402.payOrder             — broadcast source-chain ERC-20 transfer
                                 (e.g. 3 USDT on BSC)
8. poll getOrderStatus       — until INVOICED (true success) or terminal
                                 failure (FAILED / EXPIRED / CANCELLED)

Lifecycle states

The GNS backend's OrderStatus enum (no PAYMENT_PENDING):

StateMeaning
CHECKOUT_VERIFIEDOrder created and signature authorized; ready for source-chain payment
PAYMENT_CONFIRMEDUpstream watcher detected the source-chain ERC-20 transfer
INVOICEDTrue success — the adaptor callback executed on GOAT and the .goat name is registered
FAILED / EXPIRED / CANCELLEDTerminal failure

For DELEGATE / callback orders (the GNS flow), PAYMENT_CONFIRMED is an intermediate state — clients must keep polling until INVOICED to confirm the name was actually registered. The CLI does this automatically.

Commit-hash binding invariant

The on-chain commit's payment tuple must match what the GNS backend re-derives in createOrder, or registration is rejected with Commitment does not match registration payload. AgentKit binds:

  • paymentToken = GOAT-side stablecoin address resolved by canonical symbol via the SDK's GNS_PAYMENT_TOKENS table (mirrors the backend's paymentTokenAddressFromSymbol).
  • maxPaymentAmount = totalWei from /names/quote, forwarded to createOrder as commitMaxAmountWei.

The register action additionally re-derives the commitment hash on-chain and rejects any drift of (secret / token / amount / years / owner / resolver / data / reverseRecord / referrer) vs the original commit.

payOrder Security Model

x402.payOrder is a money path. Beyond the lifecycle gate (require CHECKOUT_VERIFIED, refuse to double-pay or pay a dead order), it cross-checks every caller-supplied routing field against three trust anchors:

  1. Status endpoint OrderProofchainId, amountWei, fromAddress (strict type + value).
  2. calldataSignRequest.message (backend-signed, verified on-chain by the adaptor):
    • message.ownerinput.payToAddress — caller cannot redirect the merchant's payment-receive address (mandatory; fail-closed if absent).
    • message.payer ≡ wallet address — bind the signed payer authorization to the broadcasting wallet.
  3. x402Config token allowlistinput.tokenContract must appear in chains[payChainId].tokens[].contract. Unknown tokens or unsupported chains are refused.

A 3-way payer binding (wallet.getAddress() ≡ payer.getAddress() ≡ message.payer) catches mis-wired payer adapters where wallet and payer wrap different signers.

A module-scope inflight Map dedupes concurrent payOrder calls for the same orderId (double-pay TOCTOU defense).

Network Addresses (Predeployed)

The SDK's getGnsContracts(network) returns the full registry. GOAT mainnet ships with these addresses pre-wired:

Contractgoat-mainnet (chain 2345)
Controller0xdD3912A19eB9Aab0Cd88DdA107ec4fc2dA51D5e9
PublicResolver0xD56644B440B6d0326dc777a92FB2A0d9FdB4e59D
EnsRegistry0x4d652092fB4a08D186C2797e96623deaEE46aa91
ReverseRegistrar0x49275954460f2f6d3F8ba537E95Db8293C7c4058
BaseRegistrar0xB3Bd3075A9C12eFF3b23FAEF165D12B5C20966E0
x402Adaptor0xC0cEB3b632B0264b739BE115604D1911917d5471
PriceBook0x81B36cf45Ac52F868D8948c033b9572C674ec3A4

GOAT-side payment tokens

SymbolAddressDecimals
USDC0x3022b87ac063DE95b1570F46f5e470F8B53112D86
USDT0xE1AD845D93853fff44990aE0DcecD8575293681e6

Programmatic Usage

Register .goat names via the SDK
import { JsonRpcProvider, Wallet } from 'ethers';
import { ActionProvider, ExecutionRuntime, PolicyEngine } from '@goatnetwork/agentkit';
import { EvmWalletProvider } from '@goatnetwork/agentkit/core';
import {
  HttpGnsApiAdapter,
  commitAction,
  waitForCommitmentAction,
  registerAction,
  estimatePriceAction,
  getGnsContracts,
  resolveGnsPaymentToken,
} from '@goatnetwork/agentkit/plugins';

const networkKey = 'goat-mainnet';
const contracts = getGnsContracts(networkKey);
const api = new HttpGnsApiAdapter({ network: networkKey });

const rpcUrl = 'https://rpc.goat.network';
const wallet = new EvmWalletProvider(
  new Wallet(process.env.GOAT_PRIVATE_KEY!, new JsonRpcProvider(rpcUrl)),
  new JsonRpcProvider(rpcUrl),
  networkKey,
);

const provider = new ActionProvider();
provider.register(estimatePriceAction(api));
provider.register(commitAction(contracts, wallet));
provider.register(waitForCommitmentAction(contracts, wallet));
provider.register(registerAction(contracts, wallet));

const policy = new PolicyEngine({
  allowedNetworks: [networkKey],
  maxRiskWithoutConfirm: 'low',
  writeEnabled: true,
});
const runtime = new ExecutionRuntime(policy, { maxRetries: 0, retryDelayMs: 200 });

// 1. Quote the GOAT-side price
const goatToken = resolveGnsPaymentToken(networkKey, 'USDC')!;
const quote = await runtime.run(
  provider.get('goat.gns.estimatePrice'),
  { traceId: 't1', network: networkKey, now: Date.now() },
  { name: 'alice', years: 1, tokenContract: goatToken.address },
  { confirmed: true },
);
const totalWei = (quote.output as { totalWei: string }).totalWei;

// 2. Commit (binds the same paymentToken + maxPaymentAmount the backend will re-derive)
const commit = await runtime.run(
  provider.get('goat.gns.commit'),
  { traceId: 't2', network: networkKey, now: Date.now() },
  {
    name: 'alice',
    owner: (await wallet.getAddress()) as `0x${string}`,
    years: 1,
    paymentTokenAddress: goatToken.address,
    paymentTokenSymbol: goatToken.symbol,
    amountWei: totalWei,
  },
  { confirmed: true, revealedFields: ['secret'] },
);

// 3. Wait for the reveal window, then call register / renew per your flow.

Environment Variables

VariableRequiredDescription
GOAT_PRIVATE_KEYAlwaysWallet key for the GOAT-side wallet (owner of the registered name)
PAYMENT_CHAIN_RPC_URLx402-register onlyRPC URL of the source chain you're paying from (e.g. BSC)
PAYMENT_CHAIN_PRIVATE_KEYOptionalSource-chain signer key for x402-register; falls back to GOAT_PRIVATE_KEY (same wallet on both chains)
GNS_API_BASE_URLOptionalOverride default https://gns-api.goat.network/api
GOAT_MAINNET_RPC_URLOptionalOverride the default GOAT mainnet RPC endpoint

Run agentkit-gns doctor for a full env diagnostic.

Examples

On this page