Building StakeVault: A Full-Stack DeFi Staking Protocol with Solidity, Foundry, React and Ethers.js
StakeVault is a decentralized staking protocol where users deposit STAKE tokens into a smart contract and earn REWARD tokens over time.
The project was built as a full-stack Web3 application combining Solidity smart contracts with a React frontend. The protocol is deployed on Ethereum Sepolia and can be interacted with through a connected Web3 wallet.
What I Built
StakeVault consists of three primary smart contracts:
-
StakeToken— the ERC-20 token users stake. -
RewardToken— the ERC-20 token distributed as rewards. -
StakingVault— the core contract responsible for staking, reward accounting, withdrawals, reward claims and reward funding.
The frontend was built using React and Vite and communicates with the deployed contracts using Ethers.js and Wagmi.
Live Project
https://defi-staking-dhzkby4cm-wire4.vercel.app/
GitHub
https://github.com/max-wire/defi-staking
How Staking Works
The basic flow is:
User
↓
Approve STAKE
↓
StakeToken.approve()
↓
StakingVault.stake()
↓
User staking position increases
↓
Global total stake increases
↓
Reward accounting updates
↓
Time passes
↓
Rewards accrue
↓
claimRewards() or exit()
The frontend provides a simple interface for performing these operations without requiring users to interact with the smart contracts manually.
Global Staking vs User Staking
One important design aspect of StakeVault is the distinction between the global protocol state and an individual user's position.
For example, if User A stakes 410 STAKE:
- User A's staked amount becomes 410 STAKE.
- The global
totalStakedvalue also becomes 410 STAKE.
If User B subsequently stakes 10 STAKE:
- User A still has 410 STAKE.
- User B has 10 STAKE.
- The global protocol total becomes 420 STAKE.
Therefore, Total Staked on the dashboard represents the amount currently deposited across the entire staking pool, not the connected wallet's individual position.
Finite Reward Pool
A major design decision in StakeVault is the use of a finite reward pool.
The vault maintains a rewardRemaining value representing the amount of REWARD tokens available for distribution.
Rewards are emitted over time according to the configured reward rate, but the protocol cannot continue creating reward liabilities after the funded pool has been exhausted.
Conceptually:
Reward Pool
↓
rewardRemaining
↓
Rewards emitted over time
↓
Users accrue rewards
↓
Pool reaches zero
↓
Reward emissions stop
Additional REWARD tokens can be funded into the vault to continue distribution.
Reward Accounting
Rewards are calculated using cumulative rewardPerToken accounting.
Conceptually:
rewardPerToken =
rewardPerTokenStored
+ (newRewards × PRECISION / totalStaked)
A user's accrued rewards depend on:
- Their staking balance
- The total amount staked
- Time elapsed
- The configured reward rate
- The remaining reward pool
This allows multiple users to participate in the same staking pool while maintaining separate individual positions.
Frontend
The React frontend provides:
- Wallet connection
- STAKE balance
- REWARD balance
- Individual staked amount
- Global total staked
- Reward rate
- Remaining rewards
- Token approval
- Staking
- Withdrawals
- Reward claiming
- Exit functionality
- Automatic blockchain data refresh
The interface also performs a balance check before requesting token approval. This prevents users from signing an approval transaction when they do not have enough STAKE to perform the requested staking operation.
Testing
The smart contract system was tested using Foundry.
The test suite covers:
- ERC-20 token behavior
- Staking
- Multiple staking operations
- Withdrawals
- Reward accrual
- Proportional reward distribution
- Multiple-user reward calculations
- Reward claiming
- Exit functionality
- Reward funding
- Reward rate updates
- Pause and unpause functionality
- Reward pool exhaustion
- Accounting invariants
- Edge cases and failure conditions
The project currently contains 80 passing Foundry tests.
forge test
For detailed traces:
forge test -vvv
Ethereum Sepolia Deployment
The production testnet deployment runs on Ethereum Sepolia.
Network: Ethereum Sepolia
Chain ID: 11155111
Contracts
StakeToken
0x6De64f2A0D3C1c93cECB67eF06D4a22ff6e00999
RewardToken
0x1D1A21d7468040b306d06E3f1c6c30eeD249F380
StakingVault
0x9A8a5DdEC15F4B8822BF10E849B5C465D6cf2C1e
Project Hash
4ba73c3497f46ea64b0dd4e49ad8d02653085e82
This identifies the Git commit associated with the documented project state.
Technology Stack
Smart Contracts
- Solidity
- Foundry
- OpenZeppelin
- Anvil
- Ethereum Sepolia
Frontend
- React
- Vite
- Ethers.js
- Wagmi
- WalletConnect
- MetaMask
- Coinbase Wallet
- Vercel
Try the Application
The deployed application is available here:
https://defi-staking-dhzkby4cm-wire4.vercel.app/
The project is open source:
https://github.com/max-wire/defi-staking
Acknowledgements
This project was developed as part of the EtherAuthority Web3 training/internship program.
@etherauthority
@securechainai







Top comments (0)