{"id":26647548,"url":"https://github.com/ar-io/ar-io-sdk","last_synced_at":"2026-06-30T15:00:45.090Z","repository":{"id":235518526,"uuid":"754353210","full_name":"ar-io/ar-io-sdk","owner":"ar-io","description":"The featured SDK for interacting with the ar.io network. Compatible with Typescript based Web and Node projects.","archived":false,"fork":false,"pushed_at":"2026-06-25T00:50:48.000Z","size":10216,"stargazers_count":38,"open_issues_count":10,"forks_count":21,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-06-28T14:31:14.227Z","etag":null,"topics":["ao","ar-io","arns","arweave","commonjs","esm","io","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://ar.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ar-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-02-07T22:00:56.000Z","updated_at":"2026-06-20T05:15:21.000Z","dependencies_parsed_at":"2024-05-28T16:30:30.066Z","dependency_job_id":"2720cddb-1339-4304-b066-6608d01a3f0b","html_url":"https://github.com/ar-io/ar-io-sdk","commit_stats":null,"previous_names":["ar-io/ar-io-sdk"],"tags_count":483,"template":false,"template_full_name":null,"purl":"pkg:github/ar-io/ar-io-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ar-io%2Far-io-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ar-io%2Far-io-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ar-io%2Far-io-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ar-io%2Far-io-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ar-io","download_url":"https://codeload.github.com/ar-io/ar-io-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ar-io%2Far-io-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34971623,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ao","ar-io","arns","arweave","commonjs","esm","io","nodejs","typescript"],"created_at":"2025-03-24T23:52:28.852Z","updated_at":"2026-06-30T15:00:45.070Z","avatar_url":"https://github.com/ar-io.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @ar.io/sdk\n\n[![codecov](https://codecov.io/gh/ar-io/ar-io-sdk/graph/badge.svg?token=7dXKcT7dJy)](https://codecov.io/gh/ar-io/ar-io-sdk)\n\nThe Solana-native SDK for the AR.IO network. Provides typed\nclient classes (`ARIO`, `ANT`, `ANTRegistry`), PDA helpers,\ndeserializers, and escrow primitives for the AR.IO protocol on Solana.\nCodama-generated instruction builders and account decoders are\nsupplied by [`@ar.io/solana-contracts`](https://www.npmjs.com/package/@ar.io/solana-contracts).\n\n## Table of Contents\n\n\u003c!-- toc --\u003e\n\n- [Table of Contents](#table-of-contents)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Usage](#usage)\n- [ARIO Contract](#ario-contract)\n- [ANT Contracts](#ant-contracts)\n- [Escrow](#escrow)\n- [Token Conversion](#token-conversion)\n- [Logging](#logging)\n- [Pagination](#pagination)\n- [Advanced](#advanced)\n- [Resources](#resources)\n- [Developers](#developers)\n\n\u003c!-- tocstop --\u003e\n\n## Installation\n\nRequires `node\u003e=v18.0.0`.\n\n```shell\nnpm install @ar.io/sdk\n```\n\nor\n\n```shell\nyarn add @ar.io/sdk\n```\n\n## Quick Start\n\n```typescript\nimport { ARIO } from '@ar.io/sdk';\nimport { createSolanaRpc } from '@solana/kit';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\nconst ario = ARIO.init({ rpc });\nconst gateways = await ario.getGateways();\n```\n\nWrite operations need a `@solana/kit` signer plus an `rpcSubscriptions`\nclient (used by kit's `sendAndConfirmTransaction`):\n\n```typescript\nimport { ARIO } from '@ar.io/sdk';\nimport {\n  createSolanaRpc,\n  createSolanaRpcSubscriptions,\n  createKeyPairSignerFromBytes,\n} from '@solana/kit';\nimport { readFileSync } from 'node:fs';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\nconst rpcSubscriptions = createSolanaRpcSubscriptions(\n  'wss://api.mainnet-beta.solana.com',\n);\nconst signer = await createKeyPairSignerFromBytes(\n  new Uint8Array(JSON.parse(readFileSync('keypair.json', 'utf8'))),\n);\n\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nawait ario.buyRecord({\n  name: 'foo',\n  type: 'lease',\n  years: 1,\n  processId: '\u003cANT mint pubkey\u003e',\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"gatewayAddress\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"observerAddress\": \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\",\n      \"operatorStake\": 250000000000,\n      \"totalDelegatedStake\": 0,\n      \"settings\": {\n        \"allowDelegatedStaking\": true,\n        \"allowedDelegates\": [],\n        \"autoStake\": false,\n        \"delegateRewardShareRatio\": 10,\n        \"minDelegatedStake\": 100000000,\n        \"fqdn\": \"ar-io.dev\",\n        \"label\": \"ar.io Test\",\n        \"note\": \"Test Gateway operated by PDS for the ar.io ecosystem.\",\n        \"port\": 443,\n        \"properties\": \"raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY\",\n        \"protocol\": \"https\"\n      },\n      \"startTimestamp\": 1720720621424,\n      \"endTimestamp\": 0,\n      \"stats\": {\n        \"passedConsecutiveEpochs\": 30,\n        \"failedConsecutiveEpochs\": 0,\n        \"totalEpochCount\": 31,\n        \"passedEpochCount\": 30,\n        \"failedEpochCount\": 1,\n        \"observedEpochCount\": 30,\n        \"prescribedEpochCount\": 31\n      },\n      \"status\": \"joined\",\n      \"weights\": {\n        \"stakeWeight\": 5.02400000024,\n        \"tenureWeight\": 0.19444444444444,\n        \"gatewayPerformanceRatio\": 1,\n        \"observerPerformanceRatio\": 1,\n        \"gatewayRewardRatioWeight\": 1,\n        \"observerRewardRatioWeight\": 1,\n        \"compositeWeight\": 0.97688888893556,\n        \"normalizedCompositeWeight\": 0.19247316211083\n      }\n    }\n  ],\n  \"hasMore\": true,\n  \"nextCursor\": \"-4xgjroXENKYhTWqrBo57HQwvDL51mMdfsdsxJy6Y2Z_sA\",\n  \"totalItems\": 316,\n  \"limit\": 100,\n  \"sortBy\": \"startTimestamp\",\n  \"sortOrder\": \"desc\"\n}\n```\n\n\u003c/details\u003e\n\n## Usage\n\nThe SDK is published as an ES module (`\"type\": \"module\"`) and is\ncompatible with modern bundlers such as Webpack, Rollup, ESbuild, and\nVite. CommonJS consumers should migrate to ESM (Node 18+ supports ESM\nnatively). Refer to the [examples] directory to see how to use the SDK\nin various environments.\n\n### Subpath exports\n\n- `@ar.io/sdk` — main entry. `ARIO`, `ANT`, `ANTRegistry`, Solana\n  client classes, PDA helpers, deserializers, escrow primitives.\n- `@ar.io/sdk/solana` — alias of the main entry (kept for one release\n  while consumers migrate from the previous subpath layout).\n\n### Web\n\n\u003e [!WARNING]\n\u003e Polyfills are not provided by default for bundled web projects (Vite,\n\u003e ESBuild, Webpack, Rollup, etc.). Depending on your bundler config, you\n\u003e will need polyfills for `crypto`, `process` and `buffer`. Refer to\n\u003e [examples/webpack] and [examples/vite] for examples.\n\n```javascript\nimport { ARIO } from '@ar.io/sdk';\nimport { createSolanaRpc } from '@solana/kit';\n\nconst ario = ARIO.init({\n  rpc: createSolanaRpc('https://api.mainnet-beta.solana.com'),\n});\nconst gateways = await ario.getGateways();\n```\n\n### Browser bundle\n\n```html\n\u003cscript type=\"module\"\u003e\n  // replace \u003cversion\u003e with a release version\n  import { ARIO } from 'https://github.com/ar-io/ar-io-sdk/releases/download/v\u003cversion\u003e/web.bundle.min.js';\n  import { createSolanaRpc } from 'https://esm.sh/@solana/kit@6';\n\n  const ario = ARIO.init({\n    rpc: createSolanaRpc('https://api.mainnet-beta.solana.com'),\n  });\n  const gateways = await ario.getGateways();\n\u003c/script\u003e\n```\n\n### TypeScript\n\nThe SDK ships TypeScript types alongside the JS output. Types are\nexported from `./lib/types/solana/index.d.ts` and resolve automatically\nfor ESM consumers.\n\n\u003e [!NOTE]\n\u003e TypeScript 5.3+ is recommended (the SDK uses `nodenext` module\n\u003e resolution with `.js` extensions in relative imports).\n\n## ARIO Contract\n\n### General\n\n#### `init({ rpc, rpcSubscriptions?, signer? })`\n\nFactory function that creates a read-only or writeable ARIO client.\nProviding `signer` plus `rpcSubscriptions` enables write methods\n(`joinNetwork`, `delegateStake`, `buyRecord`, etc.). Without a signer,\nthe client is read-only.\n\n```typescript\nimport { ARIO } from '@ar.io/sdk';\nimport {\n  createSolanaRpc,\n  createSolanaRpcSubscriptions,\n  createKeyPairSignerFromBytes,\n} from '@solana/kit';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\n\n// read-only client\nconst ario = ARIO.init({ rpc });\n\n// read-write client (needs rpcSubscriptions for sendAndConfirm)\nconst rpcSubscriptions = createSolanaRpcSubscriptions(\n  'wss://api.mainnet-beta.solana.com',\n);\nconst signer = await createKeyPairSignerFromBytes(/* 64-byte secret key */);\nconst arioWrite = ARIO.init({ rpc, rpcSubscriptions, signer });\n```\n\n#### `getInfo()`\n\nRetrieves the information of the ARIO process.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst info = await ario.getInfo();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"Name\": \"AR.IO\",\n  \"Ticker\": \"ARIO\",\n  \"Logo\": \"\",\n  \"Denomination\": 6,\n  \"Handlers\": [],\n  \"LastCreatedEpochIndex\": 0,\n  \"LastDistributedEpochIndex\": 0,\n  \"totalSupply\": 1000000000000000,\n  \"protocolBalance\": 0,\n  \"epochSettings\": {\n    \"durationMs\": 86400000,\n    \"prescribedNameCount\": 25,\n    \"maxObservers\": 50\n  }\n}\n```\n\n\u003e **Note**: `Handlers`, `LastCreatedEpochIndex`, and `LastDistributedEpochIndex`\n\u003e are placeholders on Solana (returned for backwards-compatible field shape\n\u003e with consumer code). `totalSupply` / `protocolBalance` are live reads from\n\u003e the `ArioConfig` PDA; `epochSettings` is live from the `EpochSettings`\n\u003e PDA. See `src/solana/io-readable.ts` for the exact projection.\n\n\u003c/details\u003e\n\n#### `getTokenSupply()`\n\nRetrieves the total supply of tokens, returned in mARIO. The total supply includes the following:\n\n- `total` - the total supply of all tokens\n- `circulating` - the total supply minus locked, withdrawn, delegated, and staked\n- `locked` - tokens that are locked in the protocol (a.k.a. vaulted)\n- `withdrawn` - tokens that have been withdrawn from the protocol by operators and delegators\n- `delegated` - tokens that have been delegated to gateways\n- `staked` - tokens that are staked in the protocol by gateway operators\n- `protocolBalance` - tokens that are held in the protocol's treasury. This is included in the circulating supply.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst supply = await ario.getTokenSupply();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"total\": 1000000000000000000,\n  \"circulating\": 998094653842520,\n  \"locked\": 0,\n  \"withdrawn\": 560563387278,\n  \"delegated\": 1750000000,\n  \"staked\": 1343032770199,\n  \"protocolBalance\": 46317263683761\n}\n```\n\n\u003c/details\u003e\n\n#### `getBalance({ address })`\n\nRetrieves the balance of the specified wallet address.\n\n```typescript\nconst ario = ARIO.init({ rpc });\n// the balance will be returned in mARIO as a value\nconst balance = await ario\n  .getBalance({\n    address: \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n  })\n  .then((balance: number) =\u003e new mARIOToken(balance).toARIO()); // convert it to ARIO for readability\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n100000\n```\n\n\u003c/details\u003e\n\n#### `getBalances({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves the balances of the ARIO process in `mARIO`, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last wallet address from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst balances = await ario.getBalances({\n  cursor: \"-4xgjroXENKYhTWqrBo57HQwvDL51mMdfsdsxJy6Y2Z_sA\",\n  limit: 100,\n  sortBy: \"balance\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"address\": \"-4xgjroXENKYhTWqrBo57HQwvDL51mMvSxJy6Y2Z_sA\",\n      \"balance\": 1000000\n    },\n    {\n      \"address\": \"-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck\",\n      \"balance\": 1000000\n    }\n    // ...98 other balances\n  ],\n  \"hasMore\": true,\n  \"nextCursor\": \"-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck\",\n  \"totalItems\": 1789,\n  \"sortBy\": \"balance\",\n  \"sortOrder\": \"desc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `transfer({ target, qty })`\n\nTransfers `mARIO` to the designated `target` recipient address. Requires `signer` to be provided on `ARIO.init` to sign the transaction.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.transfer({\n  target: 'RecipientSolanaPubkeyBase58',\n  qty: new ARIOToken(1000).toMARIO(),\n});\n```\n\n### Networks\n\nThe SDK talks to whatever cluster your `@solana/kit` RPC client points\nat — mainnet-beta by default. For devnet or a local validator, override\nthe RPC URL and (on any non-mainnet cluster) the per-program addresses:\n\n```typescript\nimport { ARIO } from '@ar.io/sdk';\nimport { createSolanaRpc, address } from '@solana/kit';\n\nconst ario = ARIO.init({\n  rpc: createSolanaRpc('https://api.devnet.solana.com'),\n  coreProgramId: address('\u003cARIO_CORE_PROGRAM_ID\u003e'),\n  garProgramId: address('\u003cARIO_GAR_PROGRAM_ID\u003e'),\n  arnsProgramId: address('\u003cARIO_ARNS_PROGRAM_ID\u003e'),\n  antProgramId: address('\u003cARIO_ANT_PROGRAM_ID\u003e'),\n});\n```\n\nOn localnet (Surfpool) source program IDs from\n`migration/localnet/out/localnet.env` in the `solana-ar-io` monorepo.\n\n\u003c!-- ##### Faucet\n\nThe SDK exposes a `createFaucet` HTTP wrapper around the ar.io faucet\nservice ([faucet.ar.io](https://faucet.ar.io)). The faucet backend has\nnot yet been ported to issue Solana-mint transfers — the SDK surface is\ndocumented here for forward-compatibility once it lands:\n\n- `createFaucet({ arioInstance, processId }).captchaUrl()` — returns\n  the captcha URL.\n- `createFaucet({ arioInstance, processId }).claimWithAuthToken({ authToken, recipient, quantity })` —\n  claim tokens for `recipient` using an auth token returned by the\n  captcha flow.\n- `createFaucet({ arioInstance, processId }).verifyAuthToken({ authToken })` —\n  check whether an auth token is still valid.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003ci\u003eExample client-side code for claiming tokens\u003c/i\u003e\u003c/summary\u003e\n\n```typescript\nimport { ARIO } from \"@ar.io/sdk\";\n\nconst ario = ARIO.init({ rpc });\nconst captchaUrl = await ario.faucet.captchaUrl();\n\n// open the captcha URL in the browser, and listen for the auth token event\nconst captchaWindow = window.open(\n  captchaUrl.captchaUrl,\n  \"_blank\",\n  \"width=600,height=600\",\n);\n/**\n * The captcha URL includes a window.parent.postMessage event that is used to send the auth token to the parent window.\n * You can store the auth token in localStorage and use it to claim tokens for the duration of the auth token's expiration (default 1 hour).\n */\nwindow.parent.addEventListener(\"message\", async (event) =\u003e {\n  if (event.data.type === \"ario-jwt-success\") {\n    localStorage.setItem(\"ario-jwt\", event.data.token);\n    localStorage.setItem(\"ario-jwt-expires-at\", event.data.expiresAt);\n    // close our captcha window\n    captchaWindow?.close();\n    // claim the tokens using the JWT token\n    const res = await testnet.faucet\n      .claimWithAuthToken({\n        authToken: event.data.token,\n        recipient: await window.arweaveWallet.getActiveAddress(),\n        quantity: new ARIOToken(100).toMARIO().valueOf(), // 100 ARIO\n      })\n      .then((res) =\u003e {\n        alert(\n          \"Successfully claimed 100 ARIO tokens! Transaction ID: \" + res.id,\n        );\n      })\n      .catch((err) =\u003e {\n        alert(`Failed to claim tokens: ${err}`);\n      });\n  }\n});\n\n/**\n * Once you have a valid JWT, you can check if it is still valid and use it for subsequent requests without having to open the captcha again.\n */\nif (\n  localStorage.getItem(\"ario-jwt-expires-at\") \u0026\u0026\n  Date.now() \u003c parseInt(localStorage.getItem(\"ario-jwt-expires-at\") ?? \"0\")\n) {\n  const res = await testnet.faucet.claimWithAuthToken({\n    authToken: localStorage.getItem(\"ario-jwt\") ?? \"\",\n    recipient: await window.arweaveWallet.getActiveAddress(),\n    quantity: new ARIOToken(100).toMARIO().valueOf(), // 100 ARIO\n  });\n}\n```\n\n\u003c/details\u003e --\u003e\n\n### Vaults\n\n#### `getVault({ address, vaultId })`\n\nRetrieves the locked-balance user vault of the ARIO process by the specified wallet address and vault ID.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst vault = await ario.getVault({\n  address: \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n  vaultId: \"vaultIdOne\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"balance\": 1000000,\n  \"startTimestamp\": 123,\n  \"endTimestamp\": 4567\n}\n```\n\n\u003c/details\u003e\n\n#### `getVaults({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all locked-balance user vaults of the ARIO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last wallet address from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst vaults = await ario.getVaults({\n  cursor: \"0\",\n  limit: 100,\n  sortBy: \"balance\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"address\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"vaultId\": \"vaultIdOne\",\n      \"balance\": 1000000,\n      \"startTimestamp\": 123,\n      \"endTimestamp\": 4567\n    },\n    {\n      \"address\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"vaultId\": \"vaultIdTwo\",\n      \"balance\": 1000000,\n      \"startTimestamp\": 123,\n      \"endTimestamp\": 4567\n    }\n    // ...98 other addresses with vaults\n  ],\n  \"hasMore\": true,\n  \"nextCursor\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n  \"totalItems\": 1789,\n  \"sortBy\": \"balance\",\n  \"sortOrder\": \"desc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `vaultedTransfer({ recipient, quantity, lockLengthMs, revokable })`\n\nTransfers `mARIO` to the designated `recipient` address and locks the balance for the specified `lockLengthMs` milliseconds. The `revokable` flag determines if the vaulted transfer can be revoked by the sender.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.vaultedTransfer(\n  {\n    recipient: \"-5dV7nk7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5\",\n    quantity: new ARIOToken(1000).toMARIO(),\n    lockLengthMs: 1000 * 60 * 60 * 24 * 365, // 1 year\n    revokable: true,\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `revokeVault({ recipient, vaultId })`\n\nRevokes a vaulted transfer by the recipient address and vault ID. Only the sender of the vaulted transfer can revoke it.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.revokeVault({\n  recipient: \"-5dV7nk7waR8v4STuwPnTck1zFVkQqJh5K9q9Zik4Y5\",\n  vaultId: \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\",\n});\n```\n\n#### `createVault({ lockLengthMs, quantity })`\n\nCreates a vault for the specified `quantity` of mARIO from the signer's balance and locks it for the specified `lockLengthMs` milliseconds.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\nconst { id: txId } = await ario.createVault({\n  lockLengthMs: 1000 * 60 * 60 * 24 * 365, // 1 year\n  quantity: new ARIOToken(1000).toMARIO(),\n});\n```\n\n#### `extendVault({ vaultId, extendLengthMs })`\n\nExtends the lock length of a signer's vault by the specified `extendLengthMs` milliseconds.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\nconst { id: txId } = await ario.extendVault({\n  vaultId: \"vaultIdOne\",\n  extendLengthMs: 1000 * 60 * 60 * 24 * 365, // 1 year\n});\n```\n\n#### `increaseVault({ vaultId, quantity })`\n\nIncreases the balance of a signer's vault by the specified `quantity` of mARIO.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.increaseVault({\n  vaultId: \"vaultIdOne\",\n  quantity: new ARIOToken(1000).toMARIO(),\n});\n```\n\n### Gateways\n\n#### `getGateway({ address })`\n\nRetrieves a gateway's info by its staking wallet address.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst gateway = await ario.getGateway({\n  address: \"-7vXsQZQDk8TMDlpiSLy3CnLi5PDPlAaN2DaynORpck\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"observerAddress\": \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\",\n  \"operatorStake\": 250000000000,\n  \"totalDelegatedStake\": 0,\n  \"settings\": {\n    \"allowDelegatedStaking\": true,\n    \"allowedDelegates\": [],\n    \"autoStake\": false,\n    \"delegateRewardShareRatio\": 10,\n    \"minDelegatedStake\": 100000000,\n    \"fqdn\": \"ar-io.dev\",\n    \"label\": \"ar.io Test\",\n    \"note\": \"Test Gateway operated by PDS for the ar.io ecosystem.\",\n    \"port\": 443,\n    \"properties\": \"raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY\",\n    \"protocol\": \"https\"\n  },\n  \"startTimestamp\": 1720720620813,\n  \"endTimestamp\": 0,\n  \"stats\": {\n    \"passedConsecutiveEpochs\": 30,\n    \"failedConsecutiveEpochs\": 0,\n    \"totalEpochCount\": 31,\n    \"passedEpochCount\": 30,\n    \"failedEpochCount\": 1,\n    \"observedEpochCount\": 30,\n    \"prescribedEpochCount\": 31\n  },\n  \"status\": \"joined\",\n  \"weights\": {\n    \"stakeWeight\": 5.02400000024,\n    \"tenureWeight\": 0.19444444444444,\n    \"gatewayPerformanceRatio\": 1,\n    \"observerPerformanceRatio\": 1,\n    \"gatewayRewardRatioWeight\": 1,\n    \"observerRewardRatioWeight\": 1,\n    \"compositeWeight\": 0.97688888893556,\n    \"normalizedCompositeWeight\": 0.19247316211083\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getGateways({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves registered gateways of the ARIO process, using pagination and sorting by the specified criteria. The `cursor` used for pagination is the last gateway address from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst gateways = await ario.getGateways({\n  limit: 100,\n  sortOrder: \"desc\",\n  sortBy: \"operatorStake\",\n});\n```\n\nAvailable `sortBy` options are any of the keys on the gateway object, e.g. `operatorStake`, `start`, `status`, `settings.fqdn`, `settings.label`, `settings.note`, `settings.port`, `settings.protocol`, `stats.failedConsecutiveEpochs`, `stats.passedConsecutiveEpochs`, etc.\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"gatewayAddress\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"observerAddress\": \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\",\n      \"operatorStake\": 250000000000,\n      \"totalDelegatedStake\": 0,\n      \"settings\": {\n        \"allowDelegatedStaking\": true,\n        \"allowedDelegates\": [],\n        \"autoStake\": false,\n        \"delegateRewardShareRatio\": 10,\n        \"minDelegatedStake\": 100000000,\n        \"fqdn\": \"ar-io.dev\",\n        \"label\": \"ar.io Test\",\n        \"note\": \"Test Gateway operated by PDS for the ar.io ecosystem.\",\n        \"port\": 443,\n        \"properties\": \"raJgvbFU-YAnku-WsupIdbTsqqGLQiYpGzoqk9SCVgY\",\n        \"protocol\": \"https\"\n      },\n      \"startTimestamp\": 1720720620813,\n      \"endTimestamp\": 0,\n      \"stats\": {\n        \"passedConsecutiveEpochs\": 30,\n        \"failedConsecutiveEpochs\": 0,\n        \"totalEpochCount\": 31,\n        \"passedEpochCount\": 30,\n        \"failedEpochCount\": 1,\n        \"observedEpochCount\": 30,\n        \"prescribedEpochCount\": 31\n      },\n      \"status\": \"joined\",\n      \"weights\": {\n        \"stakeWeight\": 5.02400000024,\n        \"tenureWeight\": 0.19444444444444,\n        \"gatewayPerformanceRatio\": 1,\n        \"observerPerformanceRatio\": 1,\n        \"gatewayRewardRatioWeight\": 1,\n        \"observerRewardRatioWeight\": 1,\n        \"compositeWeight\": 0.97688888893556,\n        \"normalizedCompositeWeight\": 0.19247316211083\n      }\n    }\n  ],\n  \"hasMore\": true,\n  \"nextCursor\": \"-4xgjroXENKYhTWqrBo57HQwvDL51mMdfsdsxJy6Y2Z_sA\",\n  \"totalItems\": 316,\n  \"limit\": 100,\n  \"sortBy\": \"operatorStake\",\n  \"sortOrder\": \"desc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `getGatewayDelegates({ address, cursor, limit, sortBy, sortOrder })`\n\nRetrieves all delegates for a specific gateway, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last delegate address from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst delegates = await ario.getGatewayDelegates({\n  address: \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n  limit: 3,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"nextCursor\": \"ScEtph9-vfY7lgqlUWwUwOmm99ySeZGQhOX0MFAyFEs\",\n  \"limit\": 3,\n  \"sortBy\": \"startTimestamp\",\n  \"totalItems\": 32,\n  \"sortOrder\": \"desc\",\n  \"hasMore\": true,\n  \"items\": [\n    {\n      \"delegatedStake\": 600000000,\n      \"address\": \"qD5VLaMYyIHlT6vH59TgYIs6g3EFlVjlPqljo6kqVxk\",\n      \"startTimestamp\": 1732716956301\n    },\n    {\n      \"delegatedStake\": 508999038,\n      \"address\": \"KG8TlcWk-8pvroCjiLD2J5zkG9rqC6yYaBuZNqHEyY4\",\n      \"startTimestamp\": 1731828123742\n    },\n    {\n      \"delegatedStake\": 510926479,\n      \"address\": \"ScEtph9-vfY7lgqlUWwUwOmm99ySeZGQhOX0MFAyFEs\",\n      \"startTimestamp\": 1731689356040\n    }\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `joinNetwork(params)`\n\nJoins a gateway to the ar.io network via its associated wallet.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.joinNetwork(\n  {\n    qty: new ARIOToken(10_000).toMARIO(), // minimum operator stake allowed\n    autoStake: true, // auto-stake operator rewards to the gateway\n    allowDelegatedStaking: true, // allows delegated staking\n    minDelegatedStake: new ARIOToken(100).toMARIO(), // minimum delegated stake allowed\n    delegateRewardShareRatio: 10, // percentage of rewards to share with delegates (e.g. 10%)\n    label: \"john smith\", // min 1, max 64 characters\n    note: \"The example gateway\", // max 256 characters\n    properties: \"FH1aVetOoulPGqgYukj0VE0wIhDy90WiQoV3U2PeY44\", // Arweave transaction ID containing additional properties of the Gateway\n    observerWallet: \"0VE0wIhDy90WiQoV3U2PeY44FH1aVetOoulPGqgYukj\", // wallet address of the observer, must match OBSERVER_WALLET on the observer\n    fqdn: \"example.com\", // fully qualified domain name - note: you must own the domain and set the OBSERVER_WALLET on your gateway to match `observerWallet`\n    port: 443, // port number\n    protocol: \"https\", // only 'https' is supported\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `leaveNetwork()`\n\nSets the gateway as `leaving` on the ar.io network. Requires `signer` to be provided on `ARIO.init` to sign the transaction. The gateways operator and delegate stakes are vaulted and will be returned after leave periods. The gateway will be removed from the network after the leave period.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\nconst { id: txId } = await ario.leaveNetwork(\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `updateGatewaySettings({ ...settings })`\n\nWrites new gateway settings to the callers gateway configuration.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.updateGatewaySettings(\n  {\n    // any other settings you want to update\n    minDelegatedStake: new ARIOToken(100).toMARIO(),\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `increaseDelegateStake({ target, qty })`\n\nIncreases the callers stake on the target gateway.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.increaseDelegateStake(\n  {\n    target: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n    qty: new ARIOToken(100).toMARIO(),\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `decreaseDelegateStake({ target, qty, instant })`\n\nDecreases the callers stake on the target gateway. Can instantly decrease stake by setting instant to `true`.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.decreaseDelegateStake(\n  {\n    target: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n    qty: new ARIOToken(100).toMARIO(),\n  },\n  {\n    tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }],\n  },\n);\n```\n\nPay the early withdrawal fee and withdraw instantly.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.decreaseDelegateStake({\n  target: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  qty: new ARIOToken(100).toMARIO(),\n  instant: true, // Immediately withdraw this stake and pay the instant withdrawal fee\n});\n```\n\n#### `getDelegations({ address, cursor, limit, sortBy, sortOrder })`\n\nRetrieves all active and vaulted stakes across all gateways for a specific address, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last delegationId (concatenated gateway and startTimestamp of the delgation) from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst vaults = await ario.getDelegations({\n  address: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  cursor: \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ_123456789\",\n  limit: 2,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"asc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"asc\",\n  \"hasMore\": true,\n  \"totalItems\": 95,\n  \"limit\": 2,\n  \"sortBy\": \"startTimestamp\",\n  \"items\": [\n    {\n      \"type\": \"stake\",\n      \"startTimestamp\": 1727815440632,\n      \"gatewayAddress\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"delegationId\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ_1727815440632\",\n      \"balance\": 1383212512\n    },\n    {\n      \"type\": \"vault\",\n      \"startTimestamp\": 1730996691117,\n      \"gatewayAddress\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n      \"delegationId\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ_1730996691117\",\n      \"vaultId\": \"_sGDS7X1hyLCVpfe40GWioH9BSOb7f0XWbhHBa1q4-g\",\n      \"balance\": 50000000,\n      \"endTimestamp\": 1733588691117\n    }\n  ],\n  \"nextCursor\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ_1730996691117\"\n}\n```\n\n\u003c/details\u003e\n\n#### `instantWithdrawal({ gatewayAddress, vaultId })`\n\nInstantly withdraws an existing vault on a gateway. If no `gatewayAddress` is provided, the signer's address will be used.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n// removes a delegated vault from a gateway\nconst { id: txId } = await ario.instantWithdrawal(\n  {\n    // gateway address where delegate vault exists\n    gatewayAddress: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n    // delegated vault id to cancel\n    vaultId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n  },\n  // optional additional tags\n  {\n    tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }],\n  },\n);\n// removes an operator vault from a gateway\nconst { id: txId } = await ario.instantWithdrawal({\n  vaultId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n});\n```\n\n#### `cancelWithdrawal({ gatewayAddress, vaultId })`\n\nCancels an existing vault on a gateway. The vaulted stake will be returned to the callers stake. If no `gatewayAddress` is provided, the signer's address will be used.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n// cancels a delegated vault from a gateway\nconst { id: txId } = await ario.cancelWithdrawal(\n  {\n    // gateway address where vault exists\n    gatewayAddress: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n    // vault id to cancel\n    vaultId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n// cancels an operator vault from a gateway\nconst { id: txId } = await ario.cancelWithdrawal({\n  // operator vault id to cancel\n  vaultId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n});\n```\n\n#### `getAllowedDelegates({ address, cursor, limit, sortBy, sortOrder })`\n\nRetrieves all allowed delegates for a specific address. The `cursor` used for pagination is the last address from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst allowedDelegates = await ario.getAllowedDelegates({\n  address: \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": false,\n  \"totalItems\": 4,\n  \"limit\": 100,\n  \"items\": [\n    \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM\",\n    \"N4h8M9A9hasa3tF47qQyNvcKjm4APBKuFs7vqUVm-SI\",\n    \"JcC4ZLUY76vmWha5y6RwKsFqYTrMZhbockl8iM9p5lQ\",\n    \"31LPFYoow2G7j-eSSsrIh8OlNaARZ84-80J-8ba68d8\"\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `getGatewayVaults({ address, cursor, limit, sortBy, sortOrder })`\n\nRetrieves all vaults across all gateways for a specific address, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last vaultId from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst vaults = await ario.getGatewayVaults({\n  address: '\"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM',\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": false,\n  \"totalItems\": 1,\n  \"limit\": 100,\n  \"sortBy\": \"endTimestamp\",\n  \"items\": [\n    {\n      \"cursorId\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM_1728067635857\",\n      \"startTimestamp\": 1728067635857,\n      \"balance\": 50000000000,\n      \"vaultId\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM\",\n      \"endTimestamp\": 1735843635857\n    }\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `getAllGatewayVaults({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all vaults across all gateways, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last vaultId from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst vaults = await ario.getAllGatewayVaults({\n  limit: 1,\n  sortBy: \"endTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": true,\n  \"totalItems\": 95,\n  \"limit\": 1,\n  \"sortBy\": \"endTimestamp\",\n  \"items\": [\n    {\n      \"cursorId\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM_E-QVU3dta36Wia2uQw6tQLjQk7Qw5uN0Z6fUzsoqzUc\",\n      \"gatewayAddress\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM\",\n      \"startTimestamp\": 1728067635857,\n      \"balance\": 50000000000,\n      \"vaultId\": \"E-QVU3dta36Wia2uQw6tQLjQk7Qw5uN0Z6fUzsoqzUc\",\n      \"endTimestamp\": 1735843635857\n    }\n  ],\n  \"nextCursor\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM_E-QVU3dta36Wia2uQw6tQLjQk7Qw5uN0Z6fUzsoqzUc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `getWithdrawals({ address, cursor, limit, sortBy, sortOrder })`\n\nReturns every pending stake withdrawal owned by `address` — covering both operator-stake decreases (`isDelegate: false`) and delegate-stake decreases (`isDelegate: true`). A withdrawal is claimable when `Date.now() \u003e= endTimestamp`; call `claimWithdrawal({ withdrawalId: item.vaultId })` to release the tokens.\n\nThis is the per-owner read needed to drive \"you have X claimable withdrawals\" UIs without fanning out across every gateway the wallet has interacted with.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\nconst withdrawals = await ario.getWithdrawals({\n  address: \"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin\",\n});\n\nconst claimable = withdrawals.items.filter(\n  (w) =\u003e Date.now() \u003e= w.endTimestamp,\n);\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"hasMore\": false,\n  \"totalItems\": 2,\n  \"limit\": 100,\n  \"items\": [\n    {\n      \"cursorId\": \"8CSdSjf7gXqQ5p1U2qfdwHzVw9sZRYHJpDpV87dnvb4d\",\n      \"vaultId\": \"0\",\n      \"gatewayAddress\": \"Bxz7Q2tWfqr9Q5T6cZjUnVxRk9CnHwShfgUaW5fY1Mvr\",\n      \"balance\": 50000000000,\n      \"startTimestamp\": 1735843635857,\n      \"endTimestamp\": 1738435635857,\n      \"isDelegate\": true\n    },\n    {\n      \"cursorId\": \"FmWUz4w7vSdLcz1nN8H1n2KkjJgrQQXR1n4kV3WqJ7Hf\",\n      \"vaultId\": \"1\",\n      \"gatewayAddress\": \"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin\",\n      \"balance\": 10000000000,\n      \"startTimestamp\": 1735843835857,\n      \"endTimestamp\": 1738435835857,\n      \"isDelegate\": false\n    }\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `increaseOperatorStake({ qty })`\n\nIncreases the callers operator stake. Must be executed with a wallet registered as a gateway operator.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.increaseOperatorStake(\n  {\n    qty: new ARIOToken(100).toMARIO(),\n  },\n  {\n    tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }],\n  },\n);\n```\n\n#### `decreaseOperatorStake({ qty })`\n\nDecreases the callers operator stake. Must be executed with a wallet registered as a gateway operator. Requires `signer` to be provided on `ARIO.init` to sign the transaction.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.decreaseOperatorStake(\n  {\n    qty: new ARIOToken(100).toMARIO(),\n  },\n  {\n    tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }],\n  },\n);\n```\n\n#### `redelegateStake({ target, source, stakeQty, vaultId })`\n\nRedelegates the stake of a specific address to a new gateway. Vault ID may be optionally included in order to redelegate from an existing withdrawal vault. The redelegation fee is calculated based on the fee rate and the stake amount. Users are allowed one free redelegation every seven epochs. Each additional redelegation beyond the free redelegation will increase the fee by 10%, capping at a 60% redelegation fee.\n\ne.g: If 1000 mARIO is redelegated and the fee rate is 10%, the fee will be 100 mARIO. Resulting in 900 mARIO being redelegated to the new gateway and 100 mARIO being deducted back to the protocol balance.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\nconst { id: txId } = await ario.redelegateStake({\n  target: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  source: \"HwFceQaMQnOBgKDpnFqCqgwKwEU5LBme1oXRuQOWSRA\",\n  stakeQty: new ARIOToken(1000).toMARIO(),\n  vaultId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n});\n```\n\n#### `getRedelegationFee({ address })`\n\nRetrieves the fee rate as percentage required to redelegate the stake of a specific address. Fee rate ranges from 0% to 60% based on the number of redelegations since the last fee reset.\n\n```typescript\nconst ario = ARIO.init({ rpc });\n\nconst fee = await ario.getRedelegationFee({\n  address: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"redelegationFeeRate\": 10,\n  \"feeResetTimestamp\": 1730996691117\n}\n```\n\n\u003c/details\u003e\n\n#### `getAllDelegates({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all delegates across all gateways, paginated and sorted by the specified criteria. The `cursor` used for pagination is a `cursorId` derived from delegate address and the gatewayAddress from the previous request. e.g `address_gatewayAddress`.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst delegates = await ario.getAllDelegates({\n  limit: 2,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": true,\n  \"totalItems\": 95,\n  \"limit\": 2,\n  \"sortBy\": \"startTimestamp\",\n  \"items\": [\n    {\n      \"startTimestamp\": 1734709397622,\n      \"cursorId\": \"9jfM0uzGNc9Mkhjo1ixGoqM7ygSem9wx_EokiVgi0Bs_E-QVU3dta36Wia2uQw6tQLjQk7Qw5uN0Z6fUzsoqzUc\",\n      \"gatewayAddress\": \"E-QVU3dta36Wia2uQw6tQLjQk7Qw5uN0Z6fUzsoqzUc\",\n      \"address\": \"9jfM0uzGNc9Mkhjo1ixGoqM7ygSem9wx_EokiVgi0Bs\",\n      \"delegatedStake\": 2521349108,\n      \"vaultedStake\": 0\n    },\n    {\n      \"startTimestamp\": 1734593229454,\n      \"cursorId\": \"LtV0aSqgK3YI7c5FmfvZd-wG95TJ9sezj_a4syaLMS8_M0WP8KSzCvKpzC-HPF1WcddLgGaL9J4DGi76iMnhrN4\",\n      \"gatewayAddress\": \"M0WP8KSzCvKpzC-HPF1WcddLgGaL9J4DGi76iMnhrN4\",\n      \"address\": \"LtV0aSqgK3YI7c5FmfvZd-wG95TJ9sezj_a4syaLMS8\",\n      \"delegatedStake\": 1685148110,\n      \"vaultedStake\": 10000000\n    }\n  ],\n  \"nextCursor\": \"PZ5vIhHf8VY969TxBPQN-rYY9CNFP9ggNsMBqlWUzWM_QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\"\n}\n```\n\n\u003c/details\u003e\n\n### Arweave Name System (ArNS)\n\n#### `resolveArNSName({ name })`\n\nResolves an ArNS name to the underlying data id stored on the names corresponding ANT id.\n\n##### Resolving a base name\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst record = await ario.resolveArNSName({ name: \"ardrive\" });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"name\": \"ardrive\",\n  \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  \"txId\": \"kvhEUsIY5bXe0Wu2-YUFz20O078uYFzmQIO-7brv8qw\",\n  \"type\": \"lease\",\n  \"ttlSeconds\": 3600,\n  \"undernameLimit\": 100\n}\n```\n\n\u003c/details\u003e\n\n##### Resolving an undername\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst record = await ario.resolveArNSName({ name: \"logo_ardrive\" });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"name\": \"ardrive\",\n  \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  \"txId\": \"kvhEUsIY5bXe0Wu2-YUFz20O078uYFzmQIO-7brv8qw\",\n  \"type\": \"lease\",\n  \"ttlSeconds\": 3600,\n  \"undernameLimit\": 100\n}\n```\n\n\u003c/details\u003e\n\n#### `buyRecord({ name, type, years, processId })`\n\nPurchases a new ArNS record with the specified name, type, processId, and duration.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n**Arguments:**\n\n- `name` - _required_: the name of the ArNS record to purchase\n- `type` - _required_: the type of ArNS record to purchase\n- `processId` - _optional_: the process id of an existing ANT process. If not provided, a new ANT process using the provided `signer` will be spawned, and the ArNS record will be assigned to that process.\n- `years` - _optional_: the duration of the ArNS record in years. If not provided and `type` is `lease`, the record will be leased for 1 year. If not provided and `type` is `permabuy`, the record will be permanently registered.\n- `referrer` - _optional_: track purchase referrals for analytics (e.g. `my-app.com`)\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst record = await ario.buyRecord(\n  {\n    name: \"ardrive\",\n    type: \"lease\",\n    years: 1,\n    processId: \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\", // optional: assign to existing ANT process\n    referrer: \"my-app.com\", // optional: track purchase referrals for analytics\n  },\n  {\n    // optional tags\n    tags: [{ name: \"App-Name\", value: \"ArNS-App\" }],\n    onSigningProgress: (step, event) =\u003e {\n      console.log(`Signing progress: ${step}`);\n      if (step === \"spawning-ant\") {\n        console.log(\"Spawning ant:\", event);\n      }\n      if (step === \"registering-ant\") {\n        console.log(\"Registering ant:\", event);\n      }\n      if (step === \"verifying-state\") {\n        console.log(\"Verifying state:\", event);\n      }\n      if (step === \"buying-name\") {\n        console.log(\"Buying name:\", event);\n      }\n    },\n  },\n);\n```\n\n#### `upgradeRecord({ name })`\n\nUpgrades an existing leased ArNS record to a permanent ownership. The record must be currently owned by the caller and be of type \"lease\".\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst record = await ario.upgradeRecord(\n  {\n    name: \"ardrive\",\n    referrer: \"my-app.com\", // optional: track purchase referrals for analytics\n  },\n  {\n    // optional tags\n    tags: [{ name: \"App-Name\", value: \"ArNS-App\" }],\n  },\n);\n```\n\n#### `getArNSRecord({ name })`\n\nRetrieves the record info of the specified ArNS name.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst record = await ario.getArNSRecord({ name: \"ardrive\" });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  \"startTimestamp\": 1720720819969,\n  \"endTimestamp\": 1752256702026,\n  \"type\": \"lease\",\n  \"undernameLimit\": 100,\n  \"purchasePrice\": 75541282285\n}\n```\n\n\u003c/details\u003e\n\n#### `getArNSRecords({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all registered ArNS records of the ARIO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last ArNS name from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\n// get the newest 100 names\nconst records = await ario.getArNSRecords({\n  limit: 100,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\nAvailable `sortBy` options are any of the keys on the record object, e.g. `name`, `processId`, `endTimestamp`, `startTimestamp`, `type`, `undernames`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"name\": \"ao\",\n      \"processId\": \"eNey-H9RB9uCdoJUvPULb35qhZVXZcEXv8xds4aHhkQ\",\n      \"purchasePrice\": 75541282285,\n      \"startTimestamp\": 1720720621424,\n      \"endTimestamp\": 1752256702026,\n      \"type\": \"permabuy\",\n      \"undernameLimit\": 10\n    },\n    {\n      \"name\": \"ardrive\",\n      \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n      \"endTimestamp\": 1720720819969,\n      \"startTimestamp\": 1720720620813,\n      \"purchasePrice\": 75541282285,\n      \"type\": \"lease\",\n      \"undernameLimit\": 100\n    },\n    {\n      \"name\": \"arweave\",\n      \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n      \"endTimestamp\": 1720720819969,\n      \"startTimestamp\": 1720720620800,\n      \"purchasePrice\": 75541282285,\n      \"type\": \"lease\",\n      \"undernameLimit\": 100\n    },\n    {\n      \"name\": \"ar-io\",\n      \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n      \"endTimestamp\": 1720720819969,\n      \"startTimestamp\": 1720720619000,\n      \"purchasePrice\": 75541282285,\n      \"type\": \"lease\",\n      \"undernameLimit\": 100\n    },\n    {\n      \"name\": \"fwd\",\n      \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n      \"endTimestamp\": 1720720819969,\n      \"startTimestamp\": 1720720220811,\n      \"purchasePrice\": 75541282285,\n      \"type\": \"lease\",\n      \"undernameLimit\": 100\n    }\n    // ...95 other records\n  ],\n  \"hasMore\": true,\n  \"nextCursor\": \"fwdresearch\",\n  \"totalItems\": 21740,\n  \"sortBy\": \"startTimestamp\",\n  \"sortOrder\": \"desc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `getArNSRecordsForAddress({ address, cursor, limit, sortBy, sortOrder })`\n\nRetrieves all registered ArNS records of the specified address according to the `ANTRegistry` access control list, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last ArNS name from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst records = await ario.getArNSRecordsForAddress({\n  address: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  limit: 100,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\nAvailable `sortBy` options are any of the keys on the record object, e.g. `name`, `processId`, `endTimestamp`, `startTimestamp`, `type`, `undernames`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"limit\": 1,\n  \"totalItems\": 31,\n  \"hasMore\": true,\n  \"nextCursor\": \"ardrive\",\n  \"items\": [\n    {\n      \"startTimestamp\": 1740009600000,\n      \"name\": \"ardrive\",\n      \"endTimestamp\": 1777328018367,\n      \"type\": \"permabuy\",\n      \"purchasePrice\": 0,\n      \"undernameLimit\": 100,\n      \"processId\": \"hpF0HdijWlBLFePjWX6u_-Lg3Z2E_PrP_AoaXDVs0bA\"\n    }\n  ],\n  \"sortOrder\": \"desc\",\n  \"sortBy\": \"startTimestamp\"\n}\n```\n\n\u003c/details\u003e\n\n#### `increaseUndernameLimit({ name, qty })`\n\nIncreases the undername support of a domain up to a maximum of 10k. Domains, by default, support up to 10 undernames.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.increaseUndernameLimit(\n  {\n    name: \"ar-io\",\n    qty: 420,\n    referrer: \"my-app.com\", // optional: track purchase referrals for analytics\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `extendLease({ name, years })`\n\nExtends the lease of a registered ArNS domain, with an extension of 1-5 years depending on grace period status. Permanently registered domains cannot be extended.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.extendLease(\n  {\n    name: \"ar-io\",\n    years: 1,\n    referrer: \"my-app.com\", // optional: track purchase referrals for analytics\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `getTokenCost({ intent, ...args })`\n\nCalculates the price in mARIO to perform the interaction in question, eg a 'Buy-Name' interaction, where args are the specific params for that interaction.\n\n```typescript\nconst price = await ario\n  .getTokenCost({\n    intent: \"Buy-Name\",\n    name: \"ar-io\",\n    type: \"permabuy\",\n  })\n  .then((p) =\u003e new mARIOToken(p).toARIO()); // convert to ARIO for readability\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n1642.34\n```\n\n\u003c/details\u003e\n\n#### `getCostDetails({ intent, fromAddress, fundFrom, ...args})`\n\nCalculates the expanded cost details for the interaction in question, e.g a 'Buy-Name' interaction, where args are the specific params for that interaction. The fromAddress is the address that would be charged for the interaction, and fundFrom is where the funds would be taken from, either `balance`, `stakes`, or `any`.\n\nOn Solana, the result also includes a `gasEstimate` — the total SOL (in lamports) the wallet needs to execute the intent: transaction fees (quoted from recent on-chain prioritization fees) plus rent-exempt deposits for the accounts the flow creates. For `Buy-Name` that covers both transactions (ANT spawn + buy) and the rent for the spawned asset/PDAs and the ArNS record; first-time buyers with no ACL accounts yet are quoted the ACL bootstrap rent as well (pass `fromAddress` so that check can be made). The fee side is a conservative upper bound: the write path tightens the compute-unit limit from a pre-send simulation, so the landed fee is usually lower.\n\n```typescript\nconst costDetails = await ario.getCostDetails({\n  intent: \"Buy-Name\",\n  fromAddress: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  fundFrom: \"stakes\",\n  name: \"ar-io\",\n  type: \"permabuy\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"tokenCost\": 1907401818,\n  \"discounts\": [\n    {\n      \"name\": \"Gateway Operator\",\n      \"discountTotal\": 476850455,\n      \"multiplier\": 0.8\n    }\n  ],\n  \"gasEstimate\": {\n    \"totalLamports\": 14679680,\n    \"feeLamports\": 23000,\n    \"baseFeeLamports\": 15000,\n    \"priorityFeeLamports\": 8000,\n    \"rentLamports\": 14656680,\n    \"rentReclaimedLamports\": 0,\n    \"priorityFeeMicroLamports\": 10000,\n    \"computeUnitLimit\": 400000,\n    \"signatureCount\": 3,\n    \"transactionCount\": 2\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getDemandFactor()`\n\nRetrieves the current demand factor of the network. The demand factor is a multiplier applied to the cost of ArNS interactions based on the current network demand.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst demandFactor = await ario.getDemandFactor();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n1.05256\n```\n\n\u003c/details\u003e\n\n#### `getArNSReturnedNames({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all active returned names of the ARIO process, paginated and sorted by the specified criteria. The `cursor` used for pagination is the last returned name from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst returnedNames = await ario.getArNSReturnedNames({\n  limit: 100,\n  sortBy: \"endTimestamp\",\n  sortOrder: \"asc\", // return the returned names ending soonest first\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"items\": [\n    {\n      \"name\": \"permalink\",\n      \"startTimestamp\": 1729775641349,\n      \"endTimestamp\": 1730985241349,\n      \"initiator\": \"GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc\",\n      \"premiumMultiplier\": 50\n    }\n  ],\n  \"hasMore\": false,\n  \"totalItems\": 1,\n  \"limit\": 100,\n  \"sortBy\": \"endTimestamp\",\n  \"sortOrder\": \"asc\"\n}\n```\n\n\u003c/details\u003e\n\n#### `getArNSReturnedName({ name })`\n\nRetrieves the returned name data for the specified returned name.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst returnedName = await ario.getArNSReturnedName({ name: \"permalink\" });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"name\": \"permalink\",\n  \"startTimestamp\": 1729775641349,\n  \"endTimestamp\": 1730985241349,\n  \"initiator\": \"GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc\",\n  \"premiumMultiplier\": 50\n}\n```\n\n\u003c/details\u003e\n\n### Epochs\n\n#### `getCurrentEpoch()`\n\nReturns the current epoch data.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst epoch = await ario.getCurrentEpoch();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"epochIndex\": 0,\n  \"startHeight\": 0,\n  \"startTimestamp\": 1720720621424,\n  \"endTimestamp\": 1752256702026,\n  \"distributionTimestamp\": 1752256702026,\n  \"observations\": {\n    \"failureSummaries\": {\n      \"-Tk2DDk8k4zkwtppp_XFKKI5oUgh6IEHygAoN7mD-w8\": [\n        \"Ie2wEEUDKoU26c7IuckHNn3vMFdNQnMvfPBrFzAb3NA\"\n      ]\n    },\n    \"reports\": {\n      \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\": \"B6UUjKWjjEWDBvDSMXWNmymfwvgR9EN27z5FTkEVlX4\"\n    }\n  },\n  \"prescribedNames\": [\"ardrive\", \"ar-io\", \"arweave\", \"fwd\", \"ao\"],\n  \"prescribedObservers\": [\n    {\n      \"gatewayAddress\": \"2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE\",\n      \"observerAddress\": \"2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE\",\n      \"stake\": 10000000000,\n      \"startTimestamp\": 1720720621424,\n      \"stakeWeight\": 1,\n      \"tenureWeight\": 0.4494598765432099,\n      \"gatewayPerformanceRatio\": 1,\n      \"observerPerformanceRatio\": 1,\n      \"gatewayRewardRatioWeight\": 1,\n      \"observerRewardRatioWeight\": 1,\n      \"compositeWeight\": 0.4494598765432099,\n      \"normalizedCompositeWeight\": 0.002057032496835938\n    }\n  ],\n  \"distributions\": {\n    \"totalEligibleGateways\": 1,\n    \"totalEligibleRewards\": 100000000,\n    \"totalEligibleObserverReward\": 100000000,\n    \"totalEligibleGatewayReward\": 100000000\n  },\n  \"arnsStats\": {\n    \"totalReturnedNames\": 0,\n    \"totalActiveNames\": 0,\n    \"totalGracePeriodNames\": 0,\n    \"totalReservedNames\": 0\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getEpoch({ epochIndex })`\n\nReturns the epoch data for the specified block height. If no epoch index is provided, the current epoch is used.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst epoch = await ario.getEpoch({ epochIndex: 0 });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"epochIndex\": 0,\n  \"startHeight\": 0,\n  \"startTimestamp\": 1720720620813,\n  \"endTimestamp\": 1752256702026,\n  \"distributionTimestamp\": 1752256702026,\n  \"observations\": {\n    \"failureSummaries\": {\n      \"-Tk2DDk8k4zkwtppp_XFKKI5oUgh6IEHygAoN7mD-w8\": [\n        \"Ie2wEEUDKoU26c7IuckHNn3vMFdNQnMvfPBrFzAb3NA\"\n      ]\n    },\n    \"reports\": {\n      \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\": \"B6UUjKWjjEWDBvDSMXWNmymfwvgR9EN27z5FTkEVlX4\"\n    }\n  },\n  \"prescribedNames\": [\"ardrive\", \"ar-io\", \"arweave\", \"fwd\", \"ao\"],\n  \"prescribedObservers\": [\n    {\n      \"gatewayAddress\": \"2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE\",\n      \"observerAddress\": \"2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE\",\n      \"stake\": 10000000000,\n      \"startTimestamp\": 1720720620813,\n      \"stakeWeight\": 1,\n      \"tenureWeight\": 0.4494598765432099,\n      \"gatewayPerformanceRatio\": 1,\n      \"observerPerformanceRatio\": 1,\n      \"gatewayRewardRatioWeight\": 1,\n      \"observerRewardRatioWeight\": 1,\n      \"compositeWeight\": 0.4494598765432099,\n      \"normalizedCompositeWeight\": 0.002057032496835938\n    }\n  ],\n  \"distributions\": {\n    \"totalEligibleGateways\": 1,\n    \"totalEligibleRewards\": 100000000,\n    \"totalEligibleObserverReward\": 100000000,\n    \"totalEligibleGatewayReward\": 100000000\n  },\n  \"arnsStats\": {\n    \"totalReturnedNames\": 0,\n    \"totalActiveNames\": 0,\n    \"totalGracePeriodNames\": 0,\n    \"totalReservedNames\": 0\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getEligibleEpochRewards({ epochIndex }, { cursor, limit, sortBy, sortOrder })\n\nReturns the eligible epoch rewards for the specified block height. If no epoch index is provided, the current epoch is used.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst rewards = await ario.getEligibleEpochRewards({ epochIndex: 0 });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n  \n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": true,\n  \"totalItems\": 37,\n  \"limit\": 1,\n  \"sortBy\": \"cursorId\",\n  \"items\": [\n    {\n      \"cursorId\": \"xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0_xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0\",\n      \"recipient\": \"xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0\",\n      \"gatewayAddress\": \"xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0\",\n      \"eligibleReward\": 2627618704,\n      \"type\": \"operatorReward\"\n    }\n  ],\n  \"nextCursor\": \"xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0_xN_aVln30LmoCffwmk5_kRkcyQZyZWy1o_TNtM_CTm0\"\n}\n```\n\u003c/details\u003e\n\n#### `getObservations({ epochIndex })`\n\nReturns the epoch-indexed observation list. If no epoch index is provided, the current epoch is used.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst observations = await ario.getObservations();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"failureSummaries\": {\n    \"-Tk2DDk8k4zkwtppp_XFKKI5oUgh6IEHygAoN7mD-w8\": [\n      \"Ie2wEEUDKoU26c7IuckHNn3vMFdNQnMvfPBrFzAb3NA\"\n    ]\n  },\n  \"reports\": {\n    \"IPdwa3Mb_9pDD8c2IaJx6aad51Ss-_TfStVwBuhtXMs\": \"B6UUjKWjjEWDBvDSMXWNmymfwvgR9EN27z5FTkEVlX4\",\n    \"Ie2wEEUDKoU26c7IuckHNn3vMFdNQnMvfPBrFzAb3NA\": \"7tKsiQ2fxv0D8ZVN_QEv29fZ8hwFIgHoEDrpeEG0DIs\",\n    \"osZP4D9cqeDvbVFBaEfjIxwc1QLIvRxUBRAxDIX9je8\": \"aatgznEvC_UPcxp1v0uw_RqydhIfKm4wtt1KCpONBB0\",\n    \"qZ90I67XG68BYIAFVNfm9PUdM7v1XtFTn7u-EOZFAtk\": \"Bd8SmFK9-ktJRmwIungS8ur6JM-JtpxrvMtjt5JkB1M\"\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getDistributions({ epochIndex })`\n\nReturns the current rewards distribution information. If no epoch index is provided, the current epoch is used.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst distributions = await ario.getDistributions({ epochIndex: 0 });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"totalEligibleGateways\": 1,\n  \"totalEligibleRewards\": 100000000,\n  \"totalEligibleObserverReward\": 100000000,\n  \"totalEligibleGatewayReward\": 100000000\n}\n```\n\n#### `saveObservations({ reportTxId, failedGateways })`\n\nSaves the observations of the current epoch. Requires `signer` to be provided on `ARIO.init` to sign the transaction.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.saveObservations(\n  {\n    reportTxId: \"fDrr0_J4Iurt7caNST02cMotaz2FIbWQ4Kcj616RHl3\",\n    failedGateways: [\"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\"],\n  },\n  {\n    tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }],\n  },\n);\n```\n\n\u003c/details\u003e\n\n#### `getPrescribedObservers({ epochIndex })`\n\nRetrieves the prescribed observers of the ARIO process. To fetch prescribed observers for a previous epoch set the `epochIndex` to the desired epoch index.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst observers = await ario.getPrescribedObservers({ epochIndex: 0 });\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n[\n  {\n    \"gatewayAddress\": \"BpQlyhREz4lNGS-y3rSS1WxADfxPpAuing9Lgfdrj2U\",\n    \"observerAddress\": \"2Fk8lCmDegPg6jjprl57-UCpKmNgYiKwyhkU4vMNDnE\",\n    \"stake\": 10000000000,\n    \"startTimestamp\": 1720720620813,\n    \"stakeWeight\": 1,\n    \"tenureWeight\": 0.41453703703703704,\n    \"gatewayPerformanceRatio\": 1,\n    \"observerPerformanceRatio\": 1,\n    \"gatewayRewardRatioWeight\": 1,\n    \"observerRewardRatioWeight\": 1,\n    \"compositeWeight\": 0.41453703703703704,\n    \"normalizedCompositeWeight\": 0.0018972019546783507\n  }\n]\n```\n\n\u003c/details\u003e\n\n#### `crankEpochStep(options?)`\n\nHigh-level, permissionless epoch crank. Advances the epoch lifecycle by **one\nstep per call** and returns the action it took — run it on a loop (this is what\nthe standalone cranker and the observer-embedded cranker do). It owns the whole\nsequence so you don't orchestrate the individual instructions yourself:\n\n`create` → `tally` → `prescribe` → `distribute` → `close` — closing an epoch's\nobservation PDAs first (`close_observation`) so `close_epoch` doesn't revert —\nplus an idle-tail of permissionless maintenance: `compound` delegate rewards,\n`update_demand_factor`, and `prune_returned_names`. The close path is\nnon-wedging: a cleanup failure never blocks creation of the next epoch.\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n\n// one step\nconst result = await ario.crankEpochStep();\n// → { action, epochIndex?, txId?, progress? }\n//   action ∈ create | tally | prescribe | distribute | close\n//          | close_observation | compound | update_demand_factor\n//          | prune_returned_names | idle\n\n// or drive it on an interval\nsetInterval(async () =\u003e {\n  const r = await ario.crankEpochStep();\n  if (r.action !== 'idle') console.log(r.action, r.epochIndex, r.txId);\n}, 60_000);\n```\n\nAll options are optional: `batchSize`, `enableClose`, `epochRetention`,\n`enableCompound`, `compoundMinPendingRewards`, `enableDemandFactorRoll`,\n`enablePrune`, `pruneBatchSize`, `nameRegistryAccount`.\n\n### Primary Names\n\n#### `getPrimaryNames({ cursor, limit, sortBy, sortOrder })`\n\nRetrieves all primary names paginated and sorted by the specified criteria. The `cursor` used for pagination is the last name from the previous request.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst names = await ario.getPrimaryNames({\n  cursor: \"ao\", // this is the last name from the previous request\n  limit: 1,\n  sortBy: \"startTimestamp\",\n  sortOrder: \"desc\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"sortOrder\": \"desc\",\n  \"hasMore\": true,\n  \"totalItems\": 100,\n  \"limit\": 1,\n  \"sortBy\": \"startTimestamp\",\n  \"nextCursor\": \"arns\",\n  \"items\": [\n    {\n      \"name\": \"arns\",\n      \"owner\": \"HwFceQaMQnOBgKDpnFqCqgwKwEU5LBme1oXRuQOWSRA\",\n      \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n      \"startTimestamp\": 1719356032297\n    }\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `getPrimaryName({ name, address })`\n\nRetrieves the primary name for a given name or address.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst name = await ario.getPrimaryName({\n  name: \"arns\",\n});\n// or\nconst name = await ario.getPrimaryName({\n  address: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"name\": \"arns\",\n  \"owner\": \"HwFceQaMQnOBgKDpnFqCqgwKwEU5LBme1oXRuQOWSRA\",\n  \"processId\": \"bh9l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  \"startTimestamp\": 1719356032297\n}\n```\n\n\u003c/details\u003e\n\n#### `setPrimaryName({ name })`\n\nSets an ArNS name already owned by the `signer` as their primary name. Note: `signer` must be the owner of the `processId` that is assigned to the name. If not, the transaction will fail.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nawait ario.setPrimaryName({ name: 'my-arns-name' });\n```\n\n#### `requestPrimaryName({ name })`\n\nRequests a primary name for the `signer`'s address. The request must be approved by the new owner of the requested name via the `approvePrimaryNameRequest`[#approveprimarynamerequest-name-address-] API.\n\n_Note: Requires `signer` to be provided on `ARIO.init` to sign the transaction._\n\n```typescript\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\nconst { id: txId } = await ario.requestPrimaryName({\n  name: \"arns\",\n});\n```\n\n#### `getPrimaryNameRequest({ initiator })`\n\nRetrieves the primary name request for a a wallet address.\n\n```typescript\nconst ario = ARIO.init({ rpc });\nconst request = await ario.getPrimaryNameRequest({\n  initiator: \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"initiator\": \"t4Xr0_J4Iurt7caNST02cMotaz2FIbWQ4Kbj616RHl3\",\n  \"name\": \"arns\",\n  \"startTimestamp\": 1728067635857,\n  \"endTimestamp\": 1735843635857\n}\n```\n\n\u003c/details\u003e\n\n### Configuration\n\n`ARIO.init` accepts a `@solana/kit` RPC client plus optional program ID\noverrides for non-mainnet clusters. See [Networks](#networks) above for\nthe full shape.\n\n## ANT Contracts\n\nThe ANT client class exposes APIs for Arweave Name Tokens. On Solana an\nANT is a Metaplex Core asset; its `processId` is the asset's mint\npubkey. The `ario-ant` program owns the on-chain records / controllers\nstate attached to the asset.\n\n### Initialize\n\n#### `init({ processId, rpc, rpcSubscriptions?, signer? })`\n\nFactory that creates a read-only or writeable ANT client. Providing\n`signer` and `rpcSubscriptions` enables write methods (`setRecord`,\n`transfer`, `addController`, etc.).\n\n```typescript\nimport { ANT } from '@ar.io/sdk';\nimport {\n  createSolanaRpc,\n  createSolanaRpcSubscriptions,\n} from '@solana/kit';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\n\n// Read-only\nconst ant = await ANT.init({\n  processId: '\u003cMPL Core asset pubkey\u003e',\n  rpc,\n});\n\n// Read + write\nconst antWrite = await ANT.init({\n  processId: '\u003cMPL Core asset pubkey\u003e',\n  rpc,\n  rpcSubscriptions: createSolanaRpcSubscriptions(\n    'wss://api.mainnet-beta.solana.com',\n  ),\n  signer,\n});\n```\n\n### Spawn\n\n#### `ANT.spawn({ rpc, rpcSubscriptions, signer, state })`\n\nStatic factory that mints a new MPL Core asset and initializes the\n`ario-ant` PDAs in a single transaction. Returns\n`{ processId, mint, signature }`.\n\n```typescript\nimport { ANT } from '@ar.io/sdk';\n\nconst { processId, signature } = await ANT.spawn({\n  rpc,\n  rpcSubscriptions,\n  signer,\n  state: {\n    name: 'My ANT',\n    ticker: 'MYANT',\n    description: 'My ANT token',\n    uri: 'ar://\u003cmetadata-tx-id\u003e',\n  },\n});\n```\n\n**CLI Usage:**\n\n```bash\nar.io spawn-ant \\\n  --wallet-file wallet.json \\\n  --name \"My ANT\" \\\n  --ticker \"MYANT\" \\\n  --metadata-uri \"ar://\u003cmetadata-tx-id\u003e\"\n```\n\n**Parameters:**\n\n- `state.name: string` — display name of the ANT\n- `state.ticker?: string` — ticker symbol\n- `state.description?: string` — short description\n- `state.uri: string` — `ar://` URI of the Metaplex Core asset's JSON\n  metadata. Build via `buildAntMetadata` from `@ar.io/sdk` and upload\n  to Arweave (e.g. via `@ardrive/turbo-sdk`).\n- `state.keywords?: string[]`\n- `state.logo?: string` — Arweave TX ID of the logo\n- `state.transactionId?: string` — initial `@` record target\n\n**Returns:**\n\n```ts\n{\n  processId: string;  // the MPL Core asset mint pubkey\n  mint: Address;\n  signature: string;  // the Solana tx signature\n}\n```\n\n### Versions\n\n#### `getModuleId({ graphqlUrl?, retries? })`\n\nGets the module ID of the current ANT process by querying its spawn transaction tags. Results are cached after the first successful fetch.\n\n```typescript\nconst moduleId = await ant.getModuleId();\nconsole.log(`ANT was spawned with module: ${moduleId}`);\n\n// With custom GraphQL URL and retries\nconst moduleId = await ant.getModuleId({\n  graphqlUrl: \"https://turbo-gateway.com/graphql\",\n  retries: 5,\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n\"FKtQtOOtlcWCW2pXrwWFiCSlnuewMZOHCzhulVkyqBE\"\n```\n\n\u003c/details\u003e\n\n#### `getVersion({ antRegistryId?, graphqlUrl?, retries? })`\n\nGets the version string of the current ANT by matching its module ID with versions from the ANT registry.\n\n```typescript\nconst version = await ant.getVersion();\nconsole.log(`ANT is running version: ${version}`);\n\n// With custom ANT registry\nconst version = await ant.getVersion({\n  antRegistryId: \"custom-ant-registry-id\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n\"23\"\n```\n\n\u003c/details\u003e\n\n#### `isLatestVersion({ antRegistryId?, graphqlUrl?, retries? })`\n\nChecks if the current ANT version is the latest according to the ANT registry.\n\n```typescript\nconst isLatest = await ant.isLatestVersion();\nif (!isLatest) {\n  console.log(\"ANT can be upgraded to the latest version\");\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\ntrue\n```\n\n\u003c/details\u003e\n\n### State\n\n#### `getInfo()`\n\nRetrieves the information of the ANT process.\n\n```typescript\nconst info = await ant.getInfo();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"Name\": \"ArDrive\",\n  \"Owner\": \"QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ\",\n  \"Ticker\": \"ANT-ARDRIVE\",\n  \"Total-Supply\": \"1\",\n  \"Description\": \"This is the ANT for the ArDrive decentralized web app.\",\n  \"Keywords\": [\"File-sharing\", \"Publishing\", \"dApp\"],\n  \"Logo\": \"Sie_26dvgyok0PZD_-iQAFOhOd5YxDTkczOLoqTTL_A\",\n  \"Denomination\": \"0\",\n  \"Handlers\": [\n    \"balance\",\n    \"balances\",\n    \"totalSupply\",\n    \"info\",\n    \"controllers\",\n    \"record\",\n    \"records\",\n    \"state\",\n    \"transfer\",\n    \"addController\",\n    \"removeController\",\n    \"setRecord\",\n    \"removeRecord\",\n    \"setName\",\n    \"setTicker\",\n    \"setDescription\",\n    \"setKeywords\",\n    \"setLogo\",\n    \"initializeState\",\n    \"releaseName\",\n    \"reassignName\",\n    \"approvePrimaryName\",\n    \"removePrimaryNames\",\n    \"transferRecordOwnership\",\n    \"_eval\",\n    \"_default\"\n  ]\n}\n```\n\n\u003c/details\u003e\n\n#### `getHandlers()`\n\nRetrieves the handlers supported on the ANT\n\n```typescript\nconst handlers = await ant.getHandlers();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n[\n  \"balance\",\n  \"balances\",\n  \"totalSupply\",\n  \"info\",\n  \"controllers\",\n  \"record\",\n  \"records\",\n  \"state\",\n  \"transfer\",\n  \"addController\",\n  \"removeController\",\n  \"setRecord\",\n  \"removeRecord\",\n  \"setName\",\n  \"setTicker\",\n  \"setDescription\",\n  \"setKeywords\",\n  \"setLogo\",\n  \"initializeState\",\n  \"releaseName\",\n  \"reassignName\",\n  \"approvePrimaryName\",\n  \"removePrimaryNames\",\n  \"transferRecordOwnership\",\n  \"_eval\",\n  \"_default\"\n]\n```\n\n\u003c/details\u003e\n\n#### `getState()`\n\nRetrieves the state of the ANT process.\n\n```typescript\nconst state = await ant.getState();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"Name\": \"ar.io Foundation\",\n  \"Ticker\": \"ANT-AR-IO\",\n  \"Description\": \"A friendly description for this ANT.\",\n  \"Keywords\": [\"keyword1\", \"keyword2\", \"keyword3\"],\n  \"Denomination\": 0,\n  \"Owner\": \"98O1_xqDLrBKRfQPWjF5p7xZ4Jx6GM8P5PeJn26xwUY\",\n  \"Controllers\": [],\n  \"Records\": {\n    \"v1-0-0_whitepaper\": {\n      \"transactionId\": \"lNjWn3LpyhKC95Kqe-x8X2qgju0j98MhucdDKK85vc4\",\n      \"ttlSeconds\": 900,\n      \"targetProtocol\": 0\n    },\n    \"@\": {\n      \"transactionId\": \"2rMLb2uHAyEt7jSu6bXtKx8e-jOfIf7E-DOgQnm8EtU\",\n      \"ttlSeconds\": 3600,\n      \"targetProtocol\": 0\n    },\n    \"alice\": {\n      \"transactionId\": \"kMk95k_3R8x_7d3wB9tEOiL5v6n8QhR_VnFCh3aeE3f\",\n      \"ttlSeconds\": 900,\n      \"targetProtocol\": 0,\n      \"owner\": \"alice-wallet-address-123...\",\n      \"displayName\": \"Alice's Portfolio\",\n      \"logo\": \"avatar-tx-id-456...\",\n      \"description\": \"Personal portfolio and blog\",\n      \"keywords\": [\"portfolio\", \"personal\", \"blog\"]\n    },\n    \"whitepaper\": {\n      \"transactionId\": \"lNjWn3LpyhKC95Kqe-x8X2qgju0j98MhucdDKK85vc4\",\n      \"ttlSeconds\": 900,\n      \"targetProtocol\": 0\n    }\n  },\n  \"Balances\": {\n    \"98O1_xqDLrBKRfQPWjF5p7xZ4Jx6GM8P5PeJn26xwUY\": 1\n  },\n  \"Logo\": \"Sie_26dvgyok0PZD_-iQAFOhOd5YxDTkczOLoqTTL_A\",\n  \"TotalSupply\": 1,\n  \"Initialized\": true\n}\n```\n\n\u003c/details\u003e\n\n#### `getOwner()`\n\nReturns the owner of the configured ANT process.\n\n```typescript\nconst owner = await ant.getOwner();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n\"ccp3blG__gKUvG3hsGC2u06aDmqv4CuhuDJGOIg0jw4\"\n```\n\n\u003c/details\u003e\n\n#### `getName()`\n\nReturns the name of the ANT (not the same as ArNS name).\n\n```typescript\nconst name = await ant.getName();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n\"ArDrive\"\n```\n\n\u003c/details\u003e\n\n#### `getTicker()`\n\nReturns the ticker symbol of the ANT.\n\n```typescript\nconst ticker = await ant.getTicker();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n\"ANT-ARDRIVE\"\n```\n\n\u003c/details\u003e\n\n#### `getControllers()`\n\nReturns the controllers of the configured ANT process.\n\n```typescript\nconst controllers = await ant.getControllers();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n[\"ccp3blG__gKUvG3hsGC2u06aDmqv4CuhuDJGOIg0jw4\"]\n```\n\n\u003c/details\u003e\n\n#### `getRecords()`\n\nReturns all records on the configured ANT process, including the required `@` record that resolve connected ArNS names.\n\n```typescript\nconst records = await ant.getRecords();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"@\": {\n    \"transactionId\": \"UyC5P5qKPZaltMmmZAWdakhlDXsBF6qmyrbWYFchRTk\",\n    \"ttlSeconds\": 3600,\n    \"targetProtocol\": 0,\n    \"index\": 0\n  },\n  \"alice\": {\n    \"transactionId\": \"kMk95k_3R8x_7d3wB9tEOiL5v6n8QhR_VnFCh3aeE3f\",\n    \"ttlSeconds\": 900,\n    \"targetProtocol\": 0,\n    \"owner\": \"alice-wallet-address-123...\",\n    \"displayName\": \"Alice's Portfolio\",\n    \"logo\": \"avatar-tx-id-456...\",\n    \"description\": \"Personal portfolio and blog\",\n    \"keywords\": [\"portfolio\", \"personal\", \"blog\"],\n    \"index\": 1\n  },\n  \"zed\": {\n    \"transactionId\": \"-k7t8xMoB8hW482609Z9F4bTFMC3MnuW8bTvTyT8pFI\",\n    \"ttlSeconds\": 900,\n    \"targetProtocol\": 0,\n    \"index\": 2\n  },\n  \"ardrive\": {\n    \"transactionId\": \"-cucucachoodwedwedoiwepodiwpodiwpoidpwoiedp\",\n    \"ttlSeconds\": 900,\n    \"targetProtocol\": 0,\n    \"index\": 3\n  }\n}\n```\n\n\u003c/details\u003e\n\n#### `getRecord({ undername })`\n\nReturns a specific record by its undername.\n\n```typescript\nconst record = await ant.getRecord({ undername: \"dapp\" });\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"transactionId\": \"432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  \"ttlSeconds\": 900,\n  \"targetProtocol\": 0,\n  \"owner\": \"alice-wallet-address-123...\",\n  \"displayName\": \"Alice's Site\",\n  \"logo\": \"avatar-tx-id-456...\",\n  \"description\": \"Personal portfolio and blog\",\n  \"keywords\": [\"portfolio\", \"personal\", \"blog\"]\n}\n```\n\n\u003c/details\u003e\n\n### Balances\n\n#### `getBalances()`\n\nReturns all token balances for the ANT.\n\n```typescript\nconst balances = await ant.getBalances();\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n{\n  \"ccp3blG__gKUvG3hsGC2u06aDmqv4CuhuDJGOIg0jw4\": 1,\n  \"aGzM_yjralacHIUo8_nQXMbh9l1cy0aksiL_x9M359f\": 0\n}\n```\n\n\u003c/details\u003e\n\n#### `getBalance({ address })`\n\nReturns the balance of a specific address.\n\n```typescript\nconst balance = await ant.getBalance({\n  address: \"ccp3blG__gKUvG3hsGC2u06aDmqv4CuhuDJGOIg0jw4\",\n});\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eOutput\u003c/summary\u003e\n\n```json\n1\n```\n\n\u003c/details\u003e\n\n### Transfer\n\n#### `transfer({ target })`\n\nTransfers ownership of the ANT to a new target address. Target must be a Solana address. ANT transfers are standard Metaplex Core NFT transfers.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.transfer(\n  { target: \"aGzM_yjralacHIUo8_nQXMbh9l1cy0aksiL_x9M359f\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n### Controllers\n\n#### `addController({ controller })`\n\nAdds a new controller to the list of approved controllers on the ANT. Controllers can set records and change the ticker and name of the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.addController(\n  { controller: \"aGzM_yjralacHIUo8_nQXMbh9l1cy0aksiL_x9M359f\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `removeController({ controller })`\n\nRemoves a controller from the list of approved controllers on the ANT.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.removeController(\n  { controller: \"aGzM_yjralacHIUo8_nQXMbh9l1cy0aksiL_x9M359f\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n### Records\n\n#### `setBaseNameRecord({ transactionId, ttlSeconds, owner?, displayName?, logo?, description?, keywords? })`\n\nAdds or updates the base name record for the ANT. This is the top level name of the ANT (e.g. ardrive.ar.io). Supports undername ownership delegation and metadata.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\n// get the ant for the base name\nconst arnsRecord = await ario.getArNSRecord({ name: \"ardrive\" });\nconst ant = await ANT.init({ processId: arnsName.processId });\n\n// Basic usage\nconst { id: txId } = await ant.setBaseNameRecord({\n  transactionId: \"432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  ttlSeconds: 3600,\n});\n\n// With ownership delegation and metadata\nconst { id: txId } = await ant.setBaseNameRecord({\n  transactionId: \"432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n  ttlSeconds: 3600,\n  owner: \"user-wallet-address-123...\", // delegate ownership to another address\n  displayName: \"ArDrive\", // display name\n  logo: \"logo-tx-id-123...\", // logo transaction ID\n  description: \"Decentralized storage application\",\n  keywords: [\"storage\", \"decentralized\", \"web3\"],\n});\n\n// ardrive.ar.io will now resolve to the provided transaction id and include metadata\n```\n\n#### `setUndernameRecord({ undername, transactionId, ttlSeconds, owner?, displayName?, logo?, description?, keywords? })`\n\nAdds or updates an undername record for the ANT. An undername is appended to the base name of the ANT (e.g. dapp_ardrive.ar.io). Supports undername ownership delegation and metadata.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n\u003e Records, or `undernames` are configured with the `transactionId` - the arweave transaction id the record resolves - and `ttlSeconds`, the Time To Live in the cache of client applications.\n\n```typescript\nconst arnsRecord = await ario.getArNSRecord({ name: \"ardrive\" });\nconst ant = await ANT.init({ processId: arnsName.processId });\n\n// Basic usage\nconst { id: txId } = await ant.setUndernameRecord(\n  {\n    undername: \"dapp\",\n    transactionId: \"432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n    ttlSeconds: 900,\n  },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n\n// With ownership delegation and metadata\nconst { id: txId } = await ant.setUndernameRecord(\n  {\n    undername: \"alice\",\n    transactionId: \"432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM\",\n    ttlSeconds: 900,\n    owner: \"alice-wallet-address-123...\", // delegate ownership to Alice\n    displayName: \"Alice's Site\", // display name\n    logo: \"avatar-tx-id-123...\", // avatar/logo transaction ID\n    description: \"Personal portfolio and blog\",\n    keywords: [\"portfolio\", \"personal\", \"blog\"],\n  },\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n\n// dapp_ardrive.ar.io will now resolve to the provided transaction id\n// alice_ardrive.ar.io will be owned by Alice and include metadata\n```\n\n#### `removeUndernameRecord({ undername })`\n\nRemoves an undername record from the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.removeUndernameRecord(\n  { undername: \"dapp\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n\n// dapp_ardrive.ar.io will no longer resolve to the provided transaction id\n```\n\n#### `setRecord({ undername, transactionId, ttlSeconds })`\n\n\u003e [!WARNING]\n\u003e Deprecated: Use `setBaseNameRecord` or `setUndernameRecord` instead.\n\nAdds or updates a record for the ANT process. The `undername` parameter is used to specify the record name. Use `@` for the base name record.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n\u003e Records, or `undernames` are configured with the `transactionId` - the arweave transaction id the record resolves - and `ttlSeconds`, the Time To Live in the cache of client applications.\n\n```typescript\nconst { id: txId } = await ant.setRecord(\n  {\n    undername: '@',\n    transactionId: '432l1cy0aksiL_x9M359faGzM_yjralacHIUo8_nQXM'\n    ttlSeconds: 3600\n  },\n  // optional additional tags\n  { tags: [{ name: 'App-Name', value: 'My-Awesome-App' }] },\n);\n```\n\n#### `removeRecord({ undername })`\n\n\u003e [!WARNING]\n\u003e Deprecated: Use `removeUndernameRecord` instead.\n\nRemoves a record from the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst arnsRecord = await ario.getArNSRecord({ name: \"ardrive\" });\nconst ant = await ANT.init({ processId: arnsName.processId });\nconst { id: txId } = await ant.removeRecord(\n  { undername: \"dapp\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n\n// dapp_ardrive.ar.io will no longer resolve to the provided transaction id\n```\n\n### Metadata\n\n#### `setName({ name })`\n\nSets the name of the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.setName(\n  { name: \"My ANT\" },\n  // optional additional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `setTicker({ ticker })`\n\nSets the ticker of the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.setTicker(\n  { ticker: \"ANT-NEW-TICKER\" },\n  // optional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `setDescription({ description })`\n\nSets the description of the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.setDescription(\n  { description: \"A friendly description of this ANT\" },\n  // optional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `setKeywords({ keywords })`\n\nSets the keywords of the ANT process.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.setKeywords(\n  { keywords: [\"Game\", \"FPS\", \"AO\"] },\n  // optional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n#### `getLogo()`\n\nReturns the TX ID of the logo set for the ANT.\n\n```typescript\nconst logoTxId = await ant.getLogo();\n```\n\n#### `setLogo({ txId })`\n\nSets the Logo of the ANT - logo should be an Arweave transaction ID.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.setLogo(\n  { txId: \"U7RXcpaVShG4u9nIcPVmm2FJSM5Gru9gQCIiRaIPV7f\" },\n  // optional tags\n  { tags: [{ name: \"App-Name\", value: \"My-Awesome-App\" }] },\n);\n```\n\n### ARIO Integrations\n\n`releaseName`, `reassignName`, `approvePrimaryNameRequest`, and\n`removePrimaryNames` were AO-only orchestration helpers and have been\nremoved. Their on-chain equivalents on Solana live on the `ario-arns`\nprogram and are exposed through the `ARIO` write client\n(`upgradeRecord`, `setPrimaryName`, etc.) or — for permissionless\nmaintenance — through `SolanaARIOWriteable`'s prune helpers.\n\n### Upgrade\n\n#### `upgrade()`\n\nMigrates this ANT's on-chain state to the latest schema version (per-\nANT data migration on Solana — no process forking, no name\nreassignment). Returns `{ id, needsMigration }`.\n\n```typescript\nconst result = await ant.upgrade();\nif (result.needsMigration) {\n  console.log(`Migrated: ${result.id}`);\n}\n```\n\n### Undername Ownership\n\nNTs support ownership of undernames:\n\n1. **ANT Owner** - Has full control over the ANT and all records\n2. **Controllers** - Can manage records but cannot transfer ANT ownership\n3. **Record Owners** - Can only update their specific delegated records\n\n\u003e [!WARNING]\n\u003e When a record owner updates their own record, they **MUST** include their own address in the `owner` field. If the `owner` field is omitted or set to a different address, the record ownership will be transferred or renounced.\n\n#### `transferRecord({ undername, recipient })`\n\nTransfers ownership of a specific record (undername) to another address. This enables delegation of control for individual records within an ANT while maintaining the ANT owner's ultimate authority. The current record owner or ANT owner/controllers can transfer ownership.\n\n_Note: Requires `signer` to be provided on `ANT.init` to sign the transaction._\n\n```typescript\nconst { id: txId } = await ant.transferRecord({\n  undername: \"alice\", // the subdomain/record to transfer\n  recipient: \"new-owner-address-123...\", // address of the new owner\n});\n\n// alice_ardrive.ar.io is now owned by the new owner address\n// The new owner can update the record but not other records in the ANT\n```\n\n**CLI Usage:**\n\n```bash\n# Transfer ownership of a record using the CLI\nar.io transfer-record \\\n  --process-id \"ANT_PROCESS_ID\" \\\n  --undername \"alice\" \\\n  --recipient \"new-owner-address-123...\" \\\n  --wallet-file \"path/to/wallet.json\"\n```\n\n#### Record Owner Workflow Examples\n\n**Checking Record Ownership:**\n\n```typescript\nconst record = await ant.getRecord({ undername: \"alice\" });\nconsole.log(`Record owner: ${record.owner}`);\nconsole.log(`Transaction ID: ${record.transactionId}`);\n```\n\n**Record Owner Updating Their Own Record:**\n\n```typescript\n// Alice (record owner) updating her own record\nconst aliceAnt = await ANT.init({\n  processId: 'ANT_MINT_PUBKEY',\n  rpc,\n  rpcSubscriptions,\n  signer: aliceSigner, // Alice's @solana/kit signer\n});\n\n// ✅ CORRECT: Alice includes her own address as owner\nconst { id: txId } = await aliceAnt.setUndernameRecord({\n  undername: \"alice\",\n  transactionId: \"new-content-tx-id-456...\",\n  ttlSeconds: 1800,\n  owner: \"alice-wallet-address-123...\", // MUST be Alice's own address\n  displayName: \"Alice Updated Portfolio\",\n  description: \"Updated personal portfolio and blog\",\n});\n\n// ❌ WRONG: Omitting owner field will renounce ownership\nconst badUpdate = await aliceAnt.setUndernameRecord({\n  undername: \"alice\",\n  transactionId: \"new-content-tx-id-456...\",\n  ttlSeconds: 1800,\n  // Missing owner field - this will renounce ownership!\n});\n\n// ❌ WRONG: Setting different owner will transfer ownership\nconst badTransfer = await aliceAnt.setUndernameRecord({\n  undername: \"alice\",\n  transactionId: \"new-content-tx-id-456...\",\n  ttlSeconds: 1800,\n  owner: \"someone-else-address-789...\", // This transfers ownership to someone else!\n});\n```\n\n**What Happens When Record Ownership is Renounced:**\n\nIf a record owner updates their record without including the `owner` field, the record becomes owned by the ANT owner/controllers again:\n\n```typescript\n// Before: alice record is owned by alice-wallet-address-123...\nconst recordBefore = await ant.getRecord({ undername: \"alice\" });\nconsole.log(recordBefore.owner); // \"alice-wallet-address-123...\"\n\n// Alice updates without owner field\nawait aliceAnt.setUndernameRecord({\n  undername: \"alice\",\n  transactionId: \"new-tx-id...\",\n  ttlSeconds: 900,\n  // No owner field = renounces ownership\n});\n\n// After: record ownership reverts to ANT owner\nconst recordAfter = await ant.getRecord({ undername: \"alice\" });\nconsole.log(recordAfter.owner); // undefined (controlled by ANT owner again)\n```\n\n### Static Methods\n\n`ANT.fork()` and the static `ANT.upgrade()` were AO-only (process\nforking + name reassignment). On Solana, schema migration is a\nper-asset CPI exposed as the instance method `ant.upgrade()` documented\nabove; new ANTs are created with `ANT.spawn()`.\n\n## Escrow\n\nTrustless, multi-protocol escrow for handing an asset to a recipient identified by\nan **Arweave** or **Ethereum** address, claimable once they hold a Solana wallet.\nBacked by the `ario-ant-escrow` program. Two clients:\n\n- `TokenEscrow` — escrow liquid **ARIO** (SPL) or a **time-locked vault**.\n- `ANTEscrow` — escrow an **ANT** (Metaplex Core NFT).\n\nEach supports **deposit → claim → cancel/refund → update-recipient**. Claims work\nthree ways: **Arweave-attested** (an off-chain attestor re-signs the canonical\nclaim with Ed25519, verified on-chain), **Ethereum** (on-chain `secp256k1_recover`\n+ EIP-191), and **vault** (instruction introspection that preserves the remaining\nlock).\n\n```typescript\nimport { TokenEscrow, canonicalMessageV2 } from '@ar.io/sdk';\n\nconst escrow = new TokenEscrow({\n  rpc,\n  rpcSubscriptions,\n  signer,\n  programId,\n  coreProgram,\n});\n\n// deposit 50 ARIO to an Ethereum recipient\nawait escrow.depositTokens({\n  assetId, // 32-byte client-supplied id\n  amount: 50_000_000n,\n  arioMint,\n  depositorTokenAccount,\n  recipient: { protocol: 'ethereum', publicKey: ethAddress20 },\n});\n\n// the recipient claims (Ethereum path) once they have a Solana wallet\nawait escrow.claimTokensEthereum({\n  depositor,\n  assetId,\n  claimant,\n  claimantTokenAccount,\n  escrowTokenAccount,\n  signature, // recipient's EIP-191 signature over canonicalMessageV2(...)\n});\n```\n\nBuild the exact bytes a recipient signs with `canonicalMessage` /\n`canonicalMessageV2` — byte-identical to the on-chain program (and the off-chain\nattestor). See the contracts repo's escrow design + protocol spec for the full\nflow and the cross-language canonical-message vectors.\n\n## Token Conversion\n\nThe ARIO process stores all values as mARIO (micro-ARIO) to avoid floating-point arithmetic issues. The SDK provides an `ARIOToken` and `mARIOToken` classes to handle the conversion between ARIO and mARIO, along with rounding logic for precision.\n\n**All process interactions expect values in mARIO. If numbers are provided as inputs, they are assumed to be in raw mARIO values.**\n\n#### Converting ARIO to mARIO\n\n```typescript\nimport { ARIOToken, mARIOToken } from \"@ar.io/sdk\";\n\nconst arioValue = 1;\nconst mARIOValue = new ARIOToken(arioValue).toMARIO();\n\nconst mARIOValue = 1_000_000;\nconst arioValue = new mARIOToken(mARIOValue).toARIO();\n```\n\n## Logging\n\nThe library uses a lightweight console logger by default for both Node.js and web environments. The logger outputs structured JSON logs with timestamps. You can configure the log level via `setLogLevel()` API or provide a custom logger that satisfies the `ILogger` interface.\n\n#### Default Logger\n\n```typescript\nimport { Logger } from \"@ar.io/sdk\";\n\n// set the log level\nLogger.default.setLogLevel(\"debug\");\n\n// Create a new logger instance with a specific level\nconst logger = new Logger({ level: \"debug\" });\n```\n\n#### Custom Logger Implementation\n\nYou can provide any custom logger that implements the `ILogger` interface:\n\n```typescript\nimport { ARIO, ILogger } from \"@ar.io/sdk\";\n\n// Custom logger example\nconst customLogger: ILogger = {\n  info: (message, ...args) =\u003e console.log(`[INFO] ${message}`, ...args),\n  warn: (message, ...args) =\u003e console.warn(`[WARN] ${message}`, ...args),\n  error: (message, ...args) =\u003e console.error(`[ERROR] ${message}`, ...args),\n  debug: (message, ...args) =\u003e console.debug(`[DEBUG] ${message}`, ...args),\n  setLogLevel: (level) =\u003e {\n    /* implement level filtering */\n  },\n};\n\n// Set it as the default logger across the entire SDK — every class\n// (ARIO, ANT, ANTRegistry, etc.) will route logs through it. `ARIO.init`\n// does not accept a per-instance logger.\nLogger.default = customLogger;\n```\n\n## Pagination\n\n#### Overview\n\nCertain APIs that could return a large amount of data are paginated using cursors. The SDK uses the `cursor` pattern (as opposed to pages) to better protect against changing data while paginating through a list of items. For more information on pagination strategies refer to [this article](https://www.getknit.dev/blog/api-pagination-best-practices#api-pagination-techniques-).\n\nPaginated results include the following properties:\n\n- `items`: the list of items on the current request, defaulted to 100 items.\n- `nextCursor`: the cursor to use for the next batch of items. This is `undefined` if there are no more items to fetch.\n- `hasMore`: a boolean indicating if there are more items to fetch. This is `false` if there are no more items to fetch.\n- `totalItems`: the total number of items available. This may change as new items are added to the list, only use this for informational purposes.\n- `sortBy`: the field used to sort the items, by default this is `startTimestamp`.\n- `sortOrder`: the order used to sort the items, by default this is `desc`.\n\nTo request all the items in a list, you can iterate through the list using the `nextCursor` until `hasMore` is `false`.\n\n```typescript\nlet hasMore = true;\nlet cursor: string | undefined;\nconst gateaways = [];\nwhile (hasMore) {\n  const page = await ario.getGateways({ limit: 100, cursor });\n  gateaways.push(...items);\n  cursor = page.nextCursor;\n  hasMore = page.hasMore;\n}\n```\n\n#### Filtering\n\nPaginated APIs also support filtering by providing a `filters` parameter. Filters can be applied to any field in the response. When multiple keys are provided, they are treated as AND conditions (all conditions must match). When multiple values are provided for a single key (as an array), they are treated as OR conditions (any value can match).\n\nExample:\n\n```typescript\nconst records = await ario.getArNSRecords({\n  filters: {\n    type: \"lease\",\n    processId: [\n      \"ZkgLfyHALs5koxzojpcsEFAKA8fbpzP7l-tbM7wmQNM\",\n      \"r61rbOjyXx3u644nGl9bkwLWlWmArMEzQgxBo2R-Vu0\",\n    ],\n  },\n});\n```\n\nIn the example above, the query will return ArNS records where:\n\n- The type is \"lease\" AND\n- The processId is EITHER \"ZkgLfyHALs5koxzojpcsEFAKA8fbpzP7l-tbM7wmQNM\" OR \"r61rbOjyXx3u644nGl9bkwLWlWmArMEzQgxBo2R-Vu0\"\n\n## Advanced\n\n### RPC Configuration\n\nThe SDK accepts any `@solana/kit` RPC client. For read-only usage, only\n`rpc` is required. Write operations additionally need `rpcSubscriptions`\n(WebSocket) for transaction confirmation and a `signer`.\n\n#### Basic (read-only)\n\n```ts\nimport { ARIO } from '@ar.io/sdk';\nimport { createSolanaRpc } from '@solana/kit';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\nconst ario = ARIO.init({ rpc });\n```\n\n#### With writes (signer + WebSocket subscriptions)\n\n```ts\nimport { ARIO } from '@ar.io/sdk';\nimport {\n  createSolanaRpc,\n  createSolanaRpcSubscriptions,\n  createKeyPairSignerFromBytes,\n} from '@solana/kit';\n\nconst rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');\nconst rpcSubscriptions = createSolanaRpcSubscriptions(\n  'wss://api.mainnet-beta.solana.com',\n);\nconst signer = await createKeyPairSignerFromBytes(/* ... */);\n\nconst ario = ARIO.init({ rpc, rpcSubscriptions, signer });\n```\n\n\u003e **Note:** `rpcSubscriptions` opens a WebSocket connection and is only\n\u003e needed for writes. If your RPC provider doesn't expose a WebSocket\n\u003e endpoint, omit it and use the SDK in read-only mode.\n\n### Circuit Breaker\n\nThe SDK ships an [opossum]-backed circuit breaker that wraps the RPC\ntransport. When the primary endpoint starts failing (429 rate-limits,\n5xx errors, network timeouts) the circuit opens and subsequent calls\nroute transparently to a fallback RPC until the primary recovers.\n\n```ts\nimport { ARIO, createCircuitBreakerRpc } from '@ar.io/sdk';\n\nconst rpc = createCircuitBreakerRpc({\n  primaryUrl: 'https://my-premium-rpc.example.com',\n  fallbackUrl: 'https://api.mainnet-beta.solana.com',\n});\n\nconst ario = ARIO.init({ rpc });\n```\n\nUse `defaultFallbackUrl()` to auto-pick mainnet or devnet based on the\nprimary URL:\n\n```ts\nimport {\n  createCircuitBreakerRpc,\n  defaultFallbackUrl,\n} from '@ar.io/sdk';\n\nconst primaryUrl = 'https://my-premium-rpc.example.com';\nconst rpc = createCircuitBreakerRpc({\n  primaryUrl,\n  fallbackUrl: defaultFallbackUrl(primaryUrl), // → mainnet public RPC\n});\n```\n\nTuning knobs (all optional):\n\n| Option | Default | Description |\n|---|---|---|\n| `timeout` | `10000` | ms before a single request is timed out (`false` to disable) |\n| `errorThresholdPercentage` | `50` | error % at which to open the circuit |\n| `resetTimeout` | `30000` | ms to wait before probing the primary again (half-open) |\n| `volumeThreshold` | `5` | minimum requests in the rolling window before the circuit can trip |\n\n### Automatic Retries\n\nAll RPC **read** calls (account fetches, `getProgramAccounts`, etc.)\nautomatically retry on transient transport errors with exponential\nback-off. Writes are **not** retried (to avoid double-sends).\n\nRetried errors: HTTP 429/5xx, `fetch failed`, `ECONNRESET`,\n`ETIMEDOUT`, `AbortError` / timeouts. Non-retryable errors (account\nnot found, invalid params, deserialization) throw immediately.\n\nDefaults: **6 attempts**, 500 ms base delay, 5 s max delay. Override\nper-call with the exported `withRetry` helper:\n\n```ts\nimport { withRetry } from '@ar.io/sdk';\n\nconst result = await withRetry(() =\u003e rpc.getAccountInfo(addr).send(), {\n  maxAttempts: 3,\n  baseDelayMs: 1000,\n});\n```\n\n### Generated instruction builders\n\nFor custom transaction building, import Codama-generated typed clients\nfrom [`@ar.io/solana-contracts`](https://www.npmjs.com/package/@ar.io/solana-contracts):\n\n```ts\nimport {\n  getBuyNameInstructionAsync,\n  ARIO_ARNS_PROGRAM_ADDRESS,\n} from '@ar.io/solana-contracts/arns';\n```\n\n### Networks\n\n| Network | RPC | Programs |\n|---|---|---|\n| Mainnet | `https://api.mainnet-beta.solana.com` (mainnet-beta, default) | Not yet deployed — placeholder IDs in `src/solana/constants.ts` |\n| Devnet | `https://api.devnet.solana.com` | See `src/solana/constants.ts` for current devnet program IDs |\n| Localnet | Surfpool — `https://github.com/solana-foundation/surfpool` | Localnet harness in `solana-ar-io` monorepo |\n\nThe migration tooling (snapshot exporter, batch importer, claim app)\nlives in the [`solana-ar-io`](https://github.com/ar-io/solana-ar-io)\nmonorepo until cutover.\n\n## Resources\n\n### Bundling\n\nFor [ANS-104] bundling compatible with ar.io gateways, we recommend using [turbo-sdk](https://github.com/ardriveapp/turbo-sdk). Turbo SDK provides efficient and reliable methods for creating and uploading data bundles to the Arweave network, which are fully compatible with ar.io gateways. Turbo supports fiat and crypto bundling and uploading with a focus on ease of use and reliability.\n\n### ar.io Gateways\n\n### Running a Gateway\n\nTo run your own ar.io gateway, you can refer to the following resources:\n\n- [ar-io-node repository]: This repository contains the source code and instructions for setting up and running an ar.io gateway node.\n- [ar.io Gateway Documentation]: This comprehensive guide provides detailed information on gateway setup, configuration, and management.\n\nRunning your own gateway allows you to participate in the ar.io network, serve Arweave data, and potentially earn rewards. Make sure to follow the official documentation for the most up-to-date and accurate information on gateway operation.\n\n## Developers\n\n### Requirements\n\n- `node` \u003e= 18.0.0\n- `npm` or `yarn`\n- `docker` (recommended for testing)\n\n### Setup \u0026 Build\n\n- `nvm use` - use the correct node version\n- `yarn install` - installs dependencies\n- `yarn build` - builds web/node/bundled outputs\n\n### Testing\n\n- `yarn test` - runs e2e tests and unit tests\n- `yarn test:e2e` - runs e2e tests\n- `yarn test:unit` - runs unit tests\n- `yarn example:web` - opens up the example web page\n- `yarn example:esm` - runs example ESM node script\n- `yarn example:vite` - runs example Vite web page\n\n### Linting \u0026 Formatting\n\n- `yarn lint:check` - checks for linting errors\n- `yarn lint:fix` - fixes linting errors\n- `yarn format:check` - checks for formatting errors\n- `yarn format:fix` - fixes formatting errors\n\n### Architecture\n\n- Code to interfaces.\n- Prefer type safety over runtime safety.\n- Prefer composition over inheritance.\n- Prefer integration tests over unit tests.\n\nFor more information on how to contribute, please see [CONTRIBUTING.md].\n\n\u003c!-- ADD ALL LINK REFERENCES BELOW --\u003e\n\n[ar.io]: https://ar.io\n[examples]: ./examples\n[examples/webpack]: ./examples/webpack\n[examples/vite]: ./examples/vite\n[CONTRIBUTING.md]: ./CONTRIBUTING.md\n[ar-io-node repository]: https://github.com/ar-io/ar-io-node\n[ar.io Gateway Documentation]: https://docs.ar.io/gateways/ar-io-node/overview/\n[ANS-104]: https://github.com/ArweaveTeam/arweave-standards/blob/master/ans/ANS-104.md\n[opossum]: https://nodeshift.dev/opossum/\n\n```\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Far-io%2Far-io-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Far-io%2Far-io-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Far-io%2Far-io-sdk/lists"}