While AR.IO provides powerful tools for accessing and interacting with data on Arweave, that data must first be uploaded to the network. This guide will walk you through the process of uploading data to Arweave using the Turbo SDK, which provides a streamlined experience for data uploads.
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). Turbo Credits can be purchased with fiat currency via the Turbo Top Up App, or with supported cryptocurrencies via the Turbo SDK. Learn more about Turbo Credits and available methods for purchasing them here.
Node.js Environment
Purchasing Credits With Crypto: Node.js
import { TurboFactory, WinstonToTokenAmount } from'@ardrive/turbo-sdk'// Initialize authenticated clientconstturbo=awaitTurboFactory.authenticated({ privateKey: jwk })// Top up with AR tokensconsttopUpResult=awaitturbo.topUpWithTokens({ tokenAmount:WinstonToTokenAmount(100_000_000),// 0.0001 AR})
Browser Environment
In a browser environment, the topUpWithTokens method is not available. Instead, you'll need to manually send tokens to the Turbo wallet address and then submit the transaction for processing. Here are detailed examples for each supported chain:
Browser Top-Up Examples
import { TurboFactory } from'@ardrive/turbo-sdk/web'import Arweave from'arweave'import axios from'axios'constTURBO_AR_ADDRESS='JNC6vBhjHY1EPwV3pEeNmrsgFMxH5d38_LHsZ7jful8'constAR_AMOUNT=0.0001// Amount in AR// Function to send AR and wait for confirmationconstsendArToTurbo=async () => {if (!window.arweaveWallet) {thrownewError('Please install Wander') }// Initialize Arweaveconstarweave=Arweave.init({ host:'arweave.net', port:443, protocol:'https', })// Create transactionconsttransaction=awaitarweave.createTransaction({ target:TURBO_AR_ADDRESS, quantity:arweave.ar.arToWinston(AR_AMOUNT.toString()), })// Sign and post transactionawaitwindow.arweaveWallet.sign(transaction)constresponse=awaitarweave.transactions.post(transaction)returntransaction.id}// Function to submit transaction with retriesconstsubmitTransactionWithRetries=async (txId, maxRetries =3) => {let retries =0while (retries < maxRetries) {try {constresponse=awaitturbo.submitFundTransaction({ txId })return response } catch (error) { retries++if (retries === maxRetries) throw error// Wait 30 seconds before retryingawaitnewPromise((resolve) =>setTimeout(resolve,30000)) } }}// Complete top-up processconsttopUpWithAr=async () => {try {// Send AR and get transaction IDconsttxId=awaitsendArToTurbo()console.log('Transaction sent:', txId)// Wait 36 minutes for chain settlementawaitnewPromise((resolve) =>setTimeout(resolve,36*60*1000))// Submit transaction with retriesconstresponse=awaitsubmitTransactionWithRetries(txId)console.log('Credits added:', response) } catch (error) {console.error('Top-up failed:', error) }}
Note: The wait times for chain settlement are approximate and may need adjustment based on network conditions:
Once you have purchased Turbo credits, you can upload files and folders to Arweave. The process is the same regardless of which token type you used for authentication, but differs between Node.js and browser environments.
Node.js Environment
Node.js Upload Examples
import { TurboFactory } from'@ardrive/turbo-sdk'import fs from'fs'import path from'path'import mime from'mime-types'// Initialize authenticated clientconstturbo=awaitTurboFactory.authenticated({ privateKey: jwk })// Function to upload a single fileconstuploadFile=async (filePath) => {try {// Get file infoconstfileSize=fs.statSync(filePath).sizeconstmimeType=mime.lookup(filePath) ||'application/octet-stream'// Upload fileconstresult=awaitturbo.uploadFile({fileStreamFactory: () =>fs.createReadStream(filePath),fileSizeFactory: () => fileSize, dataItemOpts: { tags: [ { name:'Content-Type', value: mimeType, }, ], }, })console.log('File uploaded!', { id:result.id, url:`https://arweave.net/${result.id}`, owner:result.owner, dataCaches:result.dataCaches, })return result } catch (error) {console.error('Upload failed:', error)throw error }}// Example usageawaituploadFile('./path/to/your/file.pdf')
Browser Environment
Browser Upload Examples
import { TurboFactory } from'@ardrive/turbo-sdk/web'// Initialize authenticated clientconstturbo=awaitTurboFactory.authenticated({ signer })// HTML input element<input type="file" id="file-input" accept="image/*,video/*,audio/*,.pdf,.txt"/>// Function to upload a single fileconstuploadFile=async (file) => {try {constresult=awaitturbo.uploadFile({fileStreamFactory: () =>file.stream(),fileSizeFactory: () =>file.size, dataItemOpts: { tags: [ { name:'Content-Type', value:file.type ||'application/octet-stream' } ] } })console.log('File uploaded!', { id:result.id, url:`https://arweave.net/${result.id}`, owner:result.owner, dataCaches:result.dataCaches })return result } catch (error) {console.error('Upload failed:', error)throw error }}// Example usage with file inputconstfileInput=document.getElementById('file-input')fileInput.addEventListener('change',async (event) => {constfile=fileInput.files[0]if (!file) returnawaituploadFile(file)})// Example usage with drag and dropconstdropZone=document.getElementById('drop-zone')dropZone.addEventListener('dragover', (e) => {e.preventDefault()e.stopPropagation()dropZone.classList.add('drag-over')})dropZone.addEventListener('dragleave', (e) => {e.preventDefault()e.stopPropagation()dropZone.classList.remove('drag-over')})dropZone.addEventListener('drop',async (e) => {e.preventDefault()e.stopPropagation()constfile=e.dataTransfer.files[0]if (!file) returnawaituploadFile(file)})
Important Notes:
For single file uploads, always include a Content-Type tag to ensure proper file viewing
The fileStreamFactory must return a NEW stream each time it's called
Folder uploads automatically detect and set Content-Type tags for all files
You can specify additional tags in dataItemOpts for both file and folder uploads
The maxConcurrentUploads option controls how many files are uploaded simultaneously
Use throwOnFailure: true to ensure all files are uploaded successfully
Here are complete examples showing how to authenticate, check balances, and handle lazy funding for uploads. These examples demonstrate the full workflow from start to finish.
Node.js Environment
Complete Node.js Example
import { TurboFactory } from'@ardrive/turbo-sdk'import fs from'fs'import path from'path'import mime from'mime-types'import axios from'axios'// ConstantsconstFREE_UPLOAD_SIZE=100*1024// 100KB in bytesconstPRICE_BUFFER=1.1// 10% buffer for price fluctuations// Initialize authenticated clientconstturbo=awaitTurboFactory.authenticated({ privateKey: jwk })// Function to get token price from CoinGeckoconstgetTokenPrice=async (token:string) => {constresponse=awaitaxios.get(`https://api.coingecko.com/api/v3/simple/price?ids=${token}&vs_currencies=usd`, )returnresponse.data[token].usd}// Function to calculate required token amountconstcalculateTokenAmount=async (wincAmount:string, tokenType:string) => {// Get fiat rates for 1 GiBconstfiatRates=awaitturbo.getFiatRates()constusdPerGiB=fiatRates.usd// Convert winc to GiBconstwincPerGiB=1_000_000_000_000// 1 GiB in wincconstrequiredGiB=Number(wincAmount) / wincPerGiBconstrequiredUsd= requiredGiB * usdPerGiB// Get token priceconsttokenPrice=awaitgetTokenPrice(tokenType)consttokenAmount= (requiredUsd / tokenPrice) *PRICE_BUFFERreturn tokenAmount}// Function to check balance and fund if neededconstensureSufficientBalance=async (fileSize:number, tokenType:string) => {// Check current balanceconstbalance=awaitturbo.getBalance()constcurrentWinc=BigInt(balance.controlledWinc)// If file is under 100KB, it's freeif (fileSize <=FREE_UPLOAD_SIZE) {returntrue }// Get upload costconstcosts=awaitturbo.getUploadCosts({ bytes: [fileSize] })constrequiredWinc=BigInt(costs[0].winc)// If we have enough balance, return trueif (currentWinc >= requiredWinc) {returntrue }// Calculate and purchase required tokensconsttokenAmount=awaitcalculateTokenAmount(requiredWinc.toString(), tokenType, )// Top up with tokensawaitturbo.topUpWithTokens({ tokenAmount: tokenAmount, })returntrue}// Function to upload a fileconstuploadFile=async (filePath:string) => {try {// Get file infoconstfileSize=fs.statSync(filePath).sizeconstmimeType=mime.lookup(filePath) ||'application/octet-stream'// Ensure sufficient balanceawaitensureSufficientBalance(fileSize,'arweave')// Upload fileconstresult=awaitturbo.uploadFile({fileStreamFactory: () =>fs.createReadStream(filePath),fileSizeFactory: () => fileSize, dataItemOpts: { tags: [ { name:'Content-Type', value: mimeType, }, ], }, })console.log('File uploaded!', { id:result.id, url:`https://arweave.net/${result.id}`, owner:result.owner, dataCaches:result.dataCaches, })return result } catch (error) {console.error('Upload failed:', error)throw error }}// Example usageawaituploadFile('./path/to/your/file.pdf')
Browser Environment
Complete Browser Example
import { TurboFactory } from'@ardrive/turbo-sdk/web'import Arweave from'arweave'import axios from'axios'// ConstantsconstFREE_UPLOAD_SIZE=100*1024// 100KB in bytesconstPRICE_BUFFER=1.1// 10% buffer for price fluctuationsconstTURBO_AR_ADDRESS='JNC6vBhjHY1EPwV3pEeNmrsgFMxH5d38_LHsZ7jful8'// Initialize authenticated client with Wanderif (!window.arweaveWallet) {thrownewError('Please install Wander')}// Initialize Arweaveconstarweave=Arweave.init({ host:'arweave.net', port:443, protocol:'https'})constturbo=awaitTurboFactory.authenticated({ privateKey:window.arweaveWallet, token:'arweave'})// Function to get token price from CoinGeckoconstgetTokenPrice=async (token:string) => {constresponse=awaitaxios.get(`https://api.coingecko.com/api/v3/simple/price?ids=${token}&vs_currencies=usd` )returnresponse.data[token].usd}// Function to calculate required token amountconstcalculateTokenAmount=async ( wincAmount:string, tokenType:string) => {// Get fiat rates for 1 GiBconstfiatRates=awaitturbo.getFiatRates()constusdPerGiB=fiatRates.usd// Get winc cost for 1 GiBconstcosts=awaitturbo.getUploadCosts({ bytes: [1024*1024*1024] }) // 1 GiB in bytesconstwincPerGiB=BigInt(costs[0].winc)// Calculate cost per winc in USDconstusdPerWinc=Number(usdPerGiB) /Number(wincPerGiB)// Calculate required USD amountconstrequiredUsd=Number(wincAmount) * usdPerWinc// Get token priceconsttokenPrice=awaitgetTokenPrice(tokenType)consttokenAmount= (requiredUsd / tokenPrice) *PRICE_BUFFERreturn tokenAmount}// Function to check balance and fund if neededconstensureSufficientBalance=async ( fileSize:number, tokenType:string) => {// Check current balanceconstbalance=awaitturbo.getBalance()constcurrentWinc=BigInt(balance.controlledWinc)// If file is under 100KB, it's freeif (fileSize <=FREE_UPLOAD_SIZE) {returntrue }// Get upload costconstcosts=awaitturbo.getUploadCosts({ bytes: [fileSize] })constrequiredWinc=BigInt(costs[0].winc)// If we have enough balance, return trueif (currentWinc >= requiredWinc) {returntrue }// Calculate and purchase required tokensconsttokenAmount=awaitcalculateTokenAmount(requiredWinc.toString(), tokenType )// Create transactionconsttransaction=awaitarweave.createTransaction({ target:TURBO_AR_ADDRESS, quantity:arweave.ar.arToWinston(tokenAmount.toString()) })// Sign and post transactionawaitwindow.arweaveWallet.sign(transaction)awaitarweave.transactions.post(transaction)// Wait for confirmation (typically 30-36 minutes)awaitnewPromise((resolve) =>setTimeout(resolve,36*60*1000))// Submit transaction to Turboawaitturbo.submitFundTransaction({ txId:transaction.id })returntrue}// Function to upload a fileconstuploadFile=async (file:File) => {try {// Ensure sufficient balanceawaitensureSufficientBalance(file.size,'arweave')// Upload fileconstresult=awaitturbo.uploadFile({fileStreamFactory: () =>file.stream(),fileSizeFactory: () =>file.size, dataItemOpts: { tags: [ { name:'Content-Type', value:file.type ||'application/octet-stream' } ] } })console.log('File uploaded!', { id:result.id, url:`https://arweave.net/${result.id}`, owner:result.owner, dataCaches:result.dataCaches })return result } catch (error) {console.error('Upload failed:', error)throw error }}// HTML input element<input type="file" id="file-input" accept="image/*,video/*,audio/*,.pdf,.txt"/>// Example usage with file inputconstfileInput=document.getElementById('file-input')fileInput.addEventListener('change',async (event) => {constfile=fileInput.files[0]if (!file) returnawaituploadFile(file)})// Example usage with drag and dropconstdropZone=document.getElementById('drop-zone')dropZone.addEventListener('dragover', (e) => {e.preventDefault()e.stopPropagation()dropZone.classList.add('drag-over')})dropZone.addEventListener('dragleave', (e) => {e.preventDefault()e.stopPropagation()dropZone.classList.remove('drag-over')})dropZone.addEventListener('drop',async (e) => {e.preventDefault()e.stopPropagation()dropZone.classList.remove('drag-over')constfile=e.dataTransfer.files[0]if (!file) returnawaituploadFile(file)})