Ego Blockchain Developer API
Build on Ego

Ego Blockchain Developer API

Ego exposes JSON-RPC 2.0 plus a few plain HTTP routes; there is no GraphQL. A web app connects users through the Ego Wallet extension, and the app's server reads the chain and submits signed transactions through its own Ego node.

In the browserwindow.ego.request() for connect, send, contract calls and signatures.
On your serverJSON-RPC at POST http://127.0.0.1:47395/ on your own node.
Public, read-onlyhttps://api.egoblockchain.com for blocks, prices and node lists.

How the pieces connect

Users sign in their wallet; your app works through its own node Your web app runs in the browser calls window.ego.request Ego Wallet extension keeps the private key user approves each request Ego Desktop node localhost port 47395 relays to the network Your server checks sign-in signatures links wallets to accounts Your Ego node EGO_HEADLESS=1, localhost balances, nonces, tx.submit connect, send, sign tx.submit your API over HTTPS JSON-RPC, POST / Ego peer network blocks, transactions

Your web page never holds keys: it asks the extension, and your server reads and writes the chain through its own node.

Connect a wallet

The Ego Wallet browser extension injects window.ego into every page (and window.ethereum when no other wallet took it). Every call is window.ego.request({ method, params }) and returns a Promise. The user approves each connect, send, call and signature in the extension's popup; the private key never reaches the page.

MethodParamsReturnsPopup
ego_requestAccounts
alias eth_requestAccounts
none["egot1..."]Yes, the first time per site
ego_accounts
aliases eth_accounts, ego_getAccounts
none["egot1..."], or [] if not connected or lockedNo
ego_chainId
alias eth_chainId
none"0x1" (chain_id 1)No
ego_sendTransaction[{ to, amount_egoc, memo? }]tx hash "0x..."Yes
ego_callContract[{ contractAddr, entrypoint, callArgs }], callArgs as hextx hashYes
ego_signMessage
aliases ego_sign, personal_sign
[message], text or 0x hexEd25519 signature, 128 hex charactersYes

Input rules the extension enforces: to must be an egot1... address, amount_egoc at least 0.000001, memo at most 256 characters, entrypoint matches [A-Za-z_][A-Za-z0-9_]{0,63}, callArgs is even-length hex. eth_sendTransaction also works and reads value in wei (1 EGOC = 1018).

const [address] = await window.ego.request({ method: 'ego_requestAccounts' });
const hash = await window.ego.request({
  method: 'ego_sendTransaction',
  params: [{ to: 'egot1...', amount_egoc: 5, memo: 'myapp:order:42' }],
});

Proving a user owns an address (sign-in)

Have the page sign a one-time challenge with ego_signMessage, then verify on your server. The wallet signs "\x19Ego Signed Message:\n" + byteLength + "\n" + message with Ed25519. The server needs the user's 32-byte public key: take it from any transaction they have sent (public_key_ed25519 in wallet.getTransactionHistory), then check that the key derives to the claimed address (see Signing).

The extension does not hand the public key to the page yet, so a wallet that has never sent a transaction cannot sign in this way. A small ego_getPublicKey method would close it.

Run your own node

The full API lives in every Ego Desktop node, so an app should run its own node on its server and call it from the backend. The same Linux build runs without a window when started with EGO_HEADLESS=1; it syncs the chain and serves the API like the desktop app.

SettingDefaultWhat it does
EGO_HEADLESS=1offFull node with no window
EGO_RPC_PORT47395Port of the JSON-RPC and HTTP API
EGO_RPC_BIND127.0.0.1Address the API listens on. Keep it local and put a proxy in front, or set 0.0.0.0 to expose it
EGO_DATA_DIR~/.local/share/EgoDesktop on LinuxWhere the chain and wallet files live
EGO_GATEWAY_PORT47398HTTPS gateway port for phones (separate, smaller method set)
  • Endpoint: POST http://127.0.0.1:47395/ with Content-Type: application/json. Batches (a JSON array of requests) work.
  • WebSocket: ws://127.0.0.1:47395/ws, send {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]} (or newPendingTransactions) for live blocks and pending transactions.
  • CORS: a node only answers browser calls from localhost and extension origins. A page on your own domain cannot call a node directly; route it through your backend.
  • The node API has no login or rate limit. If you expose it, put nginx (or similar) in front with limits.
  • Every response follows JSON-RPC 2.0: {"jsonrpc":"2.0","id":1,"result":...} or {"error":{"code":-32000,"message":"..."}}.
curl -s http://127.0.0.1:47395/ -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"wallet.getBalance","params":{"address":"egot1..."}}'

{"jsonrpc":"2.0","id":1,"result":{"uegoc":12500000,"egoc":12.5}}

JSON-RPC: wallet and transactions

Params are a JSON object (named fields). Amounts are integers in uEGOC: 1 EGOC = 1,000,000 uEGOC.

