Token Staking Yield System Development
Token staking with yield is one of basic DeFi building blocks. Many implementations: from simple "lock tokens, get more tokens" to complex systems with veToken model, gauge voting and bribe markets (Curve, Convex, Balancer). Architecture choice depends on goal: holder retention, governance, liquidity incentivization.
Let's review two most used models — MasterChef-style and ve-model — and discuss where each fails.
MasterChef-Style: Basic Architecture
Original SushiSwap MasterChef is still forked by hundreds of projects. Core idea: one contract manages multiple pools, each pool gets share of total token emission proportional to allocPoint.
accRewardPerShare Mathematics
Key invariant: pending(user) = user.amount * accRewardPerShare / PRECISION - user.rewardDebt.
accRewardPerShare monotonously increases with every new block (or second). On deposit/withdraw user updates rewardDebt, "zeroing out" current pending (which is first paid out).
function _updatePool(uint256 pid) internal {
PoolInfo storage pool = poolInfo[pid];
if (block.timestamp <= pool.lastRewardTime) return;
uint256 elapsed = block.timestamp - pool.lastRewardTime;
uint256 reward = elapsed * rewardPerSecond * pool.allocPoint / totalAllocPoint;
// mint reward tokens (or transfer from reward wallet)
rewardToken.mint(address(this), reward);
pool.accRewardPerShare += reward * 1e18 / pool.totalStaked;
pool.lastRewardTime = block.timestamp;
}
MasterChef problem: if no rewards (rewardPerSecond = 0 or budget exhausted) — users leave. Model completely depends on constant emission, diluting token infinitely. APY drops as TVL grows, creating unstable flywheel.
Rewards in Multiple Tokens
Multi-reward staking — each pool accrues several reward types simultaneously. Reference: Synthetix StakingRewards (audited, widely forked).
// Multiple rewarders for one pool
mapping(address => RewardInfo) public rewardInfo; // rewardToken -> RewardInfo
struct RewardInfo {
uint256 rewardRate; // reward per second
uint256 lastUpdateTime;
uint256 rewardPerTokenStored;
mapping(address => uint256) userRewardPerTokenPaid;
mapping(address => uint256) rewards;
}
Important: when adding new reward-token after launch — retroactive accruals for existing stakers shouldn't happen. New rewardPerTokenStored starts from current moment.
veToken Model: Long-Term Staking
Curve Finance introduced veCRV — vote-escrowed CRV. Idea: lock CRV for term (1 week — 4 years), get veG proportional to amount × time_remaining / max_lock_time. veG: non-transferable, gradually decays to zero as lock expires.
This changes incentive: to maintain influence, must regularly refresh lock. Large holders interested in long-term governance keep maximum lock.
Implementing ve-Mechanics
struct LockedBalance {
int128 amount;
uint256 end; // lock end timestamp (rounded to week)
}
function create_lock(uint256 value, uint256 unlock_time) external nonReentrant {
LockedBalance memory locked = _locked[msg.sender];
require(locked.amount == 0, "Withdraw old tokens first");
uint256 rounded_unlock = (unlock_time / WEEK) * WEEK; // round to week
require(rounded_unlock >= block.timestamp + WEEK, "Lock too short");
require(rounded_unlock <= block.timestamp + MAXTIME, "Lock too long");
_deposit_for(msg.sender, value, rounded_unlock, locked, CREATE_LOCK_TYPE);
}
Voting power decays linearly: on 4-year lock creation — maximum power, decreases every week. Requires checkpoint mechanism for history storage (Curve stores points per week in point_history).
Complexity of ve-model implementation: honest and gas-efficient implementation — ~500-800 lines of Vyper/Solidity with thoughtful checkpoint algorithm. Using ready Curve reference implementation (MIT license) with adaptation — preferable to writing from scratch.
Gauge Voting and Bribe Market
On top of ve-staking, gauge system builds: veToken holders vote for emission distribution between different pools. Pool getting more votes — gets more rewards.
Creates "gauge wars": projects wanting deep liquidity for their token pay veToken-holders for votes. Votium (for Curve), Hidden Hand (for Balancer/Aura) — ready bribe platforms. Integration with these platforms — standard practice for new protocols.
Model Comparison
| Characteristic | MasterChef | veToken |
|---|---|---|
| Implementation complexity | Low | High |
| Long-term retention | Weak | Strong |
| Governance influence | No | Built-in |
| Claim gas cost | Low | Medium |
| Inflation risk | High | Controlled |
| Time-to-market | 1-2 weeks | 4-8 weeks |
Security: Typical Vulnerabilities
Reentrancy on claim. If reward-token has ERC-777 hook or callback token — attacker can recursively call claim, getting multiple rewards before rewardDebt updates. Protection: nonReentrant modifier + CEI pattern (effects before interactions).
Flash loan manipulation. Attacker takes flash loan, makes huge deposit, waits one block (if reward per block), claims, withdraws. At reward per second — even one block sufficient. Protection: minimum lock period or reward only for full epochs (epoch-based snapshots).
Oracle manipulation for USD reward. If contract calculates reward in dollars via on-chain oracle (Chainlink) — token price manipulation can change reward rate. Better to store reward rate in token units, not USD.
Staking Economics
APY = (annual reward emission / TVL) × 100%. At TVL = $1M and $100K/year emission → APY = 10%.
Mistake: launch with high APY (300-1000%) to attract TVL, then lower. Creates mercenary capital: TVL leaves with APY drop. Better: moderate stable APY (20-50%) with long-term reward vesting, reducing sell pressure.
For real sustainability, rewards must have non-dilutive source: protocol commissions (like Uniswap v3 fee switch), revenue sharing, not pure emission.
Process and Timeline
MasterChef with multi-rewards and frontend — 3-4 weeks development + 2 weeks audit. veToken system with gauge voting — 6-10 weeks development, mandatory 3-4 weeks audit. Fundamental recommendation: for veToken — fork reference Curve VotingEscrow with modifications, not writing from scratch.







