diff --git a/content/apis/turbo/payment-service/arns.mdx b/content/apis/turbo/payment-service/arns.mdx index dfa63a9a7..67022fc40 100644 --- a/content/apis/turbo/payment-service/arns.mdx +++ b/content/apis/turbo/payment-service/arns.mdx @@ -25,22 +25,6 @@ _openapi: - **increase-undername-limit**: Increase the number of permitted undernames - - content: > - Initiates an ArNS name purchase transaction using the user's Winston - credit balance. - - - This deducts credits from the user's account and submits the - transaction to the AR.IO network. - - - **Requirements:** - - - User must have sufficient balance to cover the mARIO cost - - - Name must be available (checked by AR.IO network) - - - All required parameters must be provided based on the intent - content: > Retrieves the status of an ArNS purchase transaction by its nonce (unique ID). @@ -73,46 +57,69 @@ _openapi: - **payment-intent**: Embedded payment flow (integrate in your UI) - content: > - Self-custody exit: a credit-authenticated owner moves a - Turbo-custodied + Receipt history for a wallet — NOT an ownership oracle. A name that + was + + transferred away keeps its receipt and still appears here, so read + + present control from the chain using the returned `antId`. + + + Open-by-address (no signature), mirroring `/account/balance`. + - content: > + Creates and (where Turbo holds the authority) immediately performs an + + ArNS action. Debits credits once, at creation, for the four + + ARIO-spending actions only. + + + **Authorization:** payer signature always (x-signature / x-public-key + / - ANT to a Solana pubkey they designate. Turbo (the on-chain owner) - signs + x-nonce). `set-record` and `remove-record` additionally require the + ANT - the transfer. + owner's action-bound proof in the `x-owner-*` headers, verified + against + the ANT's CURRENT ON-CHAIN owner. - **Authorization:** action-bound, single-use signature over - `("arns", "transfer", antId, target)` + nonce. Requires x-signature, + The returned `transaction` carries a Solana blockhash that dies in - x-public-key and x-nonce headers. + ~60-90 seconds. Sign those exact bytes — never rebuild the message, or + + Turbo's fee-payer signature stops covering it. - content: > - Turbo (the on-chain owner) writes a resolution record on the + One submit path for every action. Takes the base64 transaction + returned - credit-authenticated user's behalf. `undername` defaults to "@" (the - base + by the create call, now carrying the ANT owner's signature. - name record). + **No payer auth.** The authorization is the owner's signature inside + the - **Authorization:** action-bound, single-use signature over + transaction, which the chain enforces against those exact + instructions; - `("arns", "set-record", antId, undername, transactionId, ttlSeconds)` - + + the nonce is a random UUID capability. Requiring a second off-chain + + signature would break flows where the signer is not the payer. - nonce. Requires x-signature, x-public-key and x-nonce headers. - - content: | - Turbo (the on-chain owner) removes a resolution record on the - credit-authenticated user's behalf. - **Authorization:** action-bound, single-use signature over - `("arns", "remove-record", antId, undername)` + nonce. Requires - x-signature, x-public-key and x-nonce headers. + **Idempotent.** A replay of a completed action returns its envelope + with + + `alreadyCompleted: true` and performs no second purchase. + - content: | + Open by nonce (a random UUID handed only to the caller who created the + action). Deliberately does NOT echo the transaction bytes. --- {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Arweave Name System (ArNS) registration and management - \ No newline at end of file + \ No newline at end of file diff --git a/content/sdks/(clis)/ardrive-cli/(other-utility-operations)/persistent-caching-of-arfs-entity-metadata.mdx b/content/sdks/(clis)/ardrive-cli/(other-utility-operations)/persistent-caching-of-arfs-entity-metadata.mdx index 1f5ebe7a0..03464ad11 100644 --- a/content/sdks/(clis)/ardrive-cli/(other-utility-operations)/persistent-caching-of-arfs-entity-metadata.mdx +++ b/content/sdks/(clis)/ardrive-cli/(other-utility-operations)/persistent-caching-of-arfs-entity-metadata.mdx @@ -97,7 +97,7 @@ ardrive \ --help [ArDrive Community Discord][ardrive-discord] [ardrive]: https://ardrive.io -[arweave]: /learn/what-is-arweave +[arweave]: https://ardrive.io/what-is-arweave/ [ardrive-github]: https://github.com/ardriveapp/ [arfs]: https://ardrive.atlassian.net/l/c/m6P1vJDo [ardrive-web-app]: https://app.ardrive.io @@ -120,4 +120,4 @@ ardrive \ --help [viewblock]: https://viewblock.io/arweave/ [tx_anchors]: https://docs.arweave.org/developers/server/http-api#field-definitions [gql-guide]: https://gql-guide.vercel.app/#owners -[ardrive-turbo]: /build/upload \ No newline at end of file +[ardrive-turbo]: https://ardrive.io/turbo/ \ No newline at end of file diff --git a/content/sdks/(clis)/ario-deploy/(commands)/direct-commands.mdx b/content/sdks/(clis)/ario-deploy/(commands)/direct-commands.mdx index e7bb77c3f..d769df8e5 100644 --- a/content/sdks/(clis)/ario-deploy/(commands)/direct-commands.mdx +++ b/content/sdks/(clis)/ario-deploy/(commands)/direct-commands.mdx @@ -33,4 +33,9 @@ Deploy a single file: ```bash ario-deploy deploy --wallet ./wallet.json --deploy-file ./path/to/file.txt -``` \ No newline at end of file +``` + +`--deploy-file` overrides `--deploy-folder`, and the file is uploaded as one +transaction with **no manifest** — an ArNS name pointed at it resolves straight +to that file, served with its own content type. Useful for a PDF, a dataset, or +a single page. Manifest-only options such as `--fallback-file` do not apply. \ No newline at end of file diff --git a/content/sdks/(clis)/ario-deploy/(commands)/meta.json b/content/sdks/(clis)/ario-deploy/(commands)/meta.json index b2a1dc3a8..14f912ca0 100644 --- a/content/sdks/(clis)/ario-deploy/(commands)/meta.json +++ b/content/sdks/(clis)/ario-deploy/(commands)/meta.json @@ -3,6 +3,7 @@ "pages": [ "interactive-mode-easiest", "direct-commands", + "single-page-apps", "upload-deploy-without-arns", "advanced-usage" ], diff --git a/content/sdks/(clis)/ario-deploy/(commands)/single-page-apps.mdx b/content/sdks/(clis)/ario-deploy/(commands)/single-page-apps.mdx new file mode 100644 index 000000000..f7415ff8d --- /dev/null +++ b/content/sdks/(clis)/ario-deploy/(commands)/single-page-apps.mdx @@ -0,0 +1,36 @@ +--- +title: "Single-page apps" +description: "CLI for deploying apps to the permaweb (Arweave) and updating ArNS" +--- + +An Arweave path manifest maps each path to a transaction, and a gateway returns +404 for any path the manifest does not list. That is correct for static files +but wrong for a single-page app, whose routes are not files — `/settings` is +invented by the router and exists nowhere on disk. Without a fallback the root +loads and every deep link 404s. + +Manifests have a `fallback` for exactly this, and `ario-deploy` sets it +automatically when the build emits a `404.html`: + +```bash +ario-deploy deploy --deploy-folder ./dist +``` + +Most SPA builds do not emit one. Either copy your entry point before deploying: + +```bash +cp dist/index.html dist/404.html +``` + +…or name the fallback directly: + +```bash +ario-deploy deploy --deploy-folder ./dist --fallback-file index.html +``` + +The file must exist in the deploy folder; a path that is not there fails before +anything is uploaded, so a typo costs nothing. + +> Deep links can appear broken for up to a minute after a redeploy while +> gateways serve cached 404s from the previous manifest. Confirm with a +> cache-busting query string (`/settings?x=1`) before assuming the deploy failed. \ No newline at end of file diff --git a/content/sdks/(clis)/ario-deploy/bundler-service.mdx b/content/sdks/(clis)/ario-deploy/bundler-service.mdx index 3d1fd28b2..10b9970f6 100644 --- a/content/sdks/(clis)/ario-deploy/bundler-service.mdx +++ b/content/sdks/(clis)/ario-deploy/bundler-service.mdx @@ -3,7 +3,7 @@ title: "Bundler service" description: "CLI for deploying apps to the permaweb (Arweave) and updating ArNS" --- -Uploads go through a bundler service that accepts signed data items and posts them to Arweave. By default, ario-deploy uses the [Turbo](/build/upload) API and ArDrive’s production bundler (`https://upload.ardrive.io`). **`--uploader`** sets the **base URL** of the bundler service to use (scheme + host; typically no path). +Uploads go through a bundler service that accepts signed data items and posts them to Arweave. By default, ario-deploy uses the [Turbo](https://docs.ardrive.io/docs/turbo/) API and ArDrive’s production bundler (`https://upload.ardrive.io`). **`--uploader`** sets the **base URL** of the bundler service to use (scheme + host; typically no path). | When to use | Example value | | ------------------------- | ------------------------------------------------------- | diff --git a/content/sdks/(clis)/ario-deploy/command-options.mdx b/content/sdks/(clis)/ario-deploy/command-options.mdx index 5dd17541a..37ff9e949 100644 --- a/content/sdks/(clis)/ario-deploy/command-options.mdx +++ b/content/sdks/(clis)/ario-deploy/command-options.mdx @@ -10,7 +10,8 @@ description: "CLI for deploying apps to the permaweb (Arweave) and updating ArNS - `--cluster, -p`: Solana cluster for ArNS updates. Choices: `mainnet`, `devnet`. Default: `mainnet` - `--rpc-url`: Optional Solana RPC URL override for ArNS updates - `--deploy-folder, -d`: Folder to deploy. Default: `./dist` -- `--deploy-file, -f`: Deploy a single file instead of a folder +- `--deploy-file, -f`: Deploy a single file instead of a folder (no manifest is created) +- `--fallback-file`: Path, relative to the deploy folder, served for routes the manifest does not list. Defaults to `404.html` when the build emits one. See [Single-page apps](#single-page-apps). - `--undername, -u`: ANT undername to update. Default: `@` - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `60` diff --git a/content/sdks/(clis)/ario-deploy/features.mdx b/content/sdks/(clis)/ario-deploy/features.mdx index 577a20257..0b5bac2be 100644 --- a/content/sdks/(clis)/ario-deploy/features.mdx +++ b/content/sdks/(clis)/ario-deploy/features.mdx @@ -9,7 +9,7 @@ description: "CLI for deploying apps to the permaweb (Arweave) and updating ArNS - **Optional ArNS Updates:** Updates ArNS records via ANT with new transaction IDs - **Automated Workflow:** Integrates with GitHub Actions for continuous deployment - **Git Hash Tagging:** In CI (GitHub Actions), tags uploaded data items with the deploying commit SHA -- **404 Fallback Detection:** Automatically detects and sets 404.html as fallback +- **404 Fallback Detection:** Automatically sets `404.html` as the manifest fallback when present, so deep links into a single-page app resolve instead of 404ing. Override with `--fallback-file \` — an SPA that only builds `index.html` can point at that instead. - **Network Support:** ArNS updates run against the Solana ARIO programs on `mainnet` or `devnet`, with an optional custom RPC URL - **Flexible Deployment:** Supports deploying a folder or a single file - **Modern CLI:** Built with oclif for a robust command-line experience diff --git a/content/sdks/(clis)/ario-deploy/troubleshooting.mdx b/content/sdks/(clis)/ario-deploy/troubleshooting.mdx index ef7f0276f..6ce0ad677 100644 --- a/content/sdks/(clis)/ario-deploy/troubleshooting.mdx +++ b/content/sdks/(clis)/ario-deploy/troubleshooting.mdx @@ -9,4 +9,7 @@ description: "CLI for deploying apps to the permaweb (Arweave) and updating ArNS - **Error: "ArNS name does not exist":** Verify the ArNS name is correct and exists in the specified network - **Upload timeouts:** Files have a timeout for upload. Large files may fail and require optimization - **Insufficient Turbo Credits:** Use `--on-demand` with `--max-token-amount` to automatically fund uploads when balance is low -- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) \ No newline at end of file +- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) +- **Deep links 404 but the homepage loads:** The manifest has no `fallback`. Emit a `404.html` or pass `--fallback-file index.html` — see [Single-page apps](#single-page-apps) +- **Deep links still 404 right after a redeploy:** Gateways cache the previous manifest's 404s for around a minute. Retry with a cache-busting query string before assuming the deploy failed +- **Error: "Fallback file not found in folder":** `--fallback-file` takes a path relative to the deploy folder, e.g. `index.html`, not `./dist/index.html` \ No newline at end of file diff --git a/content/sdks/(clis)/llm.txt b/content/sdks/(clis)/llm.txt index 7c62897ba..d792d3ca0 100644 --- a/content/sdks/(clis)/llm.txt +++ b/content/sdks/(clis)/llm.txt @@ -1388,7 +1388,7 @@ Claude will build your project, detect the output folder, and run the deploy wit 4. **Runs the deployment** with appropriate flags 5. **Reports results** — transaction ID, Arweave URL, ArNS URL -See [`examples/claude-skill/`](./examples/claude-skill/) for the full skill file and customization options. +See [`examples/claude-skill/`](https://github.com/ar-io/ar-io-deploy/tree/main/examples/claude-skill) for the full skill file and customization options. --- @@ -1459,6 +1459,11 @@ Deploy a single file: ario-deploy deploy --wallet ./wallet.json --deploy-file ./path/to/file.txt ``` +`--deploy-file` overrides `--deploy-folder`, and the file is uploaded as one +transaction with **no manifest** — an ArNS name pointed at it resolves straight +to that file, served with its own content type. Useful for a PDF, a dataset, or +a single page. Manifest-only options such as `--fallback-file` do not apply. + # Interactive Mode (Easiest) (/ario-deploy/(commands)/interactive-mode-easiest) Run the deploy command without arguments to be guided through all deployment options: @@ -1481,6 +1486,40 @@ Pass `--arns-name` (or `--use-arns`) to skip the ArNS confirmation, or use the or no TTY) `deploy` does not prompt — supply everything via flags or `DEPLOY_KEY`. +# Single-page apps (/ario-deploy/(commands)/single-page-apps) + +An Arweave path manifest maps each path to a transaction, and a gateway returns +404 for any path the manifest does not list. That is correct for static files +but wrong for a single-page app, whose routes are not files — `/settings` is +invented by the router and exists nowhere on disk. Without a fallback the root +loads and every deep link 404s. + +Manifests have a `fallback` for exactly this, and `ario-deploy` sets it +automatically when the build emits a `404.html`: + +```bash +ario-deploy deploy --deploy-folder ./dist +``` + +Most SPA builds do not emit one. Either copy your entry point before deploying: + +```bash +cp dist/index.html dist/404.html +``` + +…or name the fallback directly: + +```bash +ario-deploy deploy --deploy-folder ./dist --fallback-file index.html +``` + +The file must exist in the deploy folder; a path that is not there fails before +anything is uploaded, so a typo costs nothing. + +> Deep links can appear broken for up to a minute after a redeploy while +> gateways serve cached 404s from the previous manifest. Confirm with a +> cache-busting query string (`/settings?x=1`) before assuming the deploy failed. + # Upload/deploy without ArNS (/ario-deploy/(commands)/upload-deploy-without-arns) `deploy` uploads without updating ArNS by default. You can also use the `upload` command explicitly for the same Turbo upload, dedupe cache, and payment options as deploy, minus ArNS flags: @@ -1771,7 +1810,8 @@ jobs: - `--cluster, -p`: Solana cluster for ArNS updates. Choices: `mainnet`, `devnet`. Default: `mainnet` - `--rpc-url`: Optional Solana RPC URL override for ArNS updates - `--deploy-folder, -d`: Folder to deploy. Default: `./dist` -- `--deploy-file, -f`: Deploy a single file instead of a folder +- `--deploy-file, -f`: Deploy a single file instead of a folder (no manifest is created) +- `--fallback-file`: Path, relative to the deploy folder, served for routes the manifest does not list. Defaults to `404.html` when the build emits one. See [Single-page apps](#single-page-apps). - `--undername, -u`: ANT undername to update. Default: `@` - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `60` @@ -1846,7 +1886,7 @@ The cache file is stored at `.ario-deploy/transaction-cache.json` in your projec - **Optional ArNS Updates:** Updates ArNS records via ANT with new transaction IDs - **Automated Workflow:** Integrates with GitHub Actions for continuous deployment - **Git Hash Tagging:** In CI (GitHub Actions), tags uploaded data items with the deploying commit SHA -- **404 Fallback Detection:** Automatically detects and sets 404.html as fallback +- **404 Fallback Detection:** Automatically sets `404.html` as the manifest fallback when present, so deep links into a single-page app resolve instead of 404ing. Override with `--fallback-file \` — an SPA that only builds `index.html` can point at that instead. - **Network Support:** ArNS updates run against the Solana ARIO programs on `mainnet` or `devnet`, with an optional custom RPC URL - **Flexible Deployment:** Supports deploying a folder or a single file - **Modern CLI:** Built with oclif for a robust command-line experience @@ -1931,4 +1971,7 @@ DEPLOY_KEY=$(base64 -i wallet.json) ARNS_KEY=\ pnpm deploy:on-demand - **Error: "ArNS name does not exist":** Verify the ArNS name is correct and exists in the specified network - **Upload timeouts:** Files have a timeout for upload. Large files may fail and require optimization - **Insufficient Turbo Credits:** Use `--on-demand` with `--max-token-amount` to automatically fund uploads when balance is low -- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) \ No newline at end of file +- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) +- **Deep links 404 but the homepage loads:** The manifest has no `fallback`. Emit a `404.html` or pass `--fallback-file index.html` — see [Single-page apps](#single-page-apps) +- **Deep links still 404 right after a redeploy:** Gateways cache the previous manifest's 404s for around a minute. Retry with a cache-busting query string before assuming the deploy failed +- **Error: "Fallback file not found in folder":** `--fallback-file` takes a path relative to the deploy folder, e.g. `index.html`, not `./dist/index.html` \ No newline at end of file diff --git a/content/sdks/ar-io-sdk/(advanced)/advanced-networks.mdx b/content/sdks/ar-io-sdk/(advanced)/advanced-networks.mdx index 84072c22e..0b386159f 100644 --- a/content/sdks/ar-io-sdk/(advanced)/advanced-networks.mdx +++ b/content/sdks/ar-io-sdk/(advanced)/advanced-networks.mdx @@ -10,4 +10,5 @@ description: "TypeScript/JavaScript SDK for interacting with the ar.io ecosystem | Localnet | Surfpool — `https://github.com/solana-foundation/surfpool` | Localnet harness in `solana-ar-io` monorepo | The migration tooling (snapshot exporter, batch importer, claim app) -lives in the `solana-ar-io` monorepo until cutover. \ No newline at end of file +lives in the [`solana-ar-io`](https://github.com/ar-io/solana-ar-io) +monorepo until cutover. \ No newline at end of file diff --git a/content/sdks/ar-io-sdk/llm.txt b/content/sdks/ar-io-sdk/llm.txt index 0cd905b2c..a4c0f0b0b 100644 --- a/content/sdks/ar-io-sdk/llm.txt +++ b/content/sdks/ar-io-sdk/llm.txt @@ -2974,12 +2974,12 @@ The ar.io SDK provides comprehensive tools for interacting with ar.io and the Ar } title="Pagination & Utilities" description="Handle large datasets and token conversions efficiently" - href="/sdks/ar-io-sdk/overview" + href="/sdks/ar-io-sdk/pagination" /> } title="Token Operations" description="ARIO token conversions and management utilities" - href="/sdks/ar-io-sdk/converting-ario-to-mario" + href="/sdks/ar-io-sdk/token-conversion" /> # Logging (/logging) diff --git a/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx b/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx index 36cab16eb..72b2c14f2 100644 --- a/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx +++ b/content/sdks/turbo-sdk/(apis)/turboauthenticatedclient.mdx @@ -400,15 +400,6 @@ const turbo = TurboFactory.authenticated({ signer, token: 'ario' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); - - -// ARIO on Base Network -const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ - signer, - token: 'base-ario', -}).topUpWithTokens({ - tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO -}); ``` ##### USDC Crypto Top Up diff --git a/content/sdks/turbo-sdk/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe.mdx b/content/sdks/turbo-sdk/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe.mdx new file mode 100644 index 000000000..ff9a0ce4a --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe.mdx @@ -0,0 +1,23 @@ +--- +title: "Buying a name with a credit card (fiat / Stripe)" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +`getArNSFiatPurchaseQuote` prices a purchase in fiat and returns a Stripe +payment session, so a user can buy a name without holding credits first. + +```typescript +const quote = await turbo.getArNSFiatPurchaseQuote({ + name: 'my-name', + intent: 'Buy-Name', + type: 'lease', + years: 1, + currency: 'usd', +}); +``` + +Its `paymentAmount` is the real charge and **already includes** the ANT spawn +surcharge. (On the `getArNSPriceForName` fiat estimate the split is the other +way round: `fiatEstimate.paymentAmount` is the base and +`fiatEstimate.paymentAmountWithAntSpawn` is the total.) Throws +`FiatPaymentsDisabledError` when the service has Stripe switched off. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/listing-a-wallet-s-names.mdx b/content/sdks/turbo-sdk/(arns-names)/listing-a-wallet-s-names.mdx new file mode 100644 index 000000000..ad963eacf --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/listing-a-wallet-s-names.mdx @@ -0,0 +1,11 @@ +--- +title: "Listing a wallet's names" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +```typescript +const { names } = await turbo.getArNSNames(); // defaults to the signer's address +``` + +Receipt history, not a live ownership check: a name transferred away still +appears. Verify present control on chain using the returned `antId`. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/meta.json b/content/sdks/turbo-sdk/(arns-names)/meta.json new file mode 100644 index 000000000..1b6321238 --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/meta.json @@ -0,0 +1,15 @@ +{ + "title": "ArNS Names", + "pages": [ + "you-need-a-solana-key-not-solana-funds", + "two-identities-never-conflated", + "the-nine-sponsored-actions", + "not-covered-these-still-cost-you-sol", + "pricing-quote-the-total", + "the-two-shapes-if-you-drive-it-yourself", + "nonces-retries-and-refunds", + "listing-a-wallet-s-names", + "buying-a-name-with-a-credit-card-fiat-stripe" + ], + "defaultOpen": false +} \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/nonces-retries-and-refunds.mdx b/content/sdks/turbo-sdk/(arns-names)/nonces-retries-and-refunds.mdx new file mode 100644 index 000000000..c84c8b8c9 --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/nonces-retries-and-refunds.mdx @@ -0,0 +1,16 @@ +--- +title: "Nonces, retries and refunds" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +Credits are debited when the action is **created**, not when it is signed. So: + +- **Persist the nonce before prompting for a signature** — use `onNonce`. +- **Never re-create an action to retry.** That debits again. Poll instead: + `await turbo.getArNSActionStatus(nonce)`. +- **An abandoned action refunds itself** — don't build a refund flow. +- Replaying `signArNSAction` on a completed action returns + `{ alreadyCompleted: true }` rather than buying twice. + +`InsufficientCreditsError` (HTTP 402) is thrown when the balance is short; +prompt a top-up, then create a **fresh** action. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/not-covered-these-still-cost-you-sol.mdx b/content/sdks/turbo-sdk/(arns-names)/not-covered-these-still-cost-you-sol.mdx new file mode 100644 index 000000000..68f4e7e5f --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/not-covered-these-still-cost-you-sol.mdx @@ -0,0 +1,10 @@ +--- +title: "Not covered — these still cost you SOL" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +`primary-name`, `release-name`, `reassign` and ANT metadata +(name/description/keywords/logo) are **not sponsored**. They stay on the +direct-signer path via [`@ar.io/sdk`](https://github.com/ar-io/ar-io-sdk). +Don't tell users they can "manage a name forever without SOL" — scope the claim +to the nine actions above. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/pricing-quote-the-total.mdx b/content/sdks/turbo-sdk/(arns-names)/pricing-quote-the-total.mdx new file mode 100644 index 000000000..d2597539c --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/pricing-quote-the-total.mdx @@ -0,0 +1,22 @@ +--- +title: "Pricing — quote the total" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +```typescript +const price = await turbo.getArNSPriceForName({ + intent: 'Buy-Name', + name: 'my-name', + type: 'lease', + years: 1, +}); +price.wincTotal; // <- charge or display THIS +price.winc; // the name only, EXCLUDING the ANT spawn surcharge +``` + +Buying mints a fresh ANT, and Turbo fronts that account's Solana rent. A flat +cost-recovery surcharge covers it, and in a real response **the surcharge can +exceed the name's own price** — so reading `winc` under-quotes every purchase. +`wincTotal` is added by the SDK precisely so the correct field is the obvious +one. Never hardcode the surcharge: it is config-driven and derived from live +rates. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/the-nine-sponsored-actions.mdx b/content/sdks/turbo-sdk/(arns-names)/the-nine-sponsored-actions.mdx new file mode 100644 index 000000000..a83a83644 --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/the-nine-sponsored-actions.mdx @@ -0,0 +1,51 @@ +--- +title: "The nine sponsored actions" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +```typescript +const turbo = TurboFactory.authenticated({ privateKey: jwk }); + +// Buy — the ONE signature in the whole lifecycle. +const { antId, messageId } = await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'lease', // or 'permabuy' + years: 1, // leases only + onNonce: (nonce) => persist(nonce), // fires BEFORE the wallet prompt +}); + +// Lifecycle — no signature at all. +await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +await turbo.upgradeArNSName({ name: 'my-name' }); +await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); + +// Records — free, and handled whichever shape the server picks. +await turbo.setArNSRecord({ + antId, + owner, + transactionId, + undername: '@', + ttlSeconds: 900, +}); +await turbo.removeArNSRecord({ antId, owner, undername: 'docs' }); + +// Controllers and transfer — owner-signed, free. +await turbo.addArNSController({ antId, owner }); // omit target => Turbo +await turbo.removeArNSController({ antId, owner }); // the revoke +await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); +``` + +| Action | Costs credits | Owner signature | +| -------------------------------------------------------------------- | ------------- | --------------------------- | +| `buyArNSName` | yes | **always**, once | +| `extendArNSLease` / `upgradeArNSName` / `increaseArNSUndernameLimit` | yes | no | +| `setArNSRecord` / `removeArNSRecord` | no | only after you revoke Turbo | +| `addArNSController` / `removeArNSController` / `transferArNSAnt` | no | yes | + +Only the four purchase actions debit credits. Records, controllers and transfer +are free — Turbo sponsors the SOL. + +`buyArNSName` grants Turbo controller rights **inside the same transaction you +sign**, which is why `setArNSRecord` needs no transaction signature afterwards. +Revoking is always available and always free. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/the-two-shapes-if-you-drive-it-yourself.mdx b/content/sdks/turbo-sdk/(arns-names)/the-two-shapes-if-you-drive-it-yourself.mdx new file mode 100644 index 000000000..14c86bbed --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/the-two-shapes-if-you-drive-it-yourself.mdx @@ -0,0 +1,24 @@ +--- +title: "The two shapes, if you drive it yourself" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +Every action returns one of two shapes, and **the server picks which**: + +```typescript +let res = await turbo.createArNSAction('buy-name', { name, ownerAddress }); +if (res.status === 'awaiting-signature') { + res = await turbo.signArNSAction( + res.nonce, + await owner.signTransaction(res.transaction), + ); +} +// res.status === 'completed'; res.messageId is the on-chain write +``` + +Branch on `status`, never on which action you called: `setArNSRecord` completes +alone while Turbo is a controller and flips to `awaiting-signature` the moment +you revoke Turbo. It degrades instead of breaking. + +**Sign the exact bytes returned.** Turbo has already signed as fee payer; +rebuilding the transaction invalidates that signature. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/two-identities-never-conflated.mdx b/content/sdks/turbo-sdk/(arns-names)/two-identities-never-conflated.mdx new file mode 100644 index 000000000..92aedd26d --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/two-identities-never-conflated.mdx @@ -0,0 +1,12 @@ +--- +title: "Two identities, never conflated" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +| | Who | How it travels | +| ------------- | ---------------------------------------------------------------- | --------------------- | +| **Payer** | the Turbo identity holding credits — Arweave, Ethereum or Solana | the client's signer | +| **ANT owner** | always a **Solana** address | the `owner` parameter | + +They are allowed to be different wallets, and routinely are: one account pays +while another owns. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names)/you-need-a-solana-key-not-solana-funds.mdx b/content/sdks/turbo-sdk/(arns-names)/you-need-a-solana-key-not-solana-funds.mdx new file mode 100644 index 000000000..fa0e093d9 --- /dev/null +++ b/content/sdks/turbo-sdk/(arns-names)/you-need-a-solana-key-not-solana-funds.mdx @@ -0,0 +1,35 @@ +--- +title: "You need a Solana key, not Solana funds" +description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" +--- + +An ANT is a Metaplex Core asset on Solana, so the owner is always a Solana +address — even when you pay with Arweave or Ethereum credits. But the owner +never pays: Turbo is the fee payer on every sponsored action, so **the owner's +SOL balance can stay at zero for the life of the name**. + +Supply the owner as an `ArNSOwnerSigner`: + +```typescript +import { solanaOwnerSigner } from '@ardrive/turbo-sdk'; + +// From a secret key (servers, scripts, tests) +const owner = solanaOwnerSigner(bs58SolanaSecretKey); +``` + +A browser wallet (Phantom, Solflare, or an app's embedded wallet) should +implement the interface directly rather than exposing a secret key: + +```typescript +const owner = { + getAddress: () => wallet.publicKey.toBase58(), + signTransaction: async (txBase64) => { + const tx = VersionedTransaction.deserialize( + Buffer.from(txBase64, 'base64'), + ); + const signed = await wallet.signTransaction(tx); + return Buffer.from(signed.serialize()).toString('base64'); + }, + signMessage: (message) => wallet.signMessage(message), +}; +``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records.mdx deleted file mode 100644 index 36e2409e8..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "ANT custody: transfer & manage records" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -Turbo can custody the ANT (Metaplex Core asset) backing your name. These methods let you take self-custody or manage resolution records. Each is authenticated with an **action-bound, single-use signature**: the wallet signs a canonical `arns\n\\n` message plus the UUID nonce, so a captured signature can't be replayed against a different operation. - -```typescript -// Self-custody exit: move the ANT to a Solana pubkey you control -await turbo.transferArNSAnt({ - antId: 'ant-id', - target: 'your-solana-pubkey', -}); - -// Set a resolution record (undername defaults to '@') -await turbo.setArNSRecord({ - antId: 'ant-id', - undername: 'docs', // omit for the apex '@' record - transactionId: 'arweave-tx-id', - ttlSeconds: 900, -}); - -// Remove a resolution record -await turbo.removeArNSRecord({ antId: 'ant-id', undername: 'docs' }); -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/buying-a-name.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/buying-a-name.mdx deleted file mode 100644 index d89f5a13a..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/buying-a-name.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: "Buying a name" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -`buyArNSName(params)` is the `Buy-Name` convenience wrapper over `purchaseArNSName`. Optionally, `paidBy` delegates the charge to one or more addresses that have shared credits with you. - -**`processId` is optional**, and it selects who owns the ANT (Metaplex Core asset) the name resolves to: - -- **Omit `processId`** → **Turbo custodial provisioning** (Model A): Turbo spawns and _owns_ the ANT for you. You can take self-custody later via `transferArNSAnt` (see "ANT custody" below). -- **Supply `processId`** → **user-owned ANT** (Model B): the name points at an ANT you already own; Turbo never takes custody. - -```typescript -// Custodial lease (Model A): omit processId → Turbo owns the ANT -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, -}); - -// Lease against your own ANT (Model B) for 1 year -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', -}); - -// Permanent buy, charged to a delegated payer -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'permabuy', - processId: 'ant-process-id', // optional — omit for Turbo custodial provisioning - paidBy: '\', // or an array of addresses -}); - -console.log(receipt.nonce); // capture this to poll status / retry idempotently -``` - -Full runnable example (buy → poll to terminal): - -```typescript -import { InsufficientCreditsError, TurboFactory } from '@ardrive/turbo-sdk'; - -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -async function buyName() { - try { - const { nonce } = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', - }); - - // Poll until terminal (success => messageId, failure => failedDate) - for (;;) { - const status = await turbo.getArNSPurchaseStatus({ nonce }); - if (status.messageId) { - console.log('Purchased. ArNS write tx:', status.messageId); - return status; - } - if (status.failedDate) { - throw new Error(`Purchase failed at ${status.failedDate}`); - } - await new Promise((r) => setTimeout(r, 2000)); - } - } catch (err) { - if (err instanceof InsufficientCreditsError) { - console.error('Not enough Turbo Credits — top up and retry.'); - } - throw err; - } -} -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/connecting-a-signer.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/connecting-a-signer.mdx deleted file mode 100644 index 327cc5928..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/connecting-a-signer.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "Connecting a signer" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -ArNS purchases are authenticated per wallet. Construct the client with `TurboFactory.authenticated` using any supported identity — the credit balance is keyed to that wallet's native address: - -```typescript -import { TurboFactory } from '@ardrive/turbo-sdk'; - -// Arweave -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -// Ethereum -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'ethereum', -}); - -// Solana — request nonces are signed with arbundles' HexSolanaSigner (ed25519) -const turbo = TurboFactory.authenticated({ - privateKey: bs58SolanaSecretKey, - token: 'solana', -}); -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs.mdx deleted file mode 100644 index e48d47b4a..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Dependency note (@solana/codecs)" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -ArNS/ARIO support pulls in `@solana/spl-token`, whose transitive `@solana/spl-token-metadata@0.1.6` imports `getDataEnumCodec` from `@solana/codecs@2.0.0-rc.1`. In `@solana/codecs@3+` that export was renamed to `getDiscriminatedUnionCodec`. If a web app **dedupes** `@solana/codecs` to `6.x` for its whole dependency graph, `spl-token-metadata`'s `getDataEnumCodec` import resolves to a version that no longer exports it, and the build breaks. - -There is no single codecs version that satisfies both `spl-token-metadata` (needs the old `getDataEnumCodec`) and `@solana/kit` (needs `5.x`), and `spl-token-metadata` has no release that uses the renamed API — so the fix belongs at the app's dependency-resolution layer, **not** at symbol-aliasing: - -- **Recommended:** stop deduping `@solana/codecs` so `@solana/spl-token-metadata` keeps its own nested `2.0.0-rc.1` copy. In Vite, ensure `@solana/codecs` is **not** in `resolve.dedupe`; with pnpm/yarn, allow the nested version (avoid a hoisted-to-`6.x` override for that subtree). This is cleaner than the `getDataEnumCodec → getDiscriminatedUnionCodec` alias plugin some apps use today, and removes the need for that shim. -- If you must keep a single hoisted codecs copy, a build-time alias mapping `getDataEnumCodec` to `getDiscriminatedUnionCodec` remains the fallback. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/error-handling-retries.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/error-handling-retries.mdx deleted file mode 100644 index 519d43b14..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/error-handling-retries.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: "Error handling & retries" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -- **`InsufficientCreditsError`** (HTTP `402`) — the wallet (or delegated payer) doesn't hold enough Turbo Credits. Prompt the user to top up, then retry. It exposes `.status === 402` and is exported from the package root. -- **`ProvidedInputError`** — thrown client-side (before any network call) when required per-intent params are missing/invalid (e.g. a lease `Buy-Name` without `years`, or `Extend-Lease` without a positive `years`). -- **`FailedRequestError`** — any other non-2xx response; inspect `.status` (e.g. `401`, `503`). - -**Idempotency / retry guidance:** the `nonce` is the idempotency key. Capture `response.nonce` up front; if the network drops after the request is sent, re-poll `getArNSPurchaseStatus({ nonce })` rather than blindly re-buying. On a `402`, top up and issue a fresh purchase — the captured nonce still lets you reconcile status. - -```typescript -import { InsufficientCreditsError } from '@ardrive/turbo-sdk'; - -try { - await turbo.buyArNSName({ name, type: 'permabuy', processId }); -} catch (err) { - if (err instanceof InsufficientCreditsError) { - // surface a top-up flow to the user - } else { - throw err; - } -} -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade.mdx deleted file mode 100644 index dbe2e9045..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: "Extend, increase undernames, upgrade" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -Each intent has a typed wrapper that enforces its required fields: - -```typescript -// Extend an existing lease by N years -await turbo.extendArNSLease({ name: 'my-name', years: 2 }); - -// Increase the undername limit -await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); - -// Upgrade a lease to a permanent name -await turbo.upgradeArNSName({ name: 'my-name' }); -``` - -All of them return the same `{ nonce, purchaseReceipt, arioWriteResult }` shape as `buyArNSName` and are polled the same way. `purchaseArNSName(params)` is the general form if you prefer to pass `intent` explicitly. \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/meta.json b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/meta.json deleted file mode 100644 index dadde0ceb..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/meta.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "title": "ArNS Names (paid with Turbo Credits)", - "pages": [ - "purchase-lifecycle", - "connecting-a-signer", - "pricing-a-name", - "buying-a-name", - "extend-increase-undernames-upgrade", - "polling-purchase-status", - "ant-custody-transfer-manage-records", - "error-handling-retries", - "dependency-note-solana-codecs" - ], - "defaultOpen": false -} \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/polling-purchase-status.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/polling-purchase-status.mdx deleted file mode 100644 index 15409259a..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/polling-purchase-status.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "Polling purchase status" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -`getArNSPurchaseStatus({ nonce })` is available on both the authenticated and unauthenticated clients: - -```typescript -const status = await turbo.getArNSPurchaseStatus({ nonce }); -// status.messageId -> present on terminal success (Solana ArNS write tx id) -// status.failedDate -> present on terminal failure -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/pricing-a-name.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/pricing-a-name.mdx deleted file mode 100644 index 188bb0bd4..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/pricing-a-name.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "Pricing a name" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -`getArNSPriceForName(params)` returns the cost in both Turbo Credits (`winc`) and `mARIO`. Params are validated client-side per intent (a `ProvidedInputError` is thrown for missing/invalid fields before any request is sent). - -```typescript -const { winc, mARIO } = await turbo.getArNSPriceForName({ - intent: 'Buy-Name', - name: 'my-name', - type: 'lease', // 'lease' | 'permabuy' - years: 1, // required for leases - processId: 'ant-process-id', // the ANT the name resolves to -}); -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/purchase-lifecycle.mdx b/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/purchase-lifecycle.mdx deleted file mode 100644 index 1a319e6fa..000000000 --- a/content/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/purchase-lifecycle.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: "Purchase lifecycle" -description: "SDK for interacting with Turbo, a fast and efficient data upload service for Arweave" ---- - -Every purchase is identified by a client-minted **UUID `nonce`**. The nonce is: - -1. **Signed** by your wallet and sent to the bundler (proving intent). -2. The **idempotency key** for the purchase. -3. The **status-lookup key** — poll `getArNSPurchaseStatus({ nonce })` until the purchase reaches a terminal state. - -`purchaseArNSName` returns the `nonce` on **both** `response.nonce` and `response.purchaseReceipt.nonce`. A purchase is **terminal-success** once its status carries a `messageId` (the Solana transaction id of the on-chain ArNS write) and **terminal-failure** once it carries a `failedDate`. - -``` -buyArNSName() ──▶ POST /arns/purchase ──▶ { nonce, purchaseReceipt, arioWriteResult } - │ - poll getArNSPurchaseStatus({ nonce }) - │ - ┌──────────────────────────────────────┴───────────────────────┐ - messageId present (success) failedDate present (failure) -``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/(signers)/base.mdx b/content/sdks/turbo-sdk/(signers)/base.mdx index ab0871206..d68b4a62c 100644 --- a/content/sdks/turbo-sdk/(signers)/base.mdx +++ b/content/sdks/turbo-sdk/(signers)/base.mdx @@ -19,13 +19,4 @@ const turbo = TurboFactory.authenticated({ privateKey: ethHexadecimalPrivateKey, token: 'base-usdc', }); -``` - -#### Base ARIO Private Key - -```typescript -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'base-ario', -}); ``` \ No newline at end of file diff --git a/content/sdks/turbo-sdk/llm.txt b/content/sdks/turbo-sdk/llm.txt index 98695297e..1dbe9f1ed 100644 --- a/content/sdks/turbo-sdk/llm.txt +++ b/content/sdks/turbo-sdk/llm.txt @@ -396,14 +396,6 @@ const turbo = TurboFactory.authenticated({ signer, token: 'ario' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); - -// ARIO on Base Network -const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ - signer, - token: 'base-ario', -}).topUpWithTokens({ - tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO -}); ``` ##### USDC Crypto Top Up @@ -802,210 +794,189 @@ const { status, id, ...fundResult } = await turbo.submitFundTransaction({ }); ``` -# ANT custody: transfer & manage records (/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records) +# Buying a name with a credit card (fiat / Stripe) (/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe) -Turbo can custody the ANT (Metaplex Core asset) backing your name. These methods let you take self-custody or manage resolution records. Each is authenticated with an **action-bound, single-use signature**: the wallet signs a canonical `arns\n\\n` message plus the UUID nonce, so a captured signature can't be replayed against a different operation. +`getArNSFiatPurchaseQuote` prices a purchase in fiat and returns a Stripe +payment session, so a user can buy a name without holding credits first. ```typescript -// Self-custody exit: move the ANT to a Solana pubkey you control -await turbo.transferArNSAnt({ - antId: 'ant-id', - target: 'your-solana-pubkey', +const quote = await turbo.getArNSFiatPurchaseQuote({ + name: 'my-name', + intent: 'Buy-Name', + type: 'lease', + years: 1, + currency: 'usd', }); +``` -// Set a resolution record (undername defaults to '@') -await turbo.setArNSRecord({ - antId: 'ant-id', - undername: 'docs', // omit for the apex '@' record - transactionId: 'arweave-tx-id', - ttlSeconds: 900, -}); +Its `paymentAmount` is the real charge and **already includes** the ANT spawn +surcharge. (On the `getArNSPriceForName` fiat estimate the split is the other +way round: `fiatEstimate.paymentAmount` is the base and +`fiatEstimate.paymentAmountWithAntSpawn` is the total.) Throws +`FiatPaymentsDisabledError` when the service has Stripe switched off. + +# Listing a wallet's names (/(arns-names)/listing-a-wallet-s-names) -// Remove a resolution record -await turbo.removeArNSRecord({ antId: 'ant-id', undername: 'docs' }); +```typescript +const { names } = await turbo.getArNSNames(); // defaults to the signer's address ``` -# Buying a name (/(arns-names-paid-with-turbo-credits)/buying-a-name) +Receipt history, not a live ownership check: a name transferred away still +appears. Verify present control on chain using the returned `antId`. -`buyArNSName(params)` is the `Buy-Name` convenience wrapper over `purchaseArNSName`. Optionally, `paidBy` delegates the charge to one or more addresses that have shared credits with you. +# Nonces, retries and refunds (/(arns-names)/nonces-retries-and-refunds) -**`processId` is optional**, and it selects who owns the ANT (Metaplex Core asset) the name resolves to: +Credits are debited when the action is **created**, not when it is signed. So: -- **Omit `processId`** → **Turbo custodial provisioning** (Model A): Turbo spawns and _owns_ the ANT for you. You can take self-custody later via `transferArNSAnt` (see "ANT custody" below). -- **Supply `processId`** → **user-owned ANT** (Model B): the name points at an ANT you already own; Turbo never takes custody. +- **Persist the nonce before prompting for a signature** — use `onNonce`. +- **Never re-create an action to retry.** That debits again. Poll instead: + `await turbo.getArNSActionStatus(nonce)`. +- **An abandoned action refunds itself** — don't build a refund flow. +- Replaying `signArNSAction` on a completed action returns + `{ alreadyCompleted: true }` rather than buying twice. -```typescript -// Custodial lease (Model A): omit processId → Turbo owns the ANT -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, -}); +`InsufficientCreditsError` (HTTP 402) is thrown when the balance is short; +prompt a top-up, then create a **fresh** action. + +# Not covered — these still cost you SOL (/(arns-names)/not-covered-these-still-cost-you-sol) + +`primary-name`, `release-name`, `reassign` and ANT metadata +(name/description/keywords/logo) are **not sponsored**. They stay on the +direct-signer path via [`@ar.io/sdk`](https://github.com/ar-io/ar-io-sdk). +Don't tell users they can "manage a name forever without SOL" — scope the claim +to the nine actions above. + +# Pricing — quote the total (/(arns-names)/pricing-quote-the-total) -// Lease against your own ANT (Model B) for 1 year -const receipt = await turbo.buyArNSName({ +```typescript +const price = await turbo.getArNSPriceForName({ + intent: 'Buy-Name', name: 'my-name', type: 'lease', years: 1, - processId: 'ant-process-id', -}); - -// Permanent buy, charged to a delegated payer -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'permabuy', - processId: 'ant-process-id', // optional — omit for Turbo custodial provisioning - paidBy: '\', // or an array of addresses }); - -console.log(receipt.nonce); // capture this to poll status / retry idempotently -``` - -Full runnable example (buy → poll to terminal): - -```typescript - -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -async function buyName() { - try { - const { nonce } = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', - }); - - // Poll until terminal (success => messageId, failure => failedDate) - for (;;) { - const status = await turbo.getArNSPurchaseStatus({ nonce }); - if (status.messageId) { - console.log('Purchased. ArNS write tx:', status.messageId); - return status; - } - if (status.failedDate) { - throw new Error(`Purchase failed at ${status.failedDate}`); - } - await new Promise((r) => setTimeout(r, 2000)); - } - } catch (err) { - if (err instanceof InsufficientCreditsError) { - console.error('Not enough Turbo Credits — top up and retry.'); - } - throw err; - } -} +price.wincTotal; // <- charge or display THIS +price.winc; // the name only, EXCLUDING the ANT spawn surcharge ``` -# Connecting a signer (/(arns-names-paid-with-turbo-credits)/connecting-a-signer) +Buying mints a fresh ANT, and Turbo fronts that account's Solana rent. A flat +cost-recovery surcharge covers it, and in a real response **the surcharge can +exceed the name's own price** — so reading `winc` under-quotes every purchase. +`wincTotal` is added by the SDK precisely so the correct field is the obvious +one. Never hardcode the surcharge: it is config-driven and derived from live +rates. -ArNS purchases are authenticated per wallet. Construct the client with `TurboFactory.authenticated` using any supported identity — the credit balance is keyed to that wallet's native address: +# The nine sponsored actions (/(arns-names)/the-nine-sponsored-actions) ```typescript +const turbo = TurboFactory.authenticated({ privateKey: jwk }); -// Arweave -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -// Ethereum -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'ethereum', +// Buy — the ONE signature in the whole lifecycle. +const { antId, messageId } = await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'lease', // or 'permabuy' + years: 1, // leases only + onNonce: (nonce) => persist(nonce), // fires BEFORE the wallet prompt }); -// Solana — request nonces are signed with arbundles' HexSolanaSigner (ed25519) -const turbo = TurboFactory.authenticated({ - privateKey: bs58SolanaSecretKey, - token: 'solana', +// Lifecycle — no signature at all. +await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +await turbo.upgradeArNSName({ name: 'my-name' }); +await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); + +// Records — free, and handled whichever shape the server picks. +await turbo.setArNSRecord({ + antId, + owner, + transactionId, + undername: '@', + ttlSeconds: 900, }); -``` +await turbo.removeArNSRecord({ antId, owner, undername: 'docs' }); -# Dependency note (@solana/codecs) (/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs) +// Controllers and transfer — owner-signed, free. +await turbo.addArNSController({ antId, owner }); // omit target => Turbo +await turbo.removeArNSController({ antId, owner }); // the revoke +await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); +``` -ArNS/ARIO support pulls in `@solana/spl-token`, whose transitive `@solana/spl-token-metadata@0.1.6` imports `getDataEnumCodec` from `@solana/codecs@2.0.0-rc.1`. In `@solana/codecs@3+` that -There is no single codecs version that satisfies both `spl-token-metadata` (needs the old `getDataEnumCodec`) and `@solana/kit` (needs `5.x`), and `spl-token-metadata` has no release that uses the renamed API — so the fix belongs at the app's dependency-resolution layer, **not** at symbol-aliasing: +| Action | Costs credits | Owner signature | +| -------------------------------------------------------------------- | ------------- | --------------------------- | +| `buyArNSName` | yes | **always**, once | +| `extendArNSLease` / `upgradeArNSName` / `increaseArNSUndernameLimit` | yes | no | +| `setArNSRecord` / `removeArNSRecord` | no | only after you revoke Turbo | +| `addArNSController` / `removeArNSController` / `transferArNSAnt` | no | yes | -- **Recommended:** stop deduping `@solana/codecs` so `@solana/spl-token-metadata` keeps its own nested `2.0.0-rc.1` copy. In Vite, ensure `@solana/codecs` is **not** in `resolve.dedupe`; with pnpm/yarn, allow the nested version (avoid a hoisted-to-`6.x` override for that subtree). This is cleaner than the `getDataEnumCodec → getDiscriminatedUnionCodec` alias plugin some apps use today, and removes the need for that shim. -- If you must keep a single hoisted codecs copy, a build-time alias mapping `getDataEnumCodec` to `getDiscriminatedUnionCodec` remains the fallback. +Only the four purchase actions debit credits. Records, controllers and transfer +are free — Turbo sponsors the SOL. -# Error handling & retries (/(arns-names-paid-with-turbo-credits)/error-handling-retries) +`buyArNSName` grants Turbo controller rights **inside the same transaction you +sign**, which is why `setArNSRecord` needs no transaction signature afterwards. +Revoking is always available and always free. -- **`InsufficientCreditsError`** (HTTP `402`) — the wallet (or delegated payer) doesn't hold enough Turbo Credits. Prompt the user to top up, then retry. It exposes `.status === 402` and is exported from the package root. -- **`ProvidedInputError`** — thrown client-side (before any network call) when required per-intent params are missing/invalid (e.g. a lease `Buy-Name` without `years`, or `Extend-Lease` without a positive `years`). -- **`FailedRequestError`** — any other non-2xx response; inspect `.status` (e.g. `401`, `503`). +# The two shapes, if you drive it yourself (/(arns-names)/the-two-shapes-if-you-drive-it-yourself) -**Idempotency / retry guidance:** the `nonce` is the idempotency key. Capture `response.nonce` up front; if the network drops after the request is sent, re-poll `getArNSPurchaseStatus({ nonce })` rather than blindly re-buying. On a `402`, top up and issue a fresh purchase — the captured nonce still lets you reconcile status. +Every action returns one of two shapes, and **the server picks which**: ```typescript - -try { - await turbo.buyArNSName({ name, type: 'permabuy', processId }); -} catch (err) { - if (err instanceof InsufficientCreditsError) { - // surface a top-up flow to the user - } else { - throw err; - } +let res = await turbo.createArNSAction('buy-name', { name, ownerAddress }); +if (res.status === 'awaiting-signature') { + res = await turbo.signArNSAction( + res.nonce, + await owner.signTransaction(res.transaction), + ); } +// res.status === 'completed'; res.messageId is the on-chain write ``` -# Extend, increase undernames, upgrade (/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade) +Branch on `status`, never on which action you called: `setArNSRecord` completes +alone while Turbo is a controller and flips to `awaiting-signature` the moment +you revoke Turbo. It degrades instead of breaking. -Each intent has a typed wrapper that enforces its required fields: +**Sign the exact bytes returned.** Turbo has already signed as fee payer; +rebuilding the transaction invalidates that signature. -```typescript -// Extend an existing lease by N years -await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +# Two identities, never conflated (/(arns-names)/two-identities-never-conflated) -// Increase the undername limit -await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); +| | Who | How it travels | +| ------------- | ---------------------------------------------------------------- | --------------------- | +| **Payer** | the Turbo identity holding credits — Arweave, Ethereum or Solana | the client's signer | +| **ANT owner** | always a **Solana** address | the `owner` parameter | -// Upgrade a lease to a permanent name -await turbo.upgradeArNSName({ name: 'my-name' }); -``` +They are allowed to be different wallets, and routinely are: one account pays +while another owns. -All of them return the same `{ nonce, purchaseReceipt, arioWriteResult }` shape as `buyArNSName` and are polled the same way. `purchaseArNSName(params)` is the general form if you prefer to pass `intent` explicitly. +# You need a Solana key, not Solana funds (/(arns-names)/you-need-a-solana-key-not-solana-funds) -# Polling purchase status (/(arns-names-paid-with-turbo-credits)/polling-purchase-status) +An ANT is a Metaplex Core asset on Solana, so the owner is always a Solana +address — even when you pay with Arweave or Ethereum credits. But the owner +never pays: Turbo is the fee payer on every sponsored action, so **the owner's +SOL balance can stay at zero for the life of the name**. -`getArNSPurchaseStatus({ nonce })` is available on both the authenticated and unauthenticated clients: +Supply the owner as an `ArNSOwnerSigner`: ```typescript -const status = await turbo.getArNSPurchaseStatus({ nonce }); -// status.messageId -> present on terminal success (Solana ArNS write tx id) -// status.failedDate -> present on terminal failure -``` - -# Pricing a name (/(arns-names-paid-with-turbo-credits)/pricing-a-name) -`getArNSPriceForName(params)` returns the cost in both Turbo Credits (`winc`) and `mARIO`. Params are validated client-side per intent (a `ProvidedInputError` is thrown for missing/invalid fields before any request is sent). - -```typescript -const { winc, mARIO } = await turbo.getArNSPriceForName({ - intent: 'Buy-Name', - name: 'my-name', - type: 'lease', // 'lease' | 'permabuy' - years: 1, // required for leases - processId: 'ant-process-id', // the ANT the name resolves to -}); +// From a secret key (servers, scripts, tests) +const owner = solanaOwnerSigner(bs58SolanaSecretKey); ``` -# Purchase lifecycle (/(arns-names-paid-with-turbo-credits)/purchase-lifecycle) +A browser wallet (Phantom, Solflare, or an app's embedded wallet) should +implement the interface directly rather than exposing a secret key: -Every purchase is identified by a client-minted **UUID `nonce`**. The nonce is: - -1. **Signed** by your wallet and sent to the bundler (proving intent). -2. The **idempotency key** for the purchase. -3. The **status-lookup key** — poll `getArNSPurchaseStatus({ nonce })` until the purchase reaches a terminal state. - -`purchaseArNSName` returns the `nonce` on **both** `response.nonce` and `response.purchaseReceipt.nonce`. A purchase is **terminal-success** once its status carries a `messageId` (the Solana transaction id of the on-chain ArNS write) and **terminal-failure** once it carries a `failedDate`. - -``` -buyArNSName() ──▶ POST /arns/purchase ──▶ { nonce, purchaseReceipt, arioWriteResult } - │ - poll getArNSPurchaseStatus({ nonce }) - │ - ┌──────────────────────────────────────┴───────────────────────┐ - messageId present (success) failedDate present (failure) +```typescript +const owner = { + getAddress: () => wallet.publicKey.toBase58(), + signTransaction: async (txBase64) => { + const tx = VersionedTransaction.deserialize( + Buffer.from(txBase64, 'base64'), + ); + const signed = await wallet.signTransaction(tx); + return Buffer.from(signed.serialize()).toString('base64'); + }, + signMessage: (message) => wallet.signMessage(message), +}; ``` # File Upload Events (/(events)/file-upload-events) @@ -1164,15 +1135,6 @@ const turbo = TurboFactory.authenticated({ }); ``` -#### Base ARIO Private Key - -```typescript -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'base-ario', -}); -``` - # Ethereum (/(signers)/ethereum) #### EthereumSigner @@ -1419,7 +1381,7 @@ The Turbo SDK provides a high-level interface for uploading data to Arweave thro } title="Events & Monitoring" description="Monitor upload progress and handle events in real-time" - href="/sdks/turbo-sdk/file-upload-events/" + href="/sdks/turbo-sdk/file-upload-events" /> } title="Credit Sharing" diff --git a/content/sdks/turbo-sdk/meta.json b/content/sdks/turbo-sdk/meta.json index ed4ff3a87..184a19820 100644 --- a/content/sdks/turbo-sdk/meta.json +++ b/content/sdks/turbo-sdk/meta.json @@ -3,7 +3,7 @@ "icon": "/ecosystem-logos/logo-turbo.svg", "pages": [ "(apis)", - "(arns-names-paid-with-turbo-credits)", + "(arns-names)", "(signers)", "(events)", "logging", diff --git a/content/sdks/wayfinder/llm.txt b/content/sdks/wayfinder/llm.txt index 9c07c223e..c6c4bfe0f 100644 --- a/content/sdks/wayfinder/llm.txt +++ b/content/sdks/wayfinder/llm.txt @@ -459,7 +459,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Gateway Providers" description="Learn about different gateway provider strategies" - href="/sdks/wayfinder/wayfinder-core/networkgatewaysprovider" + href="/sdks/wayfinder/wayfinder-core/gateway-providers" /> } title="Request Flow" @@ -469,7 +469,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Monitoring & Events" description="Monitor performance and handle events" - href="/sdks/wayfinder/wayfinder-core/global-events" + href="/sdks/wayfinder/wayfinder-core/events-and-monitoring" /> # Request Flow (/wayfinder-core/request-flow) diff --git a/content/sdks/wayfinder/wayfinder-core/gateway-providers.mdx b/content/sdks/wayfinder/wayfinder-core/gateway-providers.mdx index 9f6bc0043..6a147d3cf 100644 --- a/content/sdks/wayfinder/wayfinder-core/gateway-providers.mdx +++ b/content/sdks/wayfinder/wayfinder-core/gateway-providers.mdx @@ -88,7 +88,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ }), // Final fallback to static list new StaticGatewaysProvider({ - gateways: ['https://turbo-gateway.com', 'https://perma.online'], + gateways: ['https://turbo-gateway.com', 'https://g8way.io'], }), ], }); diff --git a/public/llms-full.txt b/public/llms-full.txt index 551d3fb59..acda0ae7a 100644 --- a/public/llms-full.txt +++ b/public/llms-full.txt @@ -129,7 +129,7 @@ Beyond basic gateway functionality, ar.io Gateway includes sophisticated capabil } title="Join the Network" description="Learn about the ar.io network and how to participate in the ecosystem" - href="https://ar.io/network" + href="https://ar.io/ecosystem/" /> ## Getting Started @@ -153,6 +153,14 @@ Get Arweave node info, peers and nework status Get the price (in winston) for an amount of bytes +# Rate Limiting (/apis/ar-io-node/rate-limiting) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Query and manage rate limit bucket balances. Supports both public balance queries and top-ups via x402 payments or admin authentication. + +**⚠️ EXPERIMENTAL FEATURES**: The rate limiter and x402 payment protocol are experimental features subject to change. API endpoints, parameters, behavior, and configuration options (environment variables) may evolve in future releases as these systems continue to be developed. See [docs/x402-and-rate-limiting.md](https://github.com/ar-io/ar-io-node/blob/develop/docs/x402-and-rate-limiting.md) for comprehensive documentation. + # Transactions (/apis/ar-io-node/transactions) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} @@ -246,6 +254,20 @@ For SDK alternatives to these REST APIs, visit our [SDK documentation](/sdks). Turbo provides high-performance upload and payment services for the Arweave network, offering fast, reliable data uploads with instant confirmation and transparent pricing. +## Base URL + +Both services are served together from `https://turbo.ardrive.io`, a single consolidated host that amalgamates the upload service (`upload.ardrive.io`) and the payment service (`payment.ardrive.io`). + +The two are mounted at different prefixes, which is why the examples on the reference pages differ: + +| Service | Base URL | Example | +| --- | --- | --- | +| Upload | `https://turbo.ardrive.io` | `GET /price/:token/:byteCount` | +| Payment | `https://turbo.ardrive.io/v1` | `GET /v1/price/bytes/:byteCount` | + + Upload endpoints are served at the root, **not** under `/v1` — prefixing them + returns a 404. Payment endpoints require the `/v1` prefix. + ## Services } @@ -308,6 +330,12 @@ The SDK provides a higher-level interface with built-in error handling, automati Credit sharing and approval management +# ArNS (/apis/turbo/payment-service/arns) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Arweave Name System (ArNS) registration and management + # Balance (/apis/turbo/payment-service/balance) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} @@ -338,12 +366,36 @@ Payment processing and top-up operations Pricing and cost calculation endpoints +# Protected (/apis/turbo/payment-service/protected) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Protected routes for inter-service communication (requires PRIVATE_ROUTE_SECRET) + # Redemption (/apis/turbo/payment-service/redemption) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} Credit redemption and gift processing +# System (/apis/turbo/payment-service/system) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +System health and monitoring endpoints + +# Webhooks (/apis/turbo/payment-service/webhooks) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Webhook endpoints for external payment providers + +# x402 (/apis/turbo/payment-service/x402) + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + +x402 protocol payment endpoints (EIP-3009 USDC payments) + # Account (/apis/turbo/upload-service/account) {/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} @@ -1810,9 +1862,13 @@ Data-Start: "" A JSON data object must also be uploaded with every ArFS Snapshot entity. This data contains all ArFS Drive, Folder, and File metadata changes within the associated drive, as well as any previous Snapshots. The Snapshot Data contains an array `txSnapshots`. Each item includes both the GQL and ArFS metadata details of each transaction made for the associated drive, within the snapshot's start and end period. -A `tsSnapshot` contains a `gqlNode` object which uses the same GQL tags interface returned by the Arweave Gateway. It includes all of the important `block`, `owner`, `tags`, and `bundledIn` information needed by ArFS clients. It also contains a `dataJson` object which stores the correlated Data JSON for that ArFS entity. +Each `txSnapshot` has exactly two fields, `gqlNode` and `jsonMetadata`, as siblings. + +`gqlNode` uses the same GQL tags interface returned by the Arweave Gateway. It includes all of the important `block`, `owner`, `tags`, and `bundledIn` information needed by ArFS clients. -For private drives, the `dataJson` object contains the JSON-string-escaped encrypted text of the associated file or folder. This encrypted text uses the file's existing `Cipher` and `Cipher-IV`. This ensures clients can decrypt this information quickly using the existing ArFS privacy protocols. +`jsonMetadata` is a **string**, not an object: the correlated Data JSON for that ArFS entity, serialized and escaped. It is `null` for an entry recorded for its GQL node alone, with no metadata body — a client must handle that rather than assume a string is always present. + +For private drives, `jsonMetadata` contains the JSON-string-escaped encrypted text of the associated file or folder. This encrypted text uses the file's existing `Cipher` and `Cipher-IV`. This ensures clients can decrypt this information quickly using the existing ArFS privacy protocols. ```json { @@ -1869,7 +1925,7 @@ For private drives, the `dataJson` object contains the JSON-string-escaped encry } ] }, - "dataJson": "{\"name\":\"november\",\"rootFolderId\":\"71dfc1cb-5368-4323-972a-e9dd0b1c63a0\", \"isHidden\":false}" + "jsonMetadata": "{\"name\":\"november\",\"rootFolderId\":\"71dfc1cb-5368-4323-972a-e9dd0b1c63a0\", \"isHidden\":false}" } ] } @@ -1987,7 +2043,7 @@ For more information, documentation, and community support, refer to the followi - [Arweave Official Website](https://www.arweave.org/) - [Arweave Developer Documentation](https://docs.arweave.org/) -- [Arweave Community Forums](https://community.arweave.org/) +- [Arweave Community Discord](https://discord.gg/arweave) # Privacy & Encryption (/build/advanced/arfs/privacy) @@ -3215,7 +3271,7 @@ Build and test on the [Testnet Sandbox](/build/testnet) before touching mainnet. ## Overview -A [Turbo ANS-104](https://github.com/ardriveapp/turbo-upload-service/) data item bundler can be run alongside an ar.io gateway. This allows gateways the ability to accept data items to be submitted to the Arweave blockweave. +A Turbo ANS-104 data item bundler can be run alongside an ar.io gateway. This allows gateways the ability to accept data items to be submitted to the Arweave blockweave. The bundler service can be easily run inside Docker in the same way that the gateway is. It utilizes a separate docker compose file for configuration and deployment, which also allows for the use of a separate file for environmental variables specific to the bundler service. Additionally, the separation allows operators to spin their bundler service up or down at any time without affecting their core gateway service. Despite the use of separate docker compose files, the bundler service shares a docker network with the ar.io gateway, and so is able to directly interact with the gateway service and data. @@ -5955,7 +6011,7 @@ For this walkthrough, we'll deploy directly from the command line using inline c ARIO Deploy uses [Turbo](https://console.ar.io) to upload files to Arweave. Before deploying, ensure your wallet has sufficient credits. - Visit the [Console app](https://console.ar.io/topup) and connect your deployment wallet to view your current balance. + Visit the [Console app](https://console.ar.io/account) and connect your deployment wallet to view your current balance and top-up history. A typical static site (5-10 MB) costs approximately 0.1-0.5 ARIO. Credits are applied instantly and remain in your wallet for future deployments. @@ -6512,7 +6568,7 @@ Check out the [series introduction](/build/guides/hosting-decentralised-apps) to - dApp prepared with hash routing and relative file paths - Static files built (for frameworks like React) -- Learn more about [preparing your dApp for deployment](https://docs.ardrive.io/docs/misc/deploy/) +- Learn more about [preparing your dApp for deployment](/build/guides/hosting-decentralised-apps) ## Step-by-Step Deployment @@ -7110,14 +7166,6 @@ Explore real-world applications and use cases for **Arweave** and **ar.io** infr - ArNS name assignment - Version management - **Build a decentralized NFT minting app** with Arweave and Crossmint - - **Key topics:** - - Permanent NFT storage on Arweave - - Crossmint API integration - - Payment processing - - Decentralized deployment - **Distribute software applications** using Arweave manifests and ArNS routing **Key topics:** @@ -7180,7 +7228,7 @@ This guide connects those pieces into a migration workflow. | Scenario | Recommended approach | | --- | --- | -| A few dozen files | Manual upload via [Turbo SDK](/sdks/turbo-sdk) or [console.ar.io](https://console.ar.io) | +| A few dozen files | Manual upload via [Turbo SDK](/sdks/turbo-sdk) or [console.ar.io](https://console.ar.io/upload) | | Hundreds to millions of CIDs | Custom migration script (this guide) | | NFT collection with linked metadata | Custom script + metadata rewrite (see [NFT example](#nft-collection-example) below) | | Ongoing ingestion pipeline | Build a reusable worker that fetches from IPFS and uploads to Turbo | @@ -7316,7 +7364,7 @@ For collections in the **tens or hundreds of thousands**, treat the migration as Before starting, estimate total upload cost: 1. Sum the byte size of all unique CIDs in your inventory. -2. Use the [pricing calculator](https://console.ar.io/calculator) or `turbo.getFiatEstimateForBytes()` from the SDK. +2. Use the [pricing calculator](https://console.ar.io/pricing) or `turbo.getFiatEstimateForBytes()` from the SDK. 3. Purchase sufficient [Turbo Credits](/build/upload/turbo-credits) before beginning. Uploads under **105 KiB are free** and do not require a prior top-up. For large migrations this is negligible, but worth knowing for small metadata files. @@ -8588,7 +8636,7 @@ console.log("✅ Rates fetched:", rates); --- -For more advanced implementations, see the [Next.js](./nextjs.mdx) and [Vite](./vite.mdx) framework guides, or explore the [Turbo SDK examples](https://github.com/ardriveio/turbo-sdk) repository. +For more advanced implementations, see the [Next.js](./nextjs.mdx) and [Vite](./vite.mdx) framework guides, or explore the [Turbo SDK examples](https://github.com/ardriveapp/turbo-sdk) repository. # Using Turbo in a Browser (/build/guides/using-turbo-in-a-browser) @@ -9641,11 +9689,11 @@ To verify your MetaMask integration is working correctly: - [Turbo SDK Documentation](https://docs.ardrive.io) - [Web Usage Examples](https://docs.ardrive.io) - [Next.js Webpack Configuration](https://nextjs.org/docs/pages/api-reference/next-config-js/webpack) -- [ArDrive Examples Repository](https://github.com/ardriveio/turbo-sdk) +- [ArDrive Examples Repository](https://github.com/ardriveapp/turbo-sdk) --- -For more examples and advanced usage patterns, refer to the [Turbo SDK examples directory](https://github.com/ardriveio/turbo-sdk) or the main [SDK documentation](https://docs.ardrive.io). +For more examples and advanced usage patterns, refer to the [Turbo SDK examples directory](https://github.com/ardriveapp/turbo-sdk) or the main [SDK documentation](https://docs.ardrive.io). # Using Turbo SDK with Vite (/build/guides/using-turbo-in-a-browser/vite) @@ -10160,11 +10208,11 @@ To verify your Vite setup is working correctly: - [vite-plugin-node-polyfills](https://www.npmjs.com/package/vite-plugin-node-polyfills) - [Turbo SDK Documentation](https://docs.ardrive.io) - [Web Usage Examples](https://docs.ardrive.io) -- [ArDrive Examples Repository](https://github.com/ardriveio/turbo-sdk) +- [ArDrive Examples Repository](https://github.com/ardriveapp/turbo-sdk) --- -For more examples and advanced usage patterns, refer to the [Turbo SDK examples directory](https://github.com/ardriveio/turbo-sdk) or the main [SDK documentation](https://docs.ardrive.io). +For more examples and advanced usage patterns, refer to the [Turbo SDK examples directory](https://github.com/ardriveapp/turbo-sdk) or the main [SDK documentation](https://docs.ardrive.io). # Working With Primary Names (/build/guides/working-with-arns/arns-primary-names) @@ -16382,7 +16430,7 @@ curl -s http://localhost:3000/ar-io/__gateway_metrics | grep tokens_consumed 4. **Verify Network Connectivity**: ```bash # Test facilitator connectivity - curl -I https://x402.org/facilitator + curl -I https://x402.org/facilitator/supported ``` ## Next Steps @@ -16524,7 +16572,7 @@ Ready to run a gateway with your own domain name and SSL certificates? Follow th SOLANA_UPLOAD_KEYPAIR_PATH=/app/wallets/.json ``` - By default, the Observer will use [Turbo Credits](https://docs.ardrive.io/docs/turbo/credits) to pay for uploading reports to Arweave. This allows reports under 100kb to be uploaded for free, but larger reports will fail if the Observer wallet does not contain Credits. Set `SOLANA_UPLOAD_PRIVATE_KEY` to the same base58 secret as `OBSERVER_PRIVATE_KEY` so Turbo has an explicit upload signer. If logs warn that `TurboReportSink` is not configured, this upload key is usually missing. Including `REPORT_DATA_SINK=arweave` in your `.env` file will configure the Observer to use AR tokens instead of Turbo Credits, without any free limit. + By default, the Observer will use [Turbo Credits](/build/upload/turbo-credits) to pay for uploading reports to Arweave. This allows reports under 100kb to be uploaded for free, but larger reports will fail if the Observer wallet does not contain Credits. Set `SOLANA_UPLOAD_PRIVATE_KEY` to the same base58 secret as `OBSERVER_PRIVATE_KEY` so Turbo has an explicit upload signer. If logs warn that `TurboReportSink` is not configured, this upload key is usually missing. Including `REPORT_DATA_SINK=arweave` in your `.env` file will configure the Observer to use AR tokens instead of Turbo Credits, without any free limit. **Start the Docker container:** ```bash @@ -18487,7 +18535,7 @@ Learn how to upload data to Arweave using the **Turbo SDK** for a streamlined up [Turbo Credits](/build/upload/turbo-credits) are the payment medium used by the Turbo Upload Service. Each Credit represents a 1:1 conversion from the upload power of the Arweave native token (AR). - - **Fiat Currency**: Credit/debit cards via the [Turbo Top Up App](https://turbo-topup.com/) + - **Fiat Currency**: Credit/debit cards via the [Turbo Top Up App](https://console.ar.io/topup) - **Cryptocurrencies**: AR, ETH, SOL, MATIC, ARIO, USDC, ETH (BASE) - **Multiple Wallets**: Ethereum, Solana, and Arweave wallets supported @@ -18952,7 +19000,7 @@ Purchase Turbo Credits to pay for uploads. When you upload, credits are automati **Option 1: Via the Web Interface** -- Go to [turbo-topup.com](https://turbo-topup.com) +- Go to [console.ar.io/topup](https://console.ar.io/topup) - Pay with fiat currencies (credit cards) or crypto tokens (ARIO, USDC, SOL, MATIC, AR) **Option 2: Via the SDK** @@ -19653,12 +19701,12 @@ console.log("Stored Turbo receipt:", { | AR.IO Use Case | Receipt Value | | -------------- | ------------- | | [File Storage](https://ar.io/use-cases/file-storage/) | Map internal file objects to immutable upload IDs and timestamped upload events | -| [Websites & Apps](https://ar.io/use-cases/websites-and-apps/) | Track deployment artifacts and ordering of publish history over time | -| [Apps & Game Assets](https://ar.io/use-cases/apps-and-game-assets/) | Prove that specific asset versions were accepted and in which sequence they were released | -| [Media Provenance](https://ar.io/use-cases/media-provenance/) | Establish chain-of-custody metadata for original and derivative media with event ordering | -| [Verifiable AI Data](https://ar.io/use-cases/verifiable-ai-data/) | Attach signed upload evidence and chronology to datasets, prompts, and model outputs | -| [Verifiable Computing](https://ar.io/use-cases/verifiable-computing/) | Bind compute inputs/outputs to durable upload receipts for reproducibility and timeline checks | -| [Durable Financial Data](https://ar.io/use-cases/durable-financial-data/) | Maintain publish-time evidence for disclosures, reports, and compliance records | +| [Websites & Apps](https://ar.io/use-cases/sites-and-apps/) | Track deployment artifacts and ordering of publish history over time | +| [Permanent Assets](https://ar.io/use-cases/digital-assets/) | Prove that specific asset versions were accepted and in which sequence they were released | +| [Media Provenance](https://ar.io/use-cases/durable-media/) | Establish chain-of-custody metadata for original and derivative media with event ordering | +| [Verifiable AI Data](https://ar.io/use-cases/verifiable-ai/) | Attach signed upload evidence and chronology to datasets, prompts, and model outputs | +| [Oracles & Onchain Data](https://ar.io/use-cases/oracles-and-onchain-data/) | Bind compute inputs/outputs to durable upload receipts for reproducibility and timeline checks | +| [Institutional Archiving](https://ar.io/use-cases/institutional-archiving/) | Maintain publish-time evidence for disclosures, reports, and compliance records | ## Deep Dive: Media Provenance @@ -19953,7 +20001,7 @@ Top up any address of a [supported wallet type](#supported-wallet-types) by payi #### Console App -Visit [console.ar.io](https://console.ar.io) to purchase credits with fiat or crypto using a connected wallet. +Visit [console.ar.io/topup](https://console.ar.io/topup) to purchase credits with fiat or crypto using a connected wallet. #### ArDrive App @@ -20068,7 +20116,7 @@ See the [Turbo SDK documentation](/sdks/turbo-sdk) and [HTTP API docs](https://p Turbo Credits can be shared with other users to fund their uploads while maintaining total control over who may spend them, how many they may spend, and for how long they can do so. This is perfect for organizations, collaborations, and teams. -Credits can be shared via the Console App at [console.ar.io](https://console.ar.io) or programmatically via the [Turbo CLI](https://github.com/ardriveapp/turbo-sdk/?tab=readme-ov-file#cli), [SDK](/sdks/turbo-sdk), or [API](https://payment.ardrive.io/api-docs). +Credits can be shared via the Console App at [console.ar.io/share](https://console.ar.io/share) or programmatically via the [Turbo CLI](https://github.com/ardriveapp/turbo-sdk/?tab=readme-ov-file#cli), [SDK](/sdks/turbo-sdk), or [API](https://payment.ardrive.io/api-docs). ### How Credit Sharing Works @@ -21145,7 +21193,7 @@ Manages the network's gateway infrastructure, staking, delegation, and the epoch 3. `prescribe_epoch` — Select observers and prescribed names via weighted roulette 4. `save_observations` — Observers submit pass/fail reports 5. `distribute_epoch` — Batched reward distribution - 6. `close_epoch` — Reclaim rent from completed epoch accounts + 6. `close_epoch` — Close completed observer epoch accounts and return remaining SOL rent to observers - **Gateway Pruning**: Gateways that repeatedly fail observation are removed from the network and subject to stake slashing. ### ario-arns (ArNS Registry) @@ -21733,7 +21781,7 @@ ArNS pricing is built from a few core ideas: This approach lets ArNS remain predictable enough for users while still adapting to namespace demand. -You can view current live pricing at [arns.ar.io](https://arns.ar.io/#/prices) to see these formulas in action. +You can view current live pricing at [arns.ar.io](https://arns.ar.io/#/prices) to see these formulas in action, or compare name prices alongside storage costs in the [ar.io Console name price table](https://console.ar.io/pricing?type=domains). ## Key Definitions @@ -22582,6 +22630,8 @@ graph LR E --> F[close_epoch] ``` +> _Rent versus rewards:_ Observers pay SOL for both account rent and transaction fees when submitting observations. Closing their completed epoch accounts returns the account rent in SOL, but not the transaction fees. ARIO observer rewards are handled separately by `distribute_epoch`. + ### 1. create_epoch **Initializes the epoch account and computes the reward rate.** @@ -22628,9 +22678,11 @@ graph LR ### 6. close_epoch -**Reclaims rent from completed epoch accounts.** +**Closes completed per-observer epoch accounts and returns each account's remaining SOL rent to that observer's wallet.** + +This is separate from `distribute_epoch`, which distributes ARIO rewards. `close_epoch` is permissionless, so any cranker may submit it, but the cranker does not receive the rent returned from observer accounts. -- Recovers SOL rent from completed epoch accounts +- Returns SOL rent from completed observer epoch accounts to their respective observers - Keeps onchain state lean over time ## Timing @@ -25519,7 +25571,7 @@ Claude will build your project, detect the output folder, and run the deploy wit 4. **Runs the deployment** with appropriate flags 5. **Reports results** — transaction ID, Arweave URL, ArNS URL -See [`examples/claude-skill/`](./examples/claude-skill/) for the full skill file and customization options. +See [`examples/claude-skill/`](https://github.com/ar-io/ar-io-deploy/tree/main/examples/claude-skill) for the full skill file and customization options. --- @@ -25590,6 +25642,11 @@ Deploy a single file: ario-deploy deploy --wallet ./wallet.json --deploy-file ./path/to/file.txt ``` +`--deploy-file` overrides `--deploy-folder`, and the file is uploaded as one +transaction with **no manifest** — an ArNS name pointed at it resolves straight +to that file, served with its own content type. Useful for a PDF, a dataset, or +a single page. Manifest-only options such as `--fallback-file` do not apply. + # Interactive Mode (Easiest) (/sdks/(clis)/ario-deploy/(commands)/interactive-mode-easiest) Run the deploy command without arguments to be guided through all deployment options: @@ -25612,6 +25669,40 @@ Pass `--arns-name` (or `--use-arns`) to skip the ArNS confirmation, or use the or no TTY) `deploy` does not prompt — supply everything via flags or `DEPLOY_KEY`. +# Single-page apps (/sdks/(clis)/ario-deploy/(commands)/single-page-apps) + +An Arweave path manifest maps each path to a transaction, and a gateway returns +404 for any path the manifest does not list. That is correct for static files +but wrong for a single-page app, whose routes are not files — `/settings` is +invented by the router and exists nowhere on disk. Without a fallback the root +loads and every deep link 404s. + +Manifests have a `fallback` for exactly this, and `ario-deploy` sets it +automatically when the build emits a `404.html`: + +```bash +ario-deploy deploy --deploy-folder ./dist +``` + +Most SPA builds do not emit one. Either copy your entry point before deploying: + +```bash +cp dist/index.html dist/404.html +``` + +…or name the fallback directly: + +```bash +ario-deploy deploy --deploy-folder ./dist --fallback-file index.html +``` + +The file must exist in the deploy folder; a path that is not there fails before +anything is uploaded, so a typo costs nothing. + +> Deep links can appear broken for up to a minute after a redeploy while +> gateways serve cached 404s from the previous manifest. Confirm with a +> cache-busting query string (`/settings?x=1`) before assuming the deploy failed. + # Upload/deploy without ArNS (/sdks/(clis)/ario-deploy/(commands)/upload-deploy-without-arns) `deploy` uploads without updating ArNS by default. You can also use the `upload` command explicitly for the same Turbo upload, dedupe cache, and payment options as deploy, minus ArNS flags: @@ -25902,7 +25993,8 @@ jobs: - `--cluster, -p`: Solana cluster for ArNS updates. Choices: `mainnet`, `devnet`. Default: `mainnet` - `--rpc-url`: Optional Solana RPC URL override for ArNS updates - `--deploy-folder, -d`: Folder to deploy. Default: `./dist` -- `--deploy-file, -f`: Deploy a single file instead of a folder +- `--deploy-file, -f`: Deploy a single file instead of a folder (no manifest is created) +- `--fallback-file`: Path, relative to the deploy folder, served for routes the manifest does not list. Defaults to `404.html` when the build emits one. See [Single-page apps](#single-page-apps). - `--undername, -u`: ANT undername to update. Default: `@` - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `60` @@ -25977,7 +26069,7 @@ The cache file is stored at `.ario-deploy/transaction-cache.json` in your projec - **Optional ArNS Updates:** Updates ArNS records via ANT with new transaction IDs - **Automated Workflow:** Integrates with GitHub Actions for continuous deployment - **Git Hash Tagging:** In CI (GitHub Actions), tags uploaded data items with the deploying commit SHA -- **404 Fallback Detection:** Automatically detects and sets 404.html as fallback +- **404 Fallback Detection:** Automatically sets `404.html` as the manifest fallback when present, so deep links into a single-page app resolve instead of 404ing. Override with `--fallback-file \` — an SPA that only builds `index.html` can point at that instead. - **Network Support:** ArNS updates run against the Solana ARIO programs on `mainnet` or `devnet`, with an optional custom RPC URL - **Flexible Deployment:** Supports deploying a folder or a single file - **Modern CLI:** Built with oclif for a robust command-line experience @@ -26063,6 +26155,9 @@ DEPLOY_KEY=$(base64 -i wallet.json) ARNS_KEY=\ pnpm deploy:on-demand - **Upload timeouts:** Files have a timeout for upload. Large files may fail and require optimization - **Insufficient Turbo Credits:** Use `--on-demand` with `--max-token-amount` to automatically fund uploads when balance is low - **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) +- **Deep links 404 but the homepage loads:** The manifest has no `fallback`. Emit a `404.html` or pass `--fallback-file index.html` — see [Single-page apps](#single-page-apps) +- **Deep links still 404 right after a redeploy:** Gateways cache the previous manifest's 404s for around a minute. Retry with a cache-busting query string before assuming the deploy failed +- **Error: "Fallback file not found in folder":** `--fallback-file` takes a path relative to the deploy folder, e.g. `index.html`, not `./dist/index.html` # Networks (/sdks/ar-io-sdk/(advanced)/advanced-networks) @@ -29040,12 +29135,12 @@ The ar.io SDK provides comprehensive tools for interacting with ar.io and the Ar } title="Pagination & Utilities" description="Handle large datasets and token conversions efficiently" - href="/sdks/ar-io-sdk/overview" + href="/sdks/ar-io-sdk/pagination" /> } title="Token Operations" description="ARIO token conversions and management utilities" - href="/sdks/ar-io-sdk/converting-ario-to-mario" + href="/sdks/ar-io-sdk/token-conversion" /> # Logging (/sdks/ar-io-sdk/logging) @@ -30524,14 +30619,6 @@ const turbo = TurboFactory.authenticated({ signer, token: 'ario' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); - -// ARIO on Base Network -const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ - signer, - token: 'base-ario', -}).topUpWithTokens({ - tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO -}); ``` ##### USDC Crypto Top Up @@ -30930,210 +31017,189 @@ const { status, id, ...fundResult } = await turbo.submitFundTransaction({ }); ``` -# ANT custody: transfer & manage records (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records) +# Buying a name with a credit card (fiat / Stripe) (/sdks/turbo-sdk/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe) -Turbo can custody the ANT (Metaplex Core asset) backing your name. These methods let you take self-custody or manage resolution records. Each is authenticated with an **action-bound, single-use signature**: the wallet signs a canonical `arns\n\\n` message plus the UUID nonce, so a captured signature can't be replayed against a different operation. +`getArNSFiatPurchaseQuote` prices a purchase in fiat and returns a Stripe +payment session, so a user can buy a name without holding credits first. ```typescript -// Self-custody exit: move the ANT to a Solana pubkey you control -await turbo.transferArNSAnt({ - antId: 'ant-id', - target: 'your-solana-pubkey', -}); - -// Set a resolution record (undername defaults to '@') -await turbo.setArNSRecord({ - antId: 'ant-id', - undername: 'docs', // omit for the apex '@' record - transactionId: 'arweave-tx-id', - ttlSeconds: 900, +const quote = await turbo.getArNSFiatPurchaseQuote({ + name: 'my-name', + intent: 'Buy-Name', + type: 'lease', + years: 1, + currency: 'usd', }); - -// Remove a resolution record -await turbo.removeArNSRecord({ antId: 'ant-id', undername: 'docs' }); ``` -# Buying a name (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/buying-a-name) - -`buyArNSName(params)` is the `Buy-Name` convenience wrapper over `purchaseArNSName`. Optionally, `paidBy` delegates the charge to one or more addresses that have shared credits with you. - -**`processId` is optional**, and it selects who owns the ANT (Metaplex Core asset) the name resolves to: +Its `paymentAmount` is the real charge and **already includes** the ANT spawn +surcharge. (On the `getArNSPriceForName` fiat estimate the split is the other +way round: `fiatEstimate.paymentAmount` is the base and +`fiatEstimate.paymentAmountWithAntSpawn` is the total.) Throws +`FiatPaymentsDisabledError` when the service has Stripe switched off. -- **Omit `processId`** → **Turbo custodial provisioning** (Model A): Turbo spawns and _owns_ the ANT for you. You can take self-custody later via `transferArNSAnt` (see "ANT custody" below). -- **Supply `processId`** → **user-owned ANT** (Model B): the name points at an ANT you already own; Turbo never takes custody. +# Listing a wallet's names (/sdks/turbo-sdk/(arns-names)/listing-a-wallet-s-names) ```typescript -// Custodial lease (Model A): omit processId → Turbo owns the ANT -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, -}); +const { names } = await turbo.getArNSNames(); // defaults to the signer's address +``` -// Lease against your own ANT (Model B) for 1 year -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', -}); +Receipt history, not a live ownership check: a name transferred away still +appears. Verify present control on chain using the returned `antId`. -// Permanent buy, charged to a delegated payer -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'permabuy', - processId: 'ant-process-id', // optional — omit for Turbo custodial provisioning - paidBy: '\', // or an array of addresses -}); +# Nonces, retries and refunds (/sdks/turbo-sdk/(arns-names)/nonces-retries-and-refunds) -console.log(receipt.nonce); // capture this to poll status / retry idempotently -``` +Credits are debited when the action is **created**, not when it is signed. So: -Full runnable example (buy → poll to terminal): +- **Persist the nonce before prompting for a signature** — use `onNonce`. +- **Never re-create an action to retry.** That debits again. Poll instead: + `await turbo.getArNSActionStatus(nonce)`. +- **An abandoned action refunds itself** — don't build a refund flow. +- Replaying `signArNSAction` on a completed action returns + `{ alreadyCompleted: true }` rather than buying twice. -```typescript +`InsufficientCreditsError` (HTTP 402) is thrown when the balance is short; +prompt a top-up, then create a **fresh** action. -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); +# Not covered — these still cost you SOL (/sdks/turbo-sdk/(arns-names)/not-covered-these-still-cost-you-sol) -async function buyName() { - try { - const { nonce } = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', - }); +`primary-name`, `release-name`, `reassign` and ANT metadata +(name/description/keywords/logo) are **not sponsored**. They stay on the +direct-signer path via [`@ar.io/sdk`](https://github.com/ar-io/ar-io-sdk). +Don't tell users they can "manage a name forever without SOL" — scope the claim +to the nine actions above. - // Poll until terminal (success => messageId, failure => failedDate) - for (;;) { - const status = await turbo.getArNSPurchaseStatus({ nonce }); - if (status.messageId) { - console.log('Purchased. ArNS write tx:', status.messageId); - return status; - } - if (status.failedDate) { - throw new Error(`Purchase failed at ${status.failedDate}`); - } - await new Promise((r) => setTimeout(r, 2000)); - } - } catch (err) { - if (err instanceof InsufficientCreditsError) { - console.error('Not enough Turbo Credits — top up and retry.'); - } - throw err; - } -} +# Pricing — quote the total (/sdks/turbo-sdk/(arns-names)/pricing-quote-the-total) + +```typescript +const price = await turbo.getArNSPriceForName({ + intent: 'Buy-Name', + name: 'my-name', + type: 'lease', + years: 1, +}); +price.wincTotal; // <- charge or display THIS +price.winc; // the name only, EXCLUDING the ANT spawn surcharge ``` -# Connecting a signer (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/connecting-a-signer) +Buying mints a fresh ANT, and Turbo fronts that account's Solana rent. A flat +cost-recovery surcharge covers it, and in a real response **the surcharge can +exceed the name's own price** — so reading `winc` under-quotes every purchase. +`wincTotal` is added by the SDK precisely so the correct field is the obvious +one. Never hardcode the surcharge: it is config-driven and derived from live +rates. -ArNS purchases are authenticated per wallet. Construct the client with `TurboFactory.authenticated` using any supported identity — the credit balance is keyed to that wallet's native address: +# The nine sponsored actions (/sdks/turbo-sdk/(arns-names)/the-nine-sponsored-actions) ```typescript +const turbo = TurboFactory.authenticated({ privateKey: jwk }); -// Arweave -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -// Ethereum -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'ethereum', +// Buy — the ONE signature in the whole lifecycle. +const { antId, messageId } = await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'lease', // or 'permabuy' + years: 1, // leases only + onNonce: (nonce) => persist(nonce), // fires BEFORE the wallet prompt }); -// Solana — request nonces are signed with arbundles' HexSolanaSigner (ed25519) -const turbo = TurboFactory.authenticated({ - privateKey: bs58SolanaSecretKey, - token: 'solana', +// Lifecycle — no signature at all. +await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +await turbo.upgradeArNSName({ name: 'my-name' }); +await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); + +// Records — free, and handled whichever shape the server picks. +await turbo.setArNSRecord({ + antId, + owner, + transactionId, + undername: '@', + ttlSeconds: 900, }); -``` +await turbo.removeArNSRecord({ antId, owner, undername: 'docs' }); -# Dependency note (@solana/codecs) (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs) +// Controllers and transfer — owner-signed, free. +await turbo.addArNSController({ antId, owner }); // omit target => Turbo +await turbo.removeArNSController({ antId, owner }); // the revoke +await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); +``` -ArNS/ARIO support pulls in `@solana/spl-token`, whose transitive `@solana/spl-token-metadata@0.1.6` imports `getDataEnumCodec` from `@solana/codecs@2.0.0-rc.1`. In `@solana/codecs@3+` that -There is no single codecs version that satisfies both `spl-token-metadata` (needs the old `getDataEnumCodec`) and `@solana/kit` (needs `5.x`), and `spl-token-metadata` has no release that uses the renamed API — so the fix belongs at the app's dependency-resolution layer, **not** at symbol-aliasing: +| Action | Costs credits | Owner signature | +| -------------------------------------------------------------------- | ------------- | --------------------------- | +| `buyArNSName` | yes | **always**, once | +| `extendArNSLease` / `upgradeArNSName` / `increaseArNSUndernameLimit` | yes | no | +| `setArNSRecord` / `removeArNSRecord` | no | only after you revoke Turbo | +| `addArNSController` / `removeArNSController` / `transferArNSAnt` | no | yes | -- **Recommended:** stop deduping `@solana/codecs` so `@solana/spl-token-metadata` keeps its own nested `2.0.0-rc.1` copy. In Vite, ensure `@solana/codecs` is **not** in `resolve.dedupe`; with pnpm/yarn, allow the nested version (avoid a hoisted-to-`6.x` override for that subtree). This is cleaner than the `getDataEnumCodec → getDiscriminatedUnionCodec` alias plugin some apps use today, and removes the need for that shim. -- If you must keep a single hoisted codecs copy, a build-time alias mapping `getDataEnumCodec` to `getDiscriminatedUnionCodec` remains the fallback. +Only the four purchase actions debit credits. Records, controllers and transfer +are free — Turbo sponsors the SOL. -# Error handling & retries (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/error-handling-retries) +`buyArNSName` grants Turbo controller rights **inside the same transaction you +sign**, which is why `setArNSRecord` needs no transaction signature afterwards. +Revoking is always available and always free. -- **`InsufficientCreditsError`** (HTTP `402`) — the wallet (or delegated payer) doesn't hold enough Turbo Credits. Prompt the user to top up, then retry. It exposes `.status === 402` and is exported from the package root. -- **`ProvidedInputError`** — thrown client-side (before any network call) when required per-intent params are missing/invalid (e.g. a lease `Buy-Name` without `years`, or `Extend-Lease` without a positive `years`). -- **`FailedRequestError`** — any other non-2xx response; inspect `.status` (e.g. `401`, `503`). +# The two shapes, if you drive it yourself (/sdks/turbo-sdk/(arns-names)/the-two-shapes-if-you-drive-it-yourself) -**Idempotency / retry guidance:** the `nonce` is the idempotency key. Capture `response.nonce` up front; if the network drops after the request is sent, re-poll `getArNSPurchaseStatus({ nonce })` rather than blindly re-buying. On a `402`, top up and issue a fresh purchase — the captured nonce still lets you reconcile status. +Every action returns one of two shapes, and **the server picks which**: ```typescript - -try { - await turbo.buyArNSName({ name, type: 'permabuy', processId }); -} catch (err) { - if (err instanceof InsufficientCreditsError) { - // surface a top-up flow to the user - } else { - throw err; - } +let res = await turbo.createArNSAction('buy-name', { name, ownerAddress }); +if (res.status === 'awaiting-signature') { + res = await turbo.signArNSAction( + res.nonce, + await owner.signTransaction(res.transaction), + ); } +// res.status === 'completed'; res.messageId is the on-chain write ``` -# Extend, increase undernames, upgrade (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade) +Branch on `status`, never on which action you called: `setArNSRecord` completes +alone while Turbo is a controller and flips to `awaiting-signature` the moment +you revoke Turbo. It degrades instead of breaking. -Each intent has a typed wrapper that enforces its required fields: +**Sign the exact bytes returned.** Turbo has already signed as fee payer; +rebuilding the transaction invalidates that signature. -```typescript -// Extend an existing lease by N years -await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +# Two identities, never conflated (/sdks/turbo-sdk/(arns-names)/two-identities-never-conflated) -// Increase the undername limit -await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); +| | Who | How it travels | +| ------------- | ---------------------------------------------------------------- | --------------------- | +| **Payer** | the Turbo identity holding credits — Arweave, Ethereum or Solana | the client's signer | +| **ANT owner** | always a **Solana** address | the `owner` parameter | -// Upgrade a lease to a permanent name -await turbo.upgradeArNSName({ name: 'my-name' }); -``` +They are allowed to be different wallets, and routinely are: one account pays +while another owns. -All of them return the same `{ nonce, purchaseReceipt, arioWriteResult }` shape as `buyArNSName` and are polled the same way. `purchaseArNSName(params)` is the general form if you prefer to pass `intent` explicitly. +# You need a Solana key, not Solana funds (/sdks/turbo-sdk/(arns-names)/you-need-a-solana-key-not-solana-funds) -# Polling purchase status (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/polling-purchase-status) +An ANT is a Metaplex Core asset on Solana, so the owner is always a Solana +address — even when you pay with Arweave or Ethereum credits. But the owner +never pays: Turbo is the fee payer on every sponsored action, so **the owner's +SOL balance can stay at zero for the life of the name**. -`getArNSPurchaseStatus({ nonce })` is available on both the authenticated and unauthenticated clients: +Supply the owner as an `ArNSOwnerSigner`: ```typescript -const status = await turbo.getArNSPurchaseStatus({ nonce }); -// status.messageId -> present on terminal success (Solana ArNS write tx id) -// status.failedDate -> present on terminal failure -``` - -# Pricing a name (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/pricing-a-name) -`getArNSPriceForName(params)` returns the cost in both Turbo Credits (`winc`) and `mARIO`. Params are validated client-side per intent (a `ProvidedInputError` is thrown for missing/invalid fields before any request is sent). - -```typescript -const { winc, mARIO } = await turbo.getArNSPriceForName({ - intent: 'Buy-Name', - name: 'my-name', - type: 'lease', // 'lease' | 'permabuy' - years: 1, // required for leases - processId: 'ant-process-id', // the ANT the name resolves to -}); +// From a secret key (servers, scripts, tests) +const owner = solanaOwnerSigner(bs58SolanaSecretKey); ``` -# Purchase lifecycle (/sdks/turbo-sdk/(arns-names-paid-with-turbo-credits)/purchase-lifecycle) - -Every purchase is identified by a client-minted **UUID `nonce`**. The nonce is: - -1. **Signed** by your wallet and sent to the bundler (proving intent). -2. The **idempotency key** for the purchase. -3. The **status-lookup key** — poll `getArNSPurchaseStatus({ nonce })` until the purchase reaches a terminal state. +A browser wallet (Phantom, Solflare, or an app's embedded wallet) should +implement the interface directly rather than exposing a secret key: -`purchaseArNSName` returns the `nonce` on **both** `response.nonce` and `response.purchaseReceipt.nonce`. A purchase is **terminal-success** once its status carries a `messageId` (the Solana transaction id of the on-chain ArNS write) and **terminal-failure** once it carries a `failedDate`. - -``` -buyArNSName() ──▶ POST /arns/purchase ──▶ { nonce, purchaseReceipt, arioWriteResult } - │ - poll getArNSPurchaseStatus({ nonce }) - │ - ┌──────────────────────────────────────┴───────────────────────┐ - messageId present (success) failedDate present (failure) +```typescript +const owner = { + getAddress: () => wallet.publicKey.toBase58(), + signTransaction: async (txBase64) => { + const tx = VersionedTransaction.deserialize( + Buffer.from(txBase64, 'base64'), + ); + const signed = await wallet.signTransaction(tx); + return Buffer.from(signed.serialize()).toString('base64'); + }, + signMessage: (message) => wallet.signMessage(message), +}; ``` # File Upload Events (/sdks/turbo-sdk/(events)/file-upload-events) @@ -31292,15 +31358,6 @@ const turbo = TurboFactory.authenticated({ }); ``` -#### Base ARIO Private Key - -```typescript -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'base-ario', -}); -``` - # Ethereum (/sdks/turbo-sdk/(signers)/ethereum) #### EthereumSigner @@ -31547,7 +31604,7 @@ The Turbo SDK provides a high-level interface for uploading data to Arweave thro } title="Events & Monitoring" description="Monitor upload progress and handle events in real-time" - href="/sdks/turbo-sdk/file-upload-events/" + href="/sdks/turbo-sdk/file-upload-events" /> } title="Credit Sharing" @@ -32057,7 +32114,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Gateway Providers" description="Learn about different gateway provider strategies" - href="/sdks/wayfinder/wayfinder-core/networkgatewaysprovider" + href="/sdks/wayfinder/wayfinder-core/gateway-providers" /> } title="Request Flow" @@ -32067,7 +32124,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Monitoring & Events" description="Monitor performance and handle events" - href="/sdks/wayfinder/wayfinder-core/global-events" + href="/sdks/wayfinder/wayfinder-core/events-and-monitoring" /> # Request Flow (/sdks/wayfinder/wayfinder-core/request-flow) diff --git a/public/sdks/(clis)/llm.txt b/public/sdks/(clis)/llm.txt index 7c62897ba..d792d3ca0 100644 --- a/public/sdks/(clis)/llm.txt +++ b/public/sdks/(clis)/llm.txt @@ -1388,7 +1388,7 @@ Claude will build your project, detect the output folder, and run the deploy wit 4. **Runs the deployment** with appropriate flags 5. **Reports results** — transaction ID, Arweave URL, ArNS URL -See [`examples/claude-skill/`](./examples/claude-skill/) for the full skill file and customization options. +See [`examples/claude-skill/`](https://github.com/ar-io/ar-io-deploy/tree/main/examples/claude-skill) for the full skill file and customization options. --- @@ -1459,6 +1459,11 @@ Deploy a single file: ario-deploy deploy --wallet ./wallet.json --deploy-file ./path/to/file.txt ``` +`--deploy-file` overrides `--deploy-folder`, and the file is uploaded as one +transaction with **no manifest** — an ArNS name pointed at it resolves straight +to that file, served with its own content type. Useful for a PDF, a dataset, or +a single page. Manifest-only options such as `--fallback-file` do not apply. + # Interactive Mode (Easiest) (/ario-deploy/(commands)/interactive-mode-easiest) Run the deploy command without arguments to be guided through all deployment options: @@ -1481,6 +1486,40 @@ Pass `--arns-name` (or `--use-arns`) to skip the ArNS confirmation, or use the or no TTY) `deploy` does not prompt — supply everything via flags or `DEPLOY_KEY`. +# Single-page apps (/ario-deploy/(commands)/single-page-apps) + +An Arweave path manifest maps each path to a transaction, and a gateway returns +404 for any path the manifest does not list. That is correct for static files +but wrong for a single-page app, whose routes are not files — `/settings` is +invented by the router and exists nowhere on disk. Without a fallback the root +loads and every deep link 404s. + +Manifests have a `fallback` for exactly this, and `ario-deploy` sets it +automatically when the build emits a `404.html`: + +```bash +ario-deploy deploy --deploy-folder ./dist +``` + +Most SPA builds do not emit one. Either copy your entry point before deploying: + +```bash +cp dist/index.html dist/404.html +``` + +…or name the fallback directly: + +```bash +ario-deploy deploy --deploy-folder ./dist --fallback-file index.html +``` + +The file must exist in the deploy folder; a path that is not there fails before +anything is uploaded, so a typo costs nothing. + +> Deep links can appear broken for up to a minute after a redeploy while +> gateways serve cached 404s from the previous manifest. Confirm with a +> cache-busting query string (`/settings?x=1`) before assuming the deploy failed. + # Upload/deploy without ArNS (/ario-deploy/(commands)/upload-deploy-without-arns) `deploy` uploads without updating ArNS by default. You can also use the `upload` command explicitly for the same Turbo upload, dedupe cache, and payment options as deploy, minus ArNS flags: @@ -1771,7 +1810,8 @@ jobs: - `--cluster, -p`: Solana cluster for ArNS updates. Choices: `mainnet`, `devnet`. Default: `mainnet` - `--rpc-url`: Optional Solana RPC URL override for ArNS updates - `--deploy-folder, -d`: Folder to deploy. Default: `./dist` -- `--deploy-file, -f`: Deploy a single file instead of a folder +- `--deploy-file, -f`: Deploy a single file instead of a folder (no manifest is created) +- `--fallback-file`: Path, relative to the deploy folder, served for routes the manifest does not list. Defaults to `404.html` when the build emits one. See [Single-page apps](#single-page-apps). - `--undername, -u`: ANT undername to update. Default: `@` - `--ttl-seconds, -t`: TTL in seconds for the ANT record (60-86400). Default: `60` @@ -1846,7 +1886,7 @@ The cache file is stored at `.ario-deploy/transaction-cache.json` in your projec - **Optional ArNS Updates:** Updates ArNS records via ANT with new transaction IDs - **Automated Workflow:** Integrates with GitHub Actions for continuous deployment - **Git Hash Tagging:** In CI (GitHub Actions), tags uploaded data items with the deploying commit SHA -- **404 Fallback Detection:** Automatically detects and sets 404.html as fallback +- **404 Fallback Detection:** Automatically sets `404.html` as the manifest fallback when present, so deep links into a single-page app resolve instead of 404ing. Override with `--fallback-file \` — an SPA that only builds `index.html` can point at that instead. - **Network Support:** ArNS updates run against the Solana ARIO programs on `mainnet` or `devnet`, with an optional custom RPC URL - **Flexible Deployment:** Supports deploying a folder or a single file - **Modern CLI:** Built with oclif for a robust command-line experience @@ -1931,4 +1971,7 @@ DEPLOY_KEY=$(base64 -i wallet.json) ARNS_KEY=\ pnpm deploy:on-demand - **Error: "ArNS name does not exist":** Verify the ArNS name is correct and exists in the specified network - **Upload timeouts:** Files have a timeout for upload. Large files may fail and require optimization - **Insufficient Turbo Credits:** Use `--on-demand` with `--max-token-amount` to automatically fund uploads when balance is low -- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) \ No newline at end of file +- **On-demand payment fails:** Ensure your wallet has sufficient tokens (ARIO or Base-ETH) and the token type matches your signer (`ario` with Arweave, `base-eth` with Ethereum) +- **Deep links 404 but the homepage loads:** The manifest has no `fallback`. Emit a `404.html` or pass `--fallback-file index.html` — see [Single-page apps](#single-page-apps) +- **Deep links still 404 right after a redeploy:** Gateways cache the previous manifest's 404s for around a minute. Retry with a cache-busting query string before assuming the deploy failed +- **Error: "Fallback file not found in folder":** `--fallback-file` takes a path relative to the deploy folder, e.g. `index.html`, not `./dist/index.html` \ No newline at end of file diff --git a/public/sdks/ar-io-sdk/llm.txt b/public/sdks/ar-io-sdk/llm.txt index 0cd905b2c..a4c0f0b0b 100644 --- a/public/sdks/ar-io-sdk/llm.txt +++ b/public/sdks/ar-io-sdk/llm.txt @@ -2974,12 +2974,12 @@ The ar.io SDK provides comprehensive tools for interacting with ar.io and the Ar } title="Pagination & Utilities" description="Handle large datasets and token conversions efficiently" - href="/sdks/ar-io-sdk/overview" + href="/sdks/ar-io-sdk/pagination" /> } title="Token Operations" description="ARIO token conversions and management utilities" - href="/sdks/ar-io-sdk/converting-ario-to-mario" + href="/sdks/ar-io-sdk/token-conversion" /> # Logging (/logging) diff --git a/public/sdks/turbo-sdk/llm.txt b/public/sdks/turbo-sdk/llm.txt index 98695297e..1dbe9f1ed 100644 --- a/public/sdks/turbo-sdk/llm.txt +++ b/public/sdks/turbo-sdk/llm.txt @@ -396,14 +396,6 @@ const turbo = TurboFactory.authenticated({ signer, token: 'ario' }); const { winc, status, id, ...fundResult } = await turbo.topUpWithTokens({ tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO }); - -// ARIO on Base Network -const { winc, status, id, ...fundResult } = await TurboFactory.authenticated({ - signer, - token: 'base-ario', -}).topUpWithTokens({ - tokenAmount: ARIOToTokenAmount(100), // 100 $ARIO -}); ``` ##### USDC Crypto Top Up @@ -802,210 +794,189 @@ const { status, id, ...fundResult } = await turbo.submitFundTransaction({ }); ``` -# ANT custody: transfer & manage records (/(arns-names-paid-with-turbo-credits)/ant-custody-transfer-manage-records) +# Buying a name with a credit card (fiat / Stripe) (/(arns-names)/buying-a-name-with-a-credit-card-fiat-stripe) -Turbo can custody the ANT (Metaplex Core asset) backing your name. These methods let you take self-custody or manage resolution records. Each is authenticated with an **action-bound, single-use signature**: the wallet signs a canonical `arns\n\\n` message plus the UUID nonce, so a captured signature can't be replayed against a different operation. +`getArNSFiatPurchaseQuote` prices a purchase in fiat and returns a Stripe +payment session, so a user can buy a name without holding credits first. ```typescript -// Self-custody exit: move the ANT to a Solana pubkey you control -await turbo.transferArNSAnt({ - antId: 'ant-id', - target: 'your-solana-pubkey', +const quote = await turbo.getArNSFiatPurchaseQuote({ + name: 'my-name', + intent: 'Buy-Name', + type: 'lease', + years: 1, + currency: 'usd', }); +``` -// Set a resolution record (undername defaults to '@') -await turbo.setArNSRecord({ - antId: 'ant-id', - undername: 'docs', // omit for the apex '@' record - transactionId: 'arweave-tx-id', - ttlSeconds: 900, -}); +Its `paymentAmount` is the real charge and **already includes** the ANT spawn +surcharge. (On the `getArNSPriceForName` fiat estimate the split is the other +way round: `fiatEstimate.paymentAmount` is the base and +`fiatEstimate.paymentAmountWithAntSpawn` is the total.) Throws +`FiatPaymentsDisabledError` when the service has Stripe switched off. + +# Listing a wallet's names (/(arns-names)/listing-a-wallet-s-names) -// Remove a resolution record -await turbo.removeArNSRecord({ antId: 'ant-id', undername: 'docs' }); +```typescript +const { names } = await turbo.getArNSNames(); // defaults to the signer's address ``` -# Buying a name (/(arns-names-paid-with-turbo-credits)/buying-a-name) +Receipt history, not a live ownership check: a name transferred away still +appears. Verify present control on chain using the returned `antId`. -`buyArNSName(params)` is the `Buy-Name` convenience wrapper over `purchaseArNSName`. Optionally, `paidBy` delegates the charge to one or more addresses that have shared credits with you. +# Nonces, retries and refunds (/(arns-names)/nonces-retries-and-refunds) -**`processId` is optional**, and it selects who owns the ANT (Metaplex Core asset) the name resolves to: +Credits are debited when the action is **created**, not when it is signed. So: -- **Omit `processId`** → **Turbo custodial provisioning** (Model A): Turbo spawns and _owns_ the ANT for you. You can take self-custody later via `transferArNSAnt` (see "ANT custody" below). -- **Supply `processId`** → **user-owned ANT** (Model B): the name points at an ANT you already own; Turbo never takes custody. +- **Persist the nonce before prompting for a signature** — use `onNonce`. +- **Never re-create an action to retry.** That debits again. Poll instead: + `await turbo.getArNSActionStatus(nonce)`. +- **An abandoned action refunds itself** — don't build a refund flow. +- Replaying `signArNSAction` on a completed action returns + `{ alreadyCompleted: true }` rather than buying twice. -```typescript -// Custodial lease (Model A): omit processId → Turbo owns the ANT -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, -}); +`InsufficientCreditsError` (HTTP 402) is thrown when the balance is short; +prompt a top-up, then create a **fresh** action. + +# Not covered — these still cost you SOL (/(arns-names)/not-covered-these-still-cost-you-sol) + +`primary-name`, `release-name`, `reassign` and ANT metadata +(name/description/keywords/logo) are **not sponsored**. They stay on the +direct-signer path via [`@ar.io/sdk`](https://github.com/ar-io/ar-io-sdk). +Don't tell users they can "manage a name forever without SOL" — scope the claim +to the nine actions above. + +# Pricing — quote the total (/(arns-names)/pricing-quote-the-total) -// Lease against your own ANT (Model B) for 1 year -const receipt = await turbo.buyArNSName({ +```typescript +const price = await turbo.getArNSPriceForName({ + intent: 'Buy-Name', name: 'my-name', type: 'lease', years: 1, - processId: 'ant-process-id', -}); - -// Permanent buy, charged to a delegated payer -const receipt = await turbo.buyArNSName({ - name: 'my-name', - type: 'permabuy', - processId: 'ant-process-id', // optional — omit for Turbo custodial provisioning - paidBy: '\', // or an array of addresses }); - -console.log(receipt.nonce); // capture this to poll status / retry idempotently -``` - -Full runnable example (buy → poll to terminal): - -```typescript - -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -async function buyName() { - try { - const { nonce } = await turbo.buyArNSName({ - name: 'my-name', - type: 'lease', - years: 1, - processId: 'ant-process-id', - }); - - // Poll until terminal (success => messageId, failure => failedDate) - for (;;) { - const status = await turbo.getArNSPurchaseStatus({ nonce }); - if (status.messageId) { - console.log('Purchased. ArNS write tx:', status.messageId); - return status; - } - if (status.failedDate) { - throw new Error(`Purchase failed at ${status.failedDate}`); - } - await new Promise((r) => setTimeout(r, 2000)); - } - } catch (err) { - if (err instanceof InsufficientCreditsError) { - console.error('Not enough Turbo Credits — top up and retry.'); - } - throw err; - } -} +price.wincTotal; // <- charge or display THIS +price.winc; // the name only, EXCLUDING the ANT spawn surcharge ``` -# Connecting a signer (/(arns-names-paid-with-turbo-credits)/connecting-a-signer) +Buying mints a fresh ANT, and Turbo fronts that account's Solana rent. A flat +cost-recovery surcharge covers it, and in a real response **the surcharge can +exceed the name's own price** — so reading `winc` under-quotes every purchase. +`wincTotal` is added by the SDK precisely so the correct field is the obvious +one. Never hardcode the surcharge: it is config-driven and derived from live +rates. -ArNS purchases are authenticated per wallet. Construct the client with `TurboFactory.authenticated` using any supported identity — the credit balance is keyed to that wallet's native address: +# The nine sponsored actions (/(arns-names)/the-nine-sponsored-actions) ```typescript +const turbo = TurboFactory.authenticated({ privateKey: jwk }); -// Arweave -const turbo = TurboFactory.authenticated({ privateKey: arweaveJwk }); - -// Ethereum -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'ethereum', +// Buy — the ONE signature in the whole lifecycle. +const { antId, messageId } = await turbo.buyArNSName({ + name: 'my-name', + owner, + type: 'lease', // or 'permabuy' + years: 1, // leases only + onNonce: (nonce) => persist(nonce), // fires BEFORE the wallet prompt }); -// Solana — request nonces are signed with arbundles' HexSolanaSigner (ed25519) -const turbo = TurboFactory.authenticated({ - privateKey: bs58SolanaSecretKey, - token: 'solana', +// Lifecycle — no signature at all. +await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +await turbo.upgradeArNSName({ name: 'my-name' }); +await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); + +// Records — free, and handled whichever shape the server picks. +await turbo.setArNSRecord({ + antId, + owner, + transactionId, + undername: '@', + ttlSeconds: 900, }); -``` +await turbo.removeArNSRecord({ antId, owner, undername: 'docs' }); -# Dependency note (@solana/codecs) (/(arns-names-paid-with-turbo-credits)/dependency-note-solana-codecs) +// Controllers and transfer — owner-signed, free. +await turbo.addArNSController({ antId, owner }); // omit target => Turbo +await turbo.removeArNSController({ antId, owner }); // the revoke +await turbo.transferArNSAnt({ antId, owner, target: newOwnerAddress }); +``` -ArNS/ARIO support pulls in `@solana/spl-token`, whose transitive `@solana/spl-token-metadata@0.1.6` imports `getDataEnumCodec` from `@solana/codecs@2.0.0-rc.1`. In `@solana/codecs@3+` that -There is no single codecs version that satisfies both `spl-token-metadata` (needs the old `getDataEnumCodec`) and `@solana/kit` (needs `5.x`), and `spl-token-metadata` has no release that uses the renamed API — so the fix belongs at the app's dependency-resolution layer, **not** at symbol-aliasing: +| Action | Costs credits | Owner signature | +| -------------------------------------------------------------------- | ------------- | --------------------------- | +| `buyArNSName` | yes | **always**, once | +| `extendArNSLease` / `upgradeArNSName` / `increaseArNSUndernameLimit` | yes | no | +| `setArNSRecord` / `removeArNSRecord` | no | only after you revoke Turbo | +| `addArNSController` / `removeArNSController` / `transferArNSAnt` | no | yes | -- **Recommended:** stop deduping `@solana/codecs` so `@solana/spl-token-metadata` keeps its own nested `2.0.0-rc.1` copy. In Vite, ensure `@solana/codecs` is **not** in `resolve.dedupe`; with pnpm/yarn, allow the nested version (avoid a hoisted-to-`6.x` override for that subtree). This is cleaner than the `getDataEnumCodec → getDiscriminatedUnionCodec` alias plugin some apps use today, and removes the need for that shim. -- If you must keep a single hoisted codecs copy, a build-time alias mapping `getDataEnumCodec` to `getDiscriminatedUnionCodec` remains the fallback. +Only the four purchase actions debit credits. Records, controllers and transfer +are free — Turbo sponsors the SOL. -# Error handling & retries (/(arns-names-paid-with-turbo-credits)/error-handling-retries) +`buyArNSName` grants Turbo controller rights **inside the same transaction you +sign**, which is why `setArNSRecord` needs no transaction signature afterwards. +Revoking is always available and always free. -- **`InsufficientCreditsError`** (HTTP `402`) — the wallet (or delegated payer) doesn't hold enough Turbo Credits. Prompt the user to top up, then retry. It exposes `.status === 402` and is exported from the package root. -- **`ProvidedInputError`** — thrown client-side (before any network call) when required per-intent params are missing/invalid (e.g. a lease `Buy-Name` without `years`, or `Extend-Lease` without a positive `years`). -- **`FailedRequestError`** — any other non-2xx response; inspect `.status` (e.g. `401`, `503`). +# The two shapes, if you drive it yourself (/(arns-names)/the-two-shapes-if-you-drive-it-yourself) -**Idempotency / retry guidance:** the `nonce` is the idempotency key. Capture `response.nonce` up front; if the network drops after the request is sent, re-poll `getArNSPurchaseStatus({ nonce })` rather than blindly re-buying. On a `402`, top up and issue a fresh purchase — the captured nonce still lets you reconcile status. +Every action returns one of two shapes, and **the server picks which**: ```typescript - -try { - await turbo.buyArNSName({ name, type: 'permabuy', processId }); -} catch (err) { - if (err instanceof InsufficientCreditsError) { - // surface a top-up flow to the user - } else { - throw err; - } +let res = await turbo.createArNSAction('buy-name', { name, ownerAddress }); +if (res.status === 'awaiting-signature') { + res = await turbo.signArNSAction( + res.nonce, + await owner.signTransaction(res.transaction), + ); } +// res.status === 'completed'; res.messageId is the on-chain write ``` -# Extend, increase undernames, upgrade (/(arns-names-paid-with-turbo-credits)/extend-increase-undernames-upgrade) +Branch on `status`, never on which action you called: `setArNSRecord` completes +alone while Turbo is a controller and flips to `awaiting-signature` the moment +you revoke Turbo. It degrades instead of breaking. -Each intent has a typed wrapper that enforces its required fields: +**Sign the exact bytes returned.** Turbo has already signed as fee payer; +rebuilding the transaction invalidates that signature. -```typescript -// Extend an existing lease by N years -await turbo.extendArNSLease({ name: 'my-name', years: 2 }); +# Two identities, never conflated (/(arns-names)/two-identities-never-conflated) -// Increase the undername limit -await turbo.increaseArNSUndernameLimit({ name: 'my-name', increaseQty: 5 }); +| | Who | How it travels | +| ------------- | ---------------------------------------------------------------- | --------------------- | +| **Payer** | the Turbo identity holding credits — Arweave, Ethereum or Solana | the client's signer | +| **ANT owner** | always a **Solana** address | the `owner` parameter | -// Upgrade a lease to a permanent name -await turbo.upgradeArNSName({ name: 'my-name' }); -``` +They are allowed to be different wallets, and routinely are: one account pays +while another owns. -All of them return the same `{ nonce, purchaseReceipt, arioWriteResult }` shape as `buyArNSName` and are polled the same way. `purchaseArNSName(params)` is the general form if you prefer to pass `intent` explicitly. +# You need a Solana key, not Solana funds (/(arns-names)/you-need-a-solana-key-not-solana-funds) -# Polling purchase status (/(arns-names-paid-with-turbo-credits)/polling-purchase-status) +An ANT is a Metaplex Core asset on Solana, so the owner is always a Solana +address — even when you pay with Arweave or Ethereum credits. But the owner +never pays: Turbo is the fee payer on every sponsored action, so **the owner's +SOL balance can stay at zero for the life of the name**. -`getArNSPurchaseStatus({ nonce })` is available on both the authenticated and unauthenticated clients: +Supply the owner as an `ArNSOwnerSigner`: ```typescript -const status = await turbo.getArNSPurchaseStatus({ nonce }); -// status.messageId -> present on terminal success (Solana ArNS write tx id) -// status.failedDate -> present on terminal failure -``` - -# Pricing a name (/(arns-names-paid-with-turbo-credits)/pricing-a-name) -`getArNSPriceForName(params)` returns the cost in both Turbo Credits (`winc`) and `mARIO`. Params are validated client-side per intent (a `ProvidedInputError` is thrown for missing/invalid fields before any request is sent). - -```typescript -const { winc, mARIO } = await turbo.getArNSPriceForName({ - intent: 'Buy-Name', - name: 'my-name', - type: 'lease', // 'lease' | 'permabuy' - years: 1, // required for leases - processId: 'ant-process-id', // the ANT the name resolves to -}); +// From a secret key (servers, scripts, tests) +const owner = solanaOwnerSigner(bs58SolanaSecretKey); ``` -# Purchase lifecycle (/(arns-names-paid-with-turbo-credits)/purchase-lifecycle) +A browser wallet (Phantom, Solflare, or an app's embedded wallet) should +implement the interface directly rather than exposing a secret key: -Every purchase is identified by a client-minted **UUID `nonce`**. The nonce is: - -1. **Signed** by your wallet and sent to the bundler (proving intent). -2. The **idempotency key** for the purchase. -3. The **status-lookup key** — poll `getArNSPurchaseStatus({ nonce })` until the purchase reaches a terminal state. - -`purchaseArNSName` returns the `nonce` on **both** `response.nonce` and `response.purchaseReceipt.nonce`. A purchase is **terminal-success** once its status carries a `messageId` (the Solana transaction id of the on-chain ArNS write) and **terminal-failure** once it carries a `failedDate`. - -``` -buyArNSName() ──▶ POST /arns/purchase ──▶ { nonce, purchaseReceipt, arioWriteResult } - │ - poll getArNSPurchaseStatus({ nonce }) - │ - ┌──────────────────────────────────────┴───────────────────────┐ - messageId present (success) failedDate present (failure) +```typescript +const owner = { + getAddress: () => wallet.publicKey.toBase58(), + signTransaction: async (txBase64) => { + const tx = VersionedTransaction.deserialize( + Buffer.from(txBase64, 'base64'), + ); + const signed = await wallet.signTransaction(tx); + return Buffer.from(signed.serialize()).toString('base64'); + }, + signMessage: (message) => wallet.signMessage(message), +}; ``` # File Upload Events (/(events)/file-upload-events) @@ -1164,15 +1135,6 @@ const turbo = TurboFactory.authenticated({ }); ``` -#### Base ARIO Private Key - -```typescript -const turbo = TurboFactory.authenticated({ - privateKey: ethHexadecimalPrivateKey, - token: 'base-ario', -}); -``` - # Ethereum (/(signers)/ethereum) #### EthereumSigner @@ -1419,7 +1381,7 @@ The Turbo SDK provides a high-level interface for uploading data to Arweave thro } title="Events & Monitoring" description="Monitor upload progress and handle events in real-time" - href="/sdks/turbo-sdk/file-upload-events/" + href="/sdks/turbo-sdk/file-upload-events" /> } title="Credit Sharing" diff --git a/public/sdks/wayfinder/llm.txt b/public/sdks/wayfinder/llm.txt index 9c07c223e..c6c4bfe0f 100644 --- a/public/sdks/wayfinder/llm.txt +++ b/public/sdks/wayfinder/llm.txt @@ -459,7 +459,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Gateway Providers" description="Learn about different gateway provider strategies" - href="/sdks/wayfinder/wayfinder-core/networkgatewaysprovider" + href="/sdks/wayfinder/wayfinder-core/gateway-providers" /> } title="Request Flow" @@ -469,7 +469,7 @@ const gatewayProvider = new CompositeGatewaysProvider({ } title="Monitoring & Events" description="Monitor performance and handle events" - href="/sdks/wayfinder/wayfinder-core/global-events" + href="/sdks/wayfinder/wayfinder-core/events-and-monitoring" /> # Request Flow (/wayfinder-core/request-flow)