shortened some reverts to reduce size

This commit is contained in:
2025-07-03 10:19:37 -04:00
parent c667dc197b
commit ace13dea70
2 changed files with 832 additions and 706 deletions

View File

@@ -209,6 +209,10 @@ contract PacaFinanceWithBoostAndScheduleUSDC is Initializable, ReentrancyGuardUp
unlockDelay = _delay; unlockDelay = _delay;
} }
function updatePoolRewards(uint256 _amount) external onlyOwner {
pool.totalRewards = _amount;
}
function updateRestakeBonus(uint256 _newBonus) external onlyOwner { function updateRestakeBonus(uint256 _newBonus) external onlyOwner {
restakeBonus = _newBonus; restakeBonus = _newBonus;
} }
@@ -310,31 +314,31 @@ contract PacaFinanceWithBoostAndScheduleUSDC is Initializable, ReentrancyGuardUp
emit FundsWithdrawn(msg.sender, _token, _amount); emit FundsWithdrawn(msg.sender, _token, _amount);
} }
function setUnlockScheduleByPercentage( // function setUnlockScheduleByPercentage(
address _token, // address _token,
uint256 _lockTime, // Total lock time in seconds // uint256 _lockTime, // Total lock time in seconds
uint256 _percentagePerStep // Percentage unlocked per step (in basis points, e.g., 100 = 1%) // uint256 _percentagePerStep // Percentage unlocked per step (in basis points, e.g., 100 = 1%)
) external onlyOwner { // ) external onlyOwner {
require(_lockTime != 0, "Lock time must be greater than zero"); // require(_lockTime != 0, "Lock time must be greater than zero");
require(_percentagePerStep != 0, "Percentage per step must be greater than zero"); // require(_percentagePerStep != 0, "Percentage per step must be greater than zero");
uint256 totalPercentage = 10000; // 100% in basis points // uint256 totalPercentage = 10000; // 100% in basis points
require(totalPercentage % _percentagePerStep == 0, "Percentage must divide 100% evenly"); // require(totalPercentage % _percentagePerStep == 0, "Percentage must divide 100% evenly");
uint256 steps = totalPercentage / _percentagePerStep; // Number of steps // uint256 steps = totalPercentage / _percentagePerStep; // Number of steps
uint256 stepTime = _lockTime / steps; // Time interval per step // uint256 stepTime = _lockTime / steps; // Time interval per step
delete unlockSchedules[_token]; // Clear existing schedule for this token // delete unlockSchedules[_token]; // Clear existing schedule for this token
for (uint256 i = 1; i <= steps; ++i) { // for (uint256 i = 1; i <= steps; ++i) {
unlockSchedules[_token].push(UnlockStep({ // unlockSchedules[_token].push(UnlockStep({
timeOffset: stepTime * i, // Time offset for this step // timeOffset: stepTime * i, // Time offset for this step
percentage: _percentagePerStep // percentage: _percentagePerStep
})); // }));
} // }
emit UnlockScheduleSet(_token); // emit UnlockScheduleSet(_token);
} // }
/// @notice Get the boost percentage for a given token amount /// @notice Get the boost percentage for a given token amount
function getBoost(uint256 depositedTokens) public view returns (uint256) { function getBoost(uint256 depositedTokens) public view returns (uint256) {
@@ -377,102 +381,189 @@ contract PacaFinanceWithBoostAndScheduleUSDC is Initializable, ReentrancyGuardUp
withdrawLiabilities -= clearedStakes; withdrawLiabilities -= clearedStakes;
} }
// function createStake(uint256 _amount) external { // /**
// // Scale up for wei comparison, USDC is 1e6 // * @dev Extends the lastClaimed and unlockTime for all stakes of a given address
// require(_amount * 1e12 > minStakeLock, "Amount must be greater minStakeLock"); // * @param _address The address whose stakes to extend
// * @param _seconds The number of seconds to add to lastClaimed and unlockTime
// // Transfer tokens from the user into the contract // */
// IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount); // function extendStakes(address _address, uint256 _seconds) external onlyBot {
// if (_seconds == 0) return; // Early exit for zero seconds
// // Check if user has a fixed reward rate set
// uint256 finalRewardRate; // Stake[] storage userStakes = stakes[_address];
// if (addressFixedRate[msg.sender] > 0) { // uint256 length = userStakes.length;
// // Use the fixed rate
// finalRewardRate = addressFixedRate[msg.sender]; // if (length == 0) return; // Early exit for no stakes
// } else {
// // Default logic, restake = false // // Cache the stake reference to avoid repeated array access
// finalRewardRate = getUserRewardRate(msg.sender, false); // for (uint256 i; i < length;) {
// Stake storage stake = userStakes[i];
// // Only extend active stakes with non-zero amounts
// if (!stake.complete && stake.amount > 0) {
// unchecked {
// stake.lastClaimed += _seconds;
// stake.unlockTime += _seconds;
// }
// }
// unchecked { ++i; }
// } // }
// // Create the stake
// stakes[msg.sender].push(Stake({
// amount: _amount,
// lastClaimed: block.timestamp,
// dailyRewardRate: finalRewardRate,
// unlockTime: block.timestamp + pool.lockupPeriod,
// complete: false
// }));
// // Update total staked
// pool.totalStaked += _amount;
// emit Staked(msg.sender, _amount);
// } // }
// /// @notice Restake an expired stake with a bonus daily reward // /**
// function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external { // * @dev Extends the lockedUntil, lastClaimed, and createdAt for all vestings of a given address
// require(_restakePercentage <= 100, "Invalid restake percentage"); // * @param _address The address whose vestings to extend
// Stake storage stake = stakes[msg.sender][_stakeIndex]; // * @param _seconds The number of seconds to add to the timestamps
// // Ensure there is a stake to claim // */
// require(stake.amount != 0, "No amount to claim"); // function extendVestings(address _address, uint256 _seconds) external onlyBot {
// require(block.timestamp >= stake.unlockTime, "Stake is still locked"); // if (_seconds == 0) return; // Early exit for zero seconds
// uint256 _amount = stake.amount; // Vesting[] storage userVestings = vestings[_address];
// uint rewards = getPoolRewards(msg.sender, _stakeIndex); // uint256 length = userVestings.length;
// _amount = _amount + rewards;
// if (length == 0) return; // Early exit for no vestings
// uint256 restake_amount = (_amount * _restakePercentage) / 100;
// uint256 withdraw_amount = _amount - restake_amount; // // Cache the vesting reference to avoid repeated array access
// for (uint256 i; i < length;) {
// // Update state before external calls // Vesting storage vesting = userVestings[i];
// stake.amount = 0;
// stake.complete = true; // // Only extend active vestings with non-zero amounts
// if (!vesting.complete && vesting.amount > 0) {
// // Process withdraw // unchecked {
// if (withdraw_amount > 0) { // vesting.lockedUntil += _seconds;
// withdrawLiabilities += withdraw_amount; // vesting.lastClaimed += _seconds;
// vesting.createdAt += _seconds;
// if (pool.totalStaked >= withdraw_amount) { // }
// pool.totalStaked -= withdraw_amount;
// } else {
// pool.totalStaked = 0;
// } // }
// // Create temporary the stake for the user to delay withdraw
// withdrawStake[msg.sender].push(WithdrawStake({ // unchecked { ++i; }
// stakeId: _stakeIndex,
// amount: withdraw_amount,
// unlockTime: block.timestamp + unlockDelay
// }));
// // Emit a detailed event
// emit RewardClaimed(msg.sender, withdraw_amount);
// }
// // Process restake
// if (restake_amount > 0) {
// // Check if user has a fixed reward rate set
// uint256 finalRewardRate;
// if (addressFixedRate[msg.sender] > 0) {
// // Use the fixed rate
// finalRewardRate = addressFixedRate[msg.sender];
// } else {
// // restake = true
// finalRewardRate = getUserRewardRate(msg.sender, true);
// }
// stakes[msg.sender].push(Stake({
// amount: restake_amount,
// lastClaimed: block.timestamp,
// dailyRewardRate: finalRewardRate,
// unlockTime: block.timestamp + pool.lockupPeriod,
// complete: false
// }));
// emit Staked(msg.sender, restake_amount);
// } // }
// } // }
// function adminClearSellStake(address _seller, uint256 _stakeId) external onlyOwner {
// SellStake storage sellStakeEntry = sellStakes[_seller][_stakeId];
// require(sellStakeEntry.amount != 0, "Sell stake not found");
// // Access the original stake.
// Stake storage stake = stakes[_seller][_stakeId];
// require(stake.amount == 0, "Stake not in sell state");
// // Restore the original stake's amount.
// // stake.amount = sellStakeEntry.amount;
// delete sellStakes[_seller][_stakeId];
// // Remove the key from the iteration array using swap-and-pop.
// uint256 index = sellStakeKeyIndex[_seller][_stakeId];
// uint256 lastIndex = sellStakeKeys.length - 1;
// if (index != lastIndex) {
// SellStakeKey memory lastKey = sellStakeKeys[lastIndex];
// sellStakeKeys[index] = lastKey;
// sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index;
// }
// sellStakeKeys.pop();
// delete sellStakeKeyIndex[_seller][_stakeId];
// emit StakeSaleCancelled(_seller, _stakeId);
// }
function createStake(uint256 _amount) external {
// Scale up for wei comparison, USDC is 1e6
require(_amount * 1e12 > minStakeLock, "Amount must be greater minStakeLock");
// Transfer tokens from the user into the contract
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
// Check if user has a fixed reward rate set
uint256 finalRewardRate;
if (addressFixedRate[msg.sender] > 0) {
// Use the fixed rate
finalRewardRate = addressFixedRate[msg.sender];
} else {
// Default logic, restake = false
finalRewardRate = getUserRewardRate(msg.sender, false);
}
// Create the stake
stakes[msg.sender].push(Stake({
amount: _amount,
lastClaimed: block.timestamp,
dailyRewardRate: finalRewardRate,
unlockTime: block.timestamp + pool.lockupPeriod,
complete: false
}));
// Update total staked
pool.totalStaked += _amount;
emit Staked(msg.sender, _amount);
}
/// @notice Restake an expired stake with a bonus daily reward
function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
require(_restakePercentage <= 100, "Invalid restake percentage");
Stake storage stake = stakes[msg.sender][_stakeIndex];
// Ensure there is a stake to claim
require(stake.amount != 0, "No amount to claim");
require(block.timestamp >= stake.unlockTime, "Stake is still locked");
uint256 _amount = stake.amount;
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
_amount = _amount + rewards;
uint256 restake_amount = (_amount * _restakePercentage) / 100;
uint256 withdraw_amount = _amount - restake_amount;
// Update state before external calls
stake.amount = 0;
stake.complete = true;
// Process withdraw
if (withdraw_amount > 0) {
withdrawLiabilities += withdraw_amount;
if (pool.totalStaked >= withdraw_amount) {
pool.totalStaked -= withdraw_amount;
} else {
pool.totalStaked = 0;
}
// Create temporary the stake for the user to delay withdraw
withdrawStake[msg.sender].push(WithdrawStake({
stakeId: _stakeIndex,
amount: withdraw_amount,
unlockTime: block.timestamp + unlockDelay
}));
// Emit a detailed event
emit RewardClaimed(msg.sender, withdraw_amount);
}
// Process restake
if (restake_amount > 0) {
// Check if user has a fixed reward rate set
uint256 finalRewardRate;
if (addressFixedRate[msg.sender] > 0) {
// Use the fixed rate
finalRewardRate = addressFixedRate[msg.sender];
} else {
// restake = true
finalRewardRate = getUserRewardRate(msg.sender, true);
}
stakes[msg.sender].push(Stake({
amount: restake_amount,
lastClaimed: block.timestamp,
dailyRewardRate: finalRewardRate,
unlockTime: block.timestamp + pool.lockupPeriod,
complete: false
}));
emit Staked(msg.sender, restake_amount);
}
}
function createStakeForUser(address _user, uint256 _amount) external onlyOwner { function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
require(_amount != 0, "Invalid amount"); require(_amount != 0, "Invalid amount");
@@ -536,186 +627,186 @@ contract PacaFinanceWithBoostAndScheduleUSDC 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;
// } }
// } }
// require(totalReward != 0, "No rewards to claim"); require(totalReward != 0, "No rewards to claim");
// require(pool.totalRewards >= totalReward, "Insufficient rewards in the pool"); require(pool.totalRewards >= totalReward, "Insufficient rewards in the pool");
// 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
// require(_stakeIndex < stakes[msg.sender].length, "Invalid stake index"); require(_stakeIndex < stakes[msg.sender].length, "Invalid stake index");
// // 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
// require(_amount != 0, "No amount to claim"); require(_amount != 0, "No amount to claim");
// // Ensure the stake is unlocked (if using lockup periods) // Ensure the stake is unlocked (if using lockup periods)
// require(block.timestamp >= stake.unlockTime, "Stake is still locked"); require(block.timestamp >= stake.unlockTime, "Stake is still locked");
// // 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];
// require(userStakes.length > 0, "No stakes available for withdrawal"); require(userStakes.length > 0, "No stakes available for withdrawal");
// 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) {
// require(block.timestamp >= stake.unlockTime, "Withdraw Stake is still locked"); require(block.timestamp >= stake.unlockTime, "Withdraw Stake is still locked");
// 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));
// require(poolBalance >= _amount, "Insufficient rewards in the pool"); require(poolBalance >= _amount, "Insufficient rewards in the pool");
// // 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("Invalid stake index or already withdrawn"); revert("Invalid stake index or already withdrawn");
// } }
// 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;
// } }
// } }
// require(totalReward > minStakeLock, "Not enough to compound"); require(totalReward > minStakeLock, "Not enough to compound");
// // 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 {
// require(_amount != 0, "Amount must be greater than zero"); require(_amount != 0, "Amount must be greater than zero");
// address oracle = priceOracles[_token]; address oracle = priceOracles[_token];
// require(oracle != address(0), "Price oracle not set for this token"); require(oracle != address(0), "Price oracle not set for this token");
// 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;
// require(usdPrice > minStakeLock, "Amount must be greater minStakeLock"); require(usdPrice > minStakeLock, "Amount must be greater minStakeLock");
// // 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];
@@ -778,108 +869,108 @@ contract PacaFinanceWithBoostAndScheduleUSDC is Initializable, ReentrancyGuardUp
} }
// 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];
// require(vesting.complete == false, "Stake is Complete"); require(vesting.complete == false, "Stake is Complete");
// uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex); uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
// require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); require(maxClaim >= vesting.claimedAmount, "Invalid claim amount");
// uint256 amountToClaim = maxClaim - vesting.claimedAmount; uint256 amountToClaim = maxClaim - vesting.claimedAmount;
// require(amountToClaim != 0, "No vested amount to claim"); require(amountToClaim != 0, "No vested amount to claim");
// 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;
// IERC20(vesting.token).safeTransfer(msg.sender, amountToClaim); IERC20(vesting.token).safeTransfer(msg.sender, 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);
// require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); require(maxClaim >= vesting.claimedAmount, "Invalid claim amount");
// 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++;
// } }
// } }
// } }
// require(totalReward != 0, "No rewards to claim"); require(totalReward != 0, "No rewards to claim");
// // 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;
// } }
// } }
// // Ensure the contract has enough balance to fulfill the claim // Ensure the contract has enough balance to fulfill the claim
// uint256 poolBalance = IERC20(_token).balanceOf(address(this)); uint256 poolBalance = IERC20(_token).balanceOf(address(this));
// require(poolBalance >= totalReward, "Insufficient rewards in the pool"); require(poolBalance >= totalReward, "Insufficient rewards in the pool");
// // Update vesting total // Update vesting total
// vestedTotal[_token] -= totalReward; vestedTotal[_token] -= totalReward;
// // Transfer the aggregated reward // Transfer the aggregated reward
// IERC20(_token).safeTransfer(msg.sender, totalReward); IERC20(_token).safeTransfer(msg.sender, 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);
// require(maxBonus >= vesting.claimedBonus, "Invalid claim amount"); require(maxBonus >= vesting.claimedBonus, "Invalid claim amount");
// uint256 bonusToClaim = maxBonus - vesting.claimedBonus; uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
// require(bonusToClaim != 0, "No vested amount to claim"); require(bonusToClaim != 0, "No vested amount to claim");
// 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;

