Contract Source Code:
File 1 of 1 : BotRefills
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
abstract contract IDFSRegistry {
function getAddr(bytes32 _id) public view virtual returns (address);
function addNewContract(
bytes32 _id,
address _contractAddr,
uint256 _waitPeriod
) public virtual;
function startContractChange(bytes32 _id, address _newContractAddr) public virtual;
function approveContractChange(bytes32 _id) public virtual;
function cancelContractChange(bytes32 _id) public virtual;
function changeWaitPeriod(bytes32 _id, uint256 _newWaitPeriod) public virtual;
}
interface IERC20 {
function totalSupply() external view returns (uint256 supply);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function decimals() external view returns (uint256 digits);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
library Address {
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{value: weiValue}(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
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) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
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;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
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;
}
}
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)
);
}
/// @dev Edited so it always first approves 0 and then the value, because of non standard tokens
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_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) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
/// @title A stateful contract that holds and can change owner/admin
contract AdminVault {
address public owner;
address public admin;
constructor() {
owner = msg.sender;
admin = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9;
}
/// @notice Admin is able to change owner
/// @param _owner Address of new owner
function changeOwner(address _owner) public {
require(admin == msg.sender, "msg.sender not admin");
owner = _owner;
}
/// @notice Admin is able to set new admin
/// @param _admin Address of multisig that becomes new admin
function changeAdmin(address _admin) public {
require(admin == msg.sender, "msg.sender not admin");
admin = _admin;
}
}
/// @title AdminAuth Handles owner/admin privileges over smart contracts
contract AdminAuth {
using SafeERC20 for IERC20;
AdminVault public constant adminVault = AdminVault(0xCCf3d848e08b94478Ed8f46fFead3008faF581fD);
modifier onlyOwner() {
require(adminVault.owner() == msg.sender, "msg.sender not owner");
_;
}
modifier onlyAdmin() {
require(adminVault.admin() == msg.sender, "msg.sender not admin");
_;
}
/// @notice withdraw stuck funds
function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
payable(_receiver).transfer(_amount);
} else {
IERC20(_token).safeTransfer(_receiver, _amount);
}
}
/// @notice Destroy the contract
function kill() public onlyAdmin {
selfdestruct(payable(msg.sender));
}
}
abstract contract IUniswapRouter {
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual returns (uint256[] memory amounts);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external virtual returns (uint256[] memory amounts);
function swapTokensForExactETH(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external virtual returns (uint256[] memory amounts);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external virtual returns (uint256[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
)
external
virtual
returns (
uint256 amountA,
uint256 amountB,
uint256 liquidity
);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
virtual
returns (
uint256 amountToken,
uint256 amountETH,
uint256 liquidity
);
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external virtual returns (uint256 amountA, uint256 amountB);
function quote(
uint256 amountA,
uint256 reserveA,
uint256 reserveB
) public pure virtual returns (uint256 amountB);
function getAmountsOut(uint256 amountIn, address[] memory path)
public
view
virtual
returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] memory path)
public
view
virtual
returns (uint256[] memory amounts);
}
abstract contract IBotRegistry {
function botList(address) public virtual view returns (bool);
}
abstract contract IWETH {
function allowance(address, address) public virtual view returns (uint256);
function balanceOf(address) public virtual view returns (uint256);
function approve(address, uint256) public virtual;
function transfer(address, uint256) public virtual returns (bool);
function transferFrom(
address,
address,
uint256
) public virtual returns (bool);
function deposit() public payable virtual;
function withdraw(uint256) public virtual;
}
library TokenUtils {
using SafeERC20 for IERC20;
address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
function approveToken(
address _tokenAddr,
address _to,
uint256 _amount
) internal {
if (_tokenAddr == ETH_ADDR) return;
if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
IERC20(_tokenAddr).safeApprove(_to, _amount);
}
}
function pullTokensIfNeeded(
address _token,
address _from,
uint256 _amount
) internal returns (uint256) {
// handle max uint amount
if (_amount == type(uint256).max) {
uint256 userAllowance = IERC20(_token).allowance(_from, address(this));
uint256 balance = getBalance(_token, _from);
// pull max allowance amount if balance is bigger than allowance
_amount = (balance > userAllowance) ? userAllowance : balance;
}
if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
IERC20(_token).safeTransferFrom(_from, address(this), _amount);
}
return _amount;
}
function withdrawTokens(
address _token,
address _to,
uint256 _amount
) internal returns (uint256) {
if (_amount == type(uint256).max) {
_amount = getBalance(_token, address(this));
}
if (_to != address(0) && _to != address(this) && _amount != 0) {
if (_token != ETH_ADDR) {
IERC20(_token).safeTransfer(_to, _amount);
} else {
payable(_to).transfer(_amount);
}
}
return _amount;
}
function depositWeth(uint256 _amount) internal {
IWETH(WETH_ADDR).deposit{value: _amount}();
}
function withdrawWeth(uint256 _amount) internal {
IWETH(WETH_ADDR).withdraw(_amount);
}
function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
if (_tokenAddr == ETH_ADDR) {
return _acc.balance;
} else {
return IERC20(_tokenAddr).balanceOf(_acc);
}
}
function getTokenDecimals(address _token) internal view returns (uint256) {
if (_token == ETH_ADDR) return 18;
return IERC20(_token).decimals();
}
}
/// @title Contract used to refill tx sending bots when they are low on eth
contract BotRefills is AdminAuth {
using TokenUtils for address;
address internal refillCaller = 0x33fDb79aFB4456B604f376A45A546e7ae700e880;
address internal feeAddr = 0x76720aC2574631530eC8163e4085d6F98513fb27;
address internal constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
address internal constant DAI_ADDR = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
IUniswapRouter internal router = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
mapping(address => bool) public additionalBots;
constructor() {
additionalBots[0x33fDb79aFB4456B604f376A45A546e7ae700e880] = true;
additionalBots[0x7fb85Bab66C4a14eb4c048a34CEf0AB16747778d] = true;
additionalBots[0x446aD06C447b26D129C131E893f48b3a518a63c7] = true;
}
modifier isApprovedBot(address _botAddr) {
require(
IBotRegistry(BOT_REGISTRY_ADDRESS).botList(_botAddr) || additionalBots[_botAddr],
"Not auth bot"
);
_;
}
modifier isRefillCaller {
require(msg.sender == refillCaller, "Wrong refill caller");
_;
}
function refill(uint256 _ethAmount, address _botAddress)
public
isRefillCaller
isApprovedBot(_botAddress)
{
// check if we have enough weth to send
uint256 wethBalance = IERC20(TokenUtils.WETH_ADDR).balanceOf(feeAddr);
if (wethBalance >= _ethAmount) {
IERC20(TokenUtils.WETH_ADDR).transferFrom(feeAddr, address(this), _ethAmount);
TokenUtils.withdrawWeth(_ethAmount);
payable(_botAddress).transfer(_ethAmount);
} else {
address[] memory path = new address[](2);
path[0] = DAI_ADDR;
path[1] = TokenUtils.WETH_ADDR;
// get how much dai we need to convert
uint256 daiAmount = getEth2Dai(_ethAmount);
IERC20(DAI_ADDR).transferFrom(feeAddr, address(this), daiAmount);
DAI_ADDR.approveToken(address(router), daiAmount);
// swap and transfer directly to botAddress
router.swapExactTokensForETH(daiAmount, 1, path, _botAddress, block.timestamp + 1);
}
}
function refillMany(uint256[] memory _ethAmounts, address[] memory _botAddresses) public {
for(uint i = 0; i < _botAddresses.length; ++i) {
refill(_ethAmounts[i], _botAddresses[i]);
}
}
/// @dev Returns Dai amount, given eth amount based on uniV2 pool price
function getEth2Dai(uint256 _ethAmount) internal view returns (uint256 daiAmount) {
address[] memory path = new address[](2);
path[0] = TokenUtils.WETH_ADDR;
path[1] = DAI_ADDR;
daiAmount = router.getAmountsOut(_ethAmount, path)[1];
}
function setRefillCaller(address _newBot) public onlyOwner {
refillCaller = _newBot;
}
function setFeeAddr(address _newFeeAddr) public onlyOwner {
feeAddr = _newFeeAddr;
}
function setAdditionalBot(address _botAddr, bool _approved) public onlyOwner {
additionalBots[_botAddr] = _approved;
}
receive() external payable {}
}