JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 140
  • Score
    100M100P100Q92270F
  • License MIT

Use Arweave browser wallets (Wander or any other compatible wallet) from Node.js applications. Perfect for CLI tools and scripts that need secure wallet interactions without exposing private keys.

Package Exports

  • node-arweave-wallet
  • node-arweave-wallet/package.json

Readme

node-arweave-wallet

npm version npm downloads JSDocs License

Use Arweave browser wallets (Wander or any other compatible wallet) from Node.js applications. Perfect for CLI tools and scripts that need secure wallet interactions without exposing private keys.

šŸ“‘ Table of Contents

✨ Features

  • šŸ” Full Arweave Wallet API Support - Complete implementation of the arweaveWallet API
  • 🌐 Browser-Based Security - Uses your existing browser wallet (Wander or any other compatible wallet), keeping keys secure
  • šŸ”Œ @permaweb/aoconnect Compatible - Works seamlessly with @permaweb/aoconnect

šŸ“¦ Installation

npm install node-arweave-wallet
# or
pnpm add node-arweave-wallet
# or
yarn add node-arweave-wallet

šŸš€ Quick Start

import Arweave from 'arweave'
import { NodeArweaveWallet } from 'node-arweave-wallet'

const arweave = new Arweave({
  host: 'arweave.net',
  port: 443,
  protocol: 'https',
})

// Create wallet instance
const arweaveWallet = new NodeArweaveWallet({
  port: 3737, // Optional: defaults to 3737, use 0 for random port
})

// Initialize - opens browser for wallet connection
await arweaveWallet.initialize()

// Connect with required permissions
await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION'])

// Get wallet address
const address = await arweaveWallet.getActiveAddress()
console.log('Connected wallet:', address)

// Sign a transaction
const tx = await arweave.createTransaction({ data: 'Hello Arweave!' })
await arweaveWallet.sign(tx)

// Submit the transaction
const response = await arweave.transactions.post(tx)
console.log(response.status)

// Clean up when done
await arweaveWallet.close('success')

šŸ“– API Reference

Initialization & Connection

new NodeArweaveWallet(config?)

Creates a new wallet instance.

const arweaveWallet = new NodeArweaveWallet({
  port: 3737,              // Optional: port number (default: 3737, use 0 for random)
  freePort: false,         // Optional: auto-free port if in use (default: false)
  requestTimeout: 300000,  // Optional: request timeout in ms (default: 5 minutes)
  browser: 'chrome',       // Optional: specify browser (default: system default)
  browserProfile: 'Work',  // Optional: specify browser profile
})

See Configuration for more information.

arweaveWallet.initialize()

Starts the local server and opens the browser for wallet connection.

await arweaveWallet.initialize()

arweaveWallet.close(status?)

Closes the wallet connection and server. Optionally marks completion status.

await arweaveWallet.close('success') // or 'failed'

Wallet APIs

arweaveWallet.connect(permissions, appInfo?, gateway?)

Connects to the wallet with specified permissions, app info and gateway.

await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION'], {
  name: 'My App',
  logo: 'https://arweave.net/azW8iYR5A6bPXyS6WpMmw-qLTXNleS-vv4LJDR9Hf-s',
})

Available Permissions:

  • ACCESS_ADDRESS - Get active wallet address
  • ACCESS_PUBLIC_KEY - Get public key
  • ACCESS_ALL_ADDRESSES - Get all wallet addresses
  • SIGN_TRANSACTION - Sign Arweave transactions
  • ENCRYPT - Encrypt data
  • DECRYPT - Decrypt data
  • SIGNATURE - Sign arbitrary data
  • ACCESS_ARWEAVE_CONFIG - Get Arweave gateway config
  • DISPATCH - Sign and dispatch transactions
  • ACCESS_TOKENS - Access tokens & token balances

arweaveWallet.disconnect()

Disconnects from the wallet.

await arweaveWallet.disconnect()

arweaveWallet.getActiveAddress()

Gets the currently active wallet address.

const address = await arweaveWallet.getActiveAddress()
// Returns: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

arweaveWallet.getAllAddresses()

Gets all addresses in the wallet.

const addresses = await arweaveWallet.getAllAddresses()
// Returns: ["address1", "address2", ...]

arweaveWallet.getWalletNames()

Gets wallet names mapped to addresses.

const names = await arweaveWallet.getWalletNames()
// Returns: { "address1": "Wallet 1", "address2": "Wallet 2" }

arweaveWallet.getActivePublicKey()

Gets the public key of the active wallet.