View File

@@ -138,7 +138,7 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
_; _;
} }
modifier onlyBot() { modifier onlyBot() {
require(authorizedBots[msg.sender], "Caller is not an authorized bot"); require(authorizedBots[msg.sender], "Not bot");
_; _;
} }
@@ -209,6 +209,10 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
unlockDelay = _delay; unlockDelay = _delay;
} }
function updatePoolRewards(uint256 _amount) external onlyOwner {
pool.totalRewards = _amount;
}
function updateRestakeBonus(uint256 _newBonus) external onlyOwner { function updateRestakeBonus(uint256 _newBonus) external onlyOwner {
restakeBonus = _newBonus; restakeBonus = _newBonus;
} }
@@ -377,157 +381,188 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
withdrawLiabilities -= clearedStakes; withdrawLiabilities -= clearedStakes;
} }
/** // /**
* @dev Extends the lastClaimed and unlockTime for all stakes of a given address // * @dev Extends the lastClaimed and unlockTime for all stakes of a given address
* @param _address The address whose stakes to extend // * @param _address The address whose stakes to extend
* @param _seconds The number of seconds to add to lastClaimed and unlockTime // * @param _seconds The number of seconds to add to lastClaimed and unlockTime
*/ // */
function extendStakes(address _address, uint256 _seconds) external onlyBot { // function extendStakes(address _address, uint256 _seconds) external onlyBot {
if (_seconds == 0) return; // Early exit for zero seconds // if (_seconds == 0) return; // Early exit for zero seconds
Stake[] storage userStakes = stakes[_address]; // Stake[] storage userStakes = stakes[_address];
uint256 length = userStakes.length; // uint256 length = userStakes.length;
if (length == 0) return; // Early exit for no stakes // if (length == 0) return; // Early exit for no stakes
// Cache the stake reference to avoid repeated array access // // Cache the stake reference to avoid repeated array access
for (uint256 i; i < length;) { // for (uint256 i; i < length;) {
Stake storage stake = userStakes[i]; // Stake storage stake = userStakes[i];
// Only extend active stakes with non-zero amounts // // Only extend active stakes with non-zero amounts
if (!stake.complete && stake.amount > 0) { // if (!stake.complete && stake.amount > 0) {
unchecked { // unchecked {
stake.lastClaimed += _seconds; // stake.lastClaimed += _seconds;
stake.unlockTime += _seconds; // stake.unlockTime += _seconds;
} // }
// }
// unchecked { ++i; }
// }
// }
// /**
// * @dev Extends the lockedUntil, lastClaimed, and createdAt for all vestings of a given address
// * @param _address The address whose vestings to extend
// * @param _seconds The number of seconds to add to the timestamps
// */
// function extendVestings(address _address, uint256 _seconds) external onlyBot {
// if (_seconds == 0) return; // Early exit for zero seconds
// Vesting[] storage userVestings = vestings[_address];
// uint256 length = userVestings.length;
// if (length == 0) return; // Early exit for no vestings
// // Cache the vesting reference to avoid repeated array access
// for (uint256 i; i < length;) {
// Vesting storage vesting = userVestings[i];
// // Only extend active vestings with non-zero amounts
// if (!vesting.complete && vesting.amount > 0) {
// unchecked {
// vesting.lockedUntil += _seconds;
// vesting.lastClaimed += _seconds;
// vesting.createdAt += _seconds;
// }
// }
// unchecked { ++i; }
// }
// }
// function adminClearSellStake(address _seller, uint256 _stakeId) external onlyOwner {
// SellStake storage sellStakeEntry = sellStakes[_seller][_stakeId];
// require(sellStakeEntry.amount != 0, "Sell stake not found");
// // Access the original stake.
// Stake storage stake = stakes[_seller][_stakeId];
// require(stake.amount == 0, "Stake not in sell state");
// // Restore the original stake's amount.
// // stake.amount = sellStakeEntry.amount;
// delete sellStakes[_seller][_stakeId];
// // Remove the key from the iteration array using swap-and-pop.
// uint256 index = sellStakeKeyIndex[_seller][_stakeId];
// uint256 lastIndex = sellStakeKeys.length - 1;
// if (index != lastIndex) {
// SellStakeKey memory lastKey = sellStakeKeys[lastIndex];
// sellStakeKeys[index] = lastKey;
// sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index;
// }
// sellStakeKeys.pop();
// delete sellStakeKeyIndex[_seller][_stakeId];
// emit StakeSaleCancelled(_seller, _stakeId);
// }
function createStake(uint256 _amount) external {
require(_amount > minStakeLock, "Amount must be greater minStakeLock");
// Transfer tokens from the user into the contract
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
// Check if user has a fixed reward rate set
uint256 finalRewardRate;
if (addressFixedRate[msg.sender] > 0) {
// Use the fixed rate
finalRewardRate = addressFixedRate[msg.sender];
} else {
// Default logic, restake = false
finalRewardRate = getUserRewardRate(msg.sender, false);
}
// Create the stake
stakes[msg.sender].push(Stake({
amount: _amount,
lastClaimed: block.timestamp,
dailyRewardRate: finalRewardRate,
unlockTime: block.timestamp + pool.lockupPeriod,
complete: false
}));
// Update total staked
pool.totalStaked += _amount;
emit Staked(msg.sender, _amount);
}
/// @notice Restake an expired stake with a bonus daily reward
function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
require(_restakePercentage <= 100, "Invalid percentage");
Stake storage stake = stakes[msg.sender][_stakeIndex];
// Ensure there is a stake to claim
require(stake.amount != 0, "No amount to claim");
require(block.timestamp >= stake.unlockTime, "Stake is locked");
uint256 _amount = stake.amount;
uint rewards = getPoolRewards(msg.sender, _stakeIndex);
_amount = _amount + rewards;
uint256 restake_amount = (_amount * _restakePercentage) / 100;
uint256 withdraw_amount = _amount - restake_amount;
// Update state before external calls
stake.amount = 0;
stake.complete = true;
// Process withdraw
if (withdraw_amount > 0) {
withdrawLiabilities += withdraw_amount;
if (pool.totalStaked >= withdraw_amount) {
pool.totalStaked -= withdraw_amount;
} else {
pool.totalStaked = 0;
} }
// Create temporary the stake for the user to delay withdraw
unchecked { ++i; } withdrawStake[msg.sender].push(WithdrawStake({
stakeId: _stakeIndex,
amount: withdraw_amount,
unlockTime: block.timestamp + unlockDelay
}));
// Emit a detailed event
emit RewardClaimed(msg.sender, withdraw_amount);
}
// Process restake
if (restake_amount > 0) {
// Check if user has a fixed reward rate set
uint256 finalRewardRate;
if (addressFixedRate[msg.sender] > 0) {
// Use the fixed rate
finalRewardRate = addressFixedRate[msg.sender];
} else {
// restake = true
finalRewardRate = getUserRewardRate(msg.sender, true);
}
stakes[msg.sender].push(Stake({
amount: restake_amount,
lastClaimed: block.timestamp,
dailyRewardRate: finalRewardRate,
unlockTime: block.timestamp + pool.lockupPeriod,
complete: false
}));
emit Staked(msg.sender, restake_amount);
} }
} }
function adminClearSellStake(address _seller, uint256 _stakeId) external onlyOwner {
SellStake storage sellStakeEntry = sellStakes[_seller][_stakeId];
require(sellStakeEntry.amount != 0, "Sell stake not found");
// Access the original stake.
Stake storage stake = stakes[_seller][_stakeId];
require(stake.amount == 0, "Stake not in sell state");
// Restore the original stake's amount.
// stake.amount = sellStakeEntry.amount;
delete sellStakes[_seller][_stakeId];
// Remove the key from the iteration array using swap-and-pop.
uint256 index = sellStakeKeyIndex[_seller][_stakeId];
uint256 lastIndex = sellStakeKeys.length - 1;
if (index != lastIndex) {
SellStakeKey memory lastKey = sellStakeKeys[lastIndex];
sellStakeKeys[index] = lastKey;
sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index;
}
sellStakeKeys.pop();
delete sellStakeKeyIndex[_seller][_stakeId];
emit StakeSaleCancelled(_seller, _stakeId);
}
// function createStake(uint256 _amount) external {
// require(_amount > minStakeLock, "Amount must be greater minStakeLock");
// // Transfer tokens from the user into the contract
// IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), _amount);
// // Check if user has a fixed reward rate set
// uint256 finalRewardRate;
// if (addressFixedRate[msg.sender] > 0) {
// // Use the fixed rate
// finalRewardRate = addressFixedRate[msg.sender];
// } else {
// // Default logic, restake = false
// finalRewardRate = getUserRewardRate(msg.sender, false);
// }
// // Create the stake
// stakes[msg.sender].push(Stake({
// amount: _amount,
// lastClaimed: block.timestamp,
// dailyRewardRate: finalRewardRate,
// unlockTime: block.timestamp + pool.lockupPeriod,
// complete: false
// }));
// // Update total staked
// pool.totalStaked += _amount;
// emit Staked(msg.sender, _amount);
// }
// /// @notice Restake an expired stake with a bonus daily reward
// function restake(uint256 _stakeIndex, uint256 _restakePercentage) nonReentrant external {
// require(_restakePercentage <= 100, "Invalid restake percentage");
// Stake storage stake = stakes[msg.sender][_stakeIndex];
// // Ensure there is a stake to claim
// require(stake.amount != 0, "No amount to claim");
// require(block.timestamp >= stake.unlockTime, "Stake is still locked");
// uint256 _amount = stake.amount;
// uint rewards = getPoolRewards(msg.sender, _stakeIndex);
// _amount = _amount + rewards;
// uint256 restake_amount = (_amount * _restakePercentage) / 100;
// uint256 withdraw_amount = _amount - restake_amount;
// // Update state before external calls
// stake.amount = 0;
// stake.complete = true;
// // Process withdraw
// if (withdraw_amount > 0) {
// withdrawLiabilities += withdraw_amount;
// if (pool.totalStaked >= withdraw_amount) {
// pool.totalStaked -= withdraw_amount;
// } else {
// pool.totalStaked = 0;
// }
// // Create temporary the stake for the user to delay withdraw
// withdrawStake[msg.sender].push(WithdrawStake({
// stakeId: _stakeIndex,
// amount: withdraw_amount,
// unlockTime: block.timestamp + unlockDelay
// }));
// // Emit a detailed event
// emit RewardClaimed(msg.sender, withdraw_amount);
// }
// // Process restake
// if (restake_amount > 0) {
// // Check if user has a fixed reward rate set
// uint256 finalRewardRate;
// if (addressFixedRate[msg.sender] > 0) {
// // Use the fixed rate
// finalRewardRate = addressFixedRate[msg.sender];
// } else {
// // restake = true
// finalRewardRate = getUserRewardRate(msg.sender, true);
// }
// stakes[msg.sender].push(Stake({
// amount: restake_amount,
// lastClaimed: block.timestamp,
// dailyRewardRate: finalRewardRate,
// unlockTime: block.timestamp + pool.lockupPeriod,
// complete: false
// }));
// emit Staked(msg.sender, restake_amount);
// }
// }
function createStakeForUser(address _user, uint256 _amount) external onlyOwner { function createStakeForUser(address _user, uint256 _amount) external onlyOwner {
require(_amount != 0, "Invalid amount"); require(_amount != 0, "Invalid amount");
@@ -591,177 +626,177 @@ contract PacaFinanceWithBoostAndScheduleUSDT 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;
// } }
// } }
// require(totalReward != 0, "No rewards to claim"); require(totalReward != 0, "No rewards");
// require(pool.totalRewards >= totalReward, "Insufficient rewards in the pool"); require(pool.totalRewards >= totalReward, "Insufficient rewards");
// 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
// require(_stakeIndex < stakes[msg.sender].length, "Invalid stake index"); require(_stakeIndex < stakes[msg.sender].length, "Invalid stake index");
// // 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
// require(_amount != 0, "No amount to claim"); require(_amount != 0, "amount 0");
// // Ensure the stake is unlocked (if using lockup periods) // Ensure the stake is unlocked (if using lockup periods)
// require(block.timestamp >= stake.unlockTime, "Stake is still locked"); require(block.timestamp >= stake.unlockTime, "Stake locked");
// // 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];
// require(userStakes.length > 0, "No stakes available for withdrawal"); require(userStakes.length > 0, "No stakes");
// 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) {
// require(block.timestamp >= stake.unlockTime, "Withdraw Stake is still locked"); require(block.timestamp >= stake.unlockTime, "Withdraw locked");
// uint256 _amount = stake.amount; uint256 _amount = stake.amount;
// uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this)); uint256 poolBalance = IERC20(pool.tokenAddress).balanceOf(address(this));
// require(poolBalance >= _amount, "Insufficient rewards in the pool"); require(poolBalance >= _amount, "Insufficient rewards");
// // 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("Invalid stake index or already withdrawn"); revert("Invalid stake");
// } }
// 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;
// } }
// } }
// require(totalReward > minStakeLock, "Not enough to compound"); require(totalReward > minStakeLock, "Not enough to compound");
// // 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 {
// require(_amount != 0, "Amount must be greater than zero"); require(_amount != 0, "Amount must be greater than zero");
// address oracle = priceOracles[_token]; address oracle = priceOracles[_token];
// require(oracle != address(0), "Price oracle not set for this token"); require(oracle != address(0), "Oracle not set");
// 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;
// require(usdPrice > minStakeLock, "Amount must be greater minStakeLock"); require(usdPrice > minStakeLock, "Amount must be greater minStakeLock");
// // 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];
@@ -824,108 +859,108 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
} }
// 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];
// require(vesting.complete == false, "Stake is Complete"); require(vesting.complete == false, "Stake Complete");
// uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex); uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex);
// require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); require(maxClaim >= vesting.claimedAmount, "Invalid claim");
// uint256 amountToClaim = maxClaim - vesting.claimedAmount; uint256 amountToClaim = maxClaim - vesting.claimedAmount;
// require(amountToClaim != 0, "No vested amount to claim"); require(amountToClaim != 0, "Claim 0");
// 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;
// IERC20(vesting.token).safeTransfer(msg.sender, amountToClaim); IERC20(vesting.token).safeTransfer(msg.sender, 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);
// require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); require(maxClaim >= vesting.claimedAmount, "Invalid claim");
// 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++;
// } }
// } }
// } }
// require(totalReward != 0, "No rewards to claim"); require(totalReward != 0, "No rewards to claim");
// // 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;
// } }
// } }
// // Ensure the contract has enough balance to fulfill the claim // Ensure the contract has enough balance to fulfill the claim
// uint256 poolBalance = IERC20(_token).balanceOf(address(this)); uint256 poolBalance = IERC20(_token).balanceOf(address(this));
// require(poolBalance >= totalReward, "Insufficient rewards in the pool"); require(poolBalance >= totalReward, "Insufficient rewards");
// // Update vesting total // Update vesting total
// vestedTotal[_token] -= totalReward; vestedTotal[_token] -= totalReward;
// // Transfer the aggregated reward // Transfer the aggregated reward
// IERC20(_token).safeTransfer(msg.sender, totalReward); IERC20(_token).safeTransfer(msg.sender, 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);
// require(maxBonus >= vesting.claimedBonus, "Invalid claim amount"); require(maxBonus >= vesting.claimedBonus, "Invalid claim amount");
// uint256 bonusToClaim = maxBonus - vesting.claimedBonus; uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
// require(bonusToClaim != 0, "No vested amount to claim"); require(bonusToClaim != 0, "No claim");
// 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;
@@ -1039,7 +1074,7 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
return userStakes[i]; return userStakes[i];
} }
} }
revert("WithdrawStake with the specified stakeId not found for this user."); revert("WithdrawStake not found");
} }
/// @notice Function that lets you look up an addresss stake by vestingId. /// @notice Function that lets you look up an addresss stake by vestingId.
@@ -1053,7 +1088,7 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
return userStakes[i]; return userStakes[i];
} }
} }
revert("WithdrawStake with the specified stakeId not found for this user."); revert("WithdrawStake not found");
} }
/// @notice Function that returns an array of all the user's withdrawStakes. /// @notice Function that returns an array of all the user's withdrawStakes.
@@ -1069,11 +1104,11 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
/// @param price The price of the stake. /// @param price The price of the stake.
function sellStake(uint256 _stakeId, uint256 price) external { function sellStake(uint256 _stakeId, uint256 price) external {
Stake storage stake = stakes[msg.sender][_stakeId]; Stake storage stake = stakes[msg.sender][_stakeId];
require(!stake.complete, "Stake already complete"); require(!stake.complete, "Stake complete");
require(stake.amount != 0, "Stake amount is 0"); require(stake.amount != 0, "Amount 0");
// Ensure the stake isn't already on sale. // Ensure the stake isn't already on sale.
require(sellStakes[msg.sender][_stakeId].amount == 0, "Stake already on sale"); require(sellStakes[msg.sender][_stakeId].amount == 0, "Stake already on sale");
require(price >= (stake.amount * sellMin) / 100, "Price is too low"); require(price >= (stake.amount * sellMin) / 100, "Price too low");
// Create a SellStake entry directly in the mapping. // Create a SellStake entry directly in the mapping.
sellStakes[msg.sender][_stakeId] = SellStake({ sellStakes[msg.sender][_stakeId] = SellStake({
@@ -1100,11 +1135,11 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
/// @param _stakeId The stake ID to cancel the sale. /// @param _stakeId The stake ID to cancel the sale.
function cancelSellStake(uint256 _stakeId) external { function cancelSellStake(uint256 _stakeId) external {
SellStake storage sellStakeEntry = sellStakes[msg.sender][_stakeId]; SellStake storage sellStakeEntry = sellStakes[msg.sender][_stakeId];
require(sellStakeEntry.amount != 0, "Sell stake not found"); require(sellStakeEntry.amount != 0, "Stake not found");
// Access the original stake. // Access the original stake.
Stake storage stake = stakes[msg.sender][_stakeId]; Stake storage stake = stakes[msg.sender][_stakeId];
require(stake.amount == 0, "Stake not in sell state"); require(stake.amount == 0, "Stake not for sale");
// Restore the original stake's amount. // Restore the original stake's amount.
stake.amount = sellStakeEntry.amount; stake.amount = sellStakeEntry.amount;
@@ -1130,8 +1165,8 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
/// @param newPrice The new price of the stake. /// @param newPrice The new price of the stake.
function updateSellStake(uint256 _stakeId, uint256 newPrice) external { function updateSellStake(uint256 _stakeId, uint256 newPrice) external {
SellStake storage sellStakeEntry = sellStakes[msg.sender][_stakeId]; SellStake storage sellStakeEntry = sellStakes[msg.sender][_stakeId];
require(sellStakeEntry.amount != 0, "Sell stake not found"); require(sellStakeEntry.amount != 0, "Stake not found");
require(newPrice >= (sellStakeEntry.amount * sellMin) / 100, "New price is too low"); require(newPrice >= (sellStakeEntry.amount * sellMin) / 100, "New price too low");
sellStakeEntry.bonusAmount = (newPrice * sellKickBack) / 100; sellStakeEntry.bonusAmount = (newPrice * sellKickBack) / 100;
sellStakeEntry.price = newPrice; sellStakeEntry.price = newPrice;
@@ -1149,7 +1184,7 @@ contract PacaFinanceWithBoostAndScheduleUSDT is Initializable, ReentrancyGuardUp
/// @param _stakeId The original stake id associated with the sell stake. /// @param _stakeId The original stake id associated with the sell stake.
function buySellStake(address seller, uint256 _stakeId) external nonReentrant { function buySellStake(address seller, uint256 _stakeId) external nonReentrant {
SellStake storage sellStakeEntry = sellStakes[seller][_stakeId]; SellStake storage sellStakeEntry = sellStakes[seller][_stakeId];
require(sellStakeEntry.amount != 0, "Sell stake not available"); require(sellStakeEntry.amount != 0, "Stake not available");
// Transfer the sale price from the buyer to this contract. // Transfer the sale price from the buyer to this contract.
IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), sellStakeEntry.price); IERC20(pool.tokenAddress).safeTransferFrom(msg.sender, address(this), sellStakeEntry.price);