Commented out stuff to pause functions
This commit is contained in:
@@ -553,101 +553,101 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
|||||||
// emit StakeSaleCancelled(_seller, _stakeId);
|
// emit StakeSaleCancelled(_seller, _stakeId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function createStake(uint256 _amount) external {
|
// function createStake(uint256 _amount) external {
|
||||||
// Scale up for wei comparison, USDC is 1e6
|
// // Scale up for wei comparison, USDC is 1e6
|
||||||
if (_amount * 1e12 <= minStakeLock) revert AmountBelowMinimum();
|
// if (_amount * 1e12 <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Transfer tokens from the user into the contract
|
// // Transfer tokens from the user into the contract
|
||||||
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create the stake
|
// // Create the stake
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Update total staked
|
// // Update total staked
|
||||||
pool.totalStaked += _amount;
|
// pool.totalStaked += _amount;
|
||||||
|
|
||||||
emit Staked(msg.sender, _amount);
|
// emit Staked(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
/// @notice Restake an expired stake with a bonus daily reward
|
// /// @notice Restake an expired stake with a bonus daily reward
|
||||||
function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
// function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
||||||
if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
// if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (stake.amount == 0) revert NothingToClaim();
|
// if (stake.amount == 0) revert NothingToClaim();
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
// uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
||||||
uint256 withdraw_amount = _amount - restake_amount;
|
// uint256 withdraw_amount = _amount - restake_amount;
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
|
|
||||||
// Process withdraw
|
// // Process withdraw
|
||||||
if (withdraw_amount > 0) {
|
// if (withdraw_amount > 0) {
|
||||||
withdrawLiabilities += withdraw_amount;
|
// withdrawLiabilities += withdraw_amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= withdraw_amount) {
|
// if (pool.totalStaked >= withdraw_amount) {
|
||||||
pool.totalStaked -= withdraw_amount;
|
// pool.totalStaked -= withdraw_amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: withdraw_amount,
|
// amount: withdraw_amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, withdraw_amount);
|
// emit RewardClaimed(msg.sender, withdraw_amount);
|
||||||
|
|
||||||
}
|
// }
|
||||||
// Process restake
|
// // Process restake
|
||||||
if (restake_amount > 0) {
|
// if (restake_amount > 0) {
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// restake = true
|
// // restake = true
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, true);
|
// finalRewardRate = getUserRewardRate(msg.sender, true);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: restake_amount,
|
// amount: restake_amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit Staked(msg.sender, restake_amount);
|
// emit Staked(msg.sender, restake_amount);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
if (_amount == 0) revert InvalidAmount();
|
||||||
@@ -712,222 +712,222 @@ contract PacaFinanceWithBoostAndScheduleBase is Initializable, ReentrancyGuardUp
|
|||||||
return finalRewardRate;
|
return finalRewardRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function claimRewards() external nonReentrant {
|
// function claimRewards() external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
// if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
||||||
|
|
||||||
pool.totalRewards = pool.totalRewards - totalReward;
|
// pool.totalRewards = pool.totalRewards - totalReward;
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimStake(uint256 _stakeIndex) external nonReentrant {
|
// function claimStake(uint256 _stakeIndex) external nonReentrant {
|
||||||
// Ensure the stake index is valid
|
// // Ensure the stake index is valid
|
||||||
if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
// if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
||||||
|
|
||||||
// Load the stake
|
// // Load the stake
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
|
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
|
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (_amount == 0) revert NothingToClaim();
|
// if (_amount == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Ensure the stake is unlocked (if using lockup periods)
|
// // Ensure the stake is unlocked (if using lockup periods)
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
withdrawLiabilities += _amount;
|
// withdrawLiabilities += _amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= _amount) {
|
// if (pool.totalStaked >= _amount) {
|
||||||
pool.totalStaked -= _amount;
|
// pool.totalStaked -= _amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, _amount);
|
// emit RewardClaimed(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraw a staked amount after its unlock time has passed.
|
// * @notice Withdraw a staked amount after its unlock time has passed.
|
||||||
* @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
// * @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
||||||
* and transfers tokens to the caller. For vesting stakes (where `_stakeIndex` >= 1e6),
|
// * and transfers tokens to the caller. For vesting stakes (where `_stakeIndex` >= 1e6),
|
||||||
* the stored amount (in 1e18 decimals) is scaled to USDC's 1e6 decimals by dividing by 1e12.
|
// * the stored amount (in 1e18 decimals) is scaled to USDC's 1e6 decimals by dividing by 1e12.
|
||||||
*
|
// *
|
||||||
* Requirements:
|
// * Requirements:
|
||||||
* - Caller must have at least one stake.
|
// * - Caller must have at least one stake.
|
||||||
* - The stake must exist, be unlocked, and have a non-zero amount.
|
// * - The stake must exist, be unlocked, and have a non-zero amount.
|
||||||
* - The contract must have sufficient token balance.
|
// * - The contract must have sufficient token balance.
|
||||||
*
|
// *
|
||||||
* @param _stakeIndex The identifier of the stake to withdraw.
|
// * @param _stakeIndex The identifier of the stake to withdraw.
|
||||||
*/
|
// */
|
||||||
function withdraw(uint256 _stakeIndex) external nonReentrant {
|
// function withdraw(uint256 _stakeIndex) external nonReentrant {
|
||||||
WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
// WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
||||||
if (userStakes.length == 0) revert NoStakesAvailable();
|
// if (userStakes.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userStakes.length; ++i) {
|
// for (uint256 i = 0; i < userStakes.length; ++i) {
|
||||||
WithdrawStake storage stake = userStakes[i];
|
// WithdrawStake storage stake = userStakes[i];
|
||||||
// Skip already withdrawn stakes (amount == 0)
|
// // Skip already withdrawn stakes (amount == 0)
|
||||||
if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
// if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
|
//
|
||||||
// Convert vesting stake amount to USDC decimals.
|
// // Convert vesting stake amount to USDC decimals.
|
||||||
if (_stakeIndex >= 1e6) {
|
// if (_stakeIndex >= 1e6) {
|
||||||
_amount = _amount / 1e12;
|
// _amount = _amount / 1e12;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
// uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
||||||
if (poolBalance < _amount) revert InsufficientRewards();
|
// if (poolBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
// withdrawLiabilities is in 1e18, deduct original amount
|
// // withdrawLiabilities is in 1e18, deduct original amount
|
||||||
withdrawLiabilities -= stake.amount;
|
// withdrawLiabilities -= stake.amount;
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
// emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Revert if no matching stake with non-zero amount was found
|
// // Revert if no matching stake with non-zero amount was found
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraws vesting tokens after cooldown period
|
// * @notice Withdraws vesting tokens after cooldown period
|
||||||
* @param _vestingId The vesting ID to withdraw
|
// * @param _vestingId The vesting ID to withdraw
|
||||||
*/
|
// */
|
||||||
function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
// function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
||||||
WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
// WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
||||||
if (userVestings.length == 0) revert NoStakesAvailable();
|
// if (userVestings.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userVestings.length; ++i) {
|
// for (uint256 i = 0; i < userVestings.length; ++i) {
|
||||||
WithdrawVesting storage vestingWithdraw = userVestings[i];
|
// WithdrawVesting storage vestingWithdraw = userVestings[i];
|
||||||
if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
// if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
||||||
if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
// if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = vestingWithdraw.amount;
|
// uint256 _amount = vestingWithdraw.amount;
|
||||||
address _token = vestingWithdraw.token;
|
// address _token = vestingWithdraw.token;
|
||||||
|
//
|
||||||
// Check contract has sufficient balance
|
// // Check contract has sufficient balance
|
||||||
uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
// uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
||||||
if (tokenBalance < _amount) revert InsufficientRewards();
|
// if (tokenBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
vestingWithdraw.amount = 0;
|
// vestingWithdraw.amount = 0;
|
||||||
|
//
|
||||||
// Decrement withdraw vesting liabilities for this token
|
// // Decrement withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] -= _amount;
|
// withdrawVestingLiabilities[_token] -= _amount;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(_token).safeTransfer(msg.sender, _amount);
|
// IERC20(_token).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
// emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function compoundAllRewards() external {
|
// function compoundAllRewards() external {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
// if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
||||||
|
//
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
pool.totalStaked = pool.totalStaked + totalReward;
|
// pool.totalStaked = pool.totalStaked + totalReward;
|
||||||
emit CompoundRewards(msg.sender, totalReward);
|
// emit CompoundRewards(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createVesting(address _token, uint256 _amount) external {
|
// function createVesting(address _token, uint256 _amount) external {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
// if (_amount == 0) revert InvalidAmount();
|
||||||
address oracle = priceOracles[_token];
|
// address oracle = priceOracles[_token];
|
||||||
if (oracle == address(0)) revert PriceOracleNotSet();
|
// if (oracle == address(0)) revert PriceOracleNotSet();
|
||||||
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
// uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
||||||
|
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
||||||
if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
// if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
dollarsVested[msg.sender] += usdPrice;
|
// dollarsVested[msg.sender] += usdPrice;
|
||||||
// Update token's vestedTotal
|
// // Update token's vestedTotal
|
||||||
vestedTotal[_token] += _amount;
|
// vestedTotal[_token] += _amount;
|
||||||
|
|
||||||
|
|
||||||
vestings[msg.sender].push(Vesting({
|
// vestings[msg.sender].push(Vesting({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
bonus: bonus,
|
// bonus: bonus,
|
||||||
lockedUntil: block.timestamp + lockupDuration,
|
// lockedUntil: block.timestamp + lockupDuration,
|
||||||
claimedAmount: 0,
|
// claimedAmount: 0,
|
||||||
claimedBonus: 0,
|
// claimedBonus: 0,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
createdAt: block.timestamp,
|
// createdAt: block.timestamp,
|
||||||
token: _token,
|
// token: _token,
|
||||||
complete: false,
|
// complete: false,
|
||||||
usdAmount: usdPrice
|
// usdAmount: usdPrice
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit VestingCreated(msg.sender, _amount, bonus);
|
// emit VestingCreated(msg.sender, _amount, bonus);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
||||||
Vesting storage vesting = vestings[_user][_vestingIndex];
|
Vesting storage vesting = vestings[_user][_vestingIndex];
|
||||||
@@ -990,124 +990,124 @@ function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
// function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
if (vesting.complete) revert StakeComplete();
|
// if (vesting.complete) revert StakeComplete();
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim == 0) revert NothingToClaim();
|
// if (amountToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
vestedTotal[vesting.token] -= amountToClaim;
|
// vestedTotal[vesting.token] -= amountToClaim;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: amountToClaim,
|
// amount: amountToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: vesting.token
|
// token: vesting.token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
// withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
||||||
|
|
||||||
emit VestingClaimed(msg.sender, amountToClaim, 0);
|
// emit VestingClaimed(msg.sender, amountToClaim, 0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function claimAllVestingByToken(address _token) external nonReentrant {
|
// function claimAllVestingByToken(address _token) external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
uint256 vestingsProcessed = 0;
|
// uint256 vestingsProcessed = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
||||||
Vesting storage vesting = vestings[msg.sender][i];
|
// Vesting storage vesting = vestings[msg.sender][i];
|
||||||
|
|
||||||
if (vesting.token == _token && !vesting.complete) {
|
// if (vesting.token == _token && !vesting.complete) {
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
|
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim > 0) {
|
// if (amountToClaim > 0) {
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
totalReward = totalReward + amountToClaim;
|
// totalReward = totalReward + amountToClaim;
|
||||||
vesting.lastClaimed = block.timestamp;
|
// vesting.lastClaimed = block.timestamp;
|
||||||
|
|
||||||
// Mark vesting as complete if fully claimed
|
// // Mark vesting as complete if fully claimed
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
vestingsProcessed++;
|
// vestingsProcessed++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update vesting total
|
// // Update vesting total
|
||||||
vestedTotal[_token] -= totalReward;
|
// vestedTotal[_token] -= totalReward;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: _token
|
// token: _token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] += totalReward;
|
// withdrawVestingLiabilities[_token] += totalReward;
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
// function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
// uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
// if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
||||||
uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
// uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
||||||
if (bonusToClaim == 0) revert NothingToClaim();
|
// if (bonusToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
// vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
||||||
withdrawLiabilities += bonusToClaim;
|
// withdrawLiabilities += bonusToClaim;
|
||||||
|
|
||||||
// IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
// // IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw.
|
// // Create temporary the stake for the user to delay withdraw.
|
||||||
// Add 1e6 to the vesting index to distinguish them from normal stakes.
|
// // Add 1e6 to the vesting index to distinguish them from normal stakes.
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _vestingIndex + 1e6,
|
// stakeId: _vestingIndex + 1e6,
|
||||||
amount: bonusToClaim,
|
// amount: bonusToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit BonusClaimed(msg.sender, bonusToClaim);
|
// emit BonusClaimed(msg.sender, bonusToClaim);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
||||||
priceOracles[_token] = _oracle;
|
priceOracles[_token] = _oracle;
|
||||||
@@ -1265,7 +1265,6 @@ function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @notice Function to put a stake for sale.
|
/// @notice Function to put a stake for sale.
|
||||||
/// Sets the original stake amount to 0 to prevent any alterations while for sale.
|
/// Sets the original stake amount to 0 to prevent any alterations while for sale.
|
||||||
/// @param _stakeId The stake to sell.
|
/// @param _stakeId The stake to sell.
|
||||||
|
|||||||
@@ -553,100 +553,100 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
|||||||
// emit StakeSaleCancelled(_seller, _stakeId);
|
// emit StakeSaleCancelled(_seller, _stakeId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function createStake(uint256 _amount) external {
|
// function createStake(uint256 _amount) external {
|
||||||
if (_amount <= minStakeLock) revert AmountBelowMinimum();
|
// if (_amount <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Transfer tokens from the user into the contract
|
// // Transfer tokens from the user into the contract
|
||||||
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create the stake
|
// // Create the stake
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Update total staked
|
// // Update total staked
|
||||||
pool.totalStaked += _amount;
|
// pool.totalStaked += _amount;
|
||||||
|
|
||||||
emit Staked(msg.sender, _amount);
|
// emit Staked(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
/// @notice Restake an expired stake with a bonus daily reward
|
// /// @notice Restake an expired stake with a bonus daily reward
|
||||||
function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
// function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
||||||
if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
// if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (stake.amount == 0) revert NothingToClaim();
|
// if (stake.amount == 0) revert NothingToClaim();
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
// uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
||||||
uint256 withdraw_amount = _amount - restake_amount;
|
// uint256 withdraw_amount = _amount - restake_amount;
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
|
|
||||||
// Process withdraw
|
// // Process withdraw
|
||||||
if (withdraw_amount > 0) {
|
// if (withdraw_amount > 0) {
|
||||||
withdrawLiabilities += withdraw_amount;
|
// withdrawLiabilities += withdraw_amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= withdraw_amount) {
|
// if (pool.totalStaked >= withdraw_amount) {
|
||||||
pool.totalStaked -= withdraw_amount;
|
// pool.totalStaked -= withdraw_amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: withdraw_amount,
|
// amount: withdraw_amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, withdraw_amount);
|
// emit RewardClaimed(msg.sender, withdraw_amount);
|
||||||
|
|
||||||
}
|
// }
|
||||||
// Process restake
|
// // Process restake
|
||||||
if (restake_amount > 0) {
|
// if (restake_amount > 0) {
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// restake = true
|
// // restake = true
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, true);
|
// finalRewardRate = getUserRewardRate(msg.sender, true);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: restake_amount,
|
// amount: restake_amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit Staked(msg.sender, restake_amount);
|
// emit Staked(msg.sender, restake_amount);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
if (_amount == 0) revert InvalidAmount();
|
||||||
@@ -711,213 +711,213 @@ contract PacaFinanceWithBoostAndScheduleBsc is Initializable, ReentrancyGuardUpg
|
|||||||
return finalRewardRate;
|
return finalRewardRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function claimRewards() external nonReentrant {
|
// function claimRewards() external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
// if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
||||||
|
|
||||||
pool.totalRewards = pool.totalRewards - totalReward;
|
// pool.totalRewards = pool.totalRewards - totalReward;
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimStake(uint256 _stakeIndex) external nonReentrant {
|
// function claimStake(uint256 _stakeIndex) external nonReentrant {
|
||||||
// Ensure the stake index is valid
|
// // Ensure the stake index is valid
|
||||||
if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
// if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
||||||
|
|
||||||
// Load the stake
|
// // Load the stake
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
|
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
|
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (_amount == 0) revert NothingToClaim();
|
// if (_amount == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Ensure the stake is unlocked (if using lockup periods)
|
// // Ensure the stake is unlocked (if using lockup periods)
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
withdrawLiabilities += _amount;
|
// withdrawLiabilities += _amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= _amount) {
|
// if (pool.totalStaked >= _amount) {
|
||||||
pool.totalStaked -= _amount;
|
// pool.totalStaked -= _amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, _amount);
|
// emit RewardClaimed(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraw a staked amount after its unlock time has passed.
|
// * @notice Withdraw a staked amount after its unlock time has passed.
|
||||||
* @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
// * @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
||||||
* and transfers tokens to the caller.
|
// * and transfers tokens to the caller.
|
||||||
*
|
// *
|
||||||
* Requirements:
|
// * Requirements:
|
||||||
* - Caller must have at least one stake.
|
// * - Caller must have at least one stake.
|
||||||
* - The stake must exist, be unlocked, and have a non-zero amount.
|
// * - The stake must exist, be unlocked, and have a non-zero amount.
|
||||||
* - The contract must have sufficient token balance.
|
// * - The contract must have sufficient token balance.
|
||||||
*
|
// *
|
||||||
* @param _stakeIndex The identifier of the stake to withdraw.
|
// * @param _stakeIndex The identifier of the stake to withdraw.
|
||||||
*/
|
// */
|
||||||
function withdraw(uint256 _stakeIndex) external nonReentrant {
|
// function withdraw(uint256 _stakeIndex) external nonReentrant {
|
||||||
WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
// WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
||||||
if (userStakes.length == 0) revert NoStakesAvailable();
|
// if (userStakes.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userStakes.length; ++i) {
|
// for (uint256 i = 0; i < userStakes.length; ++i) {
|
||||||
WithdrawStake storage stake = userStakes[i];
|
// WithdrawStake storage stake = userStakes[i];
|
||||||
if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
// if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
// uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
||||||
if (poolBalance < _amount) revert InsufficientRewards();
|
// if (poolBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
withdrawLiabilities -= _amount;
|
// withdrawLiabilities -= _amount;
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
// emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Revert if no matching stake with non-zero amount was found
|
// // Revert if no matching stake with non-zero amount was found
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraws vesting tokens after cooldown period
|
// * @notice Withdraws vesting tokens after cooldown period
|
||||||
* @param _vestingId The vesting ID to withdraw
|
// * @param _vestingId The vesting ID to withdraw
|
||||||
*/
|
// */
|
||||||
function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
// function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
||||||
WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
// WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
||||||
if (userVestings.length == 0) revert NoStakesAvailable();
|
// if (userVestings.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userVestings.length; ++i) {
|
// for (uint256 i = 0; i < userVestings.length; ++i) {
|
||||||
WithdrawVesting storage vestingWithdraw = userVestings[i];
|
// WithdrawVesting storage vestingWithdraw = userVestings[i];
|
||||||
if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
// if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
||||||
if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
// if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = vestingWithdraw.amount;
|
// uint256 _amount = vestingWithdraw.amount;
|
||||||
address _token = vestingWithdraw.token;
|
// address _token = vestingWithdraw.token;
|
||||||
|
//
|
||||||
// Check contract has sufficient balance
|
// // Check contract has sufficient balance
|
||||||
uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
// uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
||||||
if (tokenBalance < _amount) revert InsufficientRewards();
|
// if (tokenBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
vestingWithdraw.amount = 0;
|
// vestingWithdraw.amount = 0;
|
||||||
|
//
|
||||||
// Decrement withdraw vesting liabilities for this token
|
// // Decrement withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] -= _amount;
|
// withdrawVestingLiabilities[_token] -= _amount;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(_token).safeTransfer(msg.sender, _amount);
|
// IERC20(_token).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
// emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function compoundAllRewards() external {
|
// function compoundAllRewards() external {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
// if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
||||||
|
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
pool.totalStaked = pool.totalStaked + totalReward;
|
// pool.totalStaked = pool.totalStaked + totalReward;
|
||||||
emit CompoundRewards(msg.sender, totalReward);
|
// emit CompoundRewards(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createVesting(address _token, uint256 _amount) external {
|
// function createVesting(address _token, uint256 _amount) external {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
// if (_amount == 0) revert InvalidAmount();
|
||||||
address oracle = priceOracles[_token];
|
// address oracle = priceOracles[_token];
|
||||||
if (oracle == address(0)) revert PriceOracleNotSet();
|
// if (oracle == address(0)) revert PriceOracleNotSet();
|
||||||
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
// uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
||||||
|
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
||||||
if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
// if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
dollarsVested[msg.sender] += usdPrice;
|
// dollarsVested[msg.sender] += usdPrice;
|
||||||
// Update token's vestedTotal
|
// // Update token's vestedTotal
|
||||||
vestedTotal[_token] += _amount;
|
// vestedTotal[_token] += _amount;
|
||||||
|
|
||||||
|
|
||||||
vestings[msg.sender].push(Vesting({
|
// vestings[msg.sender].push(Vesting({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
bonus: bonus,
|
// bonus: bonus,
|
||||||
lockedUntil: block.timestamp + lockupDuration,
|
// lockedUntil: block.timestamp + lockupDuration,
|
||||||
claimedAmount: 0,
|
// claimedAmount: 0,
|
||||||
claimedBonus: 0,
|
// claimedBonus: 0,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
createdAt: block.timestamp,
|
// createdAt: block.timestamp,
|
||||||
token: _token,
|
// token: _token,
|
||||||
complete: false,
|
// complete: false,
|
||||||
usdAmount: usdPrice
|
// usdAmount: usdPrice
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit VestingCreated(msg.sender, _amount, bonus);
|
// emit VestingCreated(msg.sender, _amount, bonus);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
||||||
Vesting storage vesting = vestings[_user][_vestingIndex];
|
Vesting storage vesting = vestings[_user][_vestingIndex];
|
||||||
@@ -980,124 +980,124 @@ function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
// function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
if (vesting.complete) revert StakeComplete();
|
// if (vesting.complete) revert StakeComplete();
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim == 0) revert NothingToClaim();
|
// if (amountToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
vestedTotal[vesting.token] -= amountToClaim;
|
// vestedTotal[vesting.token] -= amountToClaim;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: amountToClaim,
|
// amount: amountToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: vesting.token
|
// token: vesting.token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
// withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
||||||
|
|
||||||
emit VestingClaimed(msg.sender, amountToClaim, 0);
|
// emit VestingClaimed(msg.sender, amountToClaim, 0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function claimAllVestingByToken(address _token) external nonReentrant {
|
// function claimAllVestingByToken(address _token) external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
uint256 vestingsProcessed = 0;
|
// uint256 vestingsProcessed = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
||||||
Vesting storage vesting = vestings[msg.sender][i];
|
// Vesting storage vesting = vestings[msg.sender][i];
|
||||||
|
|
||||||
if (vesting.token == _token && !vesting.complete) {
|
// if (vesting.token == _token && !vesting.complete) {
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
|
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim > 0) {
|
// if (amountToClaim > 0) {
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
totalReward = totalReward + amountToClaim;
|
// totalReward = totalReward + amountToClaim;
|
||||||
vesting.lastClaimed = block.timestamp;
|
// vesting.lastClaimed = block.timestamp;
|
||||||
|
|
||||||
// Mark vesting as complete if fully claimed
|
// // Mark vesting as complete if fully claimed
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
vestingsProcessed++;
|
// vestingsProcessed++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update vesting total
|
// // Update vesting total
|
||||||
vestedTotal[_token] -= totalReward;
|
// vestedTotal[_token] -= totalReward;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: _token
|
// token: _token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] += totalReward;
|
// withdrawVestingLiabilities[_token] += totalReward;
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
// function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
// uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
// if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
||||||
uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
// uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
||||||
if (bonusToClaim == 0) revert NothingToClaim();
|
// if (bonusToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
// vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
||||||
withdrawLiabilities += bonusToClaim;
|
// withdrawLiabilities += bonusToClaim;
|
||||||
|
|
||||||
// IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
// // IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw.
|
// // Create temporary the stake for the user to delay withdraw.
|
||||||
// Add 1e6 to the vesting index to distinguish them from normal stakes.
|
// // Add 1e6 to the vesting index to distinguish them from normal stakes.
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _vestingIndex + 1e6,
|
// stakeId: _vestingIndex + 1e6,
|
||||||
amount: bonusToClaim,
|
// amount: bonusToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit BonusClaimed(msg.sender, bonusToClaim);
|
// emit BonusClaimed(msg.sender, bonusToClaim);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
||||||
priceOracles[_token] = _oracle;
|
priceOracles[_token] = _oracle;
|
||||||
|
|||||||
@@ -556,101 +556,101 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
|||||||
// emit StakeSaleCancelled(_seller, _stakeId);
|
// emit StakeSaleCancelled(_seller, _stakeId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function createStake(uint256 _amount) external {
|
// function createStake(uint256 _amount) external {
|
||||||
// Scale up for wei comparison, USDC is 1e6
|
// // Scale up for wei comparison, USDC is 1e6
|
||||||
if (_amount * 1e12 <= minStakeLock) revert AmountBelowMinimum();
|
// if (_amount * 1e12 <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Transfer tokens from the user into the contract
|
// // Transfer tokens from the user into the contract
|
||||||
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create the stake
|
// // Create the stake
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Update total staked
|
// // Update total staked
|
||||||
pool.totalStaked += _amount;
|
// pool.totalStaked += _amount;
|
||||||
|
|
||||||
emit Staked(msg.sender, _amount);
|
// emit Staked(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
/// @notice Restake an expired stake with a bonus daily reward
|
// /// @notice Restake an expired stake with a bonus daily reward
|
||||||
function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
// function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
|
||||||
if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
// if (_restakePercentage > 100) revert InvalidRestakePercentage();
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (stake.amount == 0) revert NothingToClaim();
|
// if (stake.amount == 0) revert NothingToClaim();
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
// uint256 restake_amount = (_amount * _restakePercentage) / 100;
|
||||||
uint256 withdraw_amount = _amount - restake_amount;
|
// uint256 withdraw_amount = _amount - restake_amount;
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
|
|
||||||
// Process withdraw
|
// // Process withdraw
|
||||||
if (withdraw_amount > 0) {
|
// if (withdraw_amount > 0) {
|
||||||
withdrawLiabilities += withdraw_amount;
|
// withdrawLiabilities += withdraw_amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= withdraw_amount) {
|
// if (pool.totalStaked >= withdraw_amount) {
|
||||||
pool.totalStaked -= withdraw_amount;
|
// pool.totalStaked -= withdraw_amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: withdraw_amount,
|
// amount: withdraw_amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, withdraw_amount);
|
// emit RewardClaimed(msg.sender, withdraw_amount);
|
||||||
|
|
||||||
}
|
// }
|
||||||
// Process restake
|
// // Process restake
|
||||||
if (restake_amount > 0) {
|
// if (restake_amount > 0) {
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// restake = true
|
// // restake = true
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, true);
|
// finalRewardRate = getUserRewardRate(msg.sender, true);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: restake_amount,
|
// amount: restake_amount,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit Staked(msg.sender, restake_amount);
|
// emit Staked(msg.sender, restake_amount);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
if (_amount == 0) revert InvalidAmount();
|
||||||
@@ -715,222 +715,222 @@ contract PacaFinanceWithBoostAndScheduleSonic is Initializable, ReentrancyGuardU
|
|||||||
return finalRewardRate;
|
return finalRewardRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function claimRewards() external nonReentrant {
|
// function claimRewards() external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
// if (pool.totalRewards < totalReward) revert InsufficientRewards();
|
||||||
|
|
||||||
pool.totalRewards = pool.totalRewards - totalReward;
|
// pool.totalRewards = pool.totalRewards - totalReward;
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, totalReward);
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimStake(uint256 _stakeIndex) external nonReentrant {
|
// function claimStake(uint256 _stakeIndex) external nonReentrant {
|
||||||
// Ensure the stake index is valid
|
// // Ensure the stake index is valid
|
||||||
if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
// if (_stakeIndex >= stakes[msg.sender].length) revert InvalidStakeIndex();
|
||||||
|
|
||||||
// Load the stake
|
// // Load the stake
|
||||||
Stake storage stake = stakes[msg.sender][_stakeIndex];
|
// Stake storage stake = stakes[msg.sender][_stakeIndex];
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
|
|
||||||
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
|
||||||
|
|
||||||
_amount = _amount + rewards;
|
// _amount = _amount + rewards;
|
||||||
|
|
||||||
// Ensure there is a stake to claim
|
// // Ensure there is a stake to claim
|
||||||
if (_amount == 0) revert NothingToClaim();
|
// if (_amount == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Ensure the stake is unlocked (if using lockup periods)
|
// // Ensure the stake is unlocked (if using lockup periods)
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
stake.complete = true;
|
// stake.complete = true;
|
||||||
withdrawLiabilities += _amount;
|
// withdrawLiabilities += _amount;
|
||||||
|
|
||||||
if (pool.totalStaked >= _amount) {
|
// if (pool.totalStaked >= _amount) {
|
||||||
pool.totalStaked -= _amount;
|
// pool.totalStaked -= _amount;
|
||||||
} else {
|
// } else {
|
||||||
pool.totalStaked = 0;
|
// pool.totalStaked = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw
|
// // Create temporary the stake for the user to delay withdraw
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _stakeIndex,
|
// stakeId: _stakeIndex,
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
// Emit a detailed event
|
// // Emit a detailed event
|
||||||
emit RewardClaimed(msg.sender, _amount);
|
// emit RewardClaimed(msg.sender, _amount);
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraw a staked amount after its unlock time has passed.
|
// * @notice Withdraw a staked amount after its unlock time has passed.
|
||||||
* @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
// * @dev Locates the stake by `_stakeIndex`, checks that it's unlocked and non-zero,
|
||||||
* and transfers tokens to the caller. For vesting stakes (where `_stakeIndex` >= 1e6),
|
// * and transfers tokens to the caller. For vesting stakes (where `_stakeIndex` >= 1e6),
|
||||||
* the stored amount (in 1e18 decimals) is scaled to USDC's 1e6 decimals by dividing by 1e12.
|
// * the stored amount (in 1e18 decimals) is scaled to USDC's 1e6 decimals by dividing by 1e12.
|
||||||
*
|
// *
|
||||||
* Requirements:
|
// * Requirements:
|
||||||
* - Caller must have at least one stake.
|
// * - Caller must have at least one stake.
|
||||||
* - The stake must exist, be unlocked, and have a non-zero amount.
|
// * - The stake must exist, be unlocked, and have a non-zero amount.
|
||||||
* - The contract must have sufficient token balance.
|
// * - The contract must have sufficient token balance.
|
||||||
*
|
// *
|
||||||
* @param _stakeIndex The identifier of the stake to withdraw.
|
// * @param _stakeIndex The identifier of the stake to withdraw.
|
||||||
*/
|
// */
|
||||||
function withdraw(uint256 _stakeIndex) external nonReentrant {
|
// function withdraw(uint256 _stakeIndex) external nonReentrant {
|
||||||
WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
// WithdrawStake[] storage userStakes = withdrawStake[msg.sender];
|
||||||
if (userStakes.length == 0) revert NoStakesAvailable();
|
// if (userStakes.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userStakes.length; ++i) {
|
// for (uint256 i = 0; i < userStakes.length; ++i) {
|
||||||
WithdrawStake storage stake = userStakes[i];
|
// WithdrawStake storage stake = userStakes[i];
|
||||||
// Skip already withdrawn stakes (amount == 0)
|
// // Skip already withdrawn stakes (amount == 0)
|
||||||
if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
// if (stake.stakeId == _stakeIndex && stake.amount != 0) {
|
||||||
if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
// if (block.timestamp < stake.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = stake.amount;
|
// uint256 _amount = stake.amount;
|
||||||
|
//
|
||||||
// Convert vesting stake amount to USDC decimals.
|
// // Convert vesting stake amount to USDC decimals.
|
||||||
if (_stakeIndex >= 1e6) {
|
// if (_stakeIndex >= 1e6) {
|
||||||
_amount = _amount / 1e12;
|
// _amount = _amount / 1e12;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
// uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
|
||||||
if (poolBalance < _amount) revert InsufficientRewards();
|
// if (poolBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
// withdrawLiabilities is in 1e18, deduct original amount
|
// // withdrawLiabilities is in 1e18, deduct original amount
|
||||||
withdrawLiabilities -= stake.amount;
|
// withdrawLiabilities -= stake.amount;
|
||||||
stake.amount = 0;
|
// stake.amount = 0;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
// IERC20(pool.tokenAddress).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
// emit StakeWithdrawn(msg.sender, _amount, _stakeIndex);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Revert if no matching stake with non-zero amount was found
|
// // Revert if no matching stake with non-zero amount was found
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* @notice Withdraws vesting tokens after cooldown period
|
// * @notice Withdraws vesting tokens after cooldown period
|
||||||
* @param _vestingId The vesting ID to withdraw
|
// * @param _vestingId The vesting ID to withdraw
|
||||||
*/
|
// */
|
||||||
function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
// function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
||||||
WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
// WithdrawVesting[] storage userVestings = withdrawVestingActual[msg.sender];
|
||||||
if (userVestings.length == 0) revert NoStakesAvailable();
|
// if (userVestings.length == 0) revert NoStakesAvailable();
|
||||||
|
//
|
||||||
for (uint256 i = 0; i < userVestings.length; ++i) {
|
// for (uint256 i = 0; i < userVestings.length; ++i) {
|
||||||
WithdrawVesting storage vestingWithdraw = userVestings[i];
|
// WithdrawVesting storage vestingWithdraw = userVestings[i];
|
||||||
if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
// if (vestingWithdraw.vestingId == _vestingId && vestingWithdraw.amount != 0) {
|
||||||
if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
// if (block.timestamp < vestingWithdraw.unlockTime) revert StakeLocked();
|
||||||
|
//
|
||||||
uint256 _amount = vestingWithdraw.amount;
|
// uint256 _amount = vestingWithdraw.amount;
|
||||||
address _token = vestingWithdraw.token;
|
// address _token = vestingWithdraw.token;
|
||||||
|
//
|
||||||
// Check contract has sufficient balance
|
// // Check contract has sufficient balance
|
||||||
uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
// uint256 tokenBalance = IERC20(_token).balanceOf(address(this));
|
||||||
if (tokenBalance < _amount) revert InsufficientRewards();
|
// if (tokenBalance < _amount) revert InsufficientRewards();
|
||||||
|
//
|
||||||
// Update state before external calls
|
// // Update state before external calls
|
||||||
vestingWithdraw.amount = 0;
|
// vestingWithdraw.amount = 0;
|
||||||
|
//
|
||||||
// Decrement withdraw vesting liabilities for this token
|
// // Decrement withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] -= _amount;
|
// withdrawVestingLiabilities[_token] -= _amount;
|
||||||
|
//
|
||||||
// Transfer tokens
|
// // Transfer tokens
|
||||||
IERC20(_token).safeTransfer(msg.sender, _amount);
|
// IERC20(_token).safeTransfer(msg.sender, _amount);
|
||||||
emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
// emit StakeWithdrawn(msg.sender, _amount, _vestingId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
revert StakeNotFound();
|
// revert StakeNotFound();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function compoundAllRewards() external {
|
// function compoundAllRewards() external {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < stakes[msg.sender].length; ++i) {
|
||||||
Stake storage stake = stakes[msg.sender][i];
|
// Stake storage stake = stakes[msg.sender][i];
|
||||||
if (stake.amount > 0) {
|
// if (stake.amount > 0) {
|
||||||
uint rewards = getPoolRewards(msg.sender, i);
|
// uint rewards = getPoolRewards(msg.sender, i);
|
||||||
totalReward = totalReward + rewards;
|
// totalReward = totalReward + rewards;
|
||||||
stake.lastClaimed = block.timestamp;
|
// stake.lastClaimed = block.timestamp;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
// if (totalReward <= minStakeLock) revert NotEnoughToCompound();
|
||||||
|
//
|
||||||
// Check if user has a fixed reward rate set
|
// // Check if user has a fixed reward rate set
|
||||||
uint256 finalRewardRate;
|
// uint256 finalRewardRate;
|
||||||
if (addressFixedRate[msg.sender] > 0) {
|
// if (addressFixedRate[msg.sender] > 0) {
|
||||||
// Use the fixed rate
|
// // Use the fixed rate
|
||||||
finalRewardRate = addressFixedRate[msg.sender];
|
// finalRewardRate = addressFixedRate[msg.sender];
|
||||||
} else {
|
// } else {
|
||||||
// Default logic, restake = false
|
// // Default logic, restake = false
|
||||||
finalRewardRate = getUserRewardRate(msg.sender, false);
|
// finalRewardRate = getUserRewardRate(msg.sender, false);
|
||||||
}
|
// }
|
||||||
|
|
||||||
stakes[msg.sender].push(Stake({
|
// stakes[msg.sender].push(Stake({
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
dailyRewardRate: finalRewardRate,
|
// dailyRewardRate: finalRewardRate,
|
||||||
unlockTime: block.timestamp + pool.lockupPeriod,
|
// unlockTime: block.timestamp + pool.lockupPeriod,
|
||||||
complete: false
|
// complete: false
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
pool.totalStaked = pool.totalStaked + totalReward;
|
// pool.totalStaked = pool.totalStaked + totalReward;
|
||||||
emit CompoundRewards(msg.sender, totalReward);
|
// emit CompoundRewards(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createVesting(address _token, uint256 _amount) external {
|
// function createVesting(address _token, uint256 _amount) external {
|
||||||
if (_amount == 0) revert InvalidAmount();
|
// if (_amount == 0) revert InvalidAmount();
|
||||||
address oracle = priceOracles[_token];
|
// address oracle = priceOracles[_token];
|
||||||
if (oracle == address(0)) revert PriceOracleNotSet();
|
// if (oracle == address(0)) revert PriceOracleNotSet();
|
||||||
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
// IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
|
||||||
|
|
||||||
uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
// uint256 bonus = (_amount * BONUS_PERCENTAGE) / 100;
|
||||||
|
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * _amount) / 1e18;
|
||||||
if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
// if (usdPrice <= minStakeLock) revert AmountBelowMinimum();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
dollarsVested[msg.sender] += usdPrice;
|
// dollarsVested[msg.sender] += usdPrice;
|
||||||
// Update token's vestedTotal
|
// // Update token's vestedTotal
|
||||||
vestedTotal[_token] += _amount;
|
// vestedTotal[_token] += _amount;
|
||||||
|
|
||||||
|
|
||||||
vestings[msg.sender].push(Vesting({
|
// vestings[msg.sender].push(Vesting({
|
||||||
amount: _amount,
|
// amount: _amount,
|
||||||
bonus: bonus,
|
// bonus: bonus,
|
||||||
lockedUntil: block.timestamp + lockupDuration,
|
// lockedUntil: block.timestamp + lockupDuration,
|
||||||
claimedAmount: 0,
|
// claimedAmount: 0,
|
||||||
claimedBonus: 0,
|
// claimedBonus: 0,
|
||||||
lastClaimed: block.timestamp,
|
// lastClaimed: block.timestamp,
|
||||||
createdAt: block.timestamp,
|
// createdAt: block.timestamp,
|
||||||
token: _token,
|
// token: _token,
|
||||||
complete: false,
|
// complete: false,
|
||||||
usdAmount: usdPrice
|
// usdAmount: usdPrice
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit VestingCreated(msg.sender, _amount, bonus);
|
// emit VestingCreated(msg.sender, _amount, bonus);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) {
|
||||||
Vesting storage vesting = vestings[_user][_vestingIndex];
|
Vesting storage vesting = vestings[_user][_vestingIndex];
|
||||||
@@ -993,124 +993,124 @@ function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
// function claimVesting(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
if (vesting.complete) revert StakeComplete();
|
// if (vesting.complete) revert StakeComplete();
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim == 0) revert NothingToClaim();
|
// if (amountToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
vestedTotal[vesting.token] -= amountToClaim;
|
// vestedTotal[vesting.token] -= amountToClaim;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: amountToClaim,
|
// amount: amountToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: vesting.token
|
// token: vesting.token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
// withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
||||||
|
|
||||||
emit VestingClaimed(msg.sender, amountToClaim, 0);
|
// emit VestingClaimed(msg.sender, amountToClaim, 0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function claimAllVestingByToken(address _token) external nonReentrant {
|
// function claimAllVestingByToken(address _token) external nonReentrant {
|
||||||
uint256 totalReward = 0;
|
// uint256 totalReward = 0;
|
||||||
uint256 vestingsProcessed = 0;
|
// uint256 vestingsProcessed = 0;
|
||||||
|
|
||||||
for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
// for (uint256 i = 0; i < vestings[msg.sender].length; ++i) {
|
||||||
Vesting storage vesting = vestings[msg.sender][i];
|
// Vesting storage vesting = vestings[msg.sender][i];
|
||||||
|
|
||||||
if (vesting.token == _token && !vesting.complete) {
|
// if (vesting.token == _token && !vesting.complete) {
|
||||||
uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
// uint256 maxClaim = getUnlockedVesting(msg.sender, i);
|
||||||
if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
// if (maxClaim < vesting.claimedAmount) revert InvalidClaimAmount();
|
||||||
|
|
||||||
uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
// uint256 amountToClaim = maxClaim - vesting.claimedAmount;
|
||||||
if (amountToClaim > 0) {
|
// if (amountToClaim > 0) {
|
||||||
vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
// vesting.claimedAmount = vesting.claimedAmount + amountToClaim;
|
||||||
totalReward = totalReward + amountToClaim;
|
// totalReward = totalReward + amountToClaim;
|
||||||
vesting.lastClaimed = block.timestamp;
|
// vesting.lastClaimed = block.timestamp;
|
||||||
|
|
||||||
// Mark vesting as complete if fully claimed
|
// // Mark vesting as complete if fully claimed
|
||||||
if (vesting.claimedAmount >= vesting.amount) {
|
// if (vesting.claimedAmount >= vesting.amount) {
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
vestingsProcessed++;
|
// vestingsProcessed++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (totalReward == 0) revert NothingToClaim();
|
// if (totalReward == 0) revert NothingToClaim();
|
||||||
|
|
||||||
// Update user's dollarsVested
|
// // Update user's dollarsVested
|
||||||
if (dollarsVested[msg.sender] > 0) {
|
// if (dollarsVested[msg.sender] > 0) {
|
||||||
uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
// uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18;
|
||||||
if (usdPrice >= dollarsVested[msg.sender]) {
|
// if (usdPrice >= dollarsVested[msg.sender]) {
|
||||||
dollarsVested[msg.sender] = 0;
|
// dollarsVested[msg.sender] = 0;
|
||||||
} else {
|
// } else {
|
||||||
dollarsVested[msg.sender] -= usdPrice;
|
// dollarsVested[msg.sender] -= usdPrice;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update vesting total
|
// // Update vesting total
|
||||||
vestedTotal[_token] -= totalReward;
|
// vestedTotal[_token] -= totalReward;
|
||||||
|
//
|
||||||
// Add vesting claims to cooldown queue
|
// // Add vesting claims to cooldown queue
|
||||||
withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
// withdrawVestingActual[msg.sender].push(WithdrawVesting({
|
||||||
vestingId: withdrawVestingCounterActual++,
|
// vestingId: withdrawVestingCounterActual++,
|
||||||
amount: totalReward,
|
// amount: totalReward,
|
||||||
unlockTime: block.timestamp + unlockDelay,
|
// unlockTime: block.timestamp + unlockDelay,
|
||||||
token: _token
|
// token: _token
|
||||||
}));
|
// }));
|
||||||
|
//
|
||||||
// Increment withdraw vesting liabilities for this token
|
// // Increment withdraw vesting liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] += totalReward;
|
// withdrawVestingLiabilities[_token] += totalReward;
|
||||||
|
|
||||||
emit RewardClaimed(msg.sender, totalReward);
|
// emit RewardClaimed(msg.sender, totalReward);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
// function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
||||||
Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
// Vesting storage vesting = vestings[msg.sender][_vestingIndex];
|
||||||
uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
// uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex);
|
||||||
|
|
||||||
if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
// if (maxBonus < vesting.claimedBonus) revert InvalidClaimAmount();
|
||||||
uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
// uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
||||||
if (bonusToClaim == 0) revert NothingToClaim();
|
// if (bonusToClaim == 0) revert NothingToClaim();
|
||||||
|
|
||||||
vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
// vesting.claimedBonus = vesting.claimedBonus + bonusToClaim;
|
||||||
withdrawLiabilities += bonusToClaim;
|
// withdrawLiabilities += bonusToClaim;
|
||||||
|
|
||||||
// IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
// // IERC20(vesting.token).safeTransfer(msg.sender, bonusToClaim);
|
||||||
|
|
||||||
// Create temporary the stake for the user to delay withdraw.
|
// // Create temporary the stake for the user to delay withdraw.
|
||||||
// Add 1e6 to the vesting index to distinguish them from normal stakes.
|
// // Add 1e6 to the vesting index to distinguish them from normal stakes.
|
||||||
withdrawStake[msg.sender].push(WithdrawStake({
|
// withdrawStake[msg.sender].push(WithdrawStake({
|
||||||
stakeId: _vestingIndex + 1e6,
|
// stakeId: _vestingIndex + 1e6,
|
||||||
amount: bonusToClaim,
|
// amount: bonusToClaim,
|
||||||
unlockTime: block.timestamp + unlockDelay
|
// unlockTime: block.timestamp + unlockDelay
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
emit BonusClaimed(msg.sender, bonusToClaim);
|
// emit BonusClaimed(msg.sender, bonusToClaim);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
function setPriceOracle(address _token, address _oracle) external onlyOwner {
|
||||||
priceOracles[_token] = _oracle;
|
priceOracles[_token] = _oracle;
|
||||||
@@ -1388,6 +1388,55 @@ function withdrawVestingToken(uint256 _vestingId) external nonReentrant {
|
|||||||
emit StakeSold(seller, msg.sender, sellStakeEntry.price, _stakeId);
|
emit StakeSold(seller, msg.sender, sellStakeEntry.price, _stakeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Admin function to clear all sellStakes for a specific address
|
||||||
|
/// @dev Only callable by contract owners. Restores original stakes and removes all sell listings.
|
||||||
|
/// @param _address The address whose sellStakes should be cleared
|
||||||
|
function clearAllSellStakes(address _address) external onlyOwner {
|
||||||
|
|
||||||
|
// First, collect all stakeIds for this address from the sellStakeKeys array
|
||||||
|
uint256[] memory stakeIdsToRemove = new uint256[](sellStakeKeys.length);
|
||||||
|
uint256 stakeCount = 0;
|
||||||
|
|
||||||
|
// Find all stakeIds belonging to the target address
|
||||||
|
for (uint256 i = 0; i < sellStakeKeys.length; i++) {
|
||||||
|
if (sellStakeKeys[i].seller == _address) {
|
||||||
|
stakeIdsToRemove[stakeCount] = sellStakeKeys[i].stakeId;
|
||||||
|
stakeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each stake found
|
||||||
|
for (uint256 i = 0; i < stakeCount; i++) {
|
||||||
|
uint256 stakeId = stakeIdsToRemove[i];
|
||||||
|
SellStake storage sellStakeEntry = sellStakes[_address][stakeId];
|
||||||
|
|
||||||
|
// Skip if already cleared
|
||||||
|
if (sellStakeEntry.amount == 0) continue;
|
||||||
|
|
||||||
|
// Restore the original stake
|
||||||
|
Stake storage originalStake = stakes[_address][stakeId];
|
||||||
|
originalStake.amount = sellStakeEntry.amount;
|
||||||
|
|
||||||
|
// Clear the sell stake entry
|
||||||
|
delete sellStakes[_address][stakeId];
|
||||||
|
|
||||||
|
// Remove from sellStakeKeys array using swap-and-pop
|
||||||
|
uint256 keyIndex = sellStakeKeyIndex[_address][stakeId];
|
||||||
|
uint256 lastIndex = sellStakeKeys.length - 1;
|
||||||
|
|
||||||
|
if (keyIndex != lastIndex) {
|
||||||
|
SellStakeKey memory lastKey = sellStakeKeys[lastIndex];
|
||||||
|
sellStakeKeys[keyIndex] = lastKey;
|
||||||
|
sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = keyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
sellStakeKeys.pop();
|
||||||
|
delete sellStakeKeyIndex[_address][stakeId];
|
||||||
|
|
||||||
|
emit StakeSaleCancelled(_address, stakeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @notice Returns all active sell stakes with their keys and pending rewards.
|
/// @notice Returns all active sell stakes with their keys and pending rewards.
|
||||||
/// @return sellers Array of seller addresses for each stake
|
/// @return sellers Array of seller addresses for each stake
|
||||||
/// @return stakeIds Array of stake IDs corresponding to each seller
|
/// @return stakeIds Array of stake IDs corresponding to each seller
|
||||||
|
|||||||
Reference in New Issue
Block a user