编译器
0.8.10+commit.fc410830
文件 1 的 9:BaseFeeder.sol
pragma solidity 0.8.10;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interfaces/IRewardManager.sol";
abstract contract BaseFeeder is Ownable {
uint256 public constant WEEK = 7 days;
address public token;
address public rewardSource;
IRewardManager public rewardManager;
uint40 public lastRewardBlock;
uint40 public rewardEndBlock;
uint256 public rewardRatePerBlock;
mapping(address => bool) public whitelistedFeedCallers;
event Feed(uint256 feedAmount);
event SetCanDistributeRewards(bool canDistributeRewards);
event SetNewRewardEndBlock(address indexed caller, uint256 preRewardEndBlock, uint256 newRewardEndBlock);
event SetNewRewardRatePerBlock(address indexed caller, uint256 prevRate, uint256 newRate);
event SetNewRewardSource(address indexed caller, address prevSource, address newSource);
event SetNewRewardManager(address indexed caller, address prevManager, address newManager);
event SetWhitelistedFeedCaller(address indexed caller, address indexed addr, bool ok);
constructor(
address _rewardManager,
address _rewardSource,
uint256 _rewardRatePerBlock,
uint40 _lastRewardBlock,
uint40 _rewardEndBlock
) {
rewardManager = IRewardManager(_rewardManager);
token = rewardManager.rewardToken();
rewardSource = _rewardSource;
lastRewardBlock = _lastRewardBlock;
rewardEndBlock = _rewardEndBlock;
rewardRatePerBlock = _rewardRatePerBlock;
require(_lastRewardBlock < _rewardEndBlock, "bad _lastRewardBlock");
}
function feed() external {
require(whitelistedFeedCallers[msg.sender],"!whitelisted");
_feed();
}
function _feed() virtual internal;
function setRewardRatePerBlock(uint256 _newRate) virtual external onlyOwner {
_feed();
uint256 _prevRate = rewardRatePerBlock;
rewardRatePerBlock = _newRate;
emit SetNewRewardRatePerBlock(msg.sender, _prevRate, _newRate);
}
function setRewardEndBlock(uint40 _newRewardEndBlock) external onlyOwner {
uint40 _prevRewardEndBlock = rewardEndBlock;
require(_newRewardEndBlock > rewardEndBlock, "!future");
rewardEndBlock = _newRewardEndBlock;
emit SetNewRewardEndBlock(msg.sender, _prevRewardEndBlock, _newRewardEndBlock);
}
function setRewardSource(address _rewardSource) external onlyOwner {
address _prevSource = rewardSource;
rewardSource = _rewardSource;
emit SetNewRewardSource(msg.sender, _prevSource , _rewardSource);
}
function setRewardManager(address _newManager) external onlyOwner {
address _prevManager = address(rewardManager);
rewardManager = IRewardManager(_newManager);
emit SetNewRewardManager(msg.sender, _prevManager, _newManager);
}
function setWhitelistedFeedCallers(address[] calldata callers, bool ok) external onlyOwner {
for (uint256 idx = 0; idx < callers.length; idx++) {
whitelistedFeedCallers[callers[idx]] = ok;
emit SetWhitelistedFeedCaller(msg.sender, callers[idx], ok);
}
}
}
文件 2 的 9: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;
}
}
文件 3 的 9:GFFeeder01.sol
pragma solidity 0.8.10;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./base/BaseFeeder.sol";
import "./interfaces/IxGF.sol";
import "./interfaces/IStakingPoolManager.sol";
import "./SafeToken.sol";
contract GFFeeder01 is BaseFeeder {
using SafeToken for address;
struct Snapshot {
uint112 stakingPoolTvl;
uint112 xGFTvl;
uint32 blockNumber;
}
uint256 public constant MAX_BIAS = 10000;
IStakingPoolManager public stakingPoolManager;
address public stakingPool;
uint256 public bias = 3000;
mapping(uint256 => Snapshot) public weeklySnapshotOf;
event MissingSnapshot(uint40 fromBlock, uint40 toBlock);
event SetBias(uint256 oldBias, uint256 newBias);
constructor(
address _stakingPool,
address _stakingPoolManager,
address _rewardManager,
address _rewardSource,
uint256 _rewardRatePerBlock,
uint40 _lastRewardBlock,
uint40 _rewardEndBlock
) BaseFeeder(_rewardManager, _rewardSource, _rewardRatePerBlock, _lastRewardBlock, _rewardEndBlock) {
stakingPoolManager = IStakingPoolManager(_stakingPoolManager);
stakingPool = _stakingPool;
require(stakingPoolManager.reward() == rewardManager.rewardToken(), "invalid legacy reward");
token.safeApprove(_rewardManager, type(uint256).max);
}
function stakingPoolTvl() public view returns (uint256) {
return IERC20(token).balanceOf(stakingPool);
}
function xGFTvl() public view returns (uint256) {
return IxGF(rewardManager.xGF()).supply() + rewardManager.lastTokenBalance();
}
function getRate(uint256 timestamp) external view returns (uint256, uint256) {
uint256 _weekCursor = _timestampToFloorWeek(timestamp);
return _getRate(weeklySnapshotOf[_weekCursor], rewardRatePerBlock);
}
function _feed() override internal {
Snapshot memory _thisWeekSnapshot = weeklySnapshotOf[_timestampToFloorWeek(block.timestamp)];
if (_thisWeekSnapshot.blockNumber == 0) {
emit MissingSnapshot(lastRewardBlock, uint40(block.number));
} else {
_updatePools(_thisWeekSnapshot, lastRewardBlock);
}
_updateLastRewardBlock(uint40(block.number));
_takeSnapshot(_timestampToFloorWeek(block.timestamp + WEEK - 1));
}
function _updateLastRewardBlock(uint40 blockNumber) internal {
uint40 _rewardEndBlock = rewardEndBlock;
lastRewardBlock = blockNumber > _rewardEndBlock ? _rewardEndBlock : blockNumber;
}
function _updatePools(Snapshot memory _snapshot, uint40 _lastRewardBlock) internal {
(uint256 rate1, uint256 rate2) = _getRate(_snapshot, rewardRatePerBlock);
_setStakingPoolManagerRate(rate1);
uint256 _feedAmount = _feedRewardManager(rate2, _lastRewardBlock, block.number);
emit Feed(_feedAmount);
}
function _takeSnapshot(uint256 _weekCursor) internal {
weeklySnapshotOf[_weekCursor] = Snapshot({
stakingPoolTvl: uint112(stakingPoolTvl()),
xGFTvl: uint112(xGFTvl()),
blockNumber: uint32(block.number)
});
}
function _setStakingPoolManagerRate(uint256 _rate) internal {
if (stakingPoolManager.rewardPerBlock() == _rate) {
stakingPoolManager.distributeRewards();
} else {
stakingPoolManager.setRewardPerBlock(_rate);
}
}
function _feedRewardManager(
uint256 _rate,
uint256 _fromBlock,
uint256 _toBlock
) internal returns (uint256) {
uint256 blockDelta = _getMultiplier(_fromBlock, _toBlock, rewardEndBlock);
if (blockDelta == 0) {
return 0;
}
uint256 _toDistribute = _rate * blockDelta;
if (_toDistribute > 0) {
token.safeTransferFrom(rewardSource, address(this), _toDistribute);
rewardManager.feed(_toDistribute);
}
return _toDistribute;
}
function _getMultiplier(
uint256 _from,
uint256 _to,
uint256 _endBlock
) internal pure returns (uint256) {
if ((_from >= _endBlock) || (_from > _to)) {
return 0;
}
if (_to <= _endBlock) {
return _to - _from;
}
return _endBlock - _from;
}
function _getRate(Snapshot memory _snapshot, uint256 _maxRatePerBlock)
internal
view
returns (uint256 rate1, uint256 rate2)
{
if (_snapshot.stakingPoolTvl == 0 || _snapshot.xGFTvl == 0) {
rate1 = 0;
rate2 = 0;
return (rate1, rate2);
}
uint256 _bias = bias;
uint256 _adjustedV1Weight = uint256(_snapshot.stakingPoolTvl) * (MAX_BIAS - _bias);
uint256 _adjustedV2Weight = uint256(_snapshot.xGFTvl) * (MAX_BIAS + _bias);
uint256 _totalWeight = _adjustedV1Weight + _adjustedV2Weight;
rate1 = (_maxRatePerBlock * _adjustedV1Weight) / _totalWeight;
rate2 = (_maxRatePerBlock * _adjustedV2Weight) / _totalWeight;
}
function _timestampToFloorWeek(uint256 _timestamp) internal pure returns (uint256) {
return (_timestamp / WEEK) * WEEK;
}
function setBias(uint256 _newBias, bool _distribute) external onlyOwner {
require(_newBias <= MAX_BIAS, "exceed MAX_BIAS");
if (_distribute) {
_feed();
}
uint256 _oldBias = bias;
bias = _newBias;
emit SetBias(_oldBias, _newBias);
}
function injectSnapshot(uint256 _timestamp, Snapshot memory _snapshot) external onlyOwner {
uint256 _weekCursor = _timestampToFloorWeek(_timestamp);
weeklySnapshotOf[_weekCursor] = _snapshot;
}
function setRewardRatePerBlock(uint256 _newRate) override external onlyOwner {
Snapshot memory _thisWeekSnapshot = weeklySnapshotOf[_timestampToFloorWeek(block.timestamp)];
require(_thisWeekSnapshot.blockNumber != 0, "!thisWeekSnapshot");
_updatePools(_thisWeekSnapshot, lastRewardBlock);
_updateLastRewardBlock(uint40(block.number));
_takeSnapshot(_timestampToFloorWeek(block.timestamp + WEEK - 1));
uint256 _nextWeekCursor = _timestampToFloorWeek(block.timestamp + WEEK - 1);
_takeSnapshot(_nextWeekCursor);
uint256 _prevRate = rewardRatePerBlock;
rewardRatePerBlock = _newRate;
(uint newRate1,) = _getRate(_thisWeekSnapshot, _newRate);
_setStakingPoolManagerRate(newRate1);
emit SetNewRewardRatePerBlock(msg.sender, _prevRate, _newRate);
}
}
文件 4 的 9:IERC20.sol
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
文件 5 的 9:IRewardManager.sol
pragma solidity 0.8.10;
interface IRewardManager {
function xGF() external view returns (address);
function rewardToken() external returns (address);
function feed(uint256 _amount) external returns (bool);
function claim(address _for) external returns (uint256);
function pendingRewardsOf(address _user) external returns (uint256);
function lastTokenBalance() external view returns (uint256);
function checkpointToken() external view returns (uint256);
}
文件 6 的 9:IStakingPoolManager.sol
pragma solidity 0.8.10;
interface IStakingPoolManager {
function reward() external view returns (address);
function rewardPerBlock() external view returns (uint256);
function setRewardPerBlock(uint256 _rewardPerBlock) external;
function distributeRewards() external;
}
文件 7 的 9:IxGF.sol
pragma solidity 0.8.10;
struct Point {
int128 bias;
int128 slope;
uint256 timestamp;
uint256 blockNumber;
}
interface IxGF {
function userPointEpoch(address _user) external view returns (uint256);
function epoch() external view returns (uint256);
function userPointHistory(address _user, uint256 _epoch) external view returns (Point memory);
function pointHistory(uint256 _epoch) external view returns (Point memory);
function checkpoint() external;
function supply() external view returns (uint256);
}
文件 8 的 9: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());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
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);
}
}
文件 9 的 9:SafeToken.sol
pragma solidity 0.8.10;
interface ERC20Interface {
function balanceOf(address user) external view returns (uint256);
}
library SafeToken {
function myBalance(address token) internal view returns (uint256) {
return ERC20Interface(token).balanceOf(address(this));
}
function safeTransfer(address token, address to, uint256 value) internal {
require(token.code.length > 0, "!contract");
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer");
}
function safeTransferFrom(address token, address from, address to, uint256 value) internal {
require(token.code.length > 0, "!contract");
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom");
}
function safeApprove(address token, address to, uint256 value) internal {
require(token.code.length > 0, "!contract");
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove");
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{ value: value }(new bytes(0));
require(success, "!safeTransferETH");
}
}
{
"compilationTarget": {
"contracts/8.10/GFFeeder01.sol": "GFFeeder01"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_stakingPool","type":"address"},{"internalType":"address","name":"_stakingPoolManager","type":"address"},{"internalType":"address","name":"_rewardManager","type":"address"},{"internalType":"address","name":"_rewardSource","type":"address"},{"internalType":"uint256","name":"_rewardRatePerBlock","type":"uint256"},{"internalType":"uint40","name":"_lastRewardBlock","type":"uint40"},{"internalType":"uint40","name":"_rewardEndBlock","type":"uint40"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feedAmount","type":"uint256"}],"name":"Feed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint40","name":"fromBlock","type":"uint40"},{"indexed":false,"internalType":"uint40","name":"toBlock","type":"uint40"}],"name":"MissingSnapshot","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldBias","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBias","type":"uint256"}],"name":"SetBias","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"canDistributeRewards","type":"bool"}],"name":"SetCanDistributeRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"preRewardEndBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRewardEndBlock","type":"uint256"}],"name":"SetNewRewardEndBlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"prevManager","type":"address"},{"indexed":false,"internalType":"address","name":"newManager","type":"address"}],"name":"SetNewRewardManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"prevRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRate","type":"uint256"}],"name":"SetNewRewardRatePerBlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"prevSource","type":"address"},{"indexed":false,"internalType":"address","name":"newSource","type":"address"}],"name":"SetNewRewardSource","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"bool","name":"ok","type":"bool"}],"name":"SetWhitelistedFeedCaller","type":"event"},{"inputs":[],"name":"MAX_BIAS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEEK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bias","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timestamp","type":"uint256"},{"components":[{"internalType":"uint112","name":"stakingPoolTvl","type":"uint112"},{"internalType":"uint112","name":"xGFTvl","type":"uint112"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"internalType":"struct GFFeeder01.Snapshot","name":"_snapshot","type":"tuple"}],"name":"injectSnapshot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastRewardBlock","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardEndBlock","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardManager","outputs":[{"internalType":"contract IRewardManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardSource","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newBias","type":"uint256"},{"internalType":"bool","name":"_distribute","type":"bool"}],"name":"setBias","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"_newRewardEndBlock","type":"uint40"}],"name":"setRewardEndBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newManager","type":"address"}],"name":"setRewardManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newRate","type":"uint256"}],"name":"setRewardRatePerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardSource","type":"address"}],"name":"setRewardSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"callers","type":"address[]"},{"internalType":"bool","name":"ok","type":"bool"}],"name":"setWhitelistedFeedCallers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPoolManager","outputs":[{"internalType":"contract IStakingPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPoolTvl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"weeklySnapshotOf","outputs":[{"internalType":"uint112","name":"stakingPoolTvl","type":"uint112"},{"internalType":"uint112","name":"xGFTvl","type":"uint112"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedFeedCallers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xGFTvl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]