文件 1 的 10: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;
}
}
文件 2 的 10:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 3 的 10:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 4 的 10:IStakeRewards.sol
pragma solidity ^0.8.4;
interface IStakeRewards {
function claimReward(bool compound) external;
function depositRewards() external payable;
function getShares(address wallet) external view returns (uint256);
function setShare(
address shareholder,
uint256 balanceUpdate,
bool isRemoving
) external;
}
文件 5 的 10:IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
文件 6 的 10:IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
文件 7 的 10:IUniswapV2Router01.sol
pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure 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 addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, 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 removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
文件 8 的 10:IUniswapV2Router02.sol
pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
文件 9 的 10: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);
}
}
文件 10 的 10:StakeRewards.sol
pragma solidity ^0.8.9;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';
import './interfaces/IStakeRewards.sol';
contract StakeRewards is IStakeRewards, Ownable {
address public ydf;
IERC721 private sYDF;
IERC721 private slYDF;
IUniswapV2Router02 private uniswapV2Router;
uint256 public compoundBuySlippage = 2;
uint256 public totalStakedUsers;
uint256 public totalSharesDeposited;
struct Share {
uint256 amount;
uint256 stakedTime;
}
struct Reward {
uint256 totalExcluded;
uint256 totalRealised;
}
mapping(address => Share) private shares;
mapping(address => Reward) public rewards;
uint256 public totalRewards;
uint256 public totalDistributed;
uint256 public rewardsPerShare;
uint256 private constant ACC_FACTOR = 10**36;
event AddShares(address indexed user, uint256 amount);
event RemoveShares(address indexed user, uint256 amount);
event ClaimReward(address user);
event DistributeReward(address indexed user, uint256 amount);
event DepositRewards(address indexed user, uint256 amountTokens);
modifier onlyToken() {
require(
msg.sender == address(sYDF) || msg.sender == address(slYDF),
'must be stake token'
);
_;
}
constructor(address _ydf, address _dexRouter) {
ydf = _ydf;
uniswapV2Router = IUniswapV2Router02(_dexRouter);
}
function setShare(
address shareholder,
uint256 balanceUpdate,
bool isRemoving
) external override onlyToken {
if (isRemoving) {
_removeShares(shareholder, balanceUpdate);
emit RemoveShares(shareholder, balanceUpdate);
} else {
_addShares(shareholder, balanceUpdate);
emit AddShares(shareholder, balanceUpdate);
}
}
function _addShares(address shareholder, uint256 amount) private {
if (shares[shareholder].amount > 0) {
_distributeReward(shareholder, false);
}
uint256 sharesBefore = shares[shareholder].amount;
totalSharesDeposited += amount;
shares[shareholder].amount += amount;
shares[shareholder].stakedTime = block.timestamp;
if (sharesBefore == 0 && shares[shareholder].amount > 0) {
totalStakedUsers++;
}
rewards[shareholder].totalExcluded = getCumulativeRewards(
shares[shareholder].amount
);
}
function _removeShares(address shareholder, uint256 amount) private {
require(
shares[shareholder].amount > 0 &&
(amount == 0 || amount <= shares[shareholder].amount),
'you can only unstake if you have some staked'
);
_distributeReward(shareholder, false);
uint256 removeAmount = amount == 0 ? shares[shareholder].amount : amount;
totalSharesDeposited -= removeAmount;
shares[shareholder].amount -= removeAmount;
rewards[shareholder].totalExcluded = getCumulativeRewards(
shares[shareholder].amount
);
}
function depositRewards() external payable override {
uint256 _amount = msg.value;
require(_amount > 0, 'must provide ETH to deposit for rewards');
require(totalSharesDeposited > 0, 'must be shares to distribute rewards');
totalRewards += _amount;
rewardsPerShare += (ACC_FACTOR * _amount) / totalSharesDeposited;
emit DepositRewards(msg.sender, _amount);
}
function _distributeReward(address shareholder, bool compound) internal {
if (shares[shareholder].amount == 0) {
return;
}
uint256 amount = getUnpaid(shareholder);
rewards[shareholder].totalRealised += amount;
rewards[shareholder].totalExcluded = getCumulativeRewards(
shares[shareholder].amount
);
if (amount > 0) {
totalDistributed += amount;
uint256 _balBefore = address(this).balance;
if (compound) {
uint256 _tokensToReceiveNoSlip = _getTokensToReceiveOnBuyNoSlippage(
amount
);
address[] memory path = new address[](2);
path[0] = uniswapV2Router.WETH();
path[1] = ydf;
uniswapV2Router.swapExactETHForTokens{ value: amount }(
(_tokensToReceiveNoSlip * (100 - compoundBuySlippage)) / 100,
path,
shareholder,
block.timestamp
);
} else {
payable(shareholder).call{ value: amount }('');
}
require(address(this).balance >= _balBefore - amount, 'took too much');
emit DistributeReward(shareholder, amount);
}
}
function _getTokensToReceiveOnBuyNoSlippage(uint256 _amountETH)
internal
view
returns (uint256)
{
address pairAddy = IUniswapV2Factory(uniswapV2Router.factory()).getPair(
uniswapV2Router.WETH(),
ydf
);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddy);
(uint112 _r0, uint112 _r1, ) = pair.getReserves();
if (pair.token0() == uniswapV2Router.WETH()) {
return (_amountETH * _r1) / _r0;
} else {
return (_amountETH * _r0) / _r1;
}
}
function claimReward(bool _compound) external override {
_distributeReward(msg.sender, _compound);
emit ClaimReward(msg.sender);
}
function getUnpaid(address shareholder) public view returns (uint256) {
if (shares[shareholder].amount == 0) {
return 0;
}
uint256 earnedRewards = getCumulativeRewards(shares[shareholder].amount);
uint256 rewardsExcluded = rewards[shareholder].totalExcluded;
if (earnedRewards <= rewardsExcluded) {
return 0;
}
return earnedRewards - rewardsExcluded;
}
function getCumulativeRewards(uint256 share) internal view returns (uint256) {
return (share * rewardsPerShare) / ACC_FACTOR;
}
function getShares(address user) external view override returns (uint256) {
return shares[user].amount;
}
function getsYDF() external view returns (address) {
return address(sYDF);
}
function getslYDF() external view returns (address) {
return address(slYDF);
}
function setCompoundBuySlippage(uint8 _slippage) external onlyOwner {
require(_slippage <= 100, 'cannot be more than 100% slippage');
compoundBuySlippage = _slippage;
}
function setsYDF(address _sYDF) external onlyOwner {
sYDF = IERC721(_sYDF);
}
function setslYDF(address _slYDF) external onlyOwner {
slYDF = IERC721(_slYDF);
}
}
{
"compilationTarget": {
"contracts/StakeRewards.sol": "StakeRewards"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_ydf","type":"address"},{"internalType":"address","name":"_dexRouter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AddShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"ClaimReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountTokens","type":"uint256"}],"name":"DepositRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DistributeReward","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":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RemoveShares","type":"event"},{"inputs":[{"internalType":"bool","name":"_compound","type":"bool"}],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"compoundBuySlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositRewards","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"shareholder","type":"address"}],"name":"getUnpaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getsYDF","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getslYDF","outputs":[{"internalType":"address","name":"","type":"address"}],"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":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"totalExcluded","type":"uint256"},{"internalType":"uint256","name":"totalRealised","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"_slippage","type":"uint8"}],"name":"setCompoundBuySlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"shareholder","type":"address"},{"internalType":"uint256","name":"balanceUpdate","type":"uint256"},{"internalType":"bool","name":"isRemoving","type":"bool"}],"name":"setShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sYDF","type":"address"}],"name":"setsYDF","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_slYDF","type":"address"}],"name":"setslYDF","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDistributed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSharesDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakedUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ydf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]