JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 140
  • Score
    100M100P100Q92315F
  • 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 bundle 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.

✨ 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 { NodeArweaveWallet } from 'node-arweave-wallet'

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

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

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

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

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

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

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

šŸ“– API Reference

Initialization & Connection

new NodeArweaveWallet(config?)

Creates a new wallet instance.

const wallet = new NodeArweaveWallet({
  port: 3737 // Optional: port number (default: 3737, use 0 for random)
})

wallet.initialize()

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

await wallet.initialize()

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

Programmatically connects to the wallet with specified permissions.

await wallet.connect(
  ['ACCESS_ADDRESS', 'SIGN_TRANSACTION'],
  {
    name: 'My App',
    logo: 'https://example.com/logo.png'
  }
)

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

wallet.disconnect()

Disconnects from the wallet.

await wallet.disconnect()

wallet.close(status?)

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

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

Wallet APIs

wallet.getActiveAddress()

Gets the currently active wallet address.

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

wallet.getAllAddresses()

Gets all addresses in the wallet.

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

wallet.getWalletNames()

Gets wallet names mapped to addresses.

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

wallet.getActivePublicKey()

Gets the public key of the active wallet.

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

wallet.getPermissions()

Gets currently granted permissions.

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

wallet.getArweaveConfig()

Gets the Arweave gateway configuration.

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

wallet.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 wallet.sign(tx)

wallet.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 wallet.dispatch(tx)
console.log('Transaction ID:', result.id)
// Returns: { id: "transaction_id", type?: "BASE" | "BUNDLED" }

wallet.signDataItem(dataItem, options?)

Signs a single data item for ANS-104 bundling.

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

// Returns: Uint8Array of signed data item

wallet.batchSignDataItem(dataItems, options?)

Signs multiple data items in a batch.

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

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

wallet.encrypt(data, options)

Encrypts data using the wallet's public key.

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

// Returns: Uint8Array

wallet.decrypt(data, options)

Decrypts data using the wallet's private key.

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

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

wallet.signMessage(data, options?)

Signs a message with the wallet's private key.

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

// Returns: Uint8Array signature

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

Verifies a message signature.

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

// Returns: boolean

wallet.signature(data, algorithm)

Signs arbitrary data with custom algorithm.

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

// Returns: Uint8Array

wallet.privateHash(data, options?)

Creates a hash using the wallet's private key.

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

// Returns: Uint8Array

wallet.tokenBalance(id)

Gets the balance for a specific token.

const balance = await wallet.tokenBalance('xU9zFkq3X2ZQ6olwNVvr1vUWIjc3kXTWr7xKQD6dh10')
// Returns: string (balance as string representation)

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

wallet.userTokens(options?)

Gets all tokens owned by the user.

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

// Get tokens with balance
const tokensWithBalance = await wallet.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
// }

wallet.getWanderTierInfo()

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

const tierInfo = await wallet.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 * 100).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 wallet = new NodeArweaveWallet()

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

// Random available port (useful for testing or avoiding conflicts)
const wallet = 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 wallet = new NodeArweaveWallet({ freePort: true })

šŸ’” Usage Examples

CLI Tool Example

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

async function deployFile(filePath: string) {
  const wallet = new NodeArweaveWallet()

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

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

    const address = await wallet.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 deploying...')
    const result = await wallet.dispatch(tx)

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

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

deployFile(process.argv[2])

AO Process Example

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

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

  const signer = createDataItemSigner(wallet)

  const messageId = await message({
    process: 'PROCESS_ID',
    signer,
    tags: [
      { name: 'Action', value: 'Balance' }
    ]
  })

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

  const { Messages } = await result({
    message: messageId,
    process: 'PROCESS_ID'
  })

  console.log('Response:', Messages[0].Data)

  await wallet.close('success')
}

Batch Data Item Example

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

async function batchUpload(files: string[]) {
  const wallet = new NodeArweaveWallet()
  await wallet.initialize()
  await wallet.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 wallet.batchSignDataItem(dataItems)

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

  await wallet.close('success')
}

Token Management Example

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

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

  // Add a token to the wallet
  const tokenId = 'xU9zFkq3X2ZQ6olwNVvr1vUWIjc3kXTWr7xKQD6dh10' // $U token
  await wallet.addToken(tokenId, 'asset')
  console.log('āœ… Token added!')

  // Check if token is added
  const isAdded = await wallet.isTokenAdded(tokenId)
  console.log(`Token added: ${isAdded}`)

  // Get token balance
  const balance = await wallet.tokenBalance(tokenId)
  console.log(`Balance: ${balance}`)

  // Get all user tokens
  const tokens = await wallet.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 wallet.getWanderTierInfo()
    console.log(`\nšŸ† Wander Tier: ${tierInfo.tier}`)
    console.log(`   Progress: ${(tierInfo.progress * 100).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 wallet.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 wallet = new NodeArweaveWallet({ port: 8080 })

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

Browser Doesn't Open Automatically

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

http://localhost:3737

Connection Timeout

Keep the browser tab open while signing transactions. The package has a generous 5-minute timeout, but closing the tab will interrupt operations.

šŸ“„ License

MIT License Ā© Pawan Paudel