MethodParamsResult
wallet.getBalanceaddress{ uegoc, egoc }
wallet.getNonceaddress{ last_confirmed, next, fee_uegoc, chain_id, tx_version }. Use next, fee_uegoc, chain_id and tx_version to build the next transaction
tx.submittx: a signed transaction object (see Signing){ success: true, tx_hash }, or error -32000 (rejected), -32001 (mempool refused), -32602 (bad object)
wallet.getTransactionHistoryaddress, limit (default 50)array of transactions, newest first
wallet.getTransactionhashone transaction, or null. block_height is set once it is in a block
chain.getTxProoftxHashMerkle inclusion proof, or null
chain.verifyTxProofthe proof object{ valid }

Confirming a payment

Poll wallet.getTransaction until block_height is set, then compare it with chain.getFinalizedHeight. A transaction at or below the finalized height is final; Ego uses Tendermint-style finality, so a finalized block is never reverted.

Linking a wallet to an account or order

No special API is needed. Either store the address the user connected with (after the sign-in check), or have them pay with a memo such as myapp:order:<id>. The memo is signed, so it cannot be changed in transit, and it is returned on every transaction (max 256 bytes).

JSON-RPC: chain, contracts, market, network

MethodParamsResult
chain.getFinalizedHeightnone{ height }
chain.getBlocksfromHeight, limit (default 50)array of blocks
chain.getBlockHeadersfromHeight, limit (default 100)array of headers
chain.getNetworkStatsnone{ blockCount, txCount, peerCount, finalizedHeight, egocPriceUsd, currentView }
chain.getEgocPricenone{ usd }
contract.listDeployednoneup to 500 { address, name, deployer, deployedAt, codeHash }
contract.getStatecontractAddr, prefix, key{ value }, hex or null
contract.getActivitycontractAddr, limit (1 to 1,000)recent calls and their results
contract.querycontractAddr, entrypoint, argsHex, caller{ success, returnHex, error, ruUsed }. Runs the call read-only; nothing is written or paid
market.params, market.offers, market.offer, market.trade, market.trades, market.profile, market.leaderboardoffers: asset, fiat, side, limit, cursor; offer and trade: id; trades: address, limit, cursor; profile: address; leaderboard: limitescrow marketplace data
shielded.state, shielded.leaves, shielded.checkstate: address; leaves: from, limit; check: nullifiers, txsshielded pool data
p2p.getPeers, p2p.getCurrentViewnonepeers the node knows, consensus view
ego_getValidators, ego_getMempoolStatsnoneblock makers the node knows, mempool counts
A node also answers a subset of Ethereum methods (eth_chainId returns 0x577, eth_getBalance, eth_getTransactionByHash, eth_getTransactionReceipt and a few more). Several of them return placeholder values and eth_sendRawTransaction is not supported, so build on the native methods above.

HTTP routes on a node

