Skip to main content
SUBMIT A PRSUBMIT AN ISSUElast edit: May 30, 2025

Withdraw TAO from Alice Account (Local Development)

Every locally deployed dev-mode blockchain comes provisioned with an 'Alice' account holding a large bag of TAO.

This page shows how to withdraw TAO to your wallet, using a transaction that requires root permissions, and therefore is only available in local development.

Prerequesites

Deploy a Subtensor Blockchain locally

Procedure

  1. Create Wallet with MetaMask

    1. Install Metamask wallet browser extension, if you haven't already.
    2. Create a new account or import an existing one.
    3. Add the Bittensor EVM network to MetaMask:
      • Network Name: Bittensor EVM
      • RPC URL: https://test.chain.opentensor.ai
      • Chain ID: 945
      • Currency Symbol: TAO
      • Block Explorer URL: test.chain.opentensor.ai
    4. Click Save.
    5. Click Switch network.
  2. Install the EVM Examples repo

    1. Clone the Opentensor EVM-Bittensor GitHub repo:

      git clone https://github.com/opentensor/evm-bittensor.git

    2. Navigate to evm-bittensor directory:

      cd evm-bittensor

    3. Install the dependencies:npm install

Configure your request

The withdraw.js script expects your configuration to be available in config.js. Select the local configuration options for rpcURL and wsUrl.

danger

Handle your private keys with care. Do not commit them to Github.

// PROTECT YOUR PRIVATE KEYS WELL, NEVER COMMIT THEM TO GITHUB OR SHARE WITH ANYONE
const ethPrivateKey = <YOUR PRIVATE KEY>;
const subSeed = "//Alice";
const rpcUrlLocal = 'http://127.0.0.1:9946';
const rpcUrlTestnet = 'https://test.chain.opentensor.ai';
const wsUrlLocal = 'ws://127.0.0.1:9946';
const wsUrlTestnet = 'wss://test.chain.opentensor.ai';

module.exports = {
ethPrivateKey,
subSeed,
rpcUrl: rpcUrlLocal,
wsUrl: wsUrlLocal,
}

Run the script

node withdraw.js

Source code:

const { ethers } = require('ethers');
const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api');
const { convertH160ToSS58 } = require('./address-mapping.js');

// PROTECT YOUR PRIVATE KEYS WELL, NEVER COMMIT THEM TO GITHUB OR SHARE WITH ANYONE
const { ethPrivateKey, subSeed, rpcUrl, wsUrl } = require('./config.js');

function sendTransaction(api, call, signer) {
return new Promise((resolve, reject) => {
let unsubscribed = false;

const unsubscribe = call.signAndSend(signer, ({ status, events, dispatchError }) => {
const safelyUnsubscribe = () => {
if (!unsubscribed) {
unsubscribed = true;
unsubscribe.then(() => {})
.catch(error => console.error('Failed to unsubscribe:', error));
}
};

// Check for transaction errors
if (dispatchError) {
let errout = dispatchError.toString();
if (dispatchError.isModule) {
// for module errors, we have the section indexed, lookup
const decoded = api.registry.findMetaError(dispatchError.asModule);
const { docs, name, section } = decoded;
errout = `${name}: ${docs}`;
}
safelyUnsubscribe();
reject(Error(errout));
}
// Log and resolve when the transaction is included in a block
if (status.isInBlock) {
safelyUnsubscribe();
resolve(status.asInBlock);
}
}).catch((error) => {
reject(error);
});
});
}

async function main() {
const wsProvider = new WsProvider(wsUrl);
const api = await ApiPromise.create({ provider: wsProvider });
const keyring = new Keyring({ type: 'sr25519' });

const sender = keyring.addFromUri(subSeed); // Your sender's private key/seed

// Get ethereum address that matches the private key from the secrets file
const provider = new ethers.JsonRpcProvider(rpcUrl);
const signer = new ethers.Wallet(ethPrivateKey, provider);
const recipientEthereumAddress = signer.address;

const ss58Address = convertH160ToSS58(recipientEthereumAddress);
console.log(`Mirror: ${ss58Address}`);
// Amount to send: 1 TAO on Substrate side = 1*10^9
const amount = "1000000000";

// Alice funds herself with 1M TAO
const txSudoSetBalance = api.tx.sudo.sudo(
api.tx.balances.forceSetBalance(sender.address, "1000000000000000")
);
await sendTransaction(api, txSudoSetBalance, sender);
console.log('Balace force-set');

// Create a transfer transaction
const transfer = api.tx.balances.transferKeepAlive(ss58Address, amount);

// Sign and send the transaction
await sendTransaction(api, transfer, sender);
console.log(`Transfer sent to ${recipientEthereumAddress} (its ss58 mirror address is: ${ss58Address})`);
await api.disconnect();
}

main().catch(console.error);