This repository contains the smart contracts for stEDU, a liquid staking token for EDUCHAIN that uses an index-based reward system with a per-deposit unbonding period.
- Fixed Balance, Index-Accruing: Balances never rebase; instead, the
index(share-price) increases when rewards are pushed - Per-deposit Unbonding Period: Each individual stake is locked for 7 days to prevent front-running and flash loan attacks
- ERC4626 Compliant: Implements the ERC4626 Tokenized Vault Standard for better composability
- Surplus Sync Mechanism:
sync()function lets anyone fold accidental WEDU donations into the index
The contracts have been successfully deployed to the opencampus network:
Testnet:
- stEDU: 0x0324da014FC0911502766CAaa740D861634b82b9
- wstEDU: 0xE950c63D61510c2F1867378D0b6D9C3eFA574921
Mainnet:
- stEDU: 0x23642bf3c582B6eEcC0bfa9d8469f43813721b06
- wstEDU: 0x062409E8F205A278858E9ae43C9E3ea59298f968
- Users stake native EDU and receive stEDU tokens
- Each deposit is timestamped and tracked individually
- When rewards are added, the global
indexincreases - The value of each stEDU token grows over time as rewards accumulate
- Users can only unstake after the unbonding period (7 days) has passed for each deposit
- Unbonding Period: Prevents front-running attacks and flash loan exploits
- ReentrancyGuard: Protects against reentrancy attacks during unstaking
- Pausable: Allows emergency pausing of all operations
- FIFO Unstaking: Enforces first-in-first-out unstaking order based on deposit timestamps
sequenceDiagram
participant User as User
participant stEDU as stEDU Contract
participant WEDU as WEDU Token
participant Owner as Protocol Owner
User->>stEDU: stake() with EDU
stEDU->>WEDU: deposit() (wrap EDU to WEDU)
stEDU-->>User: mint stEDU tokens (EDU * 1e18 / index)
Note over stEDU: Record deposit timestamp
Owner->>stEDU: depositRewards() with EDU
stEDU->>WEDU: deposit() (wrap EDU to WEDU)
Note over stEDU: index += (rewards * 1e18) / totalSupply()
Note over User: Wait 7 days (unbonding period)
User->>stEDU: unstake(shares)
stEDU->>WEDU: withdraw(eduAmount)
stEDU-->>User: transfer EDU (shares * index / 1e18)
The index mechanism works as follows:
- Initially, the index starts at
1e18(1.0 in decimal) - When rewards are added, the index increases by:
(rewardAmount * 1e18) / totalSupply - To convert stEDU to EDU:
stEDUAmount * index / 1e18 - To convert EDU to stEDU:
eduAmount * 1e18 / index
This mechanism ensures that:
- All stakers receive rewards proportional to their stake
- The value of stEDU increases over time without changing balances
- Rewards are automatically compounded
-
Compile:
npx hardhat compile
-
Run tests:
# Run all tests npm test # Run specific test suites npm run test:basic npm run test:staking npm run test:rewards npm run test:attack npm run test:integration
-
Deploy stEDU:
// Deploy WEDU first (or use existing WEDU contract) const WEDU = await ethers.getContractFactory("WEDU"); const wedu = await WEDU.deploy(); // Deploy stEDU with WEDU address const StEDU = await ethers.getContractFactory("stEDU"); const stEDU = await StEDU.deploy(await wedu.getAddress());
-
Interact with the contract:
// Stake EDU await stEDU.stake({ value: ethers.parseEther("10") }); // Check stEDU balance const stEDUBalance = await stEDU.balanceOf(myAddress); // Check EDU value const eduValue = await stEDU.stEDUToEDU(stEDUBalance); // Deposit rewards (owner only) await stEDU.depositRewards({ value: ethers.parseEther("1") });