Skip to main content
REFERENCE
ESC

ESC Precompiled Contracts

ESC includes custom precompiled contracts at addresses 1000 through 1009 (in addition to the standard Ethereum precompiles at 19). These provide on-chain access to Elastos consensus data, P-256 cryptography, and BPoS staking primitives directly from Solidity contracts.

Source Reference

The precompile addresses and implementations are defined in the ESC source code:

Address Map

Address (Decimal)Address (Hex)Internal NamePurpose
10000x00000000000000000000000000000003E8arbitersQuery the current BPoS validator set (returns keccak256 hashes of arbiter public keys)
10010x00000000000000000000000000000003E9p256VerifyVerify a NIST P-256 (secp256r1) signature given a public key, data, and signature
10020x00000000000000000000000000000003EApbkVerifySignatureVerify a secp256k1 signature by recovering and comparing the signer's public key
10030x00000000000000000000000000000003EBpledgeBillVerifyVerify BPoS staking pledge bills (standard or multi-sig)
10040x00000000000000000000000000000003ECpledgeBillTokenIDLook up StakeTicket NFT token ID from an ELA main chain transaction hash
10050x00000000000000000000000000000003EDpledgeBillTokenDetailRetrieve BPoS staking NFT payload details (referKey, stakeAddress, heights, votes)
10060x00000000000000000000000000000003EEpledgeBillPayloadVersionQuery the payload version of a BPoS NFT
10070x00000000000000000000000000000003EFgetMainChainBlockByHeightGet a main chain block header by height (returns Previous, Bits, MerkleRoot, Hash, Height)
10080x00000000000000000000000000000003F0getMainChainLatestHeightGet the latest main chain block height synced by the SPV module
10090x00000000000000000000000000000003F1p256VerifyDigestVerify a P-256 signature against a pre-hashed digest (no double-hashing)

Calling Precompiled Contracts from Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ElastosPrecompiles {
address constant ARBITERS = address(0x00000000000000000000000000000003E8);
address constant P256_VERIFY = address(0x00000000000000000000000000000003E9);
address constant PBK_VERIFY_SIGNATURE = address(0x00000000000000000000000000000003EA);
address constant PLEDGE_BILL_VERIFY = address(0x00000000000000000000000000000003EB);
address constant PLEDGE_BILL_TOKEN_ID = address(0x00000000000000000000000000000003EC);
address constant PLEDGE_BILL_TOKEN_DETAIL = address(0x00000000000000000000000000000003ED);
address constant PLEDGE_BILL_PAYLOAD_VERSION = address(0x00000000000000000000000000000003EE);
address constant MAIN_CHAIN_BLOCK_BY_HEIGHT = address(0x00000000000000000000000000000003EF);
address constant MAIN_CHAIN_LATEST_HEIGHT = address(0x00000000000000000000000000000003F0);
address constant P256_VERIFY_DIGEST = address(0x00000000000000000000000000000003F1);

/// @notice Query the current BPoS validator set
/// @return Keccak256 hashes of all current arbiter public keys, concatenated as 32-byte words
function getArbiters() public view returns (bytes memory) {
(bool success, bytes memory result) = ARBITERS.staticcall("");
require(success, "arbiters call failed");
return result;
}

/// @notice Verify a P-256 signature
/// @param pubkey 33-byte compressed P-256 public key
/// @param data 64-byte data that was signed
/// @param sig 64-byte P-256 signature
/// @return 32-byte result (0x01 = valid, 0x00 = invalid)
function verifyP256(bytes memory pubkey, bytes memory data, bytes memory sig) public view returns (bool) {
bytes memory input = abi.encodePacked(uint256(0), pubkey, data, sig);
(bool success, bytes memory result) = P256_VERIFY.staticcall(input);
require(success, "p256Verify call failed");
return result[31] == 0x01;
}

/// @notice Get the latest main chain height synced by SPV
/// @return height The latest main chain block height
function getMainChainLatestHeight() public view returns (uint32) {
(bool success, bytes memory result) = MAIN_CHAIN_LATEST_HEIGHT.staticcall("");
require(success, "getMainChainLatestHeight call failed");
return abi.decode(result, (uint32));
}
}

Calling Precompiled Contracts from ethers.js

import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://api.elastos.io/esc");

// Address 1000: Query current BPoS arbiters
async function getArbiters(): Promise<string[]> {
const result = await provider.call({
to: "0x00000000000000000000000000000003E8", // arbiters (1000)
data: "0x",
});

// Returns concatenated 32-byte keccak256 hashes of arbiter public keys
const hashes: string[] = [];
for (let i = 2; i < result.length; i += 64) {
hashes.push("0x" + result.slice(i, i + 64));
}
return hashes;
}

// Address 1008: Get latest main chain height
async function getMainChainLatestHeight(): Promise<number> {
const result = await provider.call({
to: "0x00000000000000000000000000000003F0", // getMainChainLatestHeight (1008)
data: "0x",
});

return parseInt(result, 16);
}

// Usage
const arbiters = await getArbiters();
console.log("Active arbiters:", arbiters.length);
arbiters.forEach((hash, i) => console.log(` [${i}] ${hash}`));

const height = await getMainChainLatestHeight();
console.log("Main chain height:", height);
warning

Some precompiled contracts (like arbiters at 1000) return raw bytes, not ABI-encoded or JSON data. Decode the output according to the specific precompile's format. See Common Pitfalls for details.

EID Precompiled Contracts

The EID chain has DID-specific precompiled contracts for on-chain identity operations:

Address (Decimal)Address (Hex)Internal NamePurpose
220x0000000000000000000000000000000000000016operationDIDPerform DID operations (create, update, deactivate)
230x0000000000000000000000000000000000000017resolveDIDResolve a DID to its document
20070x00000000000000000000000000000007D7kycKYC (Know Your Customer) verification operations
Source Reference

EID DID precompiles are defined in core/vm/did_contracts.go.

// Resolve a DID via EID precompiled contract
async function resolveDID(didString: string): Promise<any> {
const eidProvider = new ethers.JsonRpcProvider("https://api.elastos.io/eid");

const result = await eidProvider.call({
to: "0x0000000000000000000000000000000000000017", // resolveDID (address 23)
data: ethers.hexlify(ethers.toUtf8Bytes(didString)),
});

if (result === "0x" || result.length <= 2) {
return null;
}

return JSON.parse(ethers.toUtf8String(result));
}
tip

For a higher-level DID SDK that wraps these precompiled contracts, see DID Integration.

Using Precompiles in a dApp

A practical example: querying BPoS staking data on-chain:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract StakingDashboard {
address constant ARBITERS = address(0x00000000000000000000000000000003E8);
address constant MAIN_CHAIN_HEIGHT = address(0x00000000000000000000000000000003F0);
address constant PLEDGE_PAYLOAD_VER = address(0x00000000000000000000000000000003EE);

event StakingSnapshot(
uint256 indexed blockNumber,
bytes arbiters,
bytes mainChainHeight,
bytes pledgeVersion
);

function takeSnapshot() public {
(, bytes memory arbiters) = ARBITERS.staticcall("");
(, bytes memory mainHeight) = MAIN_CHAIN_HEIGHT.staticcall("");
(, bytes memory pledgeVer) = PLEDGE_PAYLOAD_VER.staticcall("");

emit StakingSnapshot(block.number, arbiters, mainHeight, pledgeVer);
}
}