const publicKey = await arweaveWallet.getActivePublicKey()
// Returns: base64 encoded public key

arweaveWallet.getPermissions()

Gets currently granted permissions.

const permissions = await arweaveWallet.getPermissions()
// Returns: ["ACCESS_ADDRESS", "SIGN_TRANSACTION", ...]

arweaveWallet.getArweaveConfig()

Gets the Arweave gateway configuration.

const config = await arweaveWallet.getArweaveConfig()
// Returns: { host: "arweave.net", port: 443, protocol: "https" }

arweaveWallet.sign(transaction, options?)

Signs an Arweave transaction.

import Arweave from 'arweave'

const arweave = new Arweave({
  host: 'arweave.net',
  port: 443,
  protocol: 'https',
})

const tx = await arweave.createTransaction({
  data: 'Hello Arweave!',
})

await arweaveWallet.sign(tx)

arweaveWallet.dispatch(transaction, options?)

Signs and dispatches a transaction to the network.

const tx = await arweave.createTransaction({
  data: 'Hello Arweave!',
})
tx.addTag('Content-Type', 'text/plain')

const result = await arweaveWallet.dispatch(tx)
console.log('Transaction ID:', result.id)
// Returns: { id: "transaction_id", type?: "BASE" | "BUNDLED" }

arweaveWallet.signDataItem(dataItem, options?)

Signs a single data item for ANS-104 bundling.

const signedDataItem = await arweaveWallet.signDataItem({
  data: 'Hello from data item!',
  tags: [
    { name: 'Content-Type', value: 'text/plain' },
    { name: 'App-Name', value: 'MyApp' },
  ],
  target: 'target_address'
})

// Returns: Uint8Array of signed data item

arweaveWallet.batchSignDataItem(dataItems, options?)

Signs multiple data items in a batch.

const results = await arweaveWallet.batchSignDataItem([
  {
    data: 'First item',
    tags: [{ name: 'Type', value: 'Test1' }],
  },
  {
    data: 'Second item',
    tags: [{ name: 'Type', value: 'Test2' }],
  },
])

// Returns: Array<{ id: string, raw: Uint8Array }>

arweaveWallet.encrypt(data, options)

Encrypts data using the wallet's public key.

const encrypted = await arweaveWallet.encrypt('Secret message', {
  algorithm: 'RSA-OAEP',
  hash: 'SHA-256',
})

// Returns: Uint8Array

arweaveWallet.decrypt(data, options)

Decrypts data using the wallet's private key.

const decrypted = await arweaveWallet.decrypt(encrypted, {
  algorithm: 'RSA-OAEP',
  hash: 'SHA-256',
})

const text = new TextDecoder().decode(decrypted)
// Returns: "Secret message"

arweaveWallet.signMessage(data, options?)

Signs a message with the wallet's private key.

const data = new TextEncoder().encode('Message to sign')
const signature = await arweaveWallet.signMessage(data, {
  hashAlgorithm: 'SHA-256', // or 'SHA-384', 'SHA-512'
})

// Returns: Uint8Array signature

arweaveWallet.verifyMessage(data, signature, publicKey?, options?)

Verifies a message signature.

const isValid = await arweaveWallet.verifyMessage(
  data,
  signature,
  publicKey, // optional
  { hashAlgorithm: 'SHA-256' },
)

// Returns: boolean

arweaveWallet.signature(data, algorithm)

Signs arbitrary data with custom algorithm.

const data = new TextEncoder().encode('Data to sign')
const signature = await arweaveWallet.signature(data, {
  name: 'RSA-PSS',
  saltLength: 32,
})

// Returns: Uint8Array

arweaveWallet.privateHash(data, options?)

Creates a hash using the wallet's private key.

const data = new TextEncoder().encode('Data to hash')
const hash = await arweaveWallet.privateHash(data, {
  hashAlgorithm: 'SHA-256',
})

// Returns: Uint8Array

arweaveWallet.tokenBalance(id)

Gets the balance for a specific token.

const balance = await arweaveWallet.tokenBalance('7GoQfmSOct_aUOWKM4xbKGg6DzAmOgdKwg8Kf-CbHm4')
// Returns: string (balance as string representation)

// Convert to number if needed
const numericBalance = Number(balance)

arweaveWallet.userTokens(options?)

Gets all tokens owned by the user.

// Get tokens without balance
const tokens = await arweaveWallet.userTokens({ fetchBalance: false })

// Get tokens with balance
const tokensWithBalance = await arweaveWallet.userTokens({ fetchBalance: true })

