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

LendingPool + IncentiveController #209

Closed
wants to merge 5 commits into from
Closed
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
9 changes: 9 additions & 0 deletions contracts/StaderConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable {
keccak256('NODE_EL_REWARD_VAULT_IMPLEMENTATION');
bytes32 public constant override VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION =
keccak256('VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION');
bytes32 public constant override LENDING_POOL_CONTRACT = keccak256('LENDING_POOL_CONTRACT');

//POR Feed Proxy
bytes32 public constant override ETH_BALANCE_POR_FEED = keccak256('ETH_BALANCE_POR_FEED');
Expand Down Expand Up @@ -284,6 +285,10 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable {
setContract(ETHX_SUPPLY_POR_FEED, _ethXSupplyProxy);
}

function updateLendingPoolProxy(address _lendingPoolProxy) external onlyRole(DEFAULT_ADMIN_ROLE) {
setContract(LENDING_POOL_CONTRACT, _lendingPoolProxy);
}

function updateStaderToken(address _staderToken) external onlyRole(DEFAULT_ADMIN_ROLE) {
setToken(SD, _staderToken);
}
Expand Down Expand Up @@ -448,6 +453,10 @@ contract StaderConfig is IStaderConfig, AccessControlUpgradeable {
return contractsMap[VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION];
}

function getLendingPool() external view override returns (address) {
return contractsMap[LENDING_POOL_CONTRACT];
}

//POR Feed Proxy Getters
function getETHBalancePORFeedProxy() external view override returns (address) {
return contractsMap[ETH_BALANCE_POR_FEED];
Expand Down
19 changes: 19 additions & 0 deletions contracts/interfaces/IIncentiveController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/interfaces/IIncentiveController.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/interfaces/IIncentiveController.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

import './IStaderConfig.sol';

interface IIncentiveController {

// events
event UpdatedStaderConfig(address staderConfig);

// functions
function claim(address account) external;

function onDeposit(address account) external;

function rewardPerToken() external view returns (uint256);

function earned(address account) external view returns (uint256);
}
34 changes: 34 additions & 0 deletions contracts/interfaces/ILendingPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/interfaces/ILendingPool.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/interfaces/ILendingPool.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

import './IStaderConfig.sol';

struct UserData {
uint256 totalInterestSD;
uint256 totalCollateralInSD;
uint256 ltv;
uint256 healthFactor;
}

interface ILendingPool {

// events
event UpdatedStaderConfig(address staderConfig);

// functions
function deposit(uint256 amount) external returns (uint256);

function requestWithdraw(uint256 amount) external returns (uint256);

function claim(uint256 index) external returns (uint256);

function borrow(uint256 amount) external returns (uint256);

function repay(uint256 amount) external returns (uint256);

function liquidationCall(address account) external returns (uint256);

function claimLiquidation(uint256 index) external returns (uint256);

function getUserData(address account) external view returns (UserData memory);
}
8 changes: 8 additions & 0 deletions contracts/interfaces/ILendingPoolToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.16;

interface ILendingPoolToken {
function mint(address account, uint256 amount) external;

function burn(address account, uint256 amount) external;
}
4 changes: 4 additions & 0 deletions contracts/interfaces/IStaderConfig.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/interfaces/IStaderConfig.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/interfaces/IStaderConfig.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

interface IStaderConfig {
Expand Down Expand Up @@ -58,6 +58,8 @@

function VALIDATOR_WITHDRAWAL_VAULT_IMPLEMENTATION() external view returns (bytes32);

function LENDING_POOL_CONTRACT() external view returns (bytes32);

//POR Feed Proxy
function ETH_BALANCE_POR_FEED() external view returns (bytes32);

Expand Down Expand Up @@ -149,6 +151,8 @@
function getETHBalancePORFeedProxy() external view returns (address);

function getETHXSupplyPORFeedProxy() external view returns (address);

function getLendingPool() external view returns (address);

// Tokens
function getStaderToken() external view returns (address);
Expand Down
119 changes: 119 additions & 0 deletions contracts/lendingpool/IncentiveController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/lendingpool/IncentiveController.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/lendingpool/IncentiveController.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import '../library/UtilLib.sol';
import '../interfaces/IStaderConfig.sol';
import '../interfaces/IIncentiveController.sol';

/// @title IncentiveController
/// @notice This contract handles the distribution of reward tokens for a lending pool.
contract IncentiveController is IIncentiveController, AccessControlUpgradeable {
// The emission rate of the reward tokens per second.
uint256 public emissionPerSecond;

// The timestamp of the last reward calculation.
uint256 public lastUpdateTimestamp;

// The stored value of the reward per token, used to calculate rewards.
uint256 public rewardPerTokenStored;

// Reference to the lending pool token contract.
IERC20 public lendingPoolToken;

// Reference to the reward token contract.
IERC20 public rewardToken;

// Reference to the Stader configuration contract.
IStaderConfig public staderConfig;

// A mapping of accounts to their pending reward amounts.
mapping(address => uint256) public rewards;

// A mapping of accounts to the reward per token value at their last update.
mapping(address => uint256) public userRewardPerTokenPaid;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/// @notice Initializes the contract with necessary addresses.
/// @param _lendingPoolToken The address of the lending pool token contract.
/// @param _staderConfig The address of the Stader configuration contract.
/// @param _rewardToken The address of the reward token contract.
function initialize(address _lendingPoolToken, address _staderConfig, address _rewardToken) external initializer {
UtilLib.checkNonZeroAddress(_lendingPoolToken);
UtilLib.checkNonZeroAddress(_staderConfig);
UtilLib.checkNonZeroAddress(_rewardToken);

lendingPoolToken = IERC20(_lendingPoolToken);
staderConfig = IStaderConfig(_staderConfig);
rewardToken = IERC20(_rewardToken);

__AccessControl_init();
}

/// @notice Claims the accrued rewards for an account.
/// @param account The address of the account claiming rewards.
function claim(address account) external {
UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.LENDING_POOL_CONTRACT());

updateReward(account);

uint256 reward = rewards[account];
require(reward > 0, "No rewards to claim.");
rewards[account] = 0;
rewardToken.transfer(account, reward);

emit RewardClaimed(account, reward);
}

/// @notice Updates the reward on deposit in the lending pool.
/// @param account The account that made a deposit.
function onDeposit(address account) external {
UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.LENDING_POOL_CONTRACT());

updateReward(account);
}

/// @notice Calculates the current reward per token.
/// @return The calculated reward per token.
function rewardPerToken() public view returns (uint256) {
if (lendingPoolToken.totalSupply() == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored + (
(block.timestamp - lastUpdateTimestamp) * emissionPerSecond * 1e18 / lendingPoolToken.totalSupply()
);
}

/// @notice Calculates the total accrued reward for an account.
/// @param account The account to calculate rewards for.
/// @return The total accrued reward for the account.
function earned(address account) public view returns (uint256) {
uint256 currentBalance = lendingPoolToken.balanceOf(account);
uint256 currentRewardPerToken = rewardPerToken();

return (currentBalance * (currentRewardPerToken - userRewardPerTokenPaid[account]) / 1e18) + rewards[account];
}

/// @dev Internal function to update the reward state for an account.
/// @param account The account to update the reward for.
function updateReward(address account) internal {
rewardPerTokenStored = rewardPerToken();
lastUpdateTimestamp = block.timestamp;

if(account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
}

/// @dev Emitted when a reward is claimed.
/// @param user The user who claimed the reward.
/// @param reward The amount of reward claimed.
event RewardClaimed(address indexed user, uint256 reward);
}
55 changes: 55 additions & 0 deletions contracts/lendingpool/LendingPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/lendingpool/LendingPool.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/lendingpool/LendingPool.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';

import '../interfaces/ILendingPool.sol';
import '../interfaces/IIncentiveController.sol';
import '../interfaces/ILendingPoolToken.sol';

contract LendingPool is ILendingPool, AccessControlUpgradeable {
IIncentiveController public incentiveController;
ILendingPoolToken public lendingPoolToken;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function deposit(uint256 amount) external override returns (uint256) {
incentiveController.onDeposit(msg.sender);
lendingPoolToken.mint(msg.sender, amount);
return 0;
}

function requestWithdraw(uint256 amount) external override returns (uint256) {
incentiveController.claim(msg.sender);
lendingPoolToken.burn(msg.sender, amount);
return 0;
}

function claim(uint256 index) external override returns (uint256) {
incentiveController.claim(msg.sender);
return 0;
}

function borrow(uint256 amount) external override returns (uint256) {
return 0;
}

function repay(uint256 amount) external override returns (uint256) {
return 0;
}

function liquidationCall(address account) external override returns (uint256) {
return 0;
}

function claimLiquidation(uint256 index) external override returns (uint256) {
return 0;
}

function getUserData(address account) external override view returns (UserData memory) {
return UserData(0, 0, 0, 0);
}
}
90 changes: 90 additions & 0 deletions contracts/lendingpool/SDX.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-3.0-or-later

Check failure on line 1 in contracts/lendingpool/SDX.sol

View workflow job for this annotation

GitHub Actions / Prettier

contracts/lendingpool/SDX.sol#L1

There are issues with this file's formatting, please run Prettier to fix the errors
pragma solidity 0.8.16;

import '@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol';

import '../interfaces/IStaderConfig.sol';
import '../library/UtilLib.sol';

contract SDX is Initializable, AccessControlUpgradeable, ERC20Upgradeable, PausableUpgradeable{
event UpdatedStaderConfig(address indexed _staderConfig);

IStaderConfig public staderConfig;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(address _admin, address _staderConfig) external initializer {
UtilLib.checkNonZeroAddress(_admin);
UtilLib.checkNonZeroAddress(_staderConfig);

__ERC20_init('Interest bearing SD token', 'SDx');
__Pausable_init();
__AccessControl_init();

staderConfig = IStaderConfig(_staderConfig);
_grantRole(DEFAULT_ADMIN_ROLE, _admin);

emit UpdatedStaderConfig(_staderConfig);
}

/**
* @notice Mints SDx when called by an authorized caller
* @param to the account to mint to
* @param amount the amount of SDx to mint
*/
function mint(address to, uint256 amount) external whenNotPaused {
UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.LENDING_POOL_CONTRACT());

_mint(to, amount);
}

/**
* @notice Burns SDx when called by an authorized caller
* @param account the account to burn from
* @param amount the amount of SDx to burn
*/
function burnFrom(address account, uint256 amount) external whenNotPaused {
UtilLib.onlyStaderContract(msg.sender, staderConfig, staderConfig.LENDING_POOL_CONTRACT());

_burn(account, amount);
}

/**
* @dev Triggers stopped state.
* Contract must not be paused.
*/
function pause() external {
UtilLib.onlyManagerRole(msg.sender, staderConfig);

_pause();
}

/**
* @dev Returns to normal state.
* Contract must be paused
*/
function unpause() external {
UtilLib.onlyManagerRole(msg.sender, staderConfig);

_unpause();
}

function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal override whenNotPaused {
super._beforeTokenTransfer(from, to, amount);
}

function updateStaderConfig(address _staderConfig) external onlyRole(DEFAULT_ADMIN_ROLE) {
UtilLib.checkNonZeroAddress(_staderConfig);
staderConfig = IStaderConfig(_staderConfig);
emit UpdatedStaderConfig(_staderConfig);
}
}
Loading