Skip to main content
HOW-TO
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();
}