// Returns: Array<TokenInfo>
// TokenInfo: {
//  id?: string
//  Name?: string
//  Ticker?: string
//  Logo?: string
//  Denomination: number
//  processId: string
//  lastUpdated?: string | null
//  type?: 'asset' | 'collectible'
//  hidden?: boolean
//  balance?: string
// }

arweaveWallet.getWanderTierInfo()

Gets Wander wallet tier information (Wander wallet specific feature).

const tierInfo = await arweaveWallet.getWanderTierInfo()

// Returns: {
//   tier: 'Prime' | 'Edge' | 'Reserve' | 'Select' | 'Core'
//   balance: string
//   rank: '' | number
//   progress: number  // 0 to 1
//   snapshotTimestamp: number
//   totalHolders: number
// }

console.log(`Tier: ${tierInfo.tier}`)
console.log(`Progress: ${tierInfo.progress.toFixed(2)}%`)
console.log(`Total Holders: ${tierInfo.totalHolders}`)

šŸ› ļø Configuration

Port Configuration

The package uses a local HTTP server to communicate with the browser. You can configure the port:

// Default port (3737)
const arweaveWallet = new NodeArweaveWallet()

// Custom port
const arweaveWallet = new NodeArweaveWallet({ port: 8080 })

// Random available port (useful for testing or avoiding conflicts)
const arweaveWallet = new NodeArweaveWallet({ port: 0 })

Automatic Port Freeing

If your desired port is already in use, you can enable automatic port freeing to free it up and retry:

const arweaveWallet = new NodeArweaveWallet({ freePort: true })

Request Timeout Configuration

You can configure how long the wallet will wait for user responses before timing out. This is useful if you need more time to review transactions or if you want faster failures:

// Default timeout (5 minutes = 300000ms)
const arweaveWallet = new NodeArweaveWallet()

// Custom timeout (10 minutes)
const arweaveWallet = new NodeArweaveWallet({ 
  requestTimeout: 600000 // 10 minutes in milliseconds
})

// Shorter timeout (1 minute) for faster failures
const arweaveWallet = new NodeArweaveWallet({ 
  requestTimeout: 60000 // 1 minute in milliseconds
})

// All options together
const arweaveWallet = new NodeArweaveWallet({
  port: 3737,
  freePort: true,
  requestTimeout: 300000, // 5 minutes
})

Note: The timeout applies to individual wallet operations (signing, encrypting, etc.). If the user doesn't respond within this time, the operation will fail with a timeout error.

Browser Options:

You can specify which browser to open for wallet connection:

// Use a specific browser by name
const arweaveWallet = new NodeArweaveWallet({ browser: 'chrome' })    // Google Chrome
const arweaveWallet = new NodeArweaveWallet({ browser: 'firefox' })   // Firefox
const arweaveWallet = new NodeArweaveWallet({ browser: 'edge' })      // Microsoft Edge
const arweaveWallet = new NodeArweaveWallet({ browser: 'brave' })     // Brave Browser
const arweaveWallet = new NodeArweaveWallet({ browser: 'opera' })     // Opera

// Disable auto-opening (you'll need to open the URL manually)
const arweaveWallet = new NodeArweaveWallet({ browser: false })

Browser Profile Options:

You can also specify a browser profile to use:

// Chromium-based browsers (Chrome, Edge, Brave, Opera, Vivaldi)
// Use profile directory name OR display name (auto-resolved)
const arweaveWallet = new NodeArweaveWallet({ 
  browser: 'chrome',
  browserProfile: 'Profile 1'  // or 'Default', 'Profile 2', 'Work', 'Personal', etc.
})

// Firefox-based browsers (Firefox, Zen)
// Use profile name exactly as shown in profile manager
const arweaveWallet = new NodeArweaveWallet({ 
  browser: 'firefox',
  browserProfile: 'dev-edition-default'  // or your custom profile name
})

// Opera (Note: Opera doesn't support profile arguments, profile option is ignored)
const arweaveWallet = new NodeArweaveWallet({ 
  browser: 'opera'
  // browserProfile is not supported for Opera
})

Important: The library automatically resolves display names to directory names for some Chromium-based browsers!

  • Chrome/Edge/Brave/Vivaldi: You can use either the display name ("Work") or directory name ("Profile 2")
  • Firefox/Zen: Use the profile name exactly as shown in the profile manager
  • Opera: Profile selection is not supported (profile option is ignored)