RouteReturns
GET /health{ status, block_height, chain_tip, finalized_height, tx_count }
GET /chain/blocks?fromHeight=&limit=blocks, newest 500 by default, max 1,000
GET /chain/transactions?fromHeight=&limit=the transactions in those blocks
GET /resolve/:namethe on-chain record of a hosted site
GET /site/:name and /site/:name/*paththe files of a site hosted on Ego
GET /site-status/:namewhether a hosted site is live
GET /cid/:cid (also /file/:cid)a public file by its content id
GET /nodesAPI addresses of nodes this node knows
GET /hosting/nodes/:domainnodes serving a hosted domain
GET /wsWebSocket, see Run your own node

Signing a transaction

Users sign through the extension, so an app only builds transactions itself for its own server wallet (payouts, fees, automated deploys). Every transaction is Ed25519-signed over a fixed byte string, and the node rejects anything that differs by one byte.

  1. Keys: a 32-byte seed gives the Ed25519 key pair (nacl.sign.keyPair.fromSeed(seed)).
  2. Address: blake2s256("ego/addr/v1" || chain_id as u32 little-endian || public key), take the first 20 bytes, prefix the byte 0x20, encode bech32m with prefix egot (chain_id 1) or ego (chain_id 2).
  3. Call wallet.getNonce and take next, fee_uegoc, chain_id and tx_version (3 now; version 2 stops being accepted at block 40,000).
  4. Build the version 3 signing bytes below. timestamp is Unix seconds and should be the current time, since old transactions expire from the mempool.
  5. Sign them with Ed25519. The hash is "0x" + hex(blake2s256(signing bytes)).
  6. Send the object with tx.submit.
"ego/tx/v3:" chain_id(u8) ":" from ":" to ":" amount(u64 LE) ":" nonce(u64 LE) ":"
timestamp(i64 LE) ":" fee_uegoc(u64 LE) ":" priority_fee_uegoc(u64 LE) ":"
len(tx_type)(u32 LE) tx_type ":" len(memo)(u32 LE) memo
import nacl from 'tweetnacl';
import { blake2s } from 'blakejs';

const enc = new TextEncoder();
const hex = (b: Uint8Array) => Array.from(b, x => x.toString(16).padStart(2, '0')).join('');
const u64 = (n: bigint) => { const b = new Uint8Array(8); new DataView(b.buffer).setBigUint64(0, BigInt.asUintN(64, n), true); return b; };
const u32 = (n: number) => { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, n, true); return b; };
const cat = (...p: Uint8Array[]) => { const o = new Uint8Array(p.reduce((s, a) => s + a.length, 0)); let i = 0; for (const a of p) { o.set(a, i); i += a.length; } return o; };

export function signTransfer(seed: Uint8Array, from: string, to: string, amountUegoc: number,
                             nonceInfo: { next: number; fee_uegoc: number; chain_id: number }, memo = '') {
  const c = enc.encode(':');
  const type = enc.encode('transfer');
  const m = enc.encode(memo);
  const timestamp = Math.floor(Date.now() / 1000);
  const bytes = cat(
    enc.encode('ego/tx/v3:'), new Uint8Array([nonceInfo.chain_id]), c,
    enc.encode(from), c, enc.encode(to), c,
    u64(BigInt(amountUegoc)), c, u64(BigInt(nonceInfo.next)), c, u64(BigInt(timestamp)), c,
    u64(BigInt(nonceInfo.fee_uegoc)), c, u64(0n), c,
    u32(type.length), type, c, u32(m.length), m,
  );
  const kp = nacl.sign.keyPair.fromSeed(seed);
  return {
    hash: '0x' + hex(blake2s(bytes, undefined, 32)),
    from, to, amount: amountUegoc, memo: memo || null, timestamp,
    signature: hex(nacl.sign.detached(bytes, kp.secretKey)),
    status: 'Pending', block_height: null, nonce: nonceInfo.next,
    public_key_ed25519: hex(kp.publicKey),
    tx_type: 'transfer', fee_uegoc: nonceInfo.fee_uegoc, priority_fee_uegoc: 0,
    tx_version: 3, chain_id: nonceInfo.chain_id,
  };
}

The Ego Wallet extension uses the same code; see extension/src/shared/crypto.ts in the Ego repository.

Deploying and calling contracts

Contracts are WebAssembly, written in Urego and compiled in the Ego Desktop dApp IDE (there is no compile endpoint on the node yet). Deploys and calls are ordinary signed transactions sent with tx.submit; every node runs them from finalized blocks.

FieldDeployCall
tx_typedeploycall
to and contract_addrthe new contract addressthe contract address
entrypointinitthe function name
call_argsinit arguments, hexarguments, hex
wasm_codethe compiled module, hex, max 512 KBempty
amount00
memo (signed)deploy:<code_hash>:<first 16 hex of blake3(call_args hex)>call:<entrypoint>:<first 16 hex of blake3(call_args hex)>
  • code_hash = hex(blake3(from + wasm_code_hex)), and the contract address is the first 20 bytes of that hash, hex. The same deployer and code always give the same address.
  • The memo is what binds entrypoint, call_args and wasm_code to the signature; a transaction whose memo does not match is refused when it executes.
  • Try a call first with contract.query (free, read-only), then send it. Afterwards contract.getActivity shows whether it ran or why it was refused.
  • From a web page, the user's call goes through ego_callContract, which builds and signs all of this in the extension. A browser deploy method does not exist yet; deploy from Ego Desktop or from your server with its own key.

Public endpoints

https://api.egoblockchain.com serves this page and the network's public read-only routes. Any website may call them (open CORS), which suits public stats, but they are not the source of truth: balances, nonces and submitting transactions go through a node. The same routes are also on https://rpc.egoblockchain.com.

RouteReturns
GET /healthserver status
GET /chain/blocks?limit=recent blocks as nodes reported them; can lag the network
GET /chain/transactions?limit=recent transactions, same caveat
GET /egoc{ symbol, usd, market_cap, supply, updated_at }
GET /prices and /price/:symbolUSD prices the network uses for fees
GET /nodesnodes seen in the last 10 minutes
GET /gatewayspublic HTTPS gateways (phones use these)
GET /hosting/nodes/:domainnodes serving a hosted site
curl -s https://api.egoblockchain.com/egoc

Requests are rate limited per IP. Only GET is accepted here.

Limits to plan around

  • No GraphQL. If your app wants one, put it in your backend on top of these methods.
  • Network settings. Keep chain id and address prefix configurable: today chain_id 1 and egot1...; mainnet uses chain_id 2 and ego1....
  • Detect the wallet by window.ego.isEgoWallet, not by chain id: the extension reports 0x1 while a node's Ethereum layer reports 0x577.
  • Filtered regions. The egoblockchain.com names are filtered in some countries (Iran today), so the public endpoints may not load from servers there. A node itself still joins through its built-in seed peers (v0.3.647 and later).
  • A node's API has no login or rate limit. Keep it on localhost or behind a proxy with limits.
  • Not over the API yet: file storage uploads and site hosting (done in Ego Desktop), a compile endpoint, a browser deploy method, and an ego_getPublicKey wallet method. Tell us which your app needs first on GitHub.