编译器
0.6.12+commit.27d51765
文件 1 的 12:Address.sol
pragma solidity ^0.6.12;
library Address {
function isContract(address account) internal view returns (bool) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
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 functionCall(target, data, "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");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 12:Context.sol
pragma solidity ^0.6.12;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 3 的 12:ERC20.sol
pragma solidity ^0.6.12;
import './Context.sol';
import './IERC20.sol';
import './SafeMath.sol';
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) internal _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
_decimals = 18;
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function decimals() public view override returns (uint8) {
return _decimals;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, 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) {
_approve(_msgSender(), spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, 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 = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(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);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(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 _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
文件 4 的 12:IERC20.sol
pragma solidity ^0.6.12;
interface IERC20 {
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 12:IUniswapV2Router02.sol
pragma solidity ^0.6.12;
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);
}
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;
}
文件 6 的 12:Ownable.sol
pragma solidity ^0.6.12;
import './Context.sol';
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 7 的 12:SURF.sol
pragma solidity ^0.6.12;
import './ERC20.sol';
import './IERC20.sol';
import './Ownable.sol';
import './Whirlpool.sol';
interface Callable {
function tokenCallback(address _from, uint256 _tokens, bytes calldata _data) external returns (bool);
function receiveApproval(address _from, uint256 _tokens, address _token, bytes calldata _data) external;
}
contract SURF is ERC20("SURF.Finance", "SURF"), Ownable {
uint256 public constant MAX_SUPPLY = 10000000 * 10**18;
bool public maxSupplyHit = false;
uint256 public transferFee = 10;
mapping(address => bool) public senderWhitelist;
mapping(address => bool) public recipientWhitelist;
address public titoAddress;
address payable public whirlpoolAddress;
address public surfPoolAddress;
function mint(address _to, uint256 _amount) public {
require(maxSupplyHit != true, "max supply hit");
require(msg.sender == titoAddress, "not Tito");
uint256 supply = totalSupply();
if (supply.add(_amount) >= MAX_SUPPLY) {
_amount = MAX_SUPPLY.sub(supply);
maxSupplyHit = true;
}
if (_amount > 0) {
_mint(_to, _amount);
_moveDelegates(address(0), _delegates[_to], _amount);
}
}
function setContractAddresses(address _titoAddress, address payable _whirlpoolAddress, address _surfPoolAddress) public onlyOwner {
if (_titoAddress != address(0)) titoAddress = _titoAddress;
if (_whirlpoolAddress != address(0)) whirlpoolAddress = _whirlpoolAddress;
if (_surfPoolAddress != address(0)) surfPoolAddress = _surfPoolAddress;
}
function setTransferFee(uint256 _transferFee) public onlyOwner {
require(_transferFee <= 100, "over 10%");
transferFee = _transferFee;
}
function addToTransferWhitelist(bool _addToSenderWhitelist, address _address) public onlyOwner {
if (_addToSenderWhitelist == true) senderWhitelist[_address] = true;
else recipientWhitelist[_address] = true;
}
function removeFromTransferWhitelist(bool _removeFromSenderWhitelist, address _address) public onlyOwner {
if (_removeFromSenderWhitelist == true) senderWhitelist[_address] = false;
else recipientWhitelist[_address] = false;
}
function migrateLockedLPTokens(address _to, uint256 _amount) public onlyOwner {
IERC20 surfPool = IERC20(surfPoolAddress);
require(_amount > 0 && _amount <= surfPool.balanceOf(address(this)), "bad amount");
surfPool.transfer(_to, _amount);
}
function approveAndCall(address _spender, uint256 _tokens, bytes calldata _data) external returns (bool) {
approve(_spender, _tokens);
Callable(_spender).receiveApproval(msg.sender, _tokens, address(this), _data);
return true;
}
function transferAndCall(address _to, uint256 _tokens, bytes calldata _data) external returns (bool) {
uint256 _balanceBefore = balanceOf(_to);
transfer(_to, _tokens);
uint256 _tokensReceived = balanceOf(_to) - _balanceBefore;
uint32 _size;
assembly {
_size := extcodesize(_to)
}
if (_size > 0) {
require(Callable(_to).tokenCallback(msg.sender, _tokensReceived, _data));
}
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal override {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
uint256 transferFeeAmount;
uint256 tokensToTransfer;
if (amount > 0) {
if (_isWhitelistedTransfer(sender, recipient) != true) {
transferFeeAmount = amount.mul(transferFee).div(1000);
_balances[whirlpoolAddress] = _balances[whirlpoolAddress].add(transferFeeAmount);
_moveDelegates(_delegates[sender], _delegates[whirlpoolAddress], transferFeeAmount);
Whirlpool(whirlpoolAddress).addSurfReward(sender, transferFeeAmount);
emit Transfer(sender, whirlpoolAddress, transferFeeAmount);
}
tokensToTransfer = amount.sub(transferFeeAmount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
if (tokensToTransfer > 0) {
_balances[recipient] = _balances[recipient].add(tokensToTransfer);
_moveDelegates(_delegates[sender], _delegates[recipient], tokensToTransfer);
if (recipient == whirlpoolAddress) Whirlpool(whirlpoolAddress).addSurfReward(sender, tokensToTransfer);
}
}
emit Transfer(sender, recipient, tokensToTransfer);
}
function _isWhitelistedTransfer(address _sender, address _recipient) internal view returns (bool) {
return
_sender == whirlpoolAddress || _recipient == whirlpoolAddress ||
_sender == titoAddress || _recipient == titoAddress ||
senderWhitelist[_sender] == true || recipientWhitelist[_recipient] == true;
}
mapping (address => address) internal _delegates;
struct Checkpoint {
uint32 fromBlock;
uint256 votes;
}
mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
mapping (address => uint32) public numCheckpoints;
bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
mapping (address => uint) public nonces;
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
function delegates(address delegator) external view returns (address) {
return _delegates[delegator];
}
function delegate(address delegatee) external {
return _delegate(msg.sender, delegatee);
}
function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) external {
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name())),
getChainId(),
address(this)
)
);
bytes32 structHash = keccak256(
abi.encode(
DELEGATION_TYPEHASH,
delegatee,
nonce,
expiry
)
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator,
structHash
)
);
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), "SURF::delegateBySig: invalid signature");
require(nonce == nonces[signatory]++, "SURF::delegateBySig: invalid nonce");
require(now <= expiry, "SURF::delegateBySig: signature expired");
return _delegate(signatory, delegatee);
}
function getCurrentVotes(address account) external view returns (uint256) {
uint32 nCheckpoints = numCheckpoints[account];
return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
function getPriorVotes(address account, uint blockNumber) external view returns (uint256) {
require(blockNumber < block.number, "SURF::getPriorVotes: not yet determined");
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2;
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
function _delegate(address delegator, address delegatee) internal {
address currentDelegate = _delegates[delegator];
uint256 delegatorBalance = balanceOf(delegator);
_delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint256 srcRepNew = srcRepOld.sub(amount);
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint256 dstRepNew = dstRepOld.add(amount);
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _writeCheckpoint(address delegatee, uint32 nCheckpoints, uint256 oldVotes, uint256 newVotes) internal {
uint32 blockNumber = safe32(block.number, "SURF::_writeCheckpoint: block number exceeds 32 bits");
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function getChainId() internal pure returns (uint) {
uint256 chainId;
assembly { chainId := chainid() }
return chainId;
}
}
文件 8 的 12:SafeERC20.sol
pragma solidity ^0.6.12;
import './SafeMath.sol';
import './Address.sol';
import './IERC20.sol';
library SafeERC20 {
using SafeMath for uint256;
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 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 9 的 12:SafeMath.sol
pragma solidity ^0.6.12;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
文件 10 的 12:Tito.sol
pragma solidity ^0.6.12;
import './Ownable.sol';
import './SafeMath.sol';
import './SafeERC20.sol';
import './IERC20.sol';
import './IUniswapV2Router02.sol';
import './UniStakingInterfaces.sol';
import './SURF.sol';
import './Whirlpool.sol';
contract Tito is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
struct UserInfo {
uint256 staked;
uint256 rewardDebt;
uint256 uniRewardDebt;
uint256 claimed;
uint256 uniClaimed;
}
struct PoolInfo {
IERC20 token;
IERC20 lpToken;
uint256 apr;
uint256 lastSurfRewardBlock;
uint256 accSurfPerShare;
uint256 accUniPerShare;
address uniStakeContract;
}
SURF public surf;
address public surfPoolAddress;
Whirlpool public whirlpool;
IUniswapV2Router02 internal uniswapRouter = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
StakingRewardsFactory internal uniStakingFactory = StakingRewardsFactory(0x3032Ab3Fa8C01d786D29dAdE018d7f2017918e12);
IERC20 internal uniToken = IERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
IERC20 internal weth;
address payable public devAddress;
PoolInfo[] public poolInfo;
mapping(address => bool) public existingPools;
mapping (uint256 => mapping (address => UserInfo)) public userInfo;
mapping(address => bool) public contractWhitelist;
uint256 public startBlock;
bool public surfPoolActive = false;
uint256 public initialSurfPoolETH = 0;
uint256 public surfSentToWhirlpool = 0;
uint256 public donatedETH = 0;
uint256 public minimumDonationAmount = 25 * 10**18;
mapping(address => address) public donaters;
mapping(address => uint256) public donations;
uint256 internal constant APPROX_BLOCKS_PER_YEAR = uint256(uint256(365 days) / uint256(13 seconds));
uint256 internal constant DEFAULT_APR = 1000;
uint256 internal constant SOFT_LAUNCH_DURATION = 1000;
uint256 internal constant SOFT_LAUNCH_SURF_PER_BLOCK = 40 * 10**18;
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Claim(address indexed user, uint256 indexed pid, uint256 surfAmount, uint256 uniAmount);
event ClaimAll(address indexed user, uint256 surfAmount, uint256 uniAmount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event SurfBuyback(address indexed user, uint256 ethSpentOnSurf, uint256 surfBought);
event SurfPoolActive(address indexed user, uint256 surfLiquidity, uint256 ethLiquidity);
constructor(
SURF _surf,
address payable _devAddress,
uint256 _startBlock
) public {
surf = _surf;
devAddress = _devAddress;
startBlock = _startBlock;
weth = IERC20(uniswapRouter.WETH());
address uniswapfactoryAddress = uniswapRouter.factory();
address surfAddress = address(surf);
address wethAddress = address(weth);
(address token0, address token1) = surfAddress < wethAddress ? (surfAddress, wethAddress) : (wethAddress, surfAddress);
surfPoolAddress = address(uint(keccak256(abi.encodePacked(
hex'ff',
uniswapfactoryAddress,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
))));
_addInitialPools();
}
receive() external payable {}
function _addPool(address _token, address _lpToken) internal {
uint256 apr = DEFAULT_APR;
if (_token == address(surf)) apr = apr * 5;
uint256 lastSurfRewardBlock = block.number > startBlock ? block.number : startBlock;
poolInfo.push(
PoolInfo({
token: IERC20(_token),
lpToken: IERC20(_lpToken),
apr: apr,
lastSurfRewardBlock: lastSurfRewardBlock,
accSurfPerShare: 0,
accUniPerShare: 0,
uniStakeContract: address(0)
})
);
existingPools[_lpToken] = true;
}
function _addInitialPools() internal {
_addPool(address(surf), surfPoolAddress);
_addPool(0xdAC17F958D2ee523a2206206994597C13D831ec7, 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852);
_addPool(0x6B175474E89094C44Da98b954EedeAC495271d0F, 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11);
_addPool(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc);
_addPool(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940);
_addPool(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984, 0xd3d2E2692501A5c9Ca623199D38826e513033a17);
_addPool(0x514910771AF9Ca656af840dff83E8264EcF986CA, 0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974);
_addPool(0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9, 0xDFC14d2Af169B0D36C4EFF567Ada9b2E0CAE044f);
_addPool(0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F, 0x43AE24960e5534731Fc831386c07755A2dc33D47);
_addPool(0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2, 0xC2aDdA861F89bBB333c90c492cB837741916A225);
_addPool(0xc00e94Cb662C3520282E6f5717214004A7f26888, 0xCFfDdeD873554F362Ac02f8Fb1f02E5ada10516f);
_addPool(0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e, 0x2fDbAdf3C4D5A8666Bc06645B8358ab803996E28);
_addPool(0xba100000625a3754423978a60c9317c58a424e3D, 0xA70d458A4d9Bc0e6571565faee18a48dA5c0D593);
_addPool(0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b, 0x4d5ef58aAc27d99935E5b6B4A6778ff292059991);
_addPool(0xD46bA6D942050d489DBd938a2C909A5d5039A161, 0xc5be99A02C6857f9Eac67BbCE58DF5572498F40c);
_addPool(0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39, 0x55D5c232D921B9eAA6b37b5845E439aCD04b4DBa);
_addPool(0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d, 0x343FD171caf4F0287aE6b87D75A8964Dc44516Ab);
_addPool(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5, 0xdc98556Ce24f007A5eF6dC1CE96322d65832A819);
_addPool(0x84294FC9710e1252d407d3D80A84bC39001bd4A8, 0x0C5136B5d184379fa15bcA330784f2d5c226Fe96);
_addPool(0x821144518dfE9e7b44fCF4d0824e15e8390d4637, 0x490B5B2489eeFC4106C69743F657e3c4A2870aC5);
_addPool(0xB9464ef80880c5aeA54C7324c0b8Dd6ca6d05A90, 0xa8D0f6769AB020877f262D8Cd747c188D9097d7E);
_addPool(0x926dbD499d701C61eABe2d576e770ECCF9c7F4F3, 0xC7c0EDf0b5f89eff96aF0E31643Bd588ad63Ea23);
_addPool(0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E, 0x260E069deAd76baAC587B5141bB606Ef8b9Bab6c);
_addPool(0x9720Bcf5a92542D4e286792fc978B63a09731CF0, 0x08538213596fB2c392e9c5d4935ad37645600a57);
_addPool(0xEEF9f339514298C6A857EfCfC1A762aF84438dEE, 0x23d15EDceb5B5B3A23347Fa425846DE80a2E8e5C);
}
function _pendingSurf(uint256 _pid, address _user) internal view returns (uint256) {
if (_pid == 0 && surfPoolActive != true) return 0;
PoolInfo memory pool = poolInfo[_pid];
UserInfo memory user = userInfo[_pid][_user];
uint256 accSurfPerShare = pool.accSurfPerShare;
uint256 lpSupply = _getPoolSupply(_pid);
if (block.number > pool.lastSurfRewardBlock && lpSupply != 0) {
uint256 surfReward = _calculateSurfReward(_pid, lpSupply);
uint256 surfTotalSupply = surf.totalSupply();
if (surfTotalSupply.add(surfReward) >= surf.MAX_SUPPLY()) {
surfReward = surf.MAX_SUPPLY().sub(surfTotalSupply);
}
accSurfPerShare = accSurfPerShare.add(surfReward.mul(1e12).div(lpSupply));
}
return user.staked.mul(accSurfPerShare).div(1e12).sub(user.rewardDebt);
}
function _pendingUni(uint256 _pid, address _user) internal view returns (uint256) {
PoolInfo memory pool = poolInfo[_pid];
UserInfo memory user = userInfo[_pid][_user];
uint256 accUniPerShare = pool.accUniPerShare;
uint256 lpSupply = _getPoolSupply(_pid);
if (pool.uniStakeContract != address(0) && lpSupply != 0) {
uint256 uniReward = IStakingRewards(pool.uniStakeContract).earned(address(this));
accUniPerShare = accUniPerShare.add(uniReward.mul(1e12).div(lpSupply));
}
return user.staked.mul(accUniPerShare).div(1e12).sub(user.uniRewardDebt);
}
function _calculateSurfReward(uint256 _pid, uint256 _lpSupply) internal view returns (uint256 surfReward) {
if (surf.maxSupplyHit() != true) {
PoolInfo memory pool = poolInfo[_pid];
uint256 multiplier = block.number - pool.lastSurfRewardBlock;
if (block.number < startBlock + SOFT_LAUNCH_DURATION) {
if (_pid != 0) {
surfReward = multiplier * SOFT_LAUNCH_SURF_PER_BLOCK;
} else if (surfPoolActive == true) {
surfReward = multiplier * 25 * SOFT_LAUNCH_SURF_PER_BLOCK;
}
} else if (_pid != 0 && surfPoolActive != true) {
surfReward = multiplier * SOFT_LAUNCH_SURF_PER_BLOCK;
} else if (surfPoolActive == true) {
uint256 surfPrice = _getSurfPrice();
uint256 lpTokenPrice = 10**18 * 2 * weth.balanceOf(address(pool.lpToken)) / pool.lpToken.totalSupply();
uint256 scaledTotalLiquidityValue = _lpSupply * lpTokenPrice;
surfReward = multiplier * ((pool.apr * scaledTotalLiquidityValue / surfPrice) / APPROX_BLOCKS_PER_YEAR) / 100;
}
}
}
function poolLength() external view returns (uint256) {
return poolInfo.length;
}
function _getPoolData(uint256 _pid) internal view returns (address, address, bool, uint256, uint256, uint256, uint256) {
PoolInfo memory pool = poolInfo[_pid];
return (address(pool.token), address(pool.lpToken), pool.uniStakeContract != address(0), pool.apr, pool.lastSurfRewardBlock, pool.accSurfPerShare, pool.accUniPerShare);
}
function _getAllPoolData() internal view returns (address[] memory, address[] memory, bool[] memory, uint[] memory, uint[] memory, uint[2][] memory) {
uint256 length = poolInfo.length;
address[] memory tokenData = new address[](length);
address[] memory lpTokenData = new address[](length);
bool[] memory isUniData = new bool[](length);
uint[] memory aprData = new uint[](length);
uint[] memory lastSurfRewardBlockData = new uint[](length);
uint[2][] memory accTokensPerShareData = new uint[2][](length);
for (uint256 pid = 0; pid < length; ++pid) {
(tokenData[pid], lpTokenData[pid], isUniData[pid], aprData[pid], lastSurfRewardBlockData[pid], accTokensPerShareData[pid][0], accTokensPerShareData[pid][1]) = _getPoolData(pid);
}
return (tokenData, lpTokenData, isUniData, aprData, lastSurfRewardBlockData, accTokensPerShareData);
}
function _getPoolMetadataFor(uint256 _pid, address _user, uint256 _surfPrice) internal view returns (uint[17] memory poolMetadata) {
PoolInfo memory pool = poolInfo[_pid];
uint256 totalSupply;
uint256 totalLPSupply;
uint256 stakedLPSupply;
uint256 tokenPrice;
uint256 lpTokenPrice;
uint256 totalLiquidityValue;
uint256 surfPerBlock;
if (_pid != 0 || surfPoolActive == true) {
totalSupply = pool.token.totalSupply();
totalLPSupply = pool.lpToken.totalSupply();
stakedLPSupply = _getPoolSupply(_pid);
tokenPrice = 10**uint256(pool.token.decimals()) * weth.balanceOf(address(pool.lpToken)) / pool.token.balanceOf(address(pool.lpToken));
lpTokenPrice = 10**18 * 2 * weth.balanceOf(address(pool.lpToken)) / totalLPSupply;
totalLiquidityValue = stakedLPSupply * lpTokenPrice / 1e18;
}
if (block.number >= startBlock + SOFT_LAUNCH_DURATION) {
surfPerBlock = ((pool.apr * 1e18 * totalLiquidityValue / _surfPrice) / APPROX_BLOCKS_PER_YEAR) / 100;
} else {
if (_pid != 0) {
surfPerBlock = SOFT_LAUNCH_SURF_PER_BLOCK;
} else if (surfPoolActive == true) {
surfPerBlock = 25 * SOFT_LAUNCH_SURF_PER_BLOCK;
}
}
poolMetadata[0] = totalSupply;
poolMetadata[1] = totalLPSupply;
poolMetadata[2] = stakedLPSupply;
poolMetadata[3] = tokenPrice;
poolMetadata[4] = lpTokenPrice;
poolMetadata[5] = totalLiquidityValue;
poolMetadata[6] = surfPerBlock;
poolMetadata[7] = pool.token.decimals();
if (_pid != 0 || surfPoolActive == true) {
UserInfo memory _userInfo = userInfo[_pid][_user];
poolMetadata[8] = pool.token.balanceOf(_user);
poolMetadata[9] = pool.token.allowance(_user, address(this));
poolMetadata[10] = pool.lpToken.balanceOf(_user);
poolMetadata[11] = pool.lpToken.allowance(_user, address(this));
poolMetadata[12] = _userInfo.staked;
poolMetadata[13] = _pendingSurf(_pid, _user);
poolMetadata[14] = _pendingUni(_pid, _user);
poolMetadata[15] = _userInfo.claimed;
poolMetadata[16] = _userInfo.uniClaimed;
}
}
function _getAllPoolMetadataFor(address _user) internal view returns (uint[17][] memory allMetadata) {
uint256 length = poolInfo.length;
allMetadata = new uint[17][](length);
uint256 surfPrice = _getSurfPrice();
for (uint256 pid = 0; pid < length; ++pid) {
allMetadata[pid] = _getPoolMetadataFor(pid, _user, surfPrice);
}
}
function getAllPoolInfoFor(address _user) external view returns (address[] memory tokens, address[] memory lpTokens, bool[] memory isUnis, uint[] memory aprs, uint[] memory lastSurfRewardBlocks, uint[2][] memory accTokensPerShares, uint[17][] memory metadatas) {
(tokens, lpTokens, isUnis, aprs, lastSurfRewardBlocks, accTokensPerShares) = _getAllPoolData();
metadatas = _getAllPoolMetadataFor(_user);
}
function _getSurfPrice() internal view returns (uint256 surfPrice) {
uint256 surfBalance = surf.balanceOf(surfPoolAddress);
if (surfBalance > 0) {
surfPrice = 10**18 * weth.balanceOf(surfPoolAddress) / surfBalance;
}
}
function getAllInfoFor(address _user) external view returns (bool poolActive, uint256[8] memory info) {
poolActive = surfPoolActive;
info[0] = blocksUntilLaunch();
info[1] = blocksUntilSurfPoolCanBeActivated();
info[2] = blocksUntilSoftLaunchEnds();
info[3] = surf.totalSupply();
info[4] = _getSurfPrice();
if (surfPoolActive) {
info[5] = IERC20(surfPoolAddress).balanceOf(address(surf));
}
info[6] = surfSentToWhirlpool;
info[7] = surf.balanceOf(_user);
}
function blocksUntilLaunch() public view returns (uint256) {
if (block.number >= startBlock) return 0;
else return startBlock.sub(block.number);
}
function blocksUntilSurfPoolCanBeActivated() public view returns (uint256) {
uint256 surfPoolActivationBlock = startBlock + SOFT_LAUNCH_DURATION.div(2);
if (block.number >= surfPoolActivationBlock) return 0;
else return surfPoolActivationBlock.sub(block.number);
}
function blocksUntilSoftLaunchEnds() public view returns (uint256) {
uint256 softLaunchEndBlock = startBlock + SOFT_LAUNCH_DURATION;
if (block.number >= softLaunchEndBlock) return 0;
else return softLaunchEndBlock.sub(block.number);
}
function massUpdatePools() public {
uint256 length = poolInfo.length;
for (uint256 pid = (surfPoolActive == true ? 0 : 1); pid < length; ++pid) {
updatePool(pid);
}
}
function updatePool(uint256 _pid) public {
require(msg.sender == tx.origin || msg.sender == owner() || contractWhitelist[msg.sender] == true, "no contracts");
PoolInfo storage pool = poolInfo[_pid];
uint256 lpSupply = _getPoolSupply(_pid);
if (_pid != 0) {
if (pool.uniStakeContract == address(0)) {
(address uniStakeContract,) = uniStakingFactory.stakingRewardsInfoByStakingToken(address(pool.lpToken));
if (uniStakeContract != address(0)) {
pool.uniStakeContract = uniStakeContract;
if (lpSupply > 0) {
pool.lpToken.safeApprove(uniStakeContract, 0);
pool.lpToken.approve(uniStakeContract, lpSupply);
IStakingRewards(pool.uniStakeContract).stake(lpSupply);
}
}
}
if (pool.uniStakeContract != address(0)) {
uint256 pendingUniTokens = IStakingRewards(pool.uniStakeContract).earned(address(this));
if (pendingUniTokens > 0) {
uint256 uniBalanceBefore = uniToken.balanceOf(address(this));
IStakingRewards(pool.uniStakeContract).getReward();
uint256 uniBalanceAfter = uniToken.balanceOf(address(this));
pendingUniTokens = uniBalanceAfter.sub(uniBalanceBefore);
pool.accUniPerShare = pool.accUniPerShare.add(pendingUniTokens.mul(1e12).div(lpSupply));
}
}
}
if (surf.maxSupplyHit() != true) {
if ((block.number <= pool.lastSurfRewardBlock) || (_pid == 0 && surfPoolActive != true)) {
return;
}
if (lpSupply == 0) {
pool.lastSurfRewardBlock = block.number;
return;
}
uint256 surfReward = _calculateSurfReward(_pid, lpSupply);
uint256 surfTotalSupply = surf.totalSupply();
if (surfTotalSupply.add(surfReward) >= surf.MAX_SUPPLY()) {
surfReward = surf.MAX_SUPPLY().sub(surfTotalSupply);
}
if (surfReward > 0) {
surf.mint(address(this), surfReward);
pool.accSurfPerShare = pool.accSurfPerShare.add(surfReward.mul(1e12).div(lpSupply));
pool.lastSurfRewardBlock = block.number;
}
if (surf.maxSupplyHit() == true) {
whirlpool.activate();
}
}
}
function _getPoolSupply(uint256 _pid) internal view returns (uint256 lpSupply) {
PoolInfo memory pool = poolInfo[_pid];
if (pool.uniStakeContract != address(0)) {
lpSupply = IStakingRewards(pool.uniStakeContract).balanceOf(address(this));
} else {
lpSupply = pool.lpToken.balanceOf(address(this));
}
}
function deposit(uint256 _pid, uint256 _amount) external {
depositFor(_pid, msg.sender, _amount);
}
function depositFor(uint256 _pid, address _user, uint256 _amount) public {
require(msg.sender == tx.origin || contractWhitelist[msg.sender] == true, "no contracts");
require(surf.maxSupplyHit() != true, "pools closed");
require(_pid != 0 || surfPoolActive == true, "surf pool not active");
require(_amount > 0, "deposit something");
updatePool(_pid);
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
_claimRewardsFromPool(_pid, _user);
uint256 stakingFeeAmount = _amount.div(10);
uint256 remainingUserAmount = _amount.sub(stakingFeeAmount);
if (pool.uniStakeContract != address(0)) {
pool.lpToken.safeApprove(pool.uniStakeContract, 0);
pool.lpToken.approve(pool.uniStakeContract, remainingUserAmount);
IStakingRewards(pool.uniStakeContract).stake(remainingUserAmount);
}
if (_pid == 0) {
pool.lpToken.transfer(address(surf), stakingFeeAmount);
} else {
uint256 deadline = block.timestamp + 5 minutes;
pool.lpToken.approve(address(uniswapRouter), stakingFeeAmount);
uniswapRouter.removeLiquidityETHSupportingFeeOnTransferTokens(address(pool.token), stakingFeeAmount, 0, 0, address(this), deadline);
uint256 ethBalanceBeforeSwap = address(this).balance;
uint256 tokensToSwap = pool.token.balanceOf(address(this));
require(tokensToSwap > 0, "bad token swap");
address[] memory poolPath = new address[](2);
poolPath[0] = address(pool.token);
poolPath[1] = address(weth);
pool.token.approve(address(uniswapRouter), tokensToSwap);
uniswapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(tokensToSwap, 0, poolPath, address(this), deadline);
uint256 ethBalanceAfterSwap = address(this).balance;
uint256 ethReceivedFromStakingFee;
uint256 teamFeeAmount;
if (surfPoolActive == true) {
require(ethBalanceAfterSwap > 0, "bad eth swap");
teamFeeAmount = ethBalanceAfterSwap.div(2);
ethReceivedFromStakingFee = ethBalanceAfterSwap.sub(teamFeeAmount);
uint256 surfBought = _buySurf(ethReceivedFromStakingFee);
surfSentToWhirlpool += surfBought;
_safeSurfTransfer(address(whirlpool), surfBought);
} else {
ethReceivedFromStakingFee = ethBalanceAfterSwap.sub(ethBalanceBeforeSwap);
require(ethReceivedFromStakingFee > 0, "bad eth swap");
teamFeeAmount = ethReceivedFromStakingFee.div(2);
}
if (teamFeeAmount > 0) devAddress.transfer(teamFeeAmount);
}
uint256 _currentRewardDebt = 0;
uint256 _currentUniRewardDebt = 0;
if (surfPoolActive != true) {
_currentRewardDebt = user.staked.mul(pool.accSurfPerShare).div(1e12).sub(user.rewardDebt);
_currentUniRewardDebt = user.staked.mul(pool.accUniPerShare).div(1e12).sub(user.uniRewardDebt);
}
user.staked = user.staked.add(remainingUserAmount);
user.rewardDebt = user.staked.mul(pool.accSurfPerShare).div(1e12).sub(_currentRewardDebt);
user.uniRewardDebt = user.staked.mul(pool.accUniPerShare).div(1e12).sub(_currentUniRewardDebt);
emit Deposit(_user, _pid, _amount);
}
function _buySurf(uint256 _amount) internal returns (uint256 surfBought) {
uint256 ethBalance = address(this).balance;
if (_amount > ethBalance) _amount = ethBalance;
if (_amount > 0) {
uint256 deadline = block.timestamp + 5 minutes;
address[] memory surfPath = new address[](2);
surfPath[0] = address(weth);
surfPath[1] = address(surf);
uint256[] memory amounts = uniswapRouter.swapExactETHForTokens{value: _amount}(0, surfPath, address(this), deadline);
surfBought = amounts[1];
}
if (surfBought > 0) emit SurfBuyback(msg.sender, _amount, surfBought);
}
function _claimRewardsFromPool(uint256 _pid, address _user) internal {
PoolInfo memory pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
if (surfPoolActive != true || user.staked == 0) return;
uint256 userUniPending = user.staked.mul(pool.accUniPerShare).div(1e12).sub(user.uniRewardDebt);
uint256 uniBalance = uniToken.balanceOf(address(this));
if (userUniPending > uniBalance) userUniPending = uniBalance;
if (userUniPending > 0) {
user.uniClaimed += userUniPending;
uniToken.transfer(_user, userUniPending);
}
uint256 userSurfPending = user.staked.mul(pool.accSurfPerShare).div(1e12).sub(user.rewardDebt);
if (userSurfPending > 0) {
user.claimed += userSurfPending;
_safeSurfTransfer(_user, userSurfPending);
}
if (userSurfPending > 0 || userUniPending > 0) {
emit Claim(_user, _pid, userSurfPending, userUniPending);
}
}
function claim(uint256 _pid) public {
require(surfPoolActive == true, "surf pool not active");
updatePool(_pid);
_claimRewardsFromPool(_pid, msg.sender);
UserInfo storage user = userInfo[_pid][msg.sender];
PoolInfo memory pool = poolInfo[_pid];
user.rewardDebt = user.staked.mul(pool.accSurfPerShare).div(1e12);
user.uniRewardDebt = user.staked.mul(pool.accUniPerShare).div(1e12);
}
function claimAll() public {
require(surfPoolActive == true, "surf pool not active");
uint256 totalPendingSurfAmount = 0;
uint256 totalPendingUniAmount = 0;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
UserInfo storage user = userInfo[pid][msg.sender];
if (user.staked > 0) {
updatePool(pid);
PoolInfo storage pool = poolInfo[pid];
uint256 accSurfPerShare = pool.accSurfPerShare;
uint256 accUniPerShare = pool.accUniPerShare;
uint256 pendingPoolSurfRewards = user.staked.mul(accSurfPerShare).div(1e12).sub(user.rewardDebt);
user.claimed += pendingPoolSurfRewards;
totalPendingSurfAmount = totalPendingSurfAmount.add(pendingPoolSurfRewards);
user.rewardDebt = user.staked.mul(accSurfPerShare).div(1e12);
uint256 pendingPoolUniRewards = user.staked.mul(accUniPerShare).div(1e12).sub(user.uniRewardDebt);
user.uniClaimed += pendingPoolUniRewards;
totalPendingUniAmount = totalPendingUniAmount.add(pendingPoolUniRewards);
user.uniRewardDebt = user.staked.mul(accUniPerShare).div(1e12);
}
}
require(totalPendingSurfAmount > 0 || totalPendingUniAmount > 0, "nothing to claim");
uint256 uniBalance = uniToken.balanceOf(address(this));
if (totalPendingUniAmount > uniBalance) totalPendingUniAmount = uniBalance;
if (totalPendingUniAmount > 0) uniToken.transfer(msg.sender, totalPendingUniAmount);
if (totalPendingSurfAmount > 0) _safeSurfTransfer(msg.sender, totalPendingSurfAmount);
emit ClaimAll(msg.sender, totalPendingSurfAmount, totalPendingUniAmount);
}
function withdraw(uint256 _pid, uint256 _amount) public {
require(surfPoolActive == true, "surf pool not active");
UserInfo storage user = userInfo[_pid][msg.sender];
require(_amount > 0 && user.staked >= _amount, "withdraw: not good");
updatePool(_pid);
_claimRewardsFromPool(_pid, msg.sender);
PoolInfo memory pool = poolInfo[_pid];
if (pool.uniStakeContract != address(0)) {
IStakingRewards(pool.uniStakeContract).withdraw(_amount);
}
user.staked = user.staked.sub(_amount);
user.rewardDebt = user.staked.mul(pool.accSurfPerShare).div(1e12);
user.uniRewardDebt = user.staked.mul(pool.accUniPerShare).div(1e12);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
emit Withdraw(msg.sender, _pid, _amount);
}
function migrateSURFLPtoWhirlpool() public {
require(whirlpool.active() == true, "whirlpool not active");
UserInfo storage user = userInfo[0][msg.sender];
uint256 amountToMigrate = user.staked;
require(amountToMigrate > 0, "migrate: not good");
updatePool(0);
_claimRewardsFromPool(0, msg.sender);
user.staked = 0;
user.rewardDebt = 0;
poolInfo[0].lpToken.approve(address(whirlpool), amountToMigrate);
whirlpool.stakeFor(msg.sender, amountToMigrate);
emit Withdraw(msg.sender, 0, amountToMigrate);
}
function emergencyWithdraw(uint256 _pid) public {
UserInfo storage user = userInfo[_pid][msg.sender];
uint256 staked = user.staked;
require(staked > 0, "no tokens");
PoolInfo memory pool = poolInfo[_pid];
if (pool.uniStakeContract != address(0)) {
IStakingRewards(pool.uniStakeContract).withdraw(staked);
}
user.staked = 0;
user.rewardDebt = 0;
user.uniRewardDebt = 0;
pool.lpToken.safeTransfer(address(msg.sender), staked);
emit EmergencyWithdraw(msg.sender, _pid, staked);
}
function _safeSurfTransfer(address _to, uint256 _amount) internal {
uint256 surfBalance = surf.balanceOf(address(this));
if (_amount > surfBalance) _amount = surfBalance;
surf.transfer(_to, _amount);
}
function activateSurfPool() public {
require(surfPoolActive == false, "already active");
require(block.number > startBlock + SOFT_LAUNCH_DURATION.div(2), "too soon");
uint256 initialEthLiquidity = address(this).balance;
require(initialEthLiquidity > 0, "need ETH");
massUpdatePools();
if (donatedETH > 0 && donatedETH < initialEthLiquidity) initialEthLiquidity = initialEthLiquidity.sub(donatedETH);
uint256 initialSurfLiquidity = 1000000 * 10**18;
surf.mint(address(this), initialSurfLiquidity);
surf.approve(address(uniswapRouter), initialSurfLiquidity);
( , , uint256 lpTokensReceived) = uniswapRouter.addLiquidityETH{value: initialEthLiquidity}(address(surf), initialSurfLiquidity, 0, 0, address(this), block.timestamp + 5 minutes);
initialSurfPoolETH = initialEthLiquidity;
surfPoolActive = true;
IERC20(surfPoolAddress).transfer(address(surf), lpTokensReceived);
uint256 donatedAmount = donatedETH;
uint256 ethBalance = address(this).balance;
if (donatedAmount > ethBalance) donatedAmount = ethBalance;
if (donatedAmount > 0) {
uint256 surfBought = _buySurf(donatedAmount);
surfSentToWhirlpool += surfBought;
_safeSurfTransfer(address(whirlpool), surfBought);
donatedETH = 0;
}
emit SurfPoolActive(msg.sender, initialSurfLiquidity, initialEthLiquidity);
}
function donate(address _lpToken) public payable {
require(msg.value >= minimumDonationAmount);
require(donaters[_lpToken] == address(0));
donatedETH = donatedETH.add(msg.value);
donaters[_lpToken] = msg.sender;
donations[_lpToken] = msg.value;
}
function removeDonation(address _lpToken) public {
require(block.number < startBlock);
address returnAddress = donaters[_lpToken];
require(msg.sender == returnAddress);
uint256 donationAmount = donations[_lpToken];
require(donationAmount > 0);
uint256 ethBalance = address(this).balance;
require(donationAmount <= ethBalance);
require(existingPools[_lpToken] != true);
donatedETH = donatedETH.sub(donationAmount);
donaters[_lpToken] = address(0);
donations[_lpToken] = 0;
msg.sender.transfer(donationAmount);
}
function setWhirlpoolContract(Whirlpool _whirlpool) public onlyOwner {
whirlpool = _whirlpool;
}
function addPool(address _token, address _lpToken, uint256 _apr, bool _requireDonation) public onlyOwner {
require(surf.maxSupplyHit() != true);
require(existingPools[_lpToken] != true, "pool exists");
require(_requireDonation != true || donations[_lpToken] >= minimumDonationAmount, "must donate");
_addPool(_token, _lpToken);
if (_apr != DEFAULT_APR) poolInfo[poolInfo.length-1].apr = _apr;
}
function setApr(uint256 _pid, uint256 _apr) public onlyOwner {
require(surf.maxSupplyHit() != true);
updatePool(_pid);
poolInfo[_pid].apr = _apr;
}
function addToWhitelist(address _contractAddress) public onlyOwner {
contractWhitelist[_contractAddress] = true;
}
function removeFromWhitelist(address _contractAddress) public onlyOwner {
contractWhitelist[_contractAddress] = false;
}
}
文件 11 的 12:UniStakingInterfaces.sol
pragma solidity ^0.6.12;
interface StakingRewardsFactory {
function stakingRewardsInfoByStakingToken(address) external view returns (address, uint256);
}
interface IStakingRewards {
function lastTimeRewardApplicable() external view returns (uint256);
function rewardPerToken() external view returns (uint256);
function earned(address account) external view returns (uint256);
function getRewardForDuration() external view returns (uint256);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function stake(uint256 amount) external;
function withdraw(uint256 amount) external;
function getReward() external;
function exit() external;
}
文件 12 的 12:Whirlpool.sol
pragma solidity ^0.6.12;
import './Ownable.sol';
import './SafeMath.sol';
import './SafeERC20.sol';
import './IERC20.sol';
import './IUniswapV2Router02.sol';
import './SURF.sol';
import './Tito.sol';
contract Whirlpool is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
struct UserInfo {
uint256 staked;
uint256 rewardDebt;
uint256 claimed;
}
SURF public surf;
Tito public tito;
IERC20 public surfPool;
IUniswapV2Router02 public uniswapRouter = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IERC20 public weth;
mapping (address => UserInfo) public userInfo;
uint256 public initialSurfReward = 0;
uint256 public initialSurfRewardPerDay;
uint256 public constant INITIAL_PAYOUT_INTERVAL = 24 hours;
uint256 public unstakingFee = 100;
uint256 public unstakingFeeConvertToSurfAmount = 500;
uint256 public startTime;
uint256 public lastPayout;
uint256 public totalPendingSurf;
uint256 public accSurfPerShare;
uint256 public totalStaked;
bool public active = false;
event Stake(address indexed user, uint256 amount);
event Claim(address indexed user, uint256 surfAmount);
event Withdraw(address indexed user, uint256 amount);
event SurfRewardAdded(address indexed user, uint256 surfReward);
event EthRewardAdded(address indexed user, uint256 ethReward);
constructor(SURF _surf, Tito _tito) public {
tito = _tito;
surf = _surf;
surfPool = IERC20(tito.surfPoolAddress());
weth = IERC20(uniswapRouter.WETH());
}
receive() external payable {
emit EthRewardAdded(msg.sender, msg.value);
}
function activate() public {
require(active != true, "already active");
require(surf.maxSupplyHit() == true, "too soon");
active = true;
startTime = block.timestamp + INITIAL_PAYOUT_INTERVAL;
lastPayout = startTime;
initialSurfRewardPerDay = initialSurfReward.div(100);
}
function addSurfReward(address _from, uint256 _amount) public {
require(msg.sender == address(surf), "not surf contract");
require(tito.surfPoolActive() == true, "no surf pool");
require(_amount > 0, "no surf");
if (active != true || totalStaked == 0) {
initialSurfReward = initialSurfReward.add(_amount);
} else {
totalPendingSurf = totalPendingSurf.add(_amount);
accSurfPerShare = accSurfPerShare.add(_amount.mul(1e12).div(totalStaked));
}
emit SurfRewardAdded(_from, _amount);
}
function addEthReward() public payable {
require(tito.surfPoolActive() == true, "no surf pool");
uint256 ethBalance = address(this).balance;
require(ethBalance > 0, "no eth");
_buySurf(ethBalance);
emit EthRewardAdded(msg.sender, msg.value);
}
function _buySurf(uint256 _amount) internal {
uint256 deadline = block.timestamp + 5 minutes;
address[] memory surfPath = new address[](2);
surfPath[0] = address(weth);
surfPath[1] = address(surf);
uniswapRouter.swapExactETHForTokens{value: _amount}(0, surfPath, address(this), deadline);
}
function _processInitialPayouts() internal {
if (active != true || block.timestamp < startTime || initialSurfReward == 0 || totalStaked == 0) return;
uint256 daysSinceLastPayout = (block.timestamp - lastPayout) / INITIAL_PAYOUT_INTERVAL;
if (daysSinceLastPayout == 0) return;
uint256 nextPayoutNumber = (block.timestamp - startTime) / INITIAL_PAYOUT_INTERVAL;
uint256 previousPayoutNumber = nextPayoutNumber - daysSinceLastPayout;
uint256 surfReward = rewardAtPayout(nextPayoutNumber) - rewardAtPayout(previousPayoutNumber);
if (surfReward > initialSurfReward) surfReward = initialSurfReward;
initialSurfReward = initialSurfReward.sub(surfReward);
totalPendingSurf = totalPendingSurf.add(surfReward);
accSurfPerShare = accSurfPerShare.add(surfReward.mul(1e12).div(totalStaked));
lastPayout += (daysSinceLastPayout * INITIAL_PAYOUT_INTERVAL);
}
function _claimReward(address _user) internal {
UserInfo storage user = userInfo[_user];
if (user.staked > 0) {
uint256 pendingSurfReward = user.staked.mul(accSurfPerShare).div(1e12).sub(user.rewardDebt);
if (pendingSurfReward > 0) {
totalPendingSurf = totalPendingSurf.sub(pendingSurfReward);
user.claimed += pendingSurfReward;
_safeSurfTransfer(_user, pendingSurfReward);
emit Claim(_user, pendingSurfReward);
}
}
}
function stake(uint256 _amount) public {
stakeFor(msg.sender, _amount);
}
function stakeFor(address _user, uint256 _amount) public {
require(active == true, "not active");
require(_amount > 0, "stake something");
_processInitialPayouts();
_claimReward(_user);
surfPool.safeTransferFrom(address(msg.sender), address(this), _amount);
UserInfo storage user = userInfo[_user];
totalStaked = totalStaked.add(_amount);
user.staked = user.staked.add(_amount);
user.rewardDebt = user.staked.mul(accSurfPerShare).div(1e12);
emit Stake(_user, _amount);
}
function claim() public {
require(active == true, "not active");
UserInfo storage user = userInfo[msg.sender];
require(user.staked > 0, "no stake");
_processInitialPayouts();
_claimReward(msg.sender);
user.rewardDebt = user.staked.mul(accSurfPerShare).div(1e12);
}
function withdraw(uint256 _amount) public {
require(active == true, "not active");
UserInfo storage user = userInfo[msg.sender];
require(_amount > 0 && user.staked >= _amount, "withdraw: not good");
_processInitialPayouts();
uint256 unstakingFeeAmount = _amount.mul(unstakingFee).div(1000);
uint256 remainingUserAmount = _amount.sub(unstakingFeeAmount);
uint256 lpTokensToConvertToSurf = unstakingFeeAmount.mul(unstakingFeeConvertToSurfAmount).div(1000);
uint256 lpTokensToLock = unstakingFeeAmount.sub(lpTokensToConvertToSurf);
if (lpTokensToConvertToSurf > 0) {
surfPool.approve(address(uniswapRouter), lpTokensToConvertToSurf);
uniswapRouter.removeLiquidityETHSupportingFeeOnTransferTokens(address(surf), lpTokensToConvertToSurf, 0, 0, address(this), block.timestamp + 5 minutes);
addEthReward();
}
if (lpTokensToLock > 0) surfPool.transfer(address(surf), lpTokensToLock);
_claimReward(msg.sender);
totalStaked = totalStaked.sub(_amount);
user.staked = user.staked.sub(_amount);
surfPool.safeTransfer(address(msg.sender), remainingUserAmount);
user.rewardDebt = user.staked.mul(accSurfPerShare).div(1e12);
emit Withdraw(msg.sender, remainingUserAmount);
}
function _safeSurfTransfer(address _to, uint256 _amount) internal {
uint256 surfBal = surf.balanceOf(address(this));
if (_amount > surfBal) {
surf.transfer(_to, surfBal);
} else {
surf.transfer(_to, _amount);
}
}
function setUnstakingFee(uint256 _unstakingFee, uint256 _convertToSurfAmount) public onlyOwner {
require(_unstakingFee <= 500, "over 50%");
require(_convertToSurfAmount <= 1000, "bad amount");
unstakingFee = _unstakingFee;
unstakingFeeConvertToSurfAmount = _convertToSurfAmount;
}
function recoverERC20(address _tokenAddress) public onlyOwner {
require(_tokenAddress != address(surf) && _tokenAddress != address(surfPool));
IERC20 token = IERC20(_tokenAddress);
uint256 tokenBalance = token.balanceOf(address(this));
token.transfer(msg.sender, tokenBalance);
}
function payoutNumber() public view returns (uint256) {
if (block.timestamp < startTime) return 0;
uint256 payout = (block.timestamp - startTime).div(INITIAL_PAYOUT_INTERVAL);
if (payout > 100) return 100;
else return payout;
}
function timeUntilNextPayout() public view returns (uint256) {
if (initialSurfReward == 0) return 0;
else {
uint256 payout = payoutNumber();
uint256 nextPayout = startTime.add((payout + 1).mul(INITIAL_PAYOUT_INTERVAL));
return nextPayout - block.timestamp;
}
}
function rewardAtPayout(uint256 _payoutNumber) public view returns (uint256) {
if (_payoutNumber == 0) return 0;
return initialSurfRewardPerDay * _payoutNumber;
}
function getAllInfoFor(address _user) external view returns (bool isActive, uint256[12] memory info) {
isActive = active;
info[0] = surf.balanceOf(address(this));
info[1] = initialSurfReward;
info[2] = totalPendingSurf;
info[3] = startTime;
info[4] = lastPayout;
info[5] = totalStaked;
info[6] = surf.balanceOf(_user);
if (tito.surfPoolActive()) {
info[7] = surfPool.balanceOf(_user);
info[8] = surfPool.allowance(_user, address(this));
}
info[9] = userInfo[_user].staked;
info[10] = userInfo[_user].staked.mul(accSurfPerShare).div(1e12).sub(userInfo[_user].rewardDebt);
info[11] = userInfo[_user].claimed;
}
}
{
"compilationTarget": {
"SURF.sol": "SURF"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"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":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_addToSenderWhitelist","type":"bool"},{"internalType":"address","name":"_address","type":"address"}],"name":"addToTransferWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"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":"_spender","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"approveAndCall","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":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint256","name":"votes","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":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCurrentVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPriorVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"maxSupplyHit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"migrateLockedLPTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"recipientWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_removeFromSenderWhitelist","type":"bool"},{"internalType":"address","name":"_address","type":"address"}],"name":"removeFromTransferWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"senderWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_titoAddress","type":"address"},{"internalType":"address payable","name":"_whirlpoolAddress","type":"address"},{"internalType":"address","name":"_surfPoolAddress","type":"address"}],"name":"setContractAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_transferFee","type":"uint256"}],"name":"setTransferFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"surfPoolAddress","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":"titoAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whirlpoolAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"}]