Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test/update incentive pool #11

Open
wants to merge 6 commits into
base: Task/ExecuteBorrow
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions contracts/IncentivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ contract IncentivePool is IIncentivePool {
require(!_initialized, 'AlreadyInitialized');
_initialized = true;
lToken = lToken_;
endTimestamp = block.timestamp + 180 * 1 days;
_lastUpdateTimestamp = block.timestamp;
endTimestamp = block.timestamp + 95 * 1 days;
}

function isClosed() public view returns (bool) {
Expand Down Expand Up @@ -152,21 +153,46 @@ contract IncentivePool is IIncentivePool {
return (_incentiveIndex, _lastUpdateTimestamp);
}

function withdrawResidue() external override {
require(msg.sender == _owner, 'onlyAdmin');
function withdrawResidue() external override onlyOwner {
require(isClosed(), 'OnlyClosed');
uint256 residue = IERC20(_incentiveAsset).balanceOf(address(this));
IERC20(_incentiveAsset).safeTransfer(_owner, residue);
emit IncentivePoolEnded();
}

modifier onlyMoneyPool {
/**
* @notice Admin can update amount per second
*/
function setAmountPerSecond(uint256 newAmountPerSecond) external override onlyOwner {
_incentiveIndex = getIncentiveIndex();

amountPerSecond = newAmountPerSecond;
_lastUpdateTimestamp = block.timestamp;

emit RewardPerSecondUpdated(newAmountPerSecond);
}

/**
* @notice Admin can update incentive pool end timestamp
*/
function setEndTimestamp(uint256 newEndTimestamp) external override onlyOwner {
endTimestamp = newEndTimestamp;

emit IncentiveEndTimestampUpdated(newEndTimestamp);
}

modifier onlyMoneyPool() {
require(msg.sender == address(_moneyPool), 'OnlyMoneyPool');
_;
}

modifier onlyLToken {
modifier onlyLToken() {
require(msg.sender == address(lToken), 'OnlyLToken');
_;
}

modifier onlyOwner() {
require(msg.sender == _owner, 'onlyAdmin');
_;
}
}
194 changes: 194 additions & 0 deletions contracts/IncentivePoolV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;

import './libraries/WadRayMath.sol';
import './interfaces/IIncentivePoolV2.sol';
import './interfaces/IMoneyPool.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import 'hardhat/console.sol';

contract IncentivePoolV2 is IIncentivePoolV2 {
using WadRayMath for uint256;
using SafeERC20 for IERC20;

constructor(
IMoneyPool moneyPool,
address incentiveAsset,
address lToken_,
uint256 amountPerSecond_,
uint256 startTimestamp,
uint256 _endTimestamp
) {
_moneyPool = moneyPool;
_incentiveAsset = incentiveAsset;
amountPerSecond = amountPerSecond_;
_owner = msg.sender;
lToken = lToken_;
_lastUpdateTimestamp = startTimestamp;
endTimestamp = _endTimestamp;
}

address private _owner;

IMoneyPool internal _moneyPool;

address internal _incentiveAsset;

uint256 internal _incentiveIndex;

uint256 internal _lastUpdateTimestamp;

mapping(address => uint256) internal _userIncentiveIndex;

mapping(address => uint256) internal _accruedIncentive;

uint256 public amountPerSecond;

address public lToken;

uint256 public endTimestamp;

function isClosed() public view returns (bool) {
if (block.timestamp > endTimestamp) {
return true;
}
return false;
}

/**
* @notice Update user incentive index and last update timestamp in minting or burining lTokens.
*/
function updateIncentivePool(address user) external override onlyLToken {
_accruedIncentive[user] = getUserIncentive(user);
_incentiveIndex = _userIncentiveIndex[user] = getIncentiveIndex();

if (isClosed()) {
_lastUpdateTimestamp = endTimestamp;
return;
}
_lastUpdateTimestamp = block.timestamp;

emit UpdateIncentivePool(user, _accruedIncentive[user], _incentiveIndex);
}

/**
* @notice If user transfered lToken, accrued reward will be updated
* and user index will be set to the current index
*/
function beforeTokenTransfer(address from, address to) external override onlyLToken {
_accruedIncentive[from] = getUserIncentive(from);
_accruedIncentive[to] = getUserIncentive(to);
_userIncentiveIndex[from] = _userIncentiveIndex[to] = getIncentiveIndex();
}

function claimIncentive() external override {
address user = msg.sender;

uint256 accruedIncentive = getUserIncentive(user);

require(accruedIncentive > 0, 'NotEnoughUserAccruedIncentive');

_userIncentiveIndex[user] = getIncentiveIndex();

_accruedIncentive[user] = 0;

IERC20(_incentiveAsset).safeTransfer(user, accruedIncentive);

emit ClaimIncentive(user, accruedIncentive, _userIncentiveIndex[user]);
}

function getIncentiveIndex() public view returns (uint256) {
uint256 currentTimestamp = block.timestamp < endTimestamp ? block.timestamp : endTimestamp;
uint256 timeDiff = currentTimestamp - _lastUpdateTimestamp;
uint256 totalSupply = IERC20(lToken).totalSupply();

if (timeDiff == 0) {
return _incentiveIndex;
}

if (totalSupply == 0) {
return 0;
}

uint256 IncentiveIndexDiff = (timeDiff * amountPerSecond * 1e9) / totalSupply;

return _incentiveIndex + IncentiveIndexDiff;
}

function getUserIncentive(address user) public view returns (uint256) {
uint256 indexDiff = 0;

if (getIncentiveIndex() >= _userIncentiveIndex[user]) {
indexDiff = getIncentiveIndex() - _userIncentiveIndex[user];
}
uint256 balance = IERC20(lToken).balanceOf(user);

uint256 result = _accruedIncentive[user] + (balance * indexDiff) / 1e9;

return result;
}

function getUserIncentiveData(address user)
public
view
returns (
uint256 userIndex,
uint256 userReward,
uint256 userLTokenBalance
)
{
return (_userIncentiveIndex[user], _accruedIncentive[user], IERC20(lToken).balanceOf(user));
}

function getIncentivePoolData()
public
view
returns (uint256 incentiveIndex, uint256 lastUpdateTimestamp)
{
return (_incentiveIndex, _lastUpdateTimestamp);
}

function withdrawResidue() external override onlyOwner {
require(isClosed(), 'OnlyClosed');
uint256 residue = IERC20(_incentiveAsset).balanceOf(address(this));
IERC20(_incentiveAsset).safeTransfer(_owner, residue);
emit IncentivePoolEnded();
}

/**
* @notice Admin can update amount per second
*/
function setAmountPerSecond(uint256 newAmountPerSecond) external override onlyOwner {
_incentiveIndex = getIncentiveIndex();

amountPerSecond = newAmountPerSecond;
_lastUpdateTimestamp = block.timestamp;

emit RewardPerSecondUpdated(newAmountPerSecond);
}

/**
* @notice Admin can update incentive pool end timestamp
*/
function setEndTimestamp(uint256 newEndTimestamp) external override onlyOwner {
endTimestamp = newEndTimestamp;

emit IncentiveEndTimestampUpdated(newEndTimestamp);
}

modifier onlyMoneyPool() {
require(msg.sender == address(_moneyPool), 'OnlyMoneyPool');
_;
}

modifier onlyLToken() {
require(msg.sender == address(lToken), 'OnlyLToken');
_;
}

modifier onlyOwner() {
require(msg.sender == _owner, 'onlyAdmin');
_;
}
}
11 changes: 11 additions & 0 deletions contracts/interfaces/IIncentivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ interface IIncentivePool {

event IncentivePoolEnded();

event RewardPerSecondUpdated(uint256 newAmountPerSecond);

event IncentiveEndTimestampUpdated(uint256 newEndTimestamp);

function initializeIncentivePool(address lToken) external;

function setAmountPerSecond(uint256 newAmountPerSecond) external;

/**
* @notice Admin can update incentive pool end timestamp
*/
function setEndTimestamp(uint256 newEndTimestamp) external;

function updateIncentivePool(address user) external;

function beforeTokenTransfer(address from, address to) external;
Expand Down
31 changes: 31 additions & 0 deletions contracts/interfaces/IIncentivePoolV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;

import '../libraries/DataStruct.sol';

interface IIncentivePoolV2 {
event ClaimIncentive(address indexed user, uint256 claimedIncentive, uint256 userIncentiveIndex);

event UpdateIncentivePool(address indexed user, uint256 accruedIncentive, uint256 incentiveIndex);

event IncentivePoolEnded();

event RewardPerSecondUpdated(uint256 newAmountPerSecond);

event IncentiveEndTimestampUpdated(uint256 newEndTimestamp);

function setAmountPerSecond(uint256 newAmountPerSecond) external;

/**
* @notice Admin can update incentive pool end timestamp
*/
function setEndTimestamp(uint256 newEndTimestamp) external;

function updateIncentivePool(address user) external;

function beforeTokenTransfer(address from, address to) external;

function claimIncentive() external;

function withdrawResidue() external;
}
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import '@openzeppelin/hardhat-upgrades';
import '@typechain/hardhat';
import 'hardhat-deploy-ethers';
import 'hardhat-deploy';
import 'hardhat-abi-exporter';
// import 'hardhat-abi-exporter';
// import "solidity-coverage"
// Gas-reporter's parser dependency makes Warning:
// Accessing non-existent property 'INVALID_ALT_NUMBER' of module exports inside circular dependency
Expand Down
Loading