ar.io Logoar.io Documentation
Turbo SDKArNS Names

The twelve sponsored actions

const turbo = TurboFactory.authenticated({ privateKey: jwk });

// Buy — the ONE signature in the whole lifecycle. Grants Turbo controller
// rights in this SAME transaction, which is why everything below needs no
// signature of its own until you revoke it.
const { antId, messageId } = await turbo.buyArNSName({
  name: 'my-name',
  owner,
  type: 'lease', // or 'permabuy'
  years: 1, // leases only
  onNonce: (nonce) => persist(nonce), // fires BEFORE the wallet prompt
});

// Lifecycle — no signature at all, spends ARIO.
await turbo.extendArNSLease({ name: 'my-name', years: 2 });
await turbo.upgradeArNSName({ name: 'my-name' });
await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 });

// Records — a small flat/derived credits margin recovers the sponsored SOL
// rent. Handled whichever shape the server picks.
await turbo.setArNSRecord({
  antId,
  owner,
  transactionId,
  undername: '@',
  ttlSeconds: 900,
});
await turbo.removeArNSRecord({ antId, owner, undername: 'docs' });

// Record metadata — display name, logo, description, keywords. Same margin,
// same shape rules as setArNSRecord. `null` clears a field; omit to leave it.
await turbo.setArNSRecordMetadata({
  antId,
  owner,
  undername: '@',
  displayName: 'My Docs',
  recordDescription: null, // clear it
});
await turbo.removeArNSRecordMetadata({ antId, owner, undername: 'docs' });

// Hand ONE record to another address — distinct from transferring the ANT.
await turbo.transferArNSRecord({
  antId,
  owner,
  undername: 'docs',
  target: newOwnerAddress,
});

// Controllers and transfer — owner-signed, same flat/derived margin.
// addArNSController is for RE-granting after a revoke, or granting some
// OTHER address — Turbo already has it from the buy above.
await turbo.addArNSController({ antId, owner }); // omit target => Turbo
await turbo.removeArNSController({ antId, owner }); // the revoke
await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress });
ActionCosts creditsOwner signature
buyArNSNameyes — ARIO purchase + ANT spawn rentalways, once
extendArNSLease / upgradeArNSName / increaseArNSUndernameLimityes — ARIO purchaseno
setArNSRecord / removeArNSRecord / setArNSRecordMetadata / removeArNSRecordMetadata / transferArNSRecordyes — small flat/derived marginonly after you revoke Turbo
addArNSController / removeArNSController / transferArNSAntyes — small flat/derived marginyes

Point the name at your content while you buy it

Pass antState and the ANT's opening record is written by the ario_ant::initialize that runs inside the transaction you already sign — free and atomic. No second action, no second signature, no second debit. Without it a fresh name resolves to the AR.IO logo, which is the on-chain default.

await turbo.buyArNSName({
  name: 'my-name',
  owner,
  type: 'permabuy',
  antState: {
    transactionId: '\<43-char Arweave tx id\>', // the root `@` target
    targetProtocol: 0, // 0 = Arweave (default), 1 = IPFS CID
    ticker: 'MYSITE',
  },
});

antState is accepted on buyArNSName only — the service rejects it on every other action. Note it is nested: a top-level transactionId on a buy is a 400 by design, because that spelling means the set-record target and silently accepting it would point the name at the logo while the caller believed otherwise.

Mind the size budget. The sponsored mint is ONE Solana transaction against the 1232-byte packet limit, shared with Turbo's fee-payer transfer and an add_controller grant. At a worst-case 51-character name only ~71 bytes are spare:

FieldsCostFits?
transactionId + targetProtocol~1 bytealways
ticker (16) + logo (43)~65 bytesyes
description (512), or a full keyword list—no

The budget is dynamic — a shorter name buys headroom — so this SDK imposes no client-side cap. The server measures the real transaction and returns a 400 naming Solana's 1232-byte limit before you are handed anything to sign, with the credit debit refunded inline. That error is deterministic: do not retry it, and surface the server's message rather than replacing it, because it names which fields to drop.

Field limits, all rejected at the service edge before any debit: description ≤ 512 characters, keywords ≤ 16 entries, and logo (plus transactionId when targetProtocol is 0 or unset) must be 43-character Arweave ids. When targetProtocol is 1 the target is an IPFS CID and is not shape-checked as an Arweave id.

See [ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state](https://github.com/ar-io/ar-io-bundler/blob/main/docs/architecture/ARNS_ACTIONS_API.md#buy-name-takes-the-ants-opening-state) in ar-io/ar-io-bundler` for the measured byte table.

Every action costs credits — gas sponsorship was never meant to be free sponsorship. The four purchase actions charge the ARIO cost (plus, for buyArNSName, a rent-derived surcharge for the ANT it mints); the other eight charge a small margin that recovers the Solana rent/fees Turbo fronts on your behalf, computed the same max(rent-derived, flat floor) way as the ANT spawn surcharge. Preview it before you pay:

const { wincQty } = await turbo.getArNSActionPrice('remove-controller');

getArNSActionPrice covers the eight non-purchase actions, by their route name (set-record, remove-record, set-record-metadata, remove-record-metadata, transfer-record, add-controller, remove-controller, transfer) — use getArNSPriceForName for the four purchase actions instead, since their cost is dominated by the ARIO purchase, not this margin.

buyArNSName grants Turbo controller rights inside the same transaction you sign — the add-controller(Turbo) instruction rides along with the mint, so there is no separate step. That's why setArNSRecord and the rest complete in a single call immediately after buying, with no signature of their own. addArNSController is for re-granting after a revoke, or adding a different controller — not something you call after a fresh buy. Revoking is always available — but, like every other action here, not free of credits.

How is this guide?