Skip to main content
MAIN CHAIN

Working with the ELA Main Chain

The ELA main chain is a UTXO-based blockchain written in Go. It uses BPoS consensus combined with AuxPoW (merged mining with Bitcoin). Transactions are structurally similar to Bitcoin but with critical differences.

Key Differences from Bitcoin

AspectBitcoinELA Main Chain
Curvesecp256k1NIST P-256 (secp256r1)
Address prefix1, 3, bc1E (standard), 8 (multi-sig), X (cross-chain), i (DID)
Transaction types~540+ (voting, DID, Council proposals, etc.)
ConsensusPoWBPoS + AuxPoW (merged mining)
Block time~10 minutes~2 minutes
ScriptingBitcoin ScriptSubset of Bitcoin Script

SDK Installation

# From npm (if published)
npm install @elastosfoundation/wallet-js-sdk

# Or from GitHub
git clone https://github.com/elastos/Elastos.ELA.Wallet.JS.SDK.git
cd Elastos.ELA.Wallet.JS.SDK
npm install && npm run build

Generating Addresses

import { Mnemonic, HDKey, Address, SignType } from "@elastosfoundation/wallet-js-sdk";

const mnemonic = Mnemonic.generate("english");
const seed = Mnemonic.toSeed(mnemonic, "");
const masterKey = HDKey.fromMasterSeed(seed, HDKey.CURVE_P256);

const accountKey = masterKey.deriveWithPath("m/44'/0'/0'");
const pubKey = accountKey.deriveWithIndex(0).getPublicKeyBytes();
const address = Address.newFromPubKey(pubKey, Address.PREFIX_STANDARD);

console.log("Address:", address.toString()); // Starts with 'E'
console.log("Public Key:", Buffer.from(pubKey).toString("hex"));

Address Version Bytes

// Standard address (version byte 0x21)
const standardAddr = Address.newFromPubKey(pubKey, 0x21);

// Multi-sig address (version byte 0xc7)
const multiSigAddr = Address.newFromMultiSigPubKeys(
3, // required signatures
[pubKey1, pubKey2, pubKey3, pubKey4, pubKey5],
0xc7
);

// Cross-chain address (version byte 0x67)
const crossChainAddr = Address.newFromPubKey(pubKey, 0x67);

Understanding UTXO Transactions

Unlike Ethereum's account model, ELA uses UTXOs. To send ELA, you:

  1. Query unspent outputs (UTXOs) belonging to your address
  2. Select UTXOs that sum to at least the amount you want to send
  3. Create a transaction with those UTXOs as inputs
  4. Add outputs: one for the recipient, one for change back to yourself
  5. Sign each input with your P-256 private key
  6. Broadcast the signed transaction
RPC Endpoints

The ELA mainchain has two types of API:

  • JSON-RPC (for node operations): https://api.elastos.io/ela
  • REST Explorer API: https://blockchain.elastos.io/api/v1/

Querying UTXOs

async function getUTXOs(address: string): Promise<UTXO[]> {
const response = await fetch("https://api.elastos.io/ela", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "listunspent",
params: [{ addresses: [address] }],
}),
});

const data = await response.json();
return data.result;
}

interface UTXO {
txid: string;
vout: number;
address: string;
amount: string;
confirmations: number;
}

Creating and Signing a Transaction

import {
Transaction,
TransactionInput,
TransactionOutput,
SignType,
} from "@elastosfoundation/wallet-js-sdk";

async function createTransferTransaction(
fromPrivKey: Buffer,
fromPubKey: Buffer,
fromAddress: string,
toAddress: string,
amountSela: bigint
): Promise<string> {
const utxos = await getUTXOs(fromAddress);

const feeSela = 10000n; // 0.0001 ELA
const targetSela = amountSela + feeSela;

let totalInput = 0n;
const selectedUTXOs: UTXO[] = [];
for (const utxo of utxos) {
selectedUTXOs.push(utxo);
totalInput += BigInt(Math.round(parseFloat(utxo.amount) * 1e8));
if (totalInput >= targetSela) break;
}

if (totalInput < targetSela) {
throw new Error(`Insufficient funds: have ${totalInput} sela, need ${targetSela} sela`);
}

const tx = new Transaction();
tx.txType = 0x02; // TransferAsset

for (const utxo of selectedUTXOs) {
const input = new TransactionInput();
input.txid = utxo.txid;
input.vout = utxo.vout;
input.sequence = 0xfffffffe;
tx.addInput(input);
}

const recipientOutput = new TransactionOutput();
recipientOutput.address = toAddress;
recipientOutput.amount = amountSela;
recipientOutput.assetId = Transaction.ELA_ASSET_ID;
tx.addOutput(recipientOutput);

const changeSela = totalInput - amountSela - feeSela;
if (changeSela > 0n) {
const changeOutput = new TransactionOutput();
changeOutput.address = fromAddress;
changeOutput.amount = changeSela;
changeOutput.assetId = Transaction.ELA_ASSET_ID;
tx.addOutput(changeOutput);
}

for (let i = 0; i < tx.inputs.length; i++) {
tx.signInput(i, fromPrivKey, fromPubKey, SignType.SIGN_TYPE_P256);
}

return tx.serialize().toString("hex");
}

Broadcasting a Transaction

async function broadcastTransaction(rawTx: string): Promise<string> {
const response = await fetch("https://api.elastos.io/ela", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "sendrawtransaction",
params: [rawTx],
}),
});

const data = await response.json();
if (data.error) {
throw new Error(`Broadcast failed: ${data.error.message}`);
}
return data.result;
}

Common Transaction Types

For quick reference, here are the types you'll use most often from the SDK:

CodeNamePurpose
0x02TransferAssetStandard ELA transfer
0x08TransferCrossChainAssetCross-chain deposit (main to ESC/EID)
0x09RegisterProducerRegister a BPoS validator
0x21RegisterCRRegister as Council candidate
0x25CRCProposalSubmit a governance proposal
0x62ExchangeVotesConvert ELA to stake votes
0x63VotingCast BPoS votes with lock time

For the complete list of all 40+ transaction types with correct hex codes, see the Transaction Types reference.

Querying the Main Chain

async function getBlockCount(): Promise<number> {
const res = await rpcCall("getblockcount", []);
return res.result;
}

async function getTransaction(txid: string): Promise<any> {
const res = await rpcCall("getrawtransaction", [txid, true]);
return res.result;
}

async function getBalance(address: string): Promise<string> {
const utxos = await getUTXOs(address);
let total = 0;
for (const utxo of utxos) {
total += parseFloat(utxo.amount);
}
return total.toFixed(8);
}

async function rpcCall(method: string, params: unknown[]): Promise<any> {
const response = await fetch("https://api.elastos.io/ela", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
});
return response.json();
}