Contract Source Code:
File 1 of 1 : Vault
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
contract Vault is ReentrancyGuard {
IEqualizerGauge public gauge;
IRouter public router;
IERC20 public token0;
IERC20 public token1;
IERC20 public stakeToken;
IERC20 public rewardToken;
address private treasury;
address private creatorTreasury;
address private owner;
bool public active = true;
uint256 public depositFee = 1;
uint256 public vaultFee;
uint256 public creatorFee;
uint256 private lpPerTicket;
uint256 public totalTickets;
uint256 public compoundedAt;
mapping(address => uint256) public userTickets; // Number of tickets per user
event Deposit(address indexed user, uint256 amount, uint256 tickets);
event Withdraw(address indexed user, uint256 amount);
event Compound(uint256 rewardAmount, uint256 lpAmount);
event ActiveStatusChanged(bool newStatus);
constructor(
address _treasury,
uint256 _vaultFee,
address _creatorTreasury,
uint256 _creatorFee,
address _gauge,
address _router,
IERC20 _stakeToken,
IERC20 _rewardToken,
IERC20 _token0,
IERC20 _token1
) {
treasury = _treasury;
vaultFee = _vaultFee;
creatorTreasury = _creatorTreasury;
creatorFee = _creatorFee;
gauge = IEqualizerGauge(_gauge);
router = IRouter(_router);
stakeToken = _stakeToken;
rewardToken = _rewardToken;
owner = msg.sender;
lpPerTicket = 1e18;
token0 = _token0;
token1 = _token1;
require(_vaultFee + _creatorFee <= 100, "Invalid fee distribution");
rewardToken.approve(address(router), type(uint256).max);
token0.approve(address(router), type(uint256).max);
token1.approve(address(router), type(uint256).max);
stakeToken.approve(address(gauge), type(uint256).max);
}
modifier whenActive() {
require(active, "Vault is not active");
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
function toggleActive() external onlyOwner {
active = !active;
emit ActiveStatusChanged(active);
}
function getRewards() internal returns (uint256) {
gauge.getReward();
return rewardToken.balanceOf(address(this));
}
function swapLPToToken0(uint256 rewardAmount) internal returns (uint256) {
require(rewardAmount > 0, "No token to swap");
IRouter.route[] memory path = new IRouter.route[](1);
path[0] = IRouter.route(address(rewardToken), address(token0), false);
router.swapExactTokensForTokens(
rewardAmount,
1,
path,
address(this),
block.timestamp
);
return token0.balanceOf(address(this));
}
function swapLPToToken1(uint256 rewardAmount) internal returns (uint256) {
require(rewardAmount > 0, "No token to swap");
IRouter.route[] memory path = new IRouter.route[](2);
path[0] = IRouter.route(address(rewardToken), address(token0), false);
path[1] = IRouter.route(address(token0), address(token1), false);
router.swapExactTokensForTokens(
rewardAmount,
1,
path,
address(this),
block.timestamp
);
return token1.balanceOf(address(this));
}
function addLiquidity() internal returns (uint256) {
uint256 token0Balance = IERC20(token0).balanceOf(address(this));
uint256 token1Balance = IERC20(token1).balanceOf(address(this));
// add liquidity
require(token0Balance > 0, "Insufficient balance for Token0 to provide liquidity");
require(token1Balance > 0, "Insufficient balance for Token1 to provide liquidity");
router.addLiquidity(
address(token0),
address(token1),
false,
token0Balance,
token1Balance,
1,
1,
address(this),
block.timestamp
);
return IERC20(stakeToken).balanceOf(address(this));
}
function updateVault() internal returns (uint256) {
uint256 totalLp = totalDeposits();
uint256 rewardAmount = 0;
if (totalTickets > 0 && totalLp > 0) {
rewardAmount = getRewards();
if (rewardAmount > 0) {
uint256 compoundAmount = (rewardAmount * vaultFee) / 1000;
uint256 creatorAmount = (rewardAmount * creatorFee) / 1000;
rewardToken.transfer(creatorTreasury, creatorAmount);
rewardToken.transfer(treasury, compoundAmount);
rewardAmount = rewardAmount - (compoundAmount + creatorAmount);
uint256 halfReward = rewardAmount / 2;
uint256 token0Amount = swapLPToToken0(halfReward);
uint256 token1Amount = swapLPToToken1(halfReward);
require(token0Amount > 0 && token1Amount > 0, "No tokens to swap");
uint256 stakeAmount = addLiquidity();
require(stakeAmount > 0, "No LP tokens to stake");
innerDeposit();
compoundedAt = block.timestamp;
}
}
return rewardAmount;
}
function innerDeposit() internal returns (uint256) {
uint256 lpAmount = stakeToken.balanceOf(address(this));
if (lpAmount > 0) {
gauge.deposit(lpAmount);
uint256 totalDeps = totalDeposits();
lpPerTicket = (totalDeps * 1e18) / totalTickets;
}
return lpPerTicket;
}
function deposit(uint256 amount) external nonReentrant whenActive {
if (totalTickets != 0) {
updateVault();
}
stakeToken.transferFrom(msg.sender, address(this), amount);
uint256 fee = (amount * depositFee) / 1000;
stakeToken.transfer(treasury, fee);
amount -= fee;
uint256 tickets = (amount * 1e18) / lpPerTicket;
userTickets[msg.sender] += tickets;
totalTickets += tickets;
gauge.deposit(amount);
emit Deposit(msg.sender, amount, tickets);
}
function _withdraw(uint256 amount) internal {
uint256 tickets = userTickets[msg.sender];
require(tickets > 0, "No tickets to withdraw");
uint256 userLp = (tickets * lpPerTicket) / 1e18;
require(userLp >= amount, "Insufficient balance");
uint256 withdrawTickets = (amount * 1e18) / lpPerTicket;
userTickets[msg.sender] -= withdrawTickets;
totalTickets -= withdrawTickets;
gauge.withdraw(amount);
stakeToken.transfer(msg.sender, amount);
emit Withdraw(msg.sender, amount);
}
function withdraw(uint256 amount) external nonReentrant {
_withdraw(amount);
}
function withdrawAll() external {
uint256 tickets = userTickets[msg.sender];
uint256 userLp = (tickets * lpPerTicket) / 1e18;
_withdraw(userLp);
}
function compound() external whenActive {
uint256 rewards = updateVault();
require(rewards > 0, "No rewards to compound");
uint256 lpAmount = stakeToken.balanceOf(address(this));
if (lpAmount > 0) {
gauge.deposit(lpAmount);
uint256 totalDeps = totalDeposits();
lpPerTicket = (totalDeps * 1e18) / totalTickets;
}
emit Compound(rewardToken.balanceOf(address(this)), lpAmount);
}
function totalDeposits() public view returns (uint256) {
return gauge.balanceOf(address(this));
}
function lpForUser(address _user) external view returns (uint256) {
uint256 tickets = userTickets[_user];
if (tickets == 0) return 0;
uint256 _lpPerTicket = lpPerTicket;
uint256 totalLp = totalDeposits();
if (totalTickets > 0 && totalLp > 0) {
uint256 rewardAmount = rewardToken.balanceOf(address(this));
if (rewardAmount > 0) {
_lpPerTicket += (rewardAmount * 1e18) / totalTickets;
}
}
return (tickets * _lpPerTicket) / 1e18;
}
}
interface IEqualizerGauge {
function deposit(uint256 _value) external;
function withdraw(uint256 amount) external;
function getReward() external;
function balanceOf(address account) external view returns (uint256);
}
interface IRouter {
struct route {
address from;
address to;
bool stable;
}
function addLiquidity(
address tokenA,
address tokenB,
bool stable,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external
returns (
uint amountA,
uint amountB,
uint liquidity
);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
route[] calldata routes,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
route[] memory routes,
address to,
uint256 deadline
) external;
}
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}