How to find your profile name:

  1. Chrome/Edge/Brave/Vivaldi:

    • Open chrome://version/ (or edge://version/, brave://version/, vivaldi://version/)
    • Look for "Profile Path"
    • You can use either:
      • The display name shown in the browser UI: 'Work', 'Personal'
      • The directory name (last part of path): 'Default', 'Profile 1', 'Profile 2'
  2. Firefox/Zen:

    • Run firefox -P (or zen -P) to open the profile manager
    • Use the exact profile name shown (e.g., 'default-release', 'dev-edition-default')
  3. Opera:

    • Profile selection is not supported by Opera's command-line interface

šŸ’” Usage Examples

CLI Tool

#!/usr/bin/env node
import fs from 'node:fs'
import process from 'node:process'
import Arweave from 'arweave'
import { NodeArweaveWallet } from 'node-arweave-wallet'

async function uploadFile(filePath: string) {
  const arweaveWallet = new NodeArweaveWallet()

  try {
    console.log('šŸš€ Initializing wallet...')
    await arweaveWallet.initialize()

    await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION', 'DISPATCH'])

    const address = await arweaveWallet.getActiveAddress()
    console.log(`āœ… Connected: ${address}`)

    const arweave = new Arweave({
      host: 'arweave.net',
      port: 443,
      protocol: 'https',
    })

    const data = fs.readFileSync(filePath)
    const tx = await arweave.createTransaction({ data })
    tx.addTag('Content-Type', 'text/plain')

    console.log('šŸ“ Signing and uploading...')
    const result = await arweaveWallet.dispatch(tx)

    console.log(`āœ… Uploaded! TX: ${result.id}`)
    console.log(`šŸ”— https://arweave.net/${result.id}`)

    await arweaveWallet.close('success')
  } catch (error) {
    console.error('āŒ Error:', error.message)
    await arweaveWallet.close('failed')
    process.exit(1)
  }
}

uploadFile(process.argv[2])

AR Transfer

import Arweave from 'arweave'
import { NodeArweaveWallet } from 'node-arweave-wallet'

const arweave = new Arweave({
  host: 'arweave.net',
  port: 443,
  protocol: 'https',
})

async function transferAR() {
  const arweaveWallet = new NodeArweaveWallet()
  await arweaveWallet.initialize()
  await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION'])

  const address = await arweaveWallet.getActiveAddress()
  console.log('Connected wallet:', address)

  // Create transfer transaction
  const tx = await arweave.createTransaction({ data: 'Hello Arweave!' })
  await arweaveWallet.sign(tx)

  // Submit the transaction
  const response = await arweave.transactions.post(tx)
  console.log(response.status)

  await arweaveWallet.close('success')
}

transferAR();

AO Token (WNDR) Transfer

import { message, result } from '@permaweb/aoconnect'
import { createDataItemSigner, NodeArweaveWallet } from 'node-arweave-wallet'

async function transferWNDR() {
  const arweaveWallet = new NodeArweaveWallet()
  await arweaveWallet.initialize()
  await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION'])

  const signer = createDataItemSigner(wallet)

  const messageId = await message({
    process: "7GoQfmSOct_aUOWKM4xbKGg6DzAmOgdKwg8Kf-CbHm4", // WNDR token process id
    signer,
    tags: [
      { name: 'Action', value: 'Transfer' },
      { name: "Recipient", value: "address_to_send_to" }, // address to send to
      { name: "Quantity", value: "1000000000000000000" } // 1 WNDR
    ]
  })

  console.log('Message sent: ', messageId)

  const response = await result({
    message: messageId,
    process: "7GoQfmSOct_aUOWKM4xbKGg6DzAmOgdKwg8Kf-CbHm4", // WNDR token process id
  })

  console.log('Response:', JSON.stringify(response, null, 2))

  await arweaveWallet.close('success')
}

transferWNDR();

ArNS domain purchase

  import { ARIO } from "@ar.io/sdk";
  import { ArconnectSigner as ArweaveSigner } from "@dha-team/arbundles";

  async function purchaseDomain() {
    const arweaveWallet = new NodeArweaveWallet();
    await arweaveWallet.initialize();
    await arweaveWallet.connect([
      "ACCESS_ADDRESS",
      "ACCESS_PUBLIC_KEY",
      "SIGN_TRANSACTION",
    ]);

    const ario = ARIO.mainnet({ signer: new ArweaveSigner(arweaveWallet) });

    const record = await ario.buyRecord(
      {
        name: "domain-name-to-purchase",
        type: "lease",
        years: 1,
      },
      {
        // optional tags
        tags: [{ name: "App-Name", value: "node-arweave-wallet" }],
        onSigningProgress: (step, event) => {
          console.log(`Signing progress: ${step}`);
          if (step === "spawning-ant") {
            console.log("Spawning ant:", event);
          }
          if (step === "registering-ant") {
            console.log("Registering ant:", event);
          }
          if (step === "verifying-state") {
            console.log("Verifying state:", event);
          }
          if (step === "buying-name") {
            console.log("Buying name:", event);
          }
        },
      }
    );

    console.log(JSON.stringify(record, null, 2));

    await arweaveWallet.close('success');
  }

  purchaseDomain();

