编译器
0.8.19+commit.7dd6d404
文件 1 的 24:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 24:BokkyPooBahsDateTimeLibrary.sol
pragma solidity ^0.8.19;
library BokkyPooBahsDateTimeLibrary {
uint constant SECONDS_PER_DAY = 24 * 60 * 60;
int constant OFFSET19700101 = 2440588;
function _daysToDate(
uint _days
) internal pure returns (uint year, uint month, uint day) {
int __days = int(_days);
int L = __days + 68569 + OFFSET19700101;
int N = (4 * L) / 146097;
L = L - (146097 * N + 3) / 4;
int _year = (4000 * (L + 1)) / 1461001;
L = L - (1461 * _year) / 4 + 31;
int _month = (80 * L) / 2447;
int _day = L - (2447 * _month) / 80;
L = _month / 11;
_month = _month + 2 - 12 * L;
_year = 100 * (N - 49) + _year + L;
year = uint(_year);
month = uint(_month);
day = uint(_day);
}
function timestampToDate(
uint timestamp
) internal pure returns (uint year, uint month, uint day) {
(year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
}
}
文件 3 的 24:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 24:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
文件 5 的 24:FixedPoint96.sol
pragma solidity >=0.4.0;
library FixedPoint96 {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
}
文件 6 的 24:IDecentralizedIndex.sol
pragma solidity ^0.8.19;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IDecentralizedIndex is IERC20 {
enum IndexType {
WEIGHTED,
UNWEIGHTED
}
struct Fees {
uint256 burn;
uint256 bond;
uint256 debond;
uint256 buy;
uint256 sell;
uint256 partner;
}
struct IndexAssetInfo {
address token;
uint256 weighting;
uint256 basePriceUSDX96;
address c1;
uint256 q1;
}
event Create(address indexed newIdx, address indexed wallet);
event Bond(
address indexed wallet,
address indexed token,
uint256 amountTokensBonded,
uint256 amountTokensMinted
);
event Debond(address indexed wallet, uint256 amountDebonded);
event AddLiquidity(
address indexed wallet,
uint256 amountTokens,
uint256 amountDAI
);
event RemoveLiquidity(address indexed wallet, uint256 amountLiquidity);
function BOND_FEE() external view returns (uint256);
function DEBOND_FEE() external view returns (uint256);
function FLASH_FEE() external view returns (uint256);
function PAIRED_LP_TOKEN() external view returns (address);
function indexType() external view returns (IndexType);
function created() external view returns (uint256);
function lpStakingPool() external view returns (address);
function lpRewardsToken() external view returns (address);
function partner() external view returns (address);
function getIdxPriceUSDX96() external view returns (uint256, uint256);
function isAsset(address token) external view returns (bool);
function getAllAssets() external view returns (IndexAssetInfo[] memory);
function getInitialAmount(
address sToken,
uint256 sAmount,
address tToken
) external view returns (uint256);
function getTokenPriceUSDX96(address token) external view returns (uint256);
function processPreSwapFeesAndSwap() external;
function bond(address token, uint256 amount, uint256 amountMintMin) external;
function debond(
uint256 amount,
address[] memory token,
uint8[] memory percentage
) external;
function addLiquidityV2(
uint256 idxTokens,
uint256 daiTokens,
uint256 slippage,
uint256 deadline
) external;
function removeLiquidityV2(
uint256 lpTokens,
uint256 minTokens,
uint256 minDAI,
uint256 deadline
) external;
function flash(
address recipient,
address token,
uint256 amount,
bytes calldata data
) external;
}
文件 7 的 24:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 8 的 24:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 9 的 24:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 10 的 24:IPEAS.sol
pragma solidity ^0.8.19;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IPEAS is IERC20 {
event Burn(address indexed user, uint256 amount);
function burn(uint256 amount) external;
}
文件 11 的 24:IPeripheryImmutableState.sol
pragma solidity >=0.5.0;
interface IPeripheryImmutableState {
function factory() external view returns (address);
function WETH9() external view returns (address);
}
文件 12 的 24:IProtocolFeeRouter.sol
pragma solidity ^0.8.19;
import './IProtocolFees.sol';
interface IProtocolFeeRouter {
function protocolFees() external view returns (IProtocolFees);
}
文件 13 的 24:IProtocolFees.sol
pragma solidity ^0.8.19;
interface IProtocolFees {
event SetYieldAdmin(uint256 newFee);
event SetYieldBurn(uint256 newFee);
function DEN() external view returns (uint256);
function yieldAdmin() external view returns (uint256);
function yieldBurn() external view returns (uint256);
}
文件 14 的 24:IStakingPoolToken.sol
pragma solidity ^0.8.19;
interface IStakingPoolToken {
event Stake(address indexed executor, address indexed user, uint256 amount);
event Unstake(address indexed user, uint256 amount);
function indexFund() external view returns (address);
function stakingToken() external view returns (address);
function poolRewards() external view returns (address);
function stakeUserRestriction() external view returns (address);
function stake(address user, uint256 amount) external;
function unstake(uint256 amount) external;
}
文件 15 的 24:ISwapRouter.sol
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
文件 16 的 24:ITokenRewards.sol
pragma solidity ^0.8.19;
interface ITokenRewards {
event AddShares(address indexed wallet, uint256 amount);
event RemoveShares(address indexed wallet, uint256 amount);
event ClaimReward(address indexed wallet);
event DistributeReward(address indexed wallet, uint256 amount);
event DepositRewards(address indexed wallet, uint256 amount);
function totalShares() external view returns (uint256);
function totalStakers() external view returns (uint256);
function rewardsToken() external view returns (address);
function trackingToken() external view returns (address);
function depositFromPairedLpToken(
uint256 amount,
uint256 slippageOverride
) external;
function depositRewards(uint256 amount) external;
function claimReward(address wallet) external;
function setShares(
address wallet,
uint256 amount,
bool sharesRemoving
) external;
}
文件 17 的 24:IUniswapV2Router02.sol
pragma solidity ^0.8.19;
interface IUniswapV2Router02 {
function factory() external view returns (address);
function WETH() external view returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function swapETHForExactTokens(
uint256 amountOut,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
文件 18 的 24:IUniswapV3SwapCallback.sol
pragma solidity >=0.5.0;
interface IUniswapV3SwapCallback {
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
文件 19 的 24:IV3TwapUtilities.sol
pragma solidity ^0.8.19;
interface IV3TwapUtilities {
function getV3Pool(
address v3Factory,
address token0,
address token1,
uint24 poolFee
) external view returns (address);
function getPoolPriceUSDX96(
address pricePool,
address nativeStablePool,
address WETH9
) external view returns (uint256);
function sqrtPriceX96FromPoolAndInterval(
address pool
) external view returns (uint160);
function priceX96FromSqrtPriceX96(
uint160 sqrtPriceX96
) external pure returns (uint256);
}
文件 20 的 24:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 21 的 24:PoolAddress.sol
pragma solidity >=0.5.0;
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH =
0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({ token0: tokenA, token1: tokenB, fee: fee });
}
function computeAddress(
address factory,
PoolKey memory key
) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
}
文件 22 的 24:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 23 的 24:StakingPoolToken.sol
pragma solidity ^0.8.19;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import './interfaces/IProtocolFeeRouter.sol';
import './interfaces/IStakingPoolToken.sol';
import './TokenRewards.sol';
contract StakingPoolToken is IStakingPoolToken, ERC20 {
using SafeERC20 for IERC20;
address public immutable override indexFund;
address public immutable override stakingToken;
address public immutable override poolRewards;
address public override stakeUserRestriction;
modifier onlyRestricted() {
require(_msgSender() == stakeUserRestriction, 'RESUSERAUTH');
_;
}
constructor(
string memory _name,
string memory _symbol,
address _pairedLpToken,
address _stakingToken,
address _rewardsToken,
address _stakeUserRestriction,
IProtocolFeeRouter _feeRouter,
IV3TwapUtilities _v3TwapUtilities
) ERC20(_name, _symbol) {
indexFund = _msgSender();
stakingToken = _stakingToken;
stakeUserRestriction = _stakeUserRestriction;
poolRewards = address(
new TokenRewards(
_feeRouter,
_v3TwapUtilities,
indexFund,
_pairedLpToken,
address(this),
_rewardsToken
)
);
}
function stake(address _user, uint256 _amount) external override {
if (stakeUserRestriction != address(0)) {
require(_user == stakeUserRestriction, 'RESTRICT');
}
_mint(_user, _amount);
IERC20(stakingToken).safeTransferFrom(_msgSender(), address(this), _amount);
emit Stake(_msgSender(), _user, _amount);
}
function unstake(uint256 _amount) external override {
_burn(_msgSender(), _amount);
IERC20(stakingToken).safeTransfer(_msgSender(), _amount);
emit Unstake(_msgSender(), _amount);
}
function removeStakeUserRestriction() external onlyRestricted {
stakeUserRestriction = address(0);
}
function setStakeUserRestriction(address _user) external onlyRestricted {
stakeUserRestriction = _user;
}
function _afterTokenTransfer(
address _from,
address _to,
uint256 _amount
) internal override {
if (_from != address(0) && _from != address(0xdead)) {
TokenRewards(poolRewards).setShares(_from, _amount, true);
}
if (_to != address(0) && _to != address(0xdead)) {
TokenRewards(poolRewards).setShares(_to, _amount, false);
}
}
}
文件 24 的 24:TokenRewards.sol
pragma solidity ^0.8.19;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Context.sol';
import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol';
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol';
import './interfaces/IDecentralizedIndex.sol';
import './interfaces/IPEAS.sol';
import './interfaces/IProtocolFees.sol';
import './interfaces/IProtocolFeeRouter.sol';
import './interfaces/ITokenRewards.sol';
import './interfaces/IV3TwapUtilities.sol';
import './interfaces/IUniswapV2Router02.sol';
import './libraries/BokkyPooBahsDateTimeLibrary.sol';
import './libraries/PoolAddress.sol';
contract TokenRewards is ITokenRewards, Context {
using SafeERC20 for IERC20;
address constant V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
uint256 constant PRECISION = 10 ** 36;
uint24 constant REWARDS_POOL_FEE = 10000;
address immutable INDEX_FUND;
address immutable PAIRED_LP_TOKEN;
IProtocolFeeRouter immutable PROTOCOL_FEE_ROUTER;
IV3TwapUtilities immutable V3_TWAP_UTILS;
struct Reward {
uint256 excluded;
uint256 realized;
}
address public immutable override trackingToken;
address public immutable override rewardsToken;
uint256 public override totalShares;
uint256 public override totalStakers;
mapping(address => uint256) public shares;
mapping(address => Reward) public rewards;
uint256 _rewardsSwapSlippage = 10;
uint256 _rewardsPerShare;
uint256 public rewardsDistributed;
uint256 public rewardsDeposited;
mapping(uint256 => uint256) public rewardsDepMonthly;
modifier onlyTrackingToken() {
require(_msgSender() == trackingToken, 'UNAUTHORIZED');
_;
}
constructor(
IProtocolFeeRouter _feeRouter,
IV3TwapUtilities _v3TwapUtilities,
address _indexFund,
address _pairedLpToken,
address _trackingToken,
address _rewardsToken
) {
PROTOCOL_FEE_ROUTER = _feeRouter;
V3_TWAP_UTILS = _v3TwapUtilities;
INDEX_FUND = _indexFund;
PAIRED_LP_TOKEN = _pairedLpToken;
trackingToken = _trackingToken;
rewardsToken = _rewardsToken;
}
function setShares(
address _wallet,
uint256 _amount,
bool _sharesRemoving
) external override onlyTrackingToken {
_setShares(_wallet, _amount, _sharesRemoving);
}
function _setShares(
address _wallet,
uint256 _amount,
bool _sharesRemoving
) internal {
_processFeesIfApplicable();
if (_sharesRemoving) {
_removeShares(_wallet, _amount);
emit RemoveShares(_wallet, _amount);
} else {
_addShares(_wallet, _amount);
emit AddShares(_wallet, _amount);
}
}
function _addShares(address _wallet, uint256 _amount) internal {
if (shares[_wallet] > 0) {
_distributeReward(_wallet);
}
uint256 sharesBefore = shares[_wallet];
totalShares += _amount;
shares[_wallet] += _amount;
if (sharesBefore == 0 && shares[_wallet] > 0) {
totalStakers++;
}
rewards[_wallet].excluded = _cumulativeRewards(shares[_wallet]);
}
function _removeShares(address _wallet, uint256 _amount) internal {
require(shares[_wallet] > 0 && _amount <= shares[_wallet], 'REMOVE');
_distributeReward(_wallet);
totalShares -= _amount;
shares[_wallet] -= _amount;
if (shares[_wallet] == 0) {
totalStakers--;
}
rewards[_wallet].excluded = _cumulativeRewards(shares[_wallet]);
}
function _processFeesIfApplicable() internal {
IDecentralizedIndex(INDEX_FUND).processPreSwapFeesAndSwap();
if (
rewardsToken != PAIRED_LP_TOKEN &&
IERC20(PAIRED_LP_TOKEN).balanceOf(address(this)) > 0
) {
depositFromPairedLpToken(0, 0);
}
}
function depositFromPairedLpToken(
uint256 _amountTknDepositing,
uint256 _slippageOverride
) public override {
require(PAIRED_LP_TOKEN != rewardsToken, 'LPREWSAME');
if (_amountTknDepositing > 0) {
IERC20(PAIRED_LP_TOKEN).safeTransferFrom(
_msgSender(),
address(this),
_amountTknDepositing
);
}
uint256 _amountTkn = IERC20(PAIRED_LP_TOKEN).balanceOf(address(this));
require(_amountTkn > 0, 'NEEDTKN');
uint256 _adminAmt;
(uint256 _yieldAdminFee, ) = _getYieldFees();
if (_yieldAdminFee > 0) {
_adminAmt =
(_amountTkn * _yieldAdminFee) /
PROTOCOL_FEE_ROUTER.protocolFees().DEN();
_amountTkn -= _adminAmt;
}
(address _token0, address _token1) = PAIRED_LP_TOKEN < rewardsToken
? (PAIRED_LP_TOKEN, rewardsToken)
: (rewardsToken, PAIRED_LP_TOKEN);
PoolAddress.PoolKey memory _poolKey = PoolAddress.PoolKey({
token0: _token0,
token1: _token1,
fee: REWARDS_POOL_FEE
});
address _pool = PoolAddress.computeAddress(
IPeripheryImmutableState(V3_ROUTER).factory(),
_poolKey
);
uint160 _rewardsSqrtPriceX96 = V3_TWAP_UTILS
.sqrtPriceX96FromPoolAndInterval(_pool);
uint256 _rewardsPriceX96 = V3_TWAP_UTILS.priceX96FromSqrtPriceX96(
_rewardsSqrtPriceX96
);
uint256 _amountOut = _token0 == PAIRED_LP_TOKEN
? (_rewardsPriceX96 * _amountTkn) / FixedPoint96.Q96
: (_amountTkn * FixedPoint96.Q96) / _rewardsPriceX96;
uint256 _rewardsBalBefore = IERC20(rewardsToken).balanceOf(address(this));
IERC20(PAIRED_LP_TOKEN).safeIncreaseAllowance(V3_ROUTER, _amountTkn);
uint256 _slippage = _slippageOverride > 0
? _slippageOverride
: _rewardsSwapSlippage;
try
ISwapRouter(V3_ROUTER).exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: PAIRED_LP_TOKEN,
tokenOut: rewardsToken,
fee: REWARDS_POOL_FEE,
recipient: address(this),
deadline: block.timestamp,
amountIn: _amountTkn,
amountOutMinimum: (_amountOut * (1000 - _slippage)) / 1000,
sqrtPriceLimitX96: 0
})
)
{
if (_adminAmt > 0) {
IERC20(PAIRED_LP_TOKEN).safeTransfer(
Ownable(address(V3_TWAP_UTILS)).owner(),
_adminAmt
);
}
_rewardsSwapSlippage = 10;
_depositRewards(
IERC20(rewardsToken).balanceOf(address(this)) - _rewardsBalBefore
);
} catch {
if (_rewardsSwapSlippage < 500) {
_rewardsSwapSlippage += 10;
}
IERC20(PAIRED_LP_TOKEN).safeDecreaseAllowance(V3_ROUTER, _amountTkn);
}
}
function depositRewards(uint256 _amount) external override {
require(_amount > 0, 'DEPAM');
uint256 _rewardsBalBefore = IERC20(rewardsToken).balanceOf(address(this));
IERC20(rewardsToken).safeTransferFrom(_msgSender(), address(this), _amount);
_depositRewards(
IERC20(rewardsToken).balanceOf(address(this)) - _rewardsBalBefore
);
}
function _depositRewards(uint256 _amountTotal) internal {
if (_amountTotal == 0) {
return;
}
if (totalShares == 0) {
_burnRewards(_amountTotal);
return;
}
uint256 _depositAmount = _amountTotal;
(, uint256 _yieldBurnFee) = _getYieldFees();
if (_yieldBurnFee > 0) {
uint256 _burnAmount = (_amountTotal * _yieldBurnFee) /
PROTOCOL_FEE_ROUTER.protocolFees().DEN();
if (_burnAmount > 0) {
_burnRewards(_burnAmount);
_depositAmount -= _burnAmount;
}
}
rewardsDeposited += _depositAmount;
rewardsDepMonthly[beginningOfMonth(block.timestamp)] += _depositAmount;
_rewardsPerShare += (PRECISION * _depositAmount) / totalShares;
emit DepositRewards(_msgSender(), _depositAmount);
}
function _distributeReward(address _wallet) internal {
if (shares[_wallet] == 0) {
return;
}
uint256 _amount = getUnpaid(_wallet);
rewards[_wallet].realized += _amount;
rewards[_wallet].excluded = _cumulativeRewards(shares[_wallet]);
if (_amount > 0) {
rewardsDistributed += _amount;
IERC20(rewardsToken).safeTransfer(_wallet, _amount);
emit DistributeReward(_wallet, _amount);
}
}
function _burnRewards(uint256 _burnAmount) internal {
try IPEAS(rewardsToken).burn(_burnAmount) {} catch {
IERC20(rewardsToken).safeTransfer(address(0xdead), _burnAmount);
}
}
function _getYieldFees()
internal
view
returns (uint256 _admin, uint256 _burn)
{
IProtocolFees _fees = PROTOCOL_FEE_ROUTER.protocolFees();
if (address(_fees) != address(0)) {
_admin = _fees.yieldAdmin();
_burn = _fees.yieldBurn();
}
}
function beginningOfMonth(uint256 _timestamp) public pure returns (uint256) {
(, , uint256 _dayOfMonth) = BokkyPooBahsDateTimeLibrary.timestampToDate(
_timestamp
);
return _timestamp - ((_dayOfMonth - 1) * 1 days) - (_timestamp % 1 days);
}
function claimReward(address _wallet) external override {
_distributeReward(_wallet);
emit ClaimReward(_wallet);
}
function getUnpaid(address _wallet) public view returns (uint256) {
if (shares[_wallet] == 0) {
return 0;
}
uint256 earnedRewards = _cumulativeRewards(shares[_wallet]);
uint256 rewardsExcluded = rewards[_wallet].excluded;
if (earnedRewards <= rewardsExcluded) {
return 0;
}
return earnedRewards - rewardsExcluded;
}
function _cumulativeRewards(uint256 _share) internal view returns (uint256) {
return (_share * _rewardsPerShare) / PRECISION;
}
}
{
"compilationTarget": {
"contracts/StakingPoolToken.sol": "StakingPoolToken"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_pairedLpToken","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"address","name":"_stakeUserRestriction","type":"address"},{"internalType":"contract IProtocolFeeRouter","name":"_feeRouter","type":"address"},{"internalType":"contract IV3TwapUtilities","name":"_v3TwapUtilities","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstake","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"indexFund","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolRewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"removeStakeUserRestriction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"setStakeUserRestriction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeUserRestriction","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"}]