207 lines
6.4 KiB
Solidity
207 lines
6.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.19;
|
|
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
|
|
import {IRewardTracker} from "./interfaces/IRewardTracker.sol";
|
|
import {IVester} from "./interfaces/IVester.sol";
|
|
import {Governable} from "../core/Governable.sol";
|
|
|
|
contract RewardRouter is ReentrancyGuard, Governable {
|
|
using SafeERC20 for IERC20;
|
|
|
|
address public cec;
|
|
address public esCec;
|
|
|
|
address public stakedCecTracker;
|
|
address public cecVester;
|
|
|
|
mapping(address sender => address receiver) public pendingReceivers;
|
|
|
|
event StakeCec(address account, address token, uint256 amount);
|
|
event UnstakeCec(address account, address token, uint256 amount);
|
|
|
|
constructor(address _cec, address _esCec, address _stakedCecTracker, address _cecVester) {
|
|
cec = _cec;
|
|
esCec = _esCec;
|
|
stakedCecTracker = _stakedCecTracker;
|
|
cecVester = _cecVester;
|
|
}
|
|
|
|
// to help users who accidentally send their tokens to this contract
|
|
function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov {
|
|
IERC20(_token).safeTransfer(_account, _amount);
|
|
}
|
|
|
|
function batchStakeCecForAccount(
|
|
address[] memory _accounts,
|
|
uint256[] memory _amounts
|
|
) external nonReentrant onlyGov {
|
|
address _cec = cec;
|
|
for (uint256 i = 0; i < _accounts.length; i++) {
|
|
_stakeCec(msg.sender, _accounts[i], _cec, _amounts[i]);
|
|
}
|
|
}
|
|
|
|
function stakeCecForAccount(address _account, uint256 _amount) external nonReentrant onlyGov {
|
|
_stakeCec(msg.sender, _account, cec, _amount);
|
|
}
|
|
|
|
function stakeCec(uint256 _amount) external nonReentrant {
|
|
_stakeCec(msg.sender, msg.sender, cec, _amount);
|
|
}
|
|
|
|
function stakeEsCec(uint256 _amount) external nonReentrant {
|
|
_stakeCec(msg.sender, msg.sender, esCec, _amount);
|
|
}
|
|
|
|
function unstakeCec(uint256 _amount) external nonReentrant {
|
|
_unstakeCec(msg.sender, cec, _amount);
|
|
}
|
|
|
|
function unstakeEsCec(uint256 _amount) external nonReentrant {
|
|
_unstakeCec(msg.sender, esCec, _amount);
|
|
}
|
|
|
|
function claim() external nonReentrant {
|
|
address account = msg.sender;
|
|
|
|
IRewardTracker(stakedCecTracker).claimForAccount(account, account);
|
|
}
|
|
|
|
function claimEsCec() external nonReentrant {
|
|
address account = msg.sender;
|
|
IRewardTracker(stakedCecTracker).claimForAccount(account, account);
|
|
}
|
|
|
|
function compound() external nonReentrant {
|
|
_compound(msg.sender);
|
|
}
|
|
|
|
function compoundForAccount(address _account) external nonReentrant onlyGov {
|
|
_compound(_account);
|
|
}
|
|
|
|
function handleRewards(
|
|
bool _shouldClaimCec,
|
|
bool _shouldStakeCec,
|
|
bool _shouldClaimEsCec,
|
|
bool _shouldStakeEsCec
|
|
) external nonReentrant {
|
|
address account = msg.sender;
|
|
|
|
uint256 cecAmount = 0;
|
|
if (_shouldClaimCec) {
|
|
cecAmount = IVester(cecVester).claimForAccount(account, account);
|
|
}
|
|
|
|
if (_shouldStakeCec && cecAmount > 0) {
|
|
_stakeCec(account, account, cec, cecAmount);
|
|
}
|
|
|
|
uint256 esCecAmount = 0;
|
|
if (_shouldClaimEsCec) {
|
|
esCecAmount = IRewardTracker(stakedCecTracker).claimForAccount(account, account);
|
|
}
|
|
|
|
if (_shouldStakeEsCec && esCecAmount > 0) {
|
|
_stakeCec(account, account, esCec, esCecAmount);
|
|
}
|
|
}
|
|
|
|
function batchCompoundForAccounts(address[] memory _accounts) external nonReentrant onlyGov {
|
|
for (uint256 i = 0; i < _accounts.length; i++) {
|
|
_compound(_accounts[i]);
|
|
}
|
|
}
|
|
|
|
function signalTransfer(address _receiver) external nonReentrant {
|
|
require(IERC20(cecVester).balanceOf(msg.sender) == 0, "sender has vested tokens");
|
|
|
|
_validateReceiver(_receiver);
|
|
|
|
pendingReceivers[msg.sender] = _receiver;
|
|
}
|
|
|
|
function acceptTransfer(address _sender) external nonReentrant {
|
|
require(IERC20(cecVester).balanceOf(_sender) == 0, "sender has vested tokens");
|
|
|
|
address receiver = msg.sender;
|
|
require(pendingReceivers[_sender] == receiver, "transfer not signalled");
|
|
delete pendingReceivers[_sender];
|
|
|
|
_validateReceiver(receiver);
|
|
_compound(_sender);
|
|
|
|
uint256 stakedCec = IRewardTracker(stakedCecTracker).depositBalances(_sender, cec);
|
|
if (stakedCec > 0) {
|
|
_unstakeCec(_sender, cec, stakedCec);
|
|
_stakeCec(_sender, receiver, cec, stakedCec);
|
|
}
|
|
|
|
uint256 stakedEsCec = IRewardTracker(stakedCecTracker).depositBalances(_sender, esCec);
|
|
if (stakedEsCec > 0) {
|
|
_unstakeCec(_sender, esCec, stakedEsCec);
|
|
_stakeCec(_sender, receiver, esCec, stakedEsCec);
|
|
}
|
|
|
|
uint256 esCecBalance = IERC20(esCec).balanceOf(_sender);
|
|
if (esCecBalance > 0) {
|
|
IERC20(esCec).transferFrom(_sender, receiver, esCecBalance);
|
|
}
|
|
|
|
IVester(cecVester).transferStakeValues(_sender, receiver);
|
|
}
|
|
|
|
function _validateReceiver(address _receiver) private view {
|
|
require(
|
|
IRewardTracker(stakedCecTracker).averageStakedAmounts(_receiver) == 0,
|
|
"stakedCecTracker.averageStakedAmounts > 0"
|
|
);
|
|
require(
|
|
IRewardTracker(stakedCecTracker).cumulativeRewards(_receiver) == 0,
|
|
"stakedCecTracker.cumulativeRewards > 0"
|
|
);
|
|
|
|
require(
|
|
IVester(cecVester).transferredAverageStakedAmounts(_receiver) == 0,
|
|
"cecVester.transferredAverageStakedAmounts > 0"
|
|
);
|
|
require(
|
|
IVester(cecVester).transferredCumulativeRewards(_receiver) == 0,
|
|
"cecVester.transferredCumulativeRewards > 0"
|
|
);
|
|
|
|
require(IERC20(cecVester).balanceOf(_receiver) == 0, "cecVester.balance > 0");
|
|
}
|
|
|
|
function _compound(address _account) private {
|
|
_compoundCec(_account);
|
|
}
|
|
|
|
function _compoundCec(address _account) private {
|
|
uint256 esCecAmount = IRewardTracker(stakedCecTracker).claimForAccount(_account, _account);
|
|
if (esCecAmount > 0) {
|
|
_stakeCec(_account, _account, esCec, esCecAmount);
|
|
}
|
|
}
|
|
|
|
function _stakeCec(address _fundingAccount, address _account, address _token, uint256 _amount) private {
|
|
require(_amount > 0, "invalid _amount");
|
|
|
|
IRewardTracker(stakedCecTracker).stakeForAccount(_fundingAccount, _account, _token, _amount);
|
|
|
|
emit StakeCec(_account, _token, _amount);
|
|
}
|
|
|
|
function _unstakeCec(address _account, address _token, uint256 _amount) private {
|
|
require(_amount > 0, "invalid _amount");
|
|
// uint256 balance = IRewardTracker(stakedCecTracker).stakedAmounts(_account);
|
|
IRewardTracker(stakedCecTracker).unstakeForAccount(_account, _token, _amount, _account);
|
|
|
|
emit UnstakeCec(_account, _token, _amount);
|
|
}
|
|
}
|