Batch Data Item

import fs from 'node:fs'
import path from 'node:path'
import { NodeArweaveWallet } from 'node-arweave-wallet'

async function batchUpload(files: string[]) {
  const arweaveWallet = new NodeArweaveWallet()
  await arweaveWallet.initialize()
  await arweaveWallet.connect(['ACCESS_ADDRESS', 'SIGN_TRANSACTION'])

  const dataItems = files.map(file => ({
    data: fs.readFileSync(file),
    tags: [
      { name: 'Content-Type', value: 'application/octet-stream' },
      { name: 'File-Name', value: path.basename(file) },
    ],
  }))

  console.log(`šŸ“ Signing ${files.length} files...`)
  const signed = await arweaveWallet.batchSignDataItem(dataItems)

  console.log('āœ… All files signed!')
  signed.forEach((item, i) => {
    console.log(`   ${i + 1}. ${item.id}`)
  })

  // upload the signed data items to the network

  await arweaveWallet.close('success')
}

Token, Balance & Tier

import { NodeArweaveWallet } from 'node-arweave-wallet'

async function manageTokens() {
  const arweaveWallet = new NodeArweaveWallet()
  await arweaveWallet.initialize()
  await arweaveWallet.connect(['ACCESS_ADDRESS', 'ACCESS_TOKENS'])

  // Get token balance
  const tokenId = '7GoQfmSOct_aUOWKM4xbKGg6DzAmOgdKwg8Kf-CbHm4' // WNDR token
  const balance = await arweaveWallet.tokenBalance(tokenId)
  console.log(`Balance: ${balance}`)

  // Get all user tokens
  const tokens = await arweaveWallet.userTokens({ fetchBalance: true })
  console.log(`\nšŸ“Š Your tokens (${tokens.length}):`)
  tokens.forEach(token => {
    console.log(`  • ${token.Name || token.Ticker || token.id}`)
    console.log(`    Denomination: ${token.Denomination}`)
  })

  // Get Wander tier info (Wander wallet only)
  try {
    const tierInfo = await arweaveWallet.getWanderTierInfo()
    console.log(`\nšŸ† Wander Tier: ${tierInfo.tier}`)
    console.log(`   Progress: ${tierInfo.progress.toFixed(2)}%`)
    console.log(`   Rank: ${tierInfo.rank || 'N/A'}`)
    console.log(`   Total Holders: ${tierInfo.totalHolders}`)
  } catch (error) {
    console.log('Wander tier info not available')
  }

  await arweaveWallet.close('success')
}

šŸ”’ Security

  • No Private Keys in Node.js - Your private keys never leave the browser wallet
  • Browser Extension Security - Leverages battle-tested browser wallet security
  • Local-Only Server - Server only listens on 127.0.0.1 (localhost)
  • Permission-Based - Request only the permissions you need

šŸ¤ Browser Wallet Compatibility

Works with any arweaveWallet compatible browser wallet:

  • āœ… Wander (Recommended)
  • āœ… Any wallet implementing the arweaveWallet API

šŸ› Troubleshooting

Port Already in Use

If port 3737 is already in use:

// Use a different port
const arweaveWallet = new NodeArweaveWallet({ port: 8080 })

// Or let the system choose an available port
const arweaveWallet = new NodeArweaveWallet({ port: 0 })

Browser Doesn't Open Automatically

The URL will be printed to the console. Open it manually:

http://localhost:3737

Request Timeout

Keep the browser tab open while signing transactions. The package has a default 5-minute timeout for each wallet operation (configurable via requestTimeout option). If you need more time to review transactions, increase the timeout:

const arweaveWallet = new NodeArweaveWallet({ 
  requestTimeout: 600000 // 10 minutes
})

Note: Closing the browser tab will immediately interrupt operations regardless of the timeout setting.

šŸ“„ License

MIT License Ā© Pawan Paudel