Multiple claims + usdc tracking in withdrawliabilities
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,6 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
|
|
||||||
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
||||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||||
|
|
||||||
@@ -41,6 +40,7 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
uint256 estDaysRemaining;
|
uint256 estDaysRemaining;
|
||||||
uint256 currentTreasuryTvl;
|
uint256 currentTreasuryTvl;
|
||||||
uint256 totalLiability; // Snapshot of totalBigStakes at epoch end
|
uint256 totalLiability; // Snapshot of totalBigStakes at epoch end
|
||||||
|
uint256 paybackPercent; // Payback percentage used for this epoch (scaled by 10000)
|
||||||
uint256 unlockPercentage; // Calculated unlock percentage (scaled by 10000)
|
uint256 unlockPercentage; // Calculated unlock percentage (scaled by 10000)
|
||||||
uint256 timestamp; // When this epoch ended
|
uint256 timestamp; // When this epoch ended
|
||||||
}
|
}
|
||||||
@@ -90,8 +90,8 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
uint256 private stakeIdCounter;
|
uint256 private stakeIdCounter;
|
||||||
uint256 private vestingStakeIdCounter;
|
uint256 private vestingStakeIdCounter;
|
||||||
|
|
||||||
// Track total withdraw vesting liabilities by token address
|
// Track total withdraw liabilities by token address (all tokens including BSC_TOKEN)
|
||||||
mapping(address => uint256) public withdrawVestingLiabilities;
|
mapping(address => uint256) public withdrawLiabilities;
|
||||||
|
|
||||||
// Epoch-based staking variables
|
// Epoch-based staking variables
|
||||||
mapping(uint256 => Epoch) public epochs;
|
mapping(uint256 => Epoch) public epochs;
|
||||||
@@ -107,13 +107,13 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
uint256 public marketplaceMin; // Minimum value for listings (in USD, e.g., 25 * 1e18 = $25)
|
uint256 public marketplaceMin; // Minimum value for listings (in USD, e.g., 25 * 1e18 = $25)
|
||||||
uint256 public cancellationFee; // Fee percentage for cancelling listings (e.g., 500 = 5%)
|
uint256 public cancellationFee; // Fee percentage for cancelling listings (e.g., 500 = 5%)
|
||||||
mapping(address => uint256) public marketplace_sales; // Track total sales per user
|
mapping(address => uint256) public marketplace_sales; // Track total sales per user
|
||||||
|
mapping(address => uint256) public pendingSellStakes; // Track total value of pending sell stakes per user
|
||||||
SellStakeKey[] public sellStakeKeys; // Array for iteration over active sell stakes
|
SellStakeKey[] public sellStakeKeys; // Array for iteration over active sell stakes
|
||||||
mapping(address => mapping(uint256 => uint256)) private sellStakeKeyIndex; // Track position in keys array
|
mapping(address => mapping(uint256 => uint256)) private sellStakeKeyIndex; // Track position in keys array
|
||||||
MarketplaceHistory[] public marketplaceHistory; // Complete history of all transactions
|
MarketplaceHistory[] public marketplaceHistory; // Complete history of all transactions
|
||||||
mapping(address => uint256) public totalClaimed; // Track total amount claimed and sent to withdrawStakes per user
|
mapping(address => uint256) public totalClaimed; // Track total amount claimed and sent to withdrawStakes per user
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
event VestingCreated(address indexed user, uint256 amount, uint256 bonus);
|
|
||||||
event VestingClaimed(address indexed user, uint256 amount, uint256 bonus);
|
event VestingClaimed(address indexed user, uint256 amount, uint256 bonus);
|
||||||
event BonusClaimed(address indexed user, uint256 bonus);
|
event BonusClaimed(address indexed user, uint256 bonus);
|
||||||
event UnlockScheduleSet(address indexed token);
|
event UnlockScheduleSet(address indexed token);
|
||||||
@@ -343,6 +343,7 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
estDaysRemaining: estDaysRemaining,
|
estDaysRemaining: estDaysRemaining,
|
||||||
currentTreasuryTvl: currentTreasuryTvl,
|
currentTreasuryTvl: currentTreasuryTvl,
|
||||||
totalLiability: totalBigStakes,
|
totalLiability: totalBigStakes,
|
||||||
|
paybackPercent: _paybackPercent,
|
||||||
unlockPercentage: unlockPercentage,
|
unlockPercentage: unlockPercentage,
|
||||||
timestamp: block.timestamp
|
timestamp: block.timestamp
|
||||||
});
|
});
|
||||||
@@ -366,11 +367,13 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
return totalUnclaimed;
|
return totalUnclaimed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Get user's net stake (big stake minus unclaimed funds)
|
/// @notice Get user's net stake (big stake minus unclaimed funds minus pending sell stakes)
|
||||||
function getNetStake(address user) public view returns (uint256) {
|
function getNetStake(address user) public view returns (uint256) {
|
||||||
uint256 bigStake = userBigStake[user];
|
uint256 bigStake = userBigStake[user];
|
||||||
uint256 unclaimed = calculateUnclaimedFunds(user);
|
uint256 unclaimed = calculateUnclaimedFunds(user);
|
||||||
return bigStake - unclaimed;
|
uint256 pending = pendingSellStakes[user];
|
||||||
|
uint256 committed = unclaimed + pending;
|
||||||
|
return bigStake > committed ? bigStake - committed : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Get comprehensive user stake information
|
/// @notice Get comprehensive user stake information
|
||||||
@@ -411,6 +414,9 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
token: BSC_TOKEN
|
token: BSC_TOKEN
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Increment withdraw liabilities for BSC_TOKEN
|
||||||
|
withdrawLiabilities[BSC_TOKEN] += unclaimedAmount;
|
||||||
|
|
||||||
emit FundsClaimed(msg.sender, unclaimedAmount);
|
emit FundsClaimed(msg.sender, unclaimedAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,10 +439,8 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
address token = stake.token;
|
address token = stake.token;
|
||||||
stake.amount = 0; // Mark as withdrawn
|
stake.amount = 0; // Mark as withdrawn
|
||||||
|
|
||||||
// Decrement withdraw vesting liabilities for non-BSC tokens
|
// Decrement withdraw liabilities for all tokens
|
||||||
if (token != BSC_TOKEN) {
|
withdrawLiabilities[token] -= amount;
|
||||||
withdrawVestingLiabilities[token] -= amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer tokens to user based on the specified token
|
// Transfer tokens to user based on the specified token
|
||||||
IERC20(token).safeTransfer(msg.sender, amount);
|
IERC20(token).safeTransfer(msg.sender, amount);
|
||||||
@@ -482,6 +486,9 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
token: BSC_TOKEN
|
token: BSC_TOKEN
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Increment withdraw liabilities for BSC_TOKEN
|
||||||
|
withdrawLiabilities[BSC_TOKEN] += payoutAmount;
|
||||||
|
|
||||||
emit FundsClaimed(msg.sender, payoutAmount);
|
emit FundsClaimed(msg.sender, payoutAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,9 +629,8 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
stakeIdCounter++;
|
stakeIdCounter++;
|
||||||
uint256 stakeId = stakeIdCounter;
|
uint256 stakeId = stakeIdCounter;
|
||||||
|
|
||||||
// Deduct value from user's big stake immediately
|
// Add to pending sell stakes tracking (don't touch actual bigStake until sale/cancel)
|
||||||
userBigStake[msg.sender] -= value;
|
pendingSellStakes[msg.sender] += value;
|
||||||
totalBigStakes -= value;
|
|
||||||
|
|
||||||
// Create the sellStake entry
|
// Create the sellStake entry
|
||||||
sellStakes[msg.sender][stakeId] = SellStake({
|
sellStakes[msg.sender][stakeId] = SellStake({
|
||||||
@@ -653,14 +659,16 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
|
|
||||||
uint256 value = sellStakeEntry.value;
|
uint256 value = sellStakeEntry.value;
|
||||||
|
|
||||||
// Calculate cancellation fee
|
// Calculate cancellation fee and apply directly to userBigStake
|
||||||
uint256 fee = (value * cancellationFee) / 10000;
|
uint256 fee = (value * cancellationFee) / 10000;
|
||||||
uint256 valueAfterFee = value - fee;
|
|
||||||
|
|
||||||
// Restore value minus fee to user's big stake
|
// Remove from pending and apply fee directly to bigStake
|
||||||
userBigStake[msg.sender] += valueAfterFee;
|
pendingSellStakes[msg.sender] -= value;
|
||||||
totalBigStakes += valueAfterFee;
|
if (fee > 0) {
|
||||||
// Note: fee reduces total liability (not added back to totalBigStakes)
|
userBigStake[msg.sender] -= fee;
|
||||||
|
totalBigStakes -= fee;
|
||||||
|
}
|
||||||
|
// Note: fee reduces total liability permanently
|
||||||
|
|
||||||
// Emit fee event
|
// Emit fee event
|
||||||
if (fee > 0) {
|
if (fee > 0) {
|
||||||
@@ -720,10 +728,12 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
// Transfer payment from buyer to seller (direct transfer)
|
// Transfer payment from buyer to seller (direct transfer)
|
||||||
IERC20(BSC_TOKEN).safeTransferFrom(msg.sender, seller, salePrice);
|
IERC20(BSC_TOKEN).safeTransferFrom(msg.sender, seller, salePrice);
|
||||||
|
|
||||||
// Add buyerStake to buyer's big stake (value - protocol share)
|
// Transfer stakes: remove from seller, add to buyer (minus protocol share)
|
||||||
|
userBigStake[seller] -= value;
|
||||||
userBigStake[msg.sender] += buyerStake;
|
userBigStake[msg.sender] += buyerStake;
|
||||||
totalBigStakes += buyerStake;
|
pendingSellStakes[seller] -= value;
|
||||||
// Note: protocolShare reduces total liability (not added back to totalBigStakes)
|
// Note: totalBigStakes decreases by protocolShare (value - buyerStake)
|
||||||
|
totalBigStakes -= (value - buyerStake);
|
||||||
|
|
||||||
// Track marketplace sales for seller
|
// Track marketplace sales for seller
|
||||||
marketplace_sales[seller] += salePrice;
|
marketplace_sales[seller] += salePrice;
|
||||||
@@ -759,27 +769,27 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
/// @notice This function will end and clear a user's vestings.
|
/// @notice This function will end and clear a user's vestings.
|
||||||
/// @dev Only to be used by bots in emergencies
|
/// @dev Only to be used by bots in emergencies
|
||||||
/// @param user The user whose vestings will be ended and 0'd
|
/// @param user The user whose vestings will be ended and 0'd
|
||||||
function clearVesting(address user) external onlyBot {
|
// function clearVesting(address user) external onlyBot {
|
||||||
for (uint256 i = 0; i < vestings[user].length; ++i) {
|
// for (uint256 i = 0; i < vestings[user].length; ++i) {
|
||||||
Vesting storage vesting = vestings[user][i];
|
// Vesting storage vesting = vestings[user][i];
|
||||||
|
|
||||||
// Decrement accounting variables before clearing
|
// // Decrement accounting variables before clearing
|
||||||
if (!vesting.complete) {
|
// if (!vesting.complete) {
|
||||||
if (dollarsVested[user] >= vesting.usdAmount) {
|
// if (dollarsVested[user] >= vesting.usdAmount) {
|
||||||
dollarsVested[user] -= vesting.usdAmount;
|
// dollarsVested[user] -= vesting.usdAmount;
|
||||||
}
|
// }
|
||||||
if (vestedTotal[vesting.token] >= vesting.amount) {
|
// if (vestedTotal[vesting.token] >= vesting.amount) {
|
||||||
vestedTotal[vesting.token] -= vesting.amount;
|
// vestedTotal[vesting.token] -= vesting.amount;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
vesting.amount = 0;
|
// vesting.amount = 0;
|
||||||
vesting.bonus = 0;
|
// vesting.bonus = 0;
|
||||||
vesting.claimedAmount = 0;
|
// vesting.claimedAmount = 0;
|
||||||
vesting.claimedBonus = 0;
|
// vesting.claimedBonus = 0;
|
||||||
vesting.complete = true;
|
// vesting.complete = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// @notice Creates a vesting for a given user
|
/// @notice Creates a vesting for a given user
|
||||||
/// @dev Only to be used by bots for manual vesting creation
|
/// @dev Only to be used by bots for manual vesting creation
|
||||||
@@ -1025,8 +1035,8 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
token: vesting.token
|
token: vesting.token
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Increment withdraw vesting liabilities for this token
|
// Increment withdraw liabilities for this token
|
||||||
withdrawVestingLiabilities[vesting.token] += amountToClaim;
|
withdrawLiabilities[vesting.token] += amountToClaim;
|
||||||
|
|
||||||
emit VestingClaimed(msg.sender, amountToClaim, 0);
|
emit VestingClaimed(msg.sender, amountToClaim, 0);
|
||||||
}
|
}
|
||||||
@@ -1073,25 +1083,40 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
token: _token
|
token: _token
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Increment withdraw vesting liabilities for this token
|
// Increment withdraw liabilities for this token
|
||||||
withdrawVestingLiabilities[_token] += totalReward;
|
withdrawLiabilities[_token] += totalReward;
|
||||||
|
|
||||||
emit VestingClaimed(msg.sender, totalReward, 0);
|
emit VestingClaimed(msg.sender, totalReward, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @notice Claim unlocked bonus tokens from a specific vesting
|
/// @notice Claim unlocked bonus tokens from multiple vestings
|
||||||
/// @param _vestingIndex The index of the vesting to claim bonus from
|
/// @param _vestingIndices Array of vesting indices to claim bonus from
|
||||||
function claimBonus(uint256 _vestingIndex) external nonReentrant {
|
function claimBonus(uint256[] calldata _vestingIndices) external nonReentrant {
|
||||||
require(_vestingIndex < vestings[msg.sender].length, "Invalid vesting index");
|
uint256 totalBonusClaimed = 0;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < _vestingIndices.length; i++) {
|
||||||
|
uint256 _vestingIndex = _vestingIndices[i];
|
||||||
|
|
||||||
|
// Skip invalid vesting indices
|
||||||
|
if (_vestingIndex >= vestings[msg.sender].length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
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");
|
// Skip if invalid claim amount or nothing to claim
|
||||||
|
if (maxBonus < vesting.claimedBonus) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
uint256 bonusToClaim = maxBonus - vesting.claimedBonus;
|
||||||
require(bonusToClaim > 0, "Nothing to claim");
|
if (bonusToClaim == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
vesting.claimedBonus += bonusToClaim;
|
vesting.claimedBonus += bonusToClaim;
|
||||||
|
totalBonusClaimed += bonusToClaim;
|
||||||
|
|
||||||
// Create withdrawable stake with unlock delay (add 1e6 to distinguish from normal stakes)
|
// Create withdrawable stake with unlock delay (add 1e6 to distinguish from normal stakes)
|
||||||
withdrawStakes[msg.sender].push(WithdrawStake({
|
withdrawStakes[msg.sender].push(WithdrawStake({
|
||||||
@@ -1100,8 +1125,14 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable {
|
|||||||
unlockTime: block.timestamp + unlockDelay,
|
unlockTime: block.timestamp + unlockDelay,
|
||||||
token: BSC_TOKEN
|
token: BSC_TOKEN
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
emit BonusClaimed(msg.sender, bonusToClaim);
|
// Only update liabilities and emit event if something was actually claimed
|
||||||
|
if (totalBonusClaimed > 0) {
|
||||||
|
// Increment withdraw liabilities for BSC_TOKEN
|
||||||
|
withdrawLiabilities[BSC_TOKEN] += totalBonusClaimed;
|
||||||
|
emit BonusClaimed(msg.sender, totalBonusClaimed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user