Package Exports
- node-arweave-wallet
- node-arweave-wallet/package.json
Readme
node-arweave-wallet
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
- Installation
- Quick Start
- API Reference
- Configuration
- Usage Examples
- Security
- Browser Wallet Compatibility
- Troubleshooting
- License
⨠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 addressACCESS_PUBLIC_KEY- Get public keyACCESS_ALL_ADDRESSES- Get all wallet addressesSIGN_TRANSACTION- Sign Arweave transactionsENCRYPT- Encrypt dataDECRYPT- Decrypt dataSIGNATURE- Sign arbitrary dataACCESS_ARWEAVE_CONFIG- Get Arweave gateway configDISPATCH- Sign and dispatch transactionsACCESS_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 keyarweaveWallet.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 itemarweaveWallet.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: Uint8ArrayarweaveWallet.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 signaturearweaveWallet.verifyMessage(data, signature, publicKey?, options?)
Verifies a message signature.
const isValid = await arweaveWallet.verifyMessage(
data,
signature,
publicKey, // optional
{ hashAlgorithm: 'SHA-256' },
)
// Returns: booleanarweaveWallet.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: Uint8ArrayarweaveWallet.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: Uint8ArrayarweaveWallet.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:
Chrome/Edge/Brave/Vivaldi:
- Open
chrome://version/(oredge://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'
- The display name shown in the browser UI:
- Open
Firefox/Zen:
- Run
firefox -P(orzen -P) to open the profile manager - Use the exact profile name shown (e.g.,
'default-release','dev-edition-default')
- Run
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:3737Request 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