Building Secure Staking Platforms with Solidity & Web3.js
A deep dive into creating production-ready staking contracts with daily reward distribution and security best practices.
Introduction
Staking platforms are at the core of DeFi — allowing users to lock their tokens and earn rewards over time. But building one that's secure, gas-efficient, and production-ready takes careful planning.
In this article, I'll walk through the architecture and key decisions behind a staking platform I built for a client using Solidity and Web3.js.
Smart Contract Architecture
The staking contract needs three core functions:
function stake(uint256 _amount) external {
require(_amount > 0, "Cannot stake 0");
stakingToken.transferFrom(msg.sender, address(this), _amount);
stakedBalance[msg.sender] += _amount;
lastStakeTime[msg.sender] = block.timestamp;
emit Staked(msg.sender, _amount);
}
Security Considerations
1. **Reentrancy Protection** — Always use ReentrancyGuard from OpenZeppelin
2. **Integer Overflow** — Solidity 0.8+ has built-in overflow checks
3. **Access Control** — Only admin can set reward rates
4. **Timelock** — Add minimum staking period to prevent flash loan attacks
Frontend Integration with Web3.js
The DApp frontend connects to the contract using Web3.js or Ethers.js. Key interactions:
Reward Calculation
Daily rewards are calculated based on:
const dailyReward = (stakedAmount * APY) / 365 / 100;
const totalReward = dailyReward * daysSinceLastClaim;
Key Takeaways
Building secure staking platforms requires a deep understanding of both smart contract development and frontend integration. The combination of Solidity's security patterns with Web3.js's flexibility makes it possible to create robust DeFi applications.
solidityfunction stake(uint256 _amount) external { require(_amount > 0, "Cannot stake 0"); stakingToken.transferFrom(msg.sender, address(this), _amount); stakedBalance[msg.sender] += _amount; lastStakeTime[msg.sender] = block.timestamp; emit Staked(msg.sender, _amount); }
javascriptconst dailyReward = (stakedAmount * APY) / 365 / 100; const totalReward = dailyReward * daysSinceLastClaim;