Testing & Testnet
Elastos has public testnets for all three chains — main chain, ESC, and EID. Use them to validate transactions, smart contracts, and DID operations before deploying to mainnet. This page covers testnet configuration, faucets, local nodes, and integration tests for each chain.
Hardhat Testnet Configuration
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.20",
networks: {
escTestnet: {
url: "https://api-testnet.elastos.io/esc",
chainId: 21,
accounts: [process.env.TESTNET_PRIVATE_KEY!],
},
escMainnet: {
url: "https://api.elastos.io/esc",
chainId: 20,
accounts: [process.env.MAINNET_PRIVATE_KEY!],
gasPrice: 1000000000,
},
},
};
export default config;
- Main Chain
- ESC
- EID
Testnet Configuration
Main Chain Testnet:
const mainChainTestnet = {
rpcUrl: "https://api-testnet.elastos.io/ela",
defaultPort: 21336,
};
Faucets
To get testnet ELA:
Main Chain Testnet: Community Discord channels often have faucet bots.
Testnet Configuration
ESC Testnet:
const escTestnetConfig = {
chainId: 21,
chainName: "Elastos Smart Chain Testnet",
rpcUrl: "https://api-testnet.elastos.io/esc",
blockExplorer: "https://esc-testnet.elastos.io",
currency: { name: "tELA", symbol: "tELA", decimals: 18 },
};
Faucets
To get testnet ELA:
- ESC Testnet Faucet: Visit
https://esc-faucet.elastos.io/or the community-run faucets
// Programmatic faucet request (if API available)
async function requestTestnetELA(address: string): Promise<void> {
const response = await fetch("https://esc-faucet.elastos.io/api/claim", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address }),
});
const data = await response.json();
console.log("Faucet response:", data);
}
Local Development Node
For fully offline development, you can run a local ESC node:
# Clone the ESC repository
git clone https://github.com/elastos/Elastos.ELA.SideChain.ESC.git
cd Elastos.ELA.SideChain.ESC
# Build
make geth
# Run in development mode
./build/bin/geth --dev --http --http.addr 0.0.0.0 --http.port 20636 \
--http.api eth,net,web3,debug,personal \
--http.corsdomain "*" \
--allow-insecure-unlock
Point your Hardhat config to http://localhost:20636.
Writing Tests
import { expect } from "chai";
import { ethers } from "hardhat";
describe("MyToken", function () {
it("should deploy with correct initial supply", async function () {
const [owner] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
const token = await MyToken.deploy(1_000_000);
await token.waitForDeployment();
const totalSupply = await token.totalSupply();
expect(totalSupply).to.equal(ethers.parseEther("1000000"));
const ownerBalance = await token.balanceOf(owner.address);
expect(ownerBalance).to.equal(totalSupply);
});
it("should allow owner to mint", async function () {
const [owner, recipient] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
const token = await MyToken.deploy(1_000_000);
await token.waitForDeployment();
await token.mint(recipient.address, ethers.parseEther("500"));
const balance = await token.balanceOf(recipient.address);
expect(balance).to.equal(ethers.parseEther("500"));
});
it("should reject mint from non-owner", async function () {
const [owner, attacker] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
const token = await MyToken.deploy(1_000_000);
await token.waitForDeployment();
await expect(
token.connect(attacker).mint(attacker.address, ethers.parseEther("500"))
).to.be.revertedWithCustomError(token, "OwnableUnauthorizedAccount");
});
});
End-to-End Test Script
import { ethers } from "ethers";
async function e2eTest() {
const provider = new ethers.JsonRpcProvider("https://api-testnet.elastos.io/esc");
// Verify connectivity
const network = await provider.getNetwork();
console.assert(network.chainId === 21n, "Expected chain ID 21 (testnet)");
// Check block production
const blockNumber = await provider.getBlockNumber();
console.assert(blockNumber > 0, "Expected non-zero block number");
// Verify a precompiled contract responds
const producersResult = await provider.call({
to: "0x00000000000000000000000000000003E8",
data: "0x",
});
console.assert(producersResult.length > 2, "Expected non-empty producers response");
console.log("All E2E checks passed");
console.log(` Chain ID: ${network.chainId}`);
console.log(` Block: ${blockNumber}`);
console.log(` Producers data length: ${producersResult.length}`);
}
e2eTest().catch(console.error);
Testnet Configuration
EID Testnet:
const eidTestnetConfig = {
chainId: 23,
chainName: "Elastos Identity Chain Testnet",
rpcUrl: "https://api-testnet.elastos.io/eid",
currency: { name: "tELA", symbol: "tELA", decimals: 18 },
};
Integration Testing with DID
import { DIDStore, RootIdentity, DIDBackend, DefaultDIDAdapter, Mnemonic } from "@elastosfoundation/did-js-sdk";
describe("DID Integration", function () {
let store: DIDStore;
before(async function () {
// Use testnet resolver
const resolver = new DefaultDIDAdapter("https://api-testnet.elastos.io/eid");
DIDBackend.initialize(resolver);
store = await DIDStore.open("./test-did-store");
});
after(async function () {
store.close();
// Clean up test store
await fs.rm("./test-did-store", { recursive: true, force: true });
});
it("should create a DID from mnemonic", async function () {
const mnemonic = Mnemonic.generate("english");
const identity = RootIdentity.createFromMnemonic(mnemonic, "", store, "testpass");
const doc = await identity.newDid("testpass");
expect(doc).to.not.be.null;
expect(doc.getSubject().toString()).to.match(/^did:elastos:i/);
});
it("should issue and verify a credential", async function () {
const mnemonic = Mnemonic.generate("english");
const identity = RootIdentity.createFromMnemonic(mnemonic, "", store, "testpass");
const doc = await identity.newDid("testpass");
const issuer = new Issuer(doc);
const vc = await issuer.issueFor(doc.getSubject())
.id(DIDURL.from("#test", doc.getSubject()))
.type("TestCredential")
.property("key", "value")
.seal("testpass");
// Locally created credentials are valid
const isGenuine = await vc.isGenuine();
expect(isGenuine).to.be.true;
});
});