Wallets

Add AION into your custom wallet.

This guide is intended to help ease the process for integrating the native AION coin with an external wallet provider.


🚧

Note

Code samples provided in this guide are not production ready, they are designed to guide you through the steps but the implementation of these functionalities should be up to you and your set up.

Infrastructure

To set up a node please refer to Aion Node Setup Guide

For more options:

  1. Blockdaemon, a node management partner offering Mainnet & Testnet (Mastery) nodes in shared or personal configurations with multiple cloud offerings & free 60 day trials.
  2. Nodesmith have hosted Aion nodes that are available for free.
  3. There's an Aion node on Azure

Accounts/Keys

Generating a random Seed Key (12 word mnemonic) for a user

// import crypto libraries
var bip39 = require('bip39');
var crypto = require('crypto');

// generate 16 byte random bytes
var  randomBytes = crypto.randomBytes(16);

// generate 12 word mnemonic from random bytes
var mnemonic = bip39.entropyToMnemonic(randomBytes.toString('hex'));
console.log(mnemonic);

Aion Derivation Path: 44'/425'

BIP44 standard describe this derivation path as m / purpose' / coin_type' / account' / change' / address_index'. One can generate multiple accounts by varying these derivation paths such as 44'/425'/0'/0'/0' , 44'/425'/0'/0'/1' and so on.

Generate Key Pair From Mnemonic and Aion Derivation Path

You will get the same key pair every time from same mnemonic & derivation path combination.

Following code generates a key pair from
Derivation path: 44'/425'/0'/0'/0'
Mnemonic: "monitor identify enter shield fog melt dance fine zone siren swamp dizzy"

🚧

Note

Derivation path for 3rd-party wallets might vary depending on their implementation. In that case only the root account (first account) will be the same, every next account after that will depend on the last 3 digit sections in the derivation path (0'/0'/0').

// import crypto libraries
var bip39 = require('bip39');

// import ed25519 HD derivation library
const { derivePath, getMasterKeyFromSeed, getPublicKey } = require('ed25519-hd-key');

// sample mnemonic
var mnemonic = "monitor identify enter shield fog melt dance fine zone siren swamp dizzy";

// generate seed from mnemonic
var seed = bip39.mnemonicToSeed(mnemonic).toString('hex');

// Sample aion derivation path 44'/425'/0'/0'/0';
const { key, chainCode} = derivePath("m/44'/425'/0'/0'/0'", seed);
console.log("Mnemonic => "+mnemonic);

// prints public - private key pair
console.log("Public Key => "+getPublicKey(key).toString('hex').substring(2,66));
console.log("Private Key => "+key.toString('hex')+getPublicKey(key).toString('hex').substring(2,66));

Expected output

Mnemonic => monitor identify enter shield fog melt dance fine zone siren swamp dizzy
Public Key => 99da6a3d2fcacb38a8a72809854f33e1702b8502f90dd549f807ac6299a2735a
Private Key => 0ae3f0696a4e4912771a34f0b22e9a2a36192c9a2ca57f23e6e8c6fa8b653a2299da6a3d2fcacb38a8a72809854f33e1702b8502f90dd549f807ac6299a2735a

Generating Aion Account Address From Key Pair

Aion account is generated from 32 byte blake2b hash of the public key and then substituting first byte of the hash with 'a0'.

// import aion-keystore library
const Accounts = require('aion-keystore');
const accs = new Accounts();

const privateKey = "0ae3f0696a4e4912771a34f0b22e9a2a36192c9a2ca57f23e6e8c6fa8b653a2299da6a3d2fcacb38a8a72809854f33e1702b8502f90dd549f807ac6299a2735a";

// load account from private key
const acc = accs.privateKeyToAccount(privateKey);
console.log("Account => "+ acc.address);

Expected output

Account => 0xa028d16f0e006e182b2744a9359cc17115c26b7ac7f10e1a8e65170fe7828c12

Account Import / Export Using Keystore Files (Storage)

Aion Keystore files can be imported or exported using aion-keystore npm package
Keystore import/export sample code is suggested here

If you will be creating a large amount key pairs, you can utilize our Java Offline Tool to generate bulk key pairs quickly. There are also Test Cases available.

Transactions

Signing Raw Transaction Offline (Promise)

// import aion-keystore library
const Accounts = require('aion-keystore');

// in node.js
const privateKey = "0xefbc7a4bb0bf24624f97409473027b62f7ff76e3d232f167e002e1f5872cc2884dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee";
const accs = new Accounts();

// load account from private key
const acc = accs.privateKeyToAccount(privateKey);

// construct transaction payload
const transaction = {
  to: "0xa050486fc4a5c236a9072961a5b7394885443cd53a704b2630d495d2fc6c268b",
  data: "",
  gasPrice: 10000000000,
  gas: 21000,
  value: new BN("1000000000000000000"),
  nonce: 1,
  timestamp: Date.now() * 1000
};
acc.signTransaction(transaction)
.then((signed) => {
  console.log(signed);
}).catch((err) => {
  console.log(err);
});

Expected output

{
    messageHash: '0xfa466752c7a073d6bfd745d89f811a803e2d0654c74230ab01e656eb52fd4369',
    signature: '0x4dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee84be4c9fdfa713e23c6b1b7f74e77f2a65037b82088611ae496c40ffc182fce2683787da136b19872cc7d9ac95a1c3400e2345202a7b09ec67c876587818010b',
    rawTransaction: '0xf8a001a0a050486fc4a5c236a9072961a5b7394885443cd53a704b2630d495d2fc6c268b880de0b6b3a764000080845b8457118252088800000002540be40001b8604dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee84be4c9fdfa713e23c6b1b7f74e77f2a65037b82088611ae496c40ffc182fce2683787da136b19872cc7d9ac95a1c3400e2345202a7b09ec67c876587818010b'
}

Signing Raw Transactions Offline (Callback)

// import aion-keystore library
const Accounts = require('aion-keystore');

// in node.js
const privateKey = "0xefbc7a4bb0bf24624f97409473027b62f7ff76e3d232f167e002e1f5872cc2884dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee";
const accs = new Accounts();

// load account from private key
const acc = accs.privateKeyToAccount(privateKey);

// construct transaction payload
const transaction = {
  to: "0xa050486fc4a5c236a9072961a5b7394885443cd53a704b2630d495d2fc6c268b",
  data: "",
  gasPrice: 10000000000,
  gas: 21000,
  value: new BN("1000000000000000000"),
  nonce: 1,
  timestamp: Date.now() * 1000
};

acc.signTransaction(transaction, (err, res) => {
  if (err) {
    console.log(err);
    return;
  }

  console.log(res);
  // outputs same as above (promise example)
});

Expected output

{
    messageHash: '0xfa466752c7a073d6bfd745d89f811a803e2d0654c74230ab01e656eb52fd4369',
    signature: '0x4dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee84be4c9fdfa713e23c6b1b7f74e77f2a65037b82088611ae496c40ffc182fce2683787da136b19872cc7d9ac95a1c3400e2345202a7b09ec67c876587818010b',
    rawTransaction: '0xf8a001a0a050486fc4a5c236a9072961a5b7394885443cd53a704b2630d495d2fc6c268b880de0b6b3a764000080845b8457118252088800000002540be40001b8604dcff097bf9912b71d619fc78100de8cf7f55dfddbc2bf5f9fdc36bd670781ee84be4c9fdfa713e23c6b1b7f74e77f2a65037b82088611ae496c40ffc182fce2683787da136b19872cc7d9ac95a1c3400e2345202a7b09ec67c876587818010b'
  }

Python

The above functionalities can also be conducted in Python. Click here for more information.

Sending Transaction

//const accs = require('aion-keystore');
const BN = require('bn.js');
const Web3 = require("aion-web3");

const web3 = new Web3(new Web3.providers.HttpProvider("https://api.nodesmith.io/v1/aion/testnet/jsonrpc?apiKey=730f950a5d3c4c39951c3ecbdcf30771"));

//run the node
//import the private key
const privateKey = "YOUR_PRIVATE_KEY";
const acc = web3.eth.accounts.privateKeyToAccount(privateKey);
console.log(acc);

const toAccount = "TO_ACCOUNT";



web3.eth.getBalance(acc.address).then(console.log);

//construct a transaction
const Tx = {
  from: acc.address,
  to: toAccount,
  data: "",
  gasPrice: 10000000000,
  gas: 21000,
  value: new BN("30000000000000000")
};


async function signTx() {


  let cur_nonce = web3.eth.getTransactionCount(Tx.from, /*'pending'*/(err, nonce) => {
    if (err) {
      console.log('getting nonce error >', err);
      return;
    } else {
      console.log('Current nonce is: ', nonce);
      return nonce;
    }
  });


  let nonce_increment = 0;
  Tx.nonce = cur_nonce + nonce_increment;

  //console.log("Nonce assigned => " + Tx.nonce);

  let signedTx = await acc.signTransaction(Tx, (err, res) => {
    if (err) {
      console.log('signTx error >', err);
      return;
    } else {
      console.log('signTx', res);
      return res;
    }
  });


  try {
    web3.eth.sendSignedTransaction(
      signedTx.rawTransaction
      ).on('transactionHash',
        txHash => { 
        console.log("txHash", txHash) }
      ).on('receipt',
        receipt => { console.log("Here is your receipt:\n", receipt) }
      ).on('error',
        error => { 
          if (Tx.nonce != cur_nonce) {
             console.log("Your transaction is in the pending queue.");
             return;
          }
          else
            console.log("error", receipt) 
        });
  } catch(err){
    console.log('something went wrong ->', err);
  };
};


signTx();

Validation

cd aion/web3
node console.js 127.0.0.01:8545 (You can also connect to remote ip)
aion-127.0.0.1:8545> eth.getTransactionReceipt('0x97bdc27dd4e6b0700b1bcb3ed5c912bedfae7bf5014e473ada55871cdccd8eee')

Expected output

{ blockHash:
   '0x315bb2a990f48de1da64b7f8a3f933e1e3a4c16c458942ece26e71256e3b8dd1',
  nrgPrice: '0x02540be400',
  logsBloom:
   '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  nrgUsed: 21000,
  contractAddress: null,
  transactionIndex: 0,
  transactionHash:
   '0x97bdc27dd4e6b0700b1bcb3ed5c912bedfae7bf5014e473ada55871cdccd8eee',
  gasLimit: '0x55f0',
  cumulativeNrgUsed: 21000,
  gasUsed: '0x5208',
  blockNumber: 21,
  root:
   'db0dfe69eedb1be75476d7b2c43e7c223e0c0ab67c29055763673cfffd29c61d',
  cumulativeGasUsed: '0x5208',
  from:
   '0xa07c95cc8729a0503c5ad50eb37ec8a27cd22d65de3bb225982ec55201366920',
  to:
   '0xa0dd7205acbaad446e7bd4e1755a9d1e8dd74b793656cc7af5876cba0f616bab',
  logs: [],
  gasPrice: '0x02540be400',
  status: '0x1' }

Monitoring

Get Balance

cd aion/web3
node console.js 127.0.0.01:8545 (You can also connect to remote ip)
aion-127.0.0.1:8545> eth.getBalance('0xa0a1e55cbbffc99d9dcaf56e5350847267471cd6d69d4dead14953e5e82d97bf')

Expected output

BigNumber { s: 1, e: 20, c: [ 1520197, 24825377779973 ] }

Mastery Testnet & Faucet

This testnet environment was released with the latest v0.3.0 Moldoveanu Peak mainnet. You may wish to to configure your Aion node here to deploy and test out smart contracts.

Using Ledger Nano S

Account Import From Ledger Nano S

This feature takes HD derivation path as an input and returns 64-byte hex String where former 32 bytes is public key and latter is account address. Input is strictly in this sequence:

1st Byte: Beginning of the message (β€˜0xeo’ for Aion)
2nd Byte: Signifies the message intention (02 for getting public key and account address)
3rd Byte: For future use (if address needs to be confirmed or just returned)
4th Byte: For future use (00 chain code)
5th Byte: Length of derivation path + 1
6th Byte: Length of derivation path / 4
7th Byte to End: Hex String of derivation path

Example

Input: For 44’/425’/0’/0’/0’
e002010015058000002c800001a9800000008000000080000000

Output: It may vary based on Nano S mnemonic but structure will remain same
b808763388bc601f5138e310c0b80c0db1efc9f9fb107697c209fe1e2d698e96a0208c72fad2b444b7d7195
bd0452c0f6c2f34cfa94a3691c1637da46430d196

where:
Public-key: b808763388bc601f5138e310c0b80c0db1efc9f9fb107697c209fe1e2d698e96
Account: a0208c72fad2b444b7d7195bd0452c0f6c2f34cfa94a3691c1637da46430d196

Integrating Aion Ledger Nano S With Wallet

Driver Location: 
https://github.com/LedgerHQ/ledger-app-aion/tree/master/hid_driver
Ubuntu Driver Usage Example
./Aion --param=e002010015058000002c800001a9800000008000000080000000
Windows Driver Usage Example
npm run get:aion-hid e002010015058000002c800001a9800000008000000080000001

Signing Transaction on Nano S

This feature takes derivation path and RLP encoded transaction as an input and returns 64-byte signature as an output. Input is strictly in this sequence:

1st Byte : Beginning of the message (β€˜0xeo’ for Aion)
2nd Byte: Signifies the message intention (04 for signing transaction)
3rd Byte: For future use (Identify the beginning but not required now as max Tx size is 230 bytes)
4th Byte: For future use (00 chain code)
5th Byte: Length of derivation path + 1+ encoded transaction
6th Byte: Length of derivation path / 4
7th Byte till end of dongle path: Dongle path bytes
End: RLP encoded transaction bytes

Example

Input: For 44’/425’/0’/0’/0’

e0040000cf058000002c800001a9800000008000000080000000f8b800a0a0185ef98ac4841900b49ad9b432af2db7235e09ec3755e5ee36e9c4947007dd89056bc75e2d63100000b87ca0f2daa8de60d0e911fb468492242d60e15757408aff2902a0f2daa8de60d0e911fb468492242d604e1e11ec6f142bfee15757408aff2902a0f2daa8de60d0e911fb468492242d604e1e11ec6f142bfee15757408aff2902a0f2daa8de60d0e911fb468492242d604e1e11ec6f14a0f2daa0f2daa0f2daa0f2daa0f28332298e8252088502540be40001

Output: 64 byte signature

1a4879c0de1c9fbabcfd0d4c0f792d9feea831d4223a2425bd6a3d360913c2d65fdda4efafa2f6eaf624e4be7cbb9e42e1c13b6415b4d37d88dade8f11224800

Official Aion Ledger app GitHub

Additional Information