Developers Forum for XinFin XDC Network

Cover image for How to Integrate XDC Network: A Technical Guide
Omkar Mestry
Omkar Mestry

Posted on

How to Integrate XDC Network: A Technical Guide

How to Integrate XDC Network: A Technical Guide

A complete technical walkthrough for integrating the XDC Network into custodial wallets, exchanges, and any backend application.

If your team is already building on EVM chains like Ethereum, Polygon, or BNB Chain, integrating XDC will feel familiar — it's fully EVM-compatible, forked from go-ethereum, and runs the same tooling (Ethers.js, Web3.py, Hardhat, Truffle, MetaMask). The main thing to watch for is the xdc address prefix, which we'll cover in detail.

Let's dive in.


1. Overview

The XDC Network is an enterprise-grade, EVM-compatible Layer-1 blockchain using XDC 2.0 consensus (an evolution of XinFin Delegated Proof-of-Stake). It's purpose-built for tokenized trade finance, real-world asset (RWA) settlement, and cross-border payments.

Key stats:

  • ~2-second block times
  • Sub-cent gas fees (often < $0.0001 per transfer)
  • Up to 108 masternodes securing finality
  • ISO 20022 messaging support
  • R3 Corda interoperability

Because XDC is EVM-compatible, most Ethereum tooling works out-of-the-box. The only material difference a custodian or application must handle is the address prefix: XDC natively uses an xdc prefix in place of 0x, while the underlying address bytes are identical.


2. Network Parameters

Mainnet

Parameter Value
Network Name XDC Network
Chain ID 50
Native Currency XDC
Block Time ~2 seconds
Consensus XDC 2.0
Min Gas Price 0.25 Gwei
Public RPC https://rpc.xinfin.network
https://rpc.xdc.org
WebSocket wss://ws.xinfin.network
Block Explorer https://www.xdcscan.com
https://www.xdcscan.io

Apothem Testnet

Parameter Value
Network Name XDC Apothem Network
Chain ID 51
Native Currency TXDC
Block Time ~2 seconds
Consensus XDC 2.0
Min Gas Price 0.25 Gwei
Public RPC https://rpc.apothem.network
https://erpc.apothem.network
WebSocket wss://ws.apothem.network
Block Explorer https://testnet.xdcscan.com
https://apothem.xdcscan.io
Faucet https://faucet.apothem.network
https://faucet.blocksscan.io

Production tip: Run your own full node or subscribe to a commercial RPC provider (Ankr, GetBlock, Thirdweb, NOWNodes) for production. Public endpoints are fine for development but have rate limits and no SLA.


3. Address Format (Critical)

This is the one thing that trips up every new integrator, so read this section carefully.

XDC addresses are semantically identical to Ethereum addresses — derived via secp256k1 key generation and Keccak-256 hashing, producing a 20-byte (40 hex character) address. The only difference is the prefix:

  • XDC-native display uses xdcxdcA4e66f4Cc17752f331eaC6A20C00756156719519
  • EVM-native display uses 0x0xA4e66f4Cc17752f331eaC6A20C00756156719519

The 40 hex characters after the prefix are always identical. Only the 2-character prefix swaps.

Conversion Rules

Normalize addresses at your I/O boundary:

  1. Internally, store and sign using the 0x form — required by all Ethereum libraries (ethers.js, web3.js, etc.).
  2. On input, if a user pastes an xdc-prefixed address, strip xdc and prepend 0x before signing or submitting.
  3. On output (deposit addresses shown to users, explorer links, confirmations), display in xdc form for XDC ecosystem conformity.

Address Conversion Utility

// Minimal address conversion helper (JavaScript / TypeScript)

function toEvm(address) {
  if (!address) return address;
  if (address.toLowerCase().startsWith('xdc')) {
    return '0x' + address.slice(3);
  }
  return address;
}

function toXdc(address) {
  if (!address) return address;
  if (address.toLowerCase().startsWith('0x')) {
    return 'xdc' + address.slice(2);
  }
  return address;
}

function isValidXdcAddress(address) {
  const evm = toEvm(address);
  return /^0x[a-fA-F0-9]{40}$/.test(evm);
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Signed transactions must always use the 0x form internally. The xdc prefix is a display convention — the JSON-RPC node and the EVM itself operate on raw 20-byte addresses.


4. Prerequisites

  • Node.js 18+ (or Python 3.10+, Go 1.21+, Java 11+ — whichever matches your stack)
  • Ethers.js v6 or Web3.js v4 (or language-equivalent SDK)
  • Access to an RPC endpoint (self-hosted full node or commercial provider)
  • A secure key management system (HSM, KMS, or MPC) for custodial signing
  • Test funds from the Apothem faucet for testnet validation

5. Integration Architecture

A typical custodial integration has four components:

  1. Node / RPC Layer — a full node or commercial RPC for reading chain state, estimating gas, and broadcasting transactions.
  2. Block Indexer — a worker service that follows the chain head (with confirmations), detects deposits to custodied addresses, and records balances.
  3. Signing Service — an isolated service (HSM/KMS/MPC) that constructs, signs, and submits withdrawal transactions. Never exposes private keys to the application layer.
  4. API Layer — the user-facing REST/GraphQL interface. Responsible for address format normalization.

6. Integration Steps

6.1 Connect to the Network

Ethers.js v6:

const { JsonRpcProvider } = require('ethers');

const provider = new JsonRpcProvider('https://rpc.xinfin.network', {
  name: 'xdc',
  chainId: 50
});

const blockNumber = await provider.getBlockNumber();
console.log('Latest block:', blockNumber);
Enter fullscreen mode Exit fullscreen mode

Web3.py:

from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://rpc.xinfin.network'))
assert w3.is_connected(), 'RPC connection failed'
print('Chain ID:', w3.eth.chain_id)        # 50
print('Latest block:', w3.eth.block_number)
Enter fullscreen mode Exit fullscreen mode

6.2 Generate a Deposit Address

For each user account, derive a fresh address using standard EVM wallet derivation (BIP-39 mnemonic + BIP-44 path). XDC's registered SLIP-44 coin type is 550, though many integrations reuse Ethereum's 60.

const { Mnemonic, HDNodeWallet } = require('ethers');

const mnemonic = Mnemonic.fromPhrase(process.env.SEED_PHRASE);
const root     = HDNodeWallet.fromMnemonic(mnemonic, "m/44'/550'/0'/0");
const account  = root.deriveChild(userIndex);

const evmAddress = account.address;              // 0x...
const xdcAddress = 'xdc' + evmAddress.slice(2);  // xdc... (for display)

// Persist { userId, derivationIndex, xdcAddress }
// NEVER persist the private key — derive on demand from the HSM-held seed
Enter fullscreen mode Exit fullscreen mode

6.3 Monitor Deposits

Poll or subscribe to new blocks. For each block, iterate transactions and match the to field (or XRC-20 Transfer log topic) against your indexed deposit addresses. Wait for at least 10–15 confirmations on mainnet before crediting.

provider.on('block', async (blockNumber) => {
  const block = await provider.getBlock(blockNumber, true);
  for (const tx of block.prefetchedTransactions) {
    if (watchedAddresses.has(tx.to?.toLowerCase())) {
      await enqueueDepositConfirmation(tx.hash, blockNumber);
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

6.4 Process Withdrawals

const tx = {
  to:       toEvm(userProvidedAddress),   // accept xdc... or 0x...
  value:    ethers.parseEther('10.5'),    // 10.5 XDC
  gasLimit: 21000n,
  gasPrice: ethers.parseUnits('0.25', 'gwei'),  // XDC minimum
  chainId:  50,
  nonce:    await provider.getTransactionCount(hotWalletAddress)
};

const signed   = await signer.signTransaction(tx);     // signer = HSM/KMS
const response = await provider.broadcastTransaction(signed);
const receipt  = await response.wait(12);              // 12 confirmations
Enter fullscreen mode Exit fullscreen mode

6.5 XRC-20 Token Transfers

XRC-20 is functionally identical to ERC-20 — same ABI, same Transfer(address,address,uint256) event signature, same allowance model. Your existing ERC-20 code works unchanged.

const erc20Abi = [
  'function balanceOf(address) view returns (uint256)',
  'function decimals() view returns (uint8)',
  'function symbol() view returns (string)',
  'function transfer(address to, uint256 amount) returns (bool)',
  'event Transfer(address indexed from, address indexed to, uint256 value)'
];

const token = new ethers.Contract(tokenContractAddress, erc20Abi, signer);

const decimals = await token.decimals();
const amount   = ethers.parseUnits('100', decimals);
const tx       = await token.transfer(toEvm(recipient), amount);
await tx.wait(12);
Enter fullscreen mode Exit fullscreen mode

7. Token Standards

Standard Equivalent To Use Case
XRC-20 ERC-20 Fungible tokens, stablecoins, utility tokens
XRC-721 ERC-721 Non-fungible tokens (NFTs), unique assets
XRC-1155 ERC-1155 Multi-token contracts (fungible + NFT in one)

All three standards are byte-for-byte compatible with their ERC counterparts at the ABI level. OpenZeppelin contracts compile and deploy unchanged.


8. Gas and Fee Handling

  • Mainnet minimum gas price is 0.25 Gwei — below this, transactions stay pending indefinitely.
  • Standard native XDC transfer = 21,000 gas (~0.00000525 XDC, typically under $0.0001).
  • XRC-20 transfers = ~50,000–65,000 gas depending on contract implementation.
  • Use eth_estimateGas for contract calls; add a 10–20% safety buffer.
  • Always verify receipt status == 1 — a receipt alone doesn't mean the tx succeeded.

9. Security Considerations

  • Store signing keys exclusively in HSM, cloud KMS (AWS/GCP/Azure), or MPC custody. Never plaintext keys in memory or config files.
  • Segregate hot/warm/cold wallets — keep no more than operational float (~5% of AUM) in any hot wallet.
  • Enforce withdrawal-address allowlisting and velocity limits per user and per time window.
  • Require multi-party approval for treasury movement above a threshold.
  • Wait 12–15 confirmations before crediting mainnet deposits; 20+ for high-value.
  • Reject deposits sent to contracts you don't control — credit only to externally-owned addresses you've derived.
  • Always validate chainId == 50 on signed transactions to prevent cross-chain replay.
  • Monitor for chain reorgs — rare on XDC 2.0, but confirmation-depth buffers protect against edge cases.
  • Rate-limit RPC calls and implement automatic failover between multiple providers.

10. Testing Checklist (Apothem Testnet)

Complete every item on Apothem before moving to mainnet:

  • [ ] Connect to Apothem RPC, confirm chainId == 51
  • [ ] Request TXDC from the faucet to test hot wallet
  • [ ] Generate 5+ deterministic deposit addresses; verify xdc0x conversion is lossless
  • [ ] Send a test deposit; confirm detection, credit at correct confirmation depth, correct user mapping
  • [ ] Execute a native TXDC withdrawal; verify status, nonce handling, on-chain balance
  • [ ] Deploy or use an existing XRC-20 test token; run full deposit + withdrawal flow
  • [ ] Simulate RPC outage; confirm failover to secondary provider
  • [ ] Simulate incorrect-prefix input (0x in xdc field and vice versa); confirm normalization
  • [ ] Simulate below-minimum-gas tx; confirm graceful rejection
  • [ ] Run a 24-hour soak test at representative transaction volume

11. Sample RPC Calls

eth_chainId

curl -X POST https://rpc.xinfin.network \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'

# -> {"jsonrpc":"2.0","id":1,"result":"0x32"}   (0x32 = 50)
Enter fullscreen mode Exit fullscreen mode

eth_blockNumber

curl -X POST https://rpc.xinfin.network \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Enter fullscreen mode Exit fullscreen mode

eth_getBalance

curl -X POST https://rpc.xinfin.network \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getBalance",
       "params":["0xA4e66f4Cc17752f331eaC6A20C00756156719519","latest"],
       "id":1}'
Enter fullscreen mode Exit fullscreen mode

Note: JSON-RPC payloads use 0x, not xdc — the RPC layer expects standard Ethereum format on the wire.


12. Official Resources


Wrapping Up

If you're building an EVM-compatible wallet, exchange, or application, integrating XDC is genuinely one of the fastest onboarding paths you'll find — most of your existing Ethereum stack works unchanged. The three things to get right are:

  1. Address prefix handling — normalize at I/O, sign with 0x internally.
  2. Gas pricing — respect the 0.25 Gwei minimum.
  3. Testing on Apothem — don't skip the checklist.

Got questions or hit a snag during integration? Drop a comment below, open a thread on the XDC Developer Forum, or reach out via the official channels. The XDC developer community is active and responsive — you won't be stuck for long.

Happy building. 🚀


Discussion (0)