Skip to content

Commit

Permalink
feat: Add delegation for iCake
Browse files Browse the repository at this point in the history
  • Loading branch information
ChefCupcake committed Sep 14, 2024
1 parent 6ec25f2 commit 89afcab
Showing 1 changed file with 111 additions and 3 deletions.
114 changes: 111 additions & 3 deletions contracts/ICakeV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma experimental ABIEncoderV2;
import "@openzeppelin-4.5.0/contracts/access/Ownable.sol";
import "@openzeppelin-4.5.0/contracts/utils/math/SafeMath.sol";
import "@openzeppelin-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin-4.5.0/contracts/security/ReentrancyGuard.sol";

interface IVeCake {
function getUserInfo(address _user) external view returns (
Expand All @@ -29,7 +30,7 @@ interface IIFOInitializable {
function endTimestamp() external view returns (uint256);
}

contract ICakeV3 is Ownable {
contract ICakeV3 is Ownable, ReentrancyGuard {
using SafeMath for uint256;

address public admin;
Expand All @@ -43,8 +44,25 @@ contract ICakeV3 is Ownable {

uint256 public constant MIN_CEILING_DURATION = 1 weeks;

/// @notice VECake delegator address in ICakeV3.
/// @dev User can delegate VECake to another address for boosted in ICakeV3.
/// Mapping from VECake account to ICakeV3 delegator account.
mapping(address => address) public delegator;

/// @notice The ICakeV3 account which was delegated by VECake account.
/// Mapping from ICakeV3 delegator account to VECake account.
mapping(address => address) public delegated;

/// @notice Gives permission to VECake account.
/// @dev Avoid malicious attacks.
/// The approval is cleared when the delegator was setted.
/// Mapping from MasterChef V3 delegator account to VECake account.
mapping(address => address) public delegatorApprove;

event UpdateRatio(uint256 newRatio);
event UpdateIfoDeployerAddress(address indexed newAddress);
event UpdateDelegator(address indexed user, address indexed oldDelegator, address indexed delegator);
event Approve(address indexed delegator, address indexed VECakeUser);

/**
* @notice Constructor
Expand All @@ -58,6 +76,85 @@ contract ICakeV3 is Ownable {
ratio = 1000;
}

struct DelegatorConfig {
address VECakeUser;
address delegator;
}

/// @notice set VECake delegators.
/// @dev In case VECake partner contract can not upgrade, owner can set delegator.
/// The delegator address can not have any position in ICakeV3.

This comment has been minimized.

This comment has been minimized.

Copy link
@chef-bun

chef-bun Sep 16, 2024

@ChefCupcake right I originally thought we don't need to do anti-cheat and check balance like bCAKE. But similar cheating could happen:

  • A committed to IFO
  • A delegate to B
  • Now B can commit again

Therefore we are going to need a similar check like this: https://github.com/pancakeswap/pancake-contracts/blob/master/projects/vecake-farm-booster/v3/contracts/FarmBoosterV3.sol#L163

But in the case of iCAKE we need to:

  1. Get latest IFO address from IFO deployer
  2. Read the MAX_POOL_ID from IFO
  3. Poll through the pid till max and check viewUserInfo to ensure the committed amount is 0 for both old and new delegator
/// The old delegator address can not have any position in ICakeV3.
/// @param _delegatorConfigs VECake delegator config.
function setDelegators(DelegatorConfig[] calldata _delegatorConfigs) external onlyOwner {
for (uint256 i = 0; i < _delegatorConfigs.length; i++) {
DelegatorConfig memory delegatorConfig = _delegatorConfigs[i];
require(
delegatorConfig.VECakeUser != address(0) && delegatorConfig.delegator != address(0),
"Invalid address"
);
// The delegator need to approve VECake contract.
require(delegatorApprove[delegatorConfig.delegator] == delegatorConfig.VECakeUser, "Not approved");

address oldDelegator = delegatorConfig.VECakeUser;
if (delegator[delegatorConfig.VECakeUser] != address(0)) {
oldDelegator = delegator[delegatorConfig.VECakeUser];
}
// clear old delegated information
delegated[oldDelegator] = address(0);

delegator[delegatorConfig.VECakeUser] = delegatorConfig.delegator;
delegated[delegatorConfig.delegator] = delegatorConfig.VECakeUser;
delegatorApprove[delegatorConfig.delegator] = address(0);
emit UpdateDelegator(delegatorConfig.VECakeUser, oldDelegator, delegatorConfig.delegator);
}
}

/// @notice Gives permission to VECake account.
/// @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals.
/// The approval is cleared when the delegator is set.
/// @param _VECakeUser VECake account address.
function approveToVECakeUser(address _VECakeUser) external nonReentrant {
require(delegated[msg.sender] == address(0), "Delegator already has VECake account");

delegatorApprove[msg.sender] = _VECakeUser;
emit Approve(msg.sender, _VECakeUser);
}

/// @notice set VECake delegator address for ICakeV3.
/// @dev The delegator address can not have any position in ICakeV3.
/// The old delegator address can not have any position in ICakeV3.
/// @param _delegator MasterChef V3 delegator address.
function setDelegator(address _delegator) external nonReentrant {
require(_delegator != address(0), "Invalid address");
// The delegator need to approve VECake contract.
require(delegatorApprove[_delegator] == msg.sender, "Not approved");

address oldDelegator = msg.sender;
if (delegator[msg.sender] != address(0)) {
oldDelegator = delegator[msg.sender];
}
// clear old delegated information
delegated[oldDelegator] = address(0);

delegator[msg.sender] = _delegator;
delegated[_delegator] = msg.sender;
delegatorApprove[_delegator] = address(0);

emit UpdateDelegator(msg.sender, oldDelegator, _delegator);
}

/// @notice Remove VECake delegator address for ICakeV3.
/// @dev The old delegator address can not have any position in ICakeV3.
function removeDelegator() external nonReentrant {
address oldDelegator = delegator[msg.sender];
require(oldDelegator != address(0), "No delegator");

delegated[oldDelegator] = address(0);
delegator[msg.sender] = address(0);
emit UpdateDelegator(msg.sender, oldDelegator, address(0));
}

/**
* @notice calculate iCake credit per user.
* @param _user: user address.
Expand Down Expand Up @@ -150,12 +247,23 @@ contract ICakeV3 is Ownable {
* @param _endTime timestamp to calculate user's veCake amount
*/
function _sumUserCredit(address _user, uint256 _endTime) internal view returns (uint256) {
// If this user has delegator , but the delegator is not the same user in MasterChef V3, use default boost factor.
if (delegator[_user] != address(0) && delegator[_user] != _user) {
return 0;
}

// If ICakeV3 user has delegated VECake account, use delegated VECake account balance to calculate boost factor.
address VEcakeUser = _user;
if (delegated[_user] != address(0)) {
VEcakeUser = delegated[_user];
}

// get native
uint256 veNative = IVeCake(veCakeAddress).balanceOfAtTime(_user, _endTime);
uint256 veNative = IVeCake(veCakeAddress).balanceOfAtTime(VEcakeUser, _endTime);

// get proxy/migrated
uint256 veMigrate = 0;
( , ,address cakePoolProxy, , , , , ) = IVeCake(veCakeAddress).getUserInfo(_user);
( , ,address cakePoolProxy, , , , , ) = IVeCake(veCakeAddress).getUserInfo(VEcakeUser);
if (cakePoolProxy != address(0)) {
veMigrate = IVeCake(veCakeAddress).balanceOfAtTime(cakePoolProxy, _endTime);
}
Expand Down

0 comments on commit 89afcab

Please sign in to comment.