文件 1 的 1:Arb.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
library SafeMath
{
function add(uint x, uint y) internal pure returns (uint z)
{
require((z = x + y) >= x, 'ds-math-add-overflow');
}
function sub(uint x, uint y) internal pure returns (uint z)
{
require((z = x - y) <= x, 'ds-math-sub-underflow');
}
function mul(uint x, uint y) internal pure returns (uint z)
{
require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
}
function min(uint x, uint y) internal pure returns (uint z)
{
z = x < y ? x : y;
}
}
interface IERC20 {
function balanceOf(address owner) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
}
library EcoERC20
{
function balanceOf(address token, address owner) internal view returns (uint)
{
(bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, owner));
require(success && data.length >= 32, 'BO');
return abi.decode(data, (uint));
}
function safeApprove(address token, address to, uint256 value) internal
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
function transfer(address token, address to, uint value) internal
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TR');
}
function try_transfer(address token, address to, uint value) internal returns (bool)
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
return success && (data.length == 0 || abi.decode(data, (bool)));
}
}
interface IUniswapV2Pair
{
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}
library EcoUniv2Pair
{
function token0(address pair) internal view returns (address)
{
(bool success, bytes memory data) = pair.staticcall(abi.encodeWithSelector(IUniswapV2Pair.token0.selector));
require(success, 'T0');
return abi.decode(data, (address));
}
function token1(address pair) internal view returns (address)
{
(bool success, bytes memory data) = pair.staticcall(abi.encodeWithSelector(IUniswapV2Pair.token1.selector));
require(success, 'T1');
return abi.decode(data, (address));
}
function getReserves(address pair) internal view returns (uint112, uint112, uint32)
{
(bool success, bytes memory data) = pair.staticcall(abi.encodeWithSelector(IUniswapV2Pair.getReserves.selector));
require(success, 'RS');
return abi.decode(data, (uint112, uint112, uint32));
}
function swap(address pair, uint amount0Out, uint amount1Out, address to) internal
{
(bool success,) = pair.call(abi.encodeWithSelector(IUniswapV2Pair.swap.selector, amount0Out, amount1Out, to, ""));
if (!success)
{
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
}
}
interface IWETH {
function deposit() external payable;
function transfer(address to, uint value) external returns (bool);
function withdraw(uint) external;
}
library EcoWETH
{
function deposit(address token, uint targetAmount) internal
{
(bool success, bytes memory data) = token.call{value : targetAmount}(abi.encodeWithSelector(IWETH.deposit.selector));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'DR');
}
function transfer(address token, address to, uint value) internal
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IWETH.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TR');
}
function try_transfer(address token, address to, uint value) internal returns (bool)
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IWETH.transfer.selector, to, value));
return success && (data.length == 0 || abi.decode(data, (bool)));
}
function withdraw(address token, uint value) internal
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IWETH.withdraw.selector, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'WR');
}
}
interface PoolInterface {
function getBalance(address token) external view returns (uint);
function swapExactAmountIn(address, uint, address, uint, uint) external returns (uint, uint);
}
library EcoPoolInterface
{
function swapExactAmountIn(address pool, address tokenIn, uint tokenAmountIn, address tokenOut, uint minAmountOut, uint maxPrice) internal returns (uint, uint)
{
(bool success, bytes memory data) = pool.call(abi.encodeWithSelector(PoolInterface.swapExactAmountIn.selector, tokenIn, tokenAmountIn, tokenOut, minAmountOut, maxPrice));
if (!success)
{
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
return abi.decode(data, (uint, uint));
}
}
interface IContractRegistry {
function addressOf(
bytes32 contractName
) external returns (address);
}
library EcoContractRegistry
{
function addressOf(address token, bytes32 contractName) internal returns (address)
{
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IContractRegistry.addressOf.selector, contractName));
require(success, 'AO');
return abi.decode(data, (address));
}
}
interface IBancorNetwork {
function conversionPath(
IERC20 _sourceToken,
IERC20 _targetToken
) external view returns (address[] memory);
function convertByPath(
address[] memory _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
) external payable returns (uint256);
}
library EcoBancorNetwork
{
function conversionPath(address bancorNetwork, IERC20 _sourceToken, IERC20 _targetToken) internal view returns (address[] memory)
{
(bool success, bytes memory data) = bancorNetwork.staticcall(abi.encodeWithSelector(IBancorNetwork.conversionPath.selector, _sourceToken, _targetToken));
require(success, 'CP');
return abi.decode(data, (address[]));
}
function convertByPath(
address bancorNetwork,
uint msgValue,
address[] memory _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
) internal returns (uint256)
{
(bool success, bytes memory data) = bancorNetwork.call{value : msgValue}(abi.encodeWithSelector
(IBancorNetwork.convertByPath.selector,
_path,
_amount,
_minReturn,
_beneficiary,
_affiliateAccount,
_affiliateFee
)
);
if (!success)
{
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
return abi.decode(data, (uint256));
}
}
interface ISwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}
library EcoSwapRouter
{
function exactInputSingle(
address router,
address tokenIn,
address tokenOut,
uint24 fee,
address recipient,
uint256 deadline,
uint256 amountIn,
uint256 amountOutMinimum,
uint160 sqrtPriceLimitX96
) internal returns (uint256)
{
(bool success, bytes memory data) = router.call(abi.encodeWithSelector
(ISwapRouter.exactInputSingle.selector, ISwapRouter.ExactInputSingleParams({
tokenIn : tokenIn,
tokenOut : tokenOut,
fee : fee,
recipient : recipient,
deadline : deadline,
amountIn : amountIn,
amountOutMinimum : amountOutMinimum,
sqrtPriceLimitX96 : sqrtPriceLimitX96
})
)
);
if (!success)
{
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
return abi.decode(data, (uint256));
}
}
contract BalancerUniswapArbitrage {
using SafeMath for uint;
uint private constant WETH_MIN = 1e15;
uint private constant MINER_BRIBE_DEFAULT = 2e15;
uint private constant BRIBE_PERCENT_100 = 1e3;
uint private constant DEFL_PERCENT_100 = 1e6;
bytes4 private constant ERC20_TRANSFER_SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address private constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
ISwapRouter private constant UNIV3_ROUTER = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
address private constant contractRegistry = 0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4;
bytes32 private constant BANCOR_NETWORK_NAME = 0x42616E636F724E6574776F726B00000000000000000000000000000000000000;
address public owner;
modifier onlyOwner
{
require(msg.sender == owner, "OOW");
_;
}
modifier onlyWorker
{
require(is_buy_worker(msg.sender), "OWR");
_;
}
constructor() public
{
owner = msg.sender;
}
receive() external payable
{
}
function is_buy_worker(address addr) private pure returns (bool result)
{
assembly
{ switch lt(addr, 0x7F35A62D646CF7224E1FA5B9139B6A5743F601D7)
case true { switch lt(addr, 0x4E3B8D047F5946CD817FEE5E8DCF5EC006F78871)
case true { switch lt(addr, 0x4C6306A0C56F5B5454CF4D63E94BC8AEB01F8EF5)
case true { switch lt(addr, 0x4760FF1872511C627D4D9272898C64B693D46996)
case true { switch lt(addr, 0x3D5F42D72CDF3217E960EBB0710EF41DEF9CCED0)
case true { result := eq(addr, 0x3D198FBC1B1A2CDDA640F9D49A8CBDB05B2DA447) }
default { result := eq(addr, 0x3D5F42D72CDF3217E960EBB0710EF41DEF9CCED0) }}
default { result := eq(addr, 0x4760FF1872511C627D4D9272898C64B693D46996) }}
default { switch lt(addr, 0x4DC11A8418EB5A73902E4D3F69C375610BD09679)
case true { result := eq(addr, 0x4C6306A0C56F5B5454CF4D63E94BC8AEB01F8EF5) }
default { result := eq(addr, 0x4DC11A8418EB5A73902E4D3F69C375610BD09679) }}}
default { switch lt(addr, 0x551724E554D3C58861A969ABCAEE60BEEA8C732D)
case true { switch lt(addr, 0x5066573C3D718502A7BCBC3B82D7F7FDAD082813)
case true { switch lt(addr, 0x505D3FB1D8E603444F8A303D0B7FC081D917CACE)
case true { result := eq(addr, 0x4E3B8D047F5946CD817FEE5E8DCF5EC006F78871) }
default { result := eq(addr, 0x505D3FB1D8E603444F8A303D0B7FC081D917CACE) }}
default { result := eq(addr, 0x5066573C3D718502A7BCBC3B82D7F7FDAD082813) }}
default { switch lt(addr, 0x67A1196268EC42EA93BE8825BF35711F79F332B2)
case true { result := eq(addr, 0x551724E554D3C58861A969ABCAEE60BEEA8C732D) }
default { result := eq(addr, 0x67A1196268EC42EA93BE8825BF35711F79F332B2) }}}}
default { switch lt(addr, 0xD4F65A64CE6B384D5F92829620FE239A2A6358AE)
case true { switch lt(addr, 0xB8961DCBA7ED9B86A7839FB747EA69A522575190)
case true { switch lt(addr, 0x94D6E6FE6D03643635B1AF2C37AA2FF3A57C95B4)
case true { switch lt(addr, 0x8CAA1F5E10CF89F5AC66F038D0174E4D724590FE)
case true { result := eq(addr, 0x7F35A62D646CF7224E1FA5B9139B6A5743F601D7) }
default { result := eq(addr, 0x8CAA1F5E10CF89F5AC66F038D0174E4D724590FE) }}
default { result := eq(addr, 0x94D6E6FE6D03643635B1AF2C37AA2FF3A57C95B4) }}
default { switch lt(addr, 0xBEC0C3AC1ED8A73CA0605864E23E1AA7A7E89BE2)
case true { result := eq(addr, 0xB8961DCBA7ED9B86A7839FB747EA69A522575190) }
default { result := eq(addr, 0xBEC0C3AC1ED8A73CA0605864E23E1AA7A7E89BE2) }}}
default { switch lt(addr, 0xFD608FCF30B772741DA11FC7C3160E703DB749EE)
case true { switch lt(addr, 0xEFDA6BC62BB0811525DC17C10FADC6D410A3235E)
case true { switch lt(addr, 0xD65C094F322ACD51B60830A5BA2A400DC53AFCA1)
case true { result := eq(addr, 0xD4F65A64CE6B384D5F92829620FE239A2A6358AE) }
default { result := eq(addr, 0xD65C094F322ACD51B60830A5BA2A400DC53AFCA1) }}
default { result := eq(addr, 0xEFDA6BC62BB0811525DC17C10FADC6D410A3235E) }}
default { switch lt(addr, 0xFD6196DE3D7F00258A5330B02127BE273F38FFBC)
case true { result := eq(addr, 0xFD608FCF30B772741DA11FC7C3160E703DB749EE) }
default { result := eq(addr, 0xFD6196DE3D7F00258A5330B02127BE273F38FFBC) }}}}}
}
function set_owner_x666x(address new_owner) public onlyOwner
{
owner = new_owner;
}
function do_direct_call(uint256 _value, address _target, bytes memory _data) public payable onlyWorker returns (bytes memory response)
{
(bool success, bytes memory ret) = _target.call{value : _value}(_data);
require(success);
response = ret;
}
function pickup_eth_76550374(address payable receiver, uint256 value) public onlyOwner
{
receiver.transfer(value);
}
function pickup_tok_18796546(address receiver, address token, uint256 value) public onlyOwner
{
_safeTransfer(token, receiver, value);
}
function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(ERC20_TRANSFER_SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TRANSFER_FAILED');
}
function charge_addresses(uint256 limit, address[] memory array) public payable onlyOwner
{
uint256 avail = msg.value;
for (uint256 i = 0; i < array.length; i++)
{
if (avail == 0) break;
address payable worker = payable(array[i]);
if (worker.balance < limit)
{
uint256 need = limit - worker.balance;
if (need > avail) need = avail;
worker.transfer(need);
avail -= need;
}
}
if (avail > 0) msg.sender.transfer(avail);
}
function charge_addresses_weth(uint256 limit, address[] memory array) public payable onlyOwner
{
uint256 need = 0;
for (uint256 i = 0; i < array.length; i++)
{
if (array[i].balance < limit) need += limit - array[i].balance;
}
uint256 avail = IERC20(WETH).balanceOf(address(this));
require(avail >= need, "low weth balance");
IWETH(WETH).withdraw(need);
for (uint256 i = 0; i < array.length; i++)
{
address payable worker = payable(array[i]);
if (worker.balance < limit) worker.transfer(limit - worker.balance);
}
}
function mixSwapV3_0(uint weiAmount, address[] memory path, uint[] memory swapSequence, uint24[] memory v3Fees) private {
uint index = 0;
uint v3FeesIndex = 0;
uint targetAmount = weiAmount;
for (uint i = 0; i < swapSequence.length; i++) {
if (swapSequence[i] == 0) {
address pair = path[index++];
address[2] memory tokens = [path[index], path[index + 1]];
index += 2;
address beneficiary = (i < swapSequence.length - 1) && swapSequence[i + 1] == 0 ? path[index] : address(this);
bool needTransferToken0 = i == 0 || (i > 0 && swapSequence[i - 1] != 0 && swapSequence[i - 1] != 2 && swapSequence[i - 1] != 3);
(uint112 reserve1, uint112 reserve2) = getReserves(IUniswapV2Pair(pair), tokens[0]);
targetAmount = swapPair([pair, beneficiary], [reserve1, reserve2], [tokens[0], tokens[1]], targetAmount, needTransferToken0);
} else if (swapSequence[i] == 1) {
address pair = path[index++];
address token0 = path[index++];
EcoERC20.safeApprove(token0, pair, targetAmount);
(targetAmount,) = EcoPoolInterface.swapExactAmountIn(
pair,
address(token0),
targetAmount,
address(path[index++]),
uint(1),
type(uint).max
);
} else if (swapSequence[i] == 2) {
address bancorNetwork = EcoContractRegistry.addressOf(contractRegistry, BANCOR_NETWORK_NAME);
address tokenSource = path[index++];
address tokenTarget = path[index++];
address[] memory swapPath = EcoBancorNetwork.conversionPath(bancorNetwork, IERC20(tokenSource), IERC20(tokenTarget));
uint msgValue = 0;
if (tokenSource == ETH_RESERVE_ADDRESS) {
EcoWETH.withdraw(WETH, targetAmount);
msgValue = targetAmount;
} else {
EcoERC20.safeApprove(tokenSource, address(bancorNetwork), targetAmount);
}
address beneficiary = (i < swapSequence.length - 1) && swapSequence[i + 1] == 0 ? path[index] : address(0);
targetAmount = EcoBancorNetwork.convertByPath(
bancorNetwork,
msgValue,
swapPath,
targetAmount,
uint(1),
beneficiary,
address(0),
0
);
if (tokenTarget == ETH_RESERVE_ADDRESS) EcoWETH.deposit(WETH, targetAmount);
} else if (swapSequence[i] == 3) {
address tokenIn = path[index++];
address tokenOut = path[index++];
address recipient = i < swapSequence.length - 1 && swapSequence[i + 1] == 0 ? path[index] : address(this);
EcoERC20.safeApprove(tokenIn, address(UNIV3_ROUTER), targetAmount);
targetAmount = EcoSwapRouter.exactInputSingle(address(UNIV3_ROUTER),
tokenIn,
tokenOut,
v3Fees[v3FeesIndex++],
recipient,
block.timestamp + 1 hours,
targetAmount,
0,
0
);
}
}
}
function mixSwapV6(uint weiAmount, uint bribe, uint gasUsed, address[] memory path, uint[] memory swapSequence, uint24[] memory v3Fees) external onlyWorker {
mixSwapV6_(weiAmount, bribe, gasUsed, path, swapSequence, v3Fees);
}
function mixSwapV6_(uint weiAmount, uint bribe, uint gasUsed, address[] memory path, uint[] memory swapSequence, uint24[] memory v3Fees) private {
uint ethBalance = EcoERC20.balanceOf(WETH, address(this));
if (weiAmount <= ethBalance) {
mixSwapV3_0(weiAmount, path, swapSequence, v3Fees);
uint wethAfter = EcoERC20.balanceOf(WETH, address(this));
require(wethAfter > ethBalance, "NP");
uint fee = tx.gasprice * gasUsed;
require(wethAfter - ethBalance > fee, "NPF");
uint pnl = wethAfter - ethBalance - fee;
uint minerBribe = bribe * pnl / BRIBE_PERCENT_100;
if (minerBribe > 0) {
EcoWETH.withdraw(WETH, minerBribe);
address payable coinbaseAddr = payable(block.coinbase);
coinbaseAddr.transfer(minerBribe);
}
}
}
function swapPair(address[2] memory pairs, uint112[2] memory reserves, address[2] memory tokens, uint amount, bool isFirstSwap) private returns (uint) {
if (isFirstSwap) _safeTransfer(tokens[0], pairs[0], amount);
uint e0 = calcUniswapSwapTokens(amount, reserves[0], reserves[1]);
uint e0_before = EcoERC20.balanceOf(tokens[1], pairs[1]);
if (EcoUniv2Pair.token0(pairs[0]) == tokens[0]) EcoUniv2Pair.swap(pairs[0], 0, e0, pairs[1]);
else EcoUniv2Pair.swap(pairs[0], e0, 0, pairs[1]);
uint e0_after = EcoERC20.balanceOf(tokens[1], pairs[1]);
uint amount_res = e0_after - e0_before;
return amount_res;
}
function getReserves(IUniswapV2Pair pair, address token0) private view returns (uint112 r1, uint112 r2) {
(uint112 reserve1, uint112 reserve2,) = EcoUniv2Pair.getReserves(address(pair));
if (EcoUniv2Pair.token0(address(pair)) != token0) (reserve1, reserve2) = (reserve2, reserve1);
return (reserve1, reserve2);
}
function calcUniswapSwapTokens(uint amount, uint reserve0, uint reserve1) private pure returns (uint)
{
uint eth_with_fee = amount.mul(997);
uint numerator = eth_with_fee.mul(reserve1);
uint denominator = reserve0.mul(1000).add(eth_with_fee);
return numerator / denominator;
}
function getBalance(address pool, address token) external view returns (uint blockNumber, uint balance){
return (block.number, PoolInterface(pool).getBalance(token));
}
}