XDC Liquid Staking Protocol — a Lido-style staking system aligned with Spec v1.5 and XDC 2.0 Staking, Rewards and Burning Upgrade. Users stake XDC to receive liquid bXDC tokens. Operators run masternodes across Core/Protector/Observer tiers to earn rewards, which automatically increase bXDC value.
📄 Technical Specification (xdc-liquid-staking-spec-v1.5.docx)
- Scale-first: Deploy masternodes across all tiers. Observer Nodes are the growth engine.
- Simplified vault: MasternodeVault handles
setupAndPropose,collectRewards,resignonly. - Operator top-up direct: Operators call
vote()/unvote()on 0x88 from their own EOA — bypasses vault. - KYC delegation: Vault reuses operator's KYC hash via
uploadKYC()beforepropose(). One-time per vault. - Blended yield: bXDC exchange rate reflects weighted average across all tiers.
- No oracle: Per-vault on-chain balance is performance data. Zero off-chain infrastructure.
yarn install
# or
npm installcp .env.sample .env
# Edit .env and add PRIVATE_KEY, etc.yarn compile
# or
npx hardhat compileyarn test
# or
npx hardhat testTestnet (xdctestnet):
yarn deploy:xdctestnet
# or
npx hardhat run scripts/liquidityStakingDeploy.js --network xdctestnetMainnet (xdc):
yarn deploy:xdc
# or
npx hardhat run scripts/liquidityStakingDeploy.js --network xdcUnder XDC 2.0, masternodes participate in a three-tier system:
| Tier | Count | Reward/Node/Epoch | Annual (approx) |
|---|---|---|---|
| Core Validator | 108 | ~55.56 XDC | ~973,333 XDC |
| Protector Node | 216 | ~46.30 XDC | ~811,111 XDC |
| Observer Node | Unlimited | ~23.15 XDC | ~405,555 XDC |
Scale economics: At 1,000 masternodes the protocol earns ~28.5× more than a 20-node deployment. Volume dominates tier.
Epoch timing: 1 epoch = 900 blocks. 1 block ≈ 2 seconds. ~48 epochs/day.
bbb-liquiditystaking/
├── contracts/
│ ├── XDCLiquidityStaking.sol # Main coordinator
│ ├── bXDC.sol # ERC-4626 liquid receipt token
│ ├── WithdrawalRequestNFT.sol # ERC-1155 NFT for delayed withdrawals
│ ├── WXDC.sol
│ ├── MasternodeVault.sol # Per-masternode proxy (setupAndPropose, collectRewards, resign)
│ ├── MasternodeVaultFactory.sol
│ ├── MasternodeManager.sol
│ ├── OperatorRegistry.sol # KYC lifecycle, kycHash storage for vault delegation
│ ├── RevenueDistributor.sol
│ ├── interfaces/
│ └── mocks/
├── scripts/
├── test/
├── hardhat.config.js
└── network.config.json
- OperatorRegistry:
registerOperator(admin, maxMasternodes),approveKYC(admin, kycHash)— stores kycHash for vault delegation - Operators:
whitelistCoinbase(coinbase)for each masternode - Users:
stake()XDC to receive bXDC; MasternodeManager auto-proposes when buffer is healthy - Keeper:
harvestRewards()collects from all vaults, distributes 90/7/3 (bXDC/operator/treasury) - Operators:
RevenueDistributor.claimCommission()to claim commission - Optional: LSP admin
submitKYC(kycHash)for protocol-level registration
Per spec v1.5, each vault reuses the operator's KYC hash:
- Operator onboards: Completes KYC off-chain. Admin calls
approveKYC(operatorAdmin, kycHash)— hash stored in OperatorRegistry. - Vault deployed: StakingPool deploys EIP-1167 proxy vault.
- Vault calls
setupAndPropose(kycHash, coinbase): Uploads KYC to 0x88, then proposes with 10M XDC. Vault becomes masternode owner. - Rewards flow: 0x88 sends epoch rewards to vault. Keeper calls
harvestRewards()→ vaultcollectRewards()→ StakingPool.
Operators can top up their masternodes directly via 0x88 — no vault routing:
vote(coinbase)— Operator sends XDC from EOA. No KYC needed.unvote(coinbase, amount)— Partial withdrawal.voterWithdrawDelayapplies.- Capital separation is automatic at 0x88 level. Protocol 10M and operator top-up are tracked separately.
- Stake XDC — Receive bXDC at current exchange rate (native or WXDC)
- Instant Exit — Withdraw immediately if amount ≤
instantExitBuffer - Delayed Withdrawal — Larger amounts get ERC-1155 NFT; redeem after unbonding
- Hold for Yield — bXDC value grows as staking rewards accrue
- Revenue Split — Configure bXDC/operator/treasury (default 90/7/3)
- Buffer Management —
addToInstantExitBuffer(),minBufferPercent,criticalBufferPercent - Parameter Management —
minStakeAmount,minWithdrawAmount,masternodeStakeAmount - Pause Control — Pause in emergencies (no emergencyWithdraw)
Exchange Rate = totalPooledXDC / bXDC Total Supply
bXDC Amount = XDC Amount × bXDC Total Supply / totalPooledXDC
XDC Amount = bXDC Amount × totalPooledXDC / bXDC Total Supply
- Initial: 1 bXDC = 1 XDC
- As rewards are harvested,
totalPooledXDCincreases → 1 bXDC > 1 XDC over time - Blended yield across Core/Protector/Observer tiers
| Recipient | Range | Description |
|---|---|---|
| bXDC Holders | 85–92% | Added to totalPooledXDC |
| Operator Commission | 5–10% | Per-vault, claimed via RevenueDistributor |
| Protocol Treasury | 2–5% | Development, audits |
KYC-expired operators: Commission redirected 50% bXDC + 50% treasury. Reversible on renewal.
await stakingPool.stake({ value: ethers.utils.parseEther("100") });// Instant if amount ≤ buffer
await stakingPool.withdraw(bxdcAmount);
// Or redeem ERC-4626 shares directly (instant only)
await stakingPool.redeem(shares, receiver, owner);await stakingPool.redeemWithdrawal(batchId);await stakingPool.harvestRewards();await revenueDistributor.claimCommission();| Parameter | Default | Description |
|---|---|---|
| minStakeAmount | 1 XDC | Minimum stake |
| minWithdrawAmount | 0.1 XDC | Minimum withdrawal |
| minBufferPercent | 5% | Below: no new masternode proposals |
| criticalBufferPercent | 2% | Below: resignations triggered |
| withdrawDelayBlocks | 1,296,000 | ~30 days unbonding for NFT path |
- ReentrancyGuard — Prevents reentrancy
- Pausable — Emergency pause only (no emergencyWithdraw)
- AccessControl — LSP_ADMIN_ROLE, MASTERNODE_MANAGER_ROLE
- Dual exit path — Instant (buffer) or delayed (NFT)
Staked— User stakedWithdrawalNFTMinted— Delayed withdrawal requestedWithdrawalRedeemed— NFT redeemed after unbondingInstantExit— Immediate withdrawalMasternodeProposed/MasternodeResignedRewardsHarvested— Harvest completedVaultCollected— Per-vault reward collectionCommissionAccrued/CommissionRedirected
Q: Can users withdraw immediately?
A: Yes, if the amount is within instantExitBuffer. Otherwise they receive an ERC-1155 NFT and redeem after unbonding.
Q: How does the exchange rate increase?
A: When harvestRewards() collects from vaults, the bXDC share is added to totalPooledXDC. bXDC supply stays constant → rate increases.
Q: Can operators top up their masternodes?
A: Yes. Operators call vote(coinbase) directly on 0x88 from their EOA. No vault routing.
Q: Can bXDC be traded?
A: Yes. bXDC is ERC-4626 compatible and can be used in DeFi.
# Deploy
npx hardhat run scripts/liquidityStakingDeploy.js --network xdc
# Admin tool
STAKING_POOL_ADDRESS=0x... npx hardhat run scripts/liquidityStakingAdmin.js --network xdc
# Console
npx hardhat console --network xdcMIT License