账户
0x6f...7d8f
0x6f...7d8f

0x6f...7d8f

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.6.6+commit.6c089d02
语言
Solidity
合同源代码
文件 1 的 12:Address.sol
合同源代码
文件 2 的 12:Context.sol
合同源代码
文件 3 的 12:IERC20.sol
合同源代码
文件 4 的 12:IUniswapV2Pair.sol
合同源代码
文件 5 的 12:IUniswapV2Router01.sol
合同源代码
文件 6 的 12:IUniswapV2Router02.sol
合同源代码
文件 7 的 12:Math.sol
合同源代码
文件 8 的 12:Ownable.sol
合同源代码
文件 9 的 12:SafeERC20.sol
合同源代码
文件 10 的 12:SafeMath.sol
合同源代码
文件 11 的 12:UniswapV2Library.sol
pragma solidity >=0.5.0;

import 'https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/interfaces/IUniswapV2Pair.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol';

library UniswapV2Library {
    using SafeMath for uint;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
            ))));
    }

    // fetches and sorts the reserves for a pair
    function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
        require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
        require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        amountB = amountA.mul(reserveB) / reserveA;
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
        require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        uint amountInWithFee = amountIn.mul(997);
        uint numerator = amountInWithFee.mul(reserveOut);
        uint denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
        require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
        uint numerator = reserveIn.mul(amountOut).mul(1000);
        uint denominator = reserveOut.sub(amountOut).mul(997);
        amountIn = (numerator / denominator).add(1);
    }

    // performs chained getAmountOut calculations on any number of pairs
    function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[0] = amountIn;
        for (uint i; i < path.length - 1; i++) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
        }
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
        require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
        amounts = new uint[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint i = path.length - 1; i > 0; i--) {
            (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
    
    function computeMaxAmount(int weiReserve, int tokenReserve, int minBuyTokenPerUnit_, int oneUnit_) internal pure returns (int) {
        int numerator = 997*tokenReserve*oneUnit_ - 1000*weiReserve*minBuyTokenPerUnit_;
        int denominator = 997*minBuyTokenPerUnit_;
        return numerator/denominator;
    }    
    
}
合同源代码
文件 12 的 12:opendao.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.6.0 <0.8.0;

import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/SafeERC20.sol';
import 'https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol';
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol';
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol';
import './UniswapV2Library.sol';

contract UniswapBuyer is Ownable {

    using SafeMath for uint;
    using SafeERC20 for IERC20;

    IUniswapV2Router02 internal constant router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    
    address internal constant factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;

    uint public oneUnit = 1e18;

    uint public buyBlock = 0;
    
    uint public buyAmount = 0;
    
    uint public sellBlock = 0;
    
    uint public weiPoolAmount;
    
    uint public tokenPoolAmount;
    
    address public tokenAddress;
    
    uint public minBuyTokenPerUnit;
    
    uint public maxSellTokenPerUnit;
    
    uint public sellRatio;
    
    modifier onlyIfNoBuy {
        require(buyBlock == 0, 'Buy already occurred');
        _;
    }

    modifier onlyIfBuy {
        require(buyBlock != 0, 'Buy did not occurr');
        _;
    }
    
    constructor(address tokenAddress_, uint minBuyTokenPerUnit_, uint maxSellTokenPerUnit_, uint sellRatio_) Ownable() payable public {
        tokenAddress = tokenAddress_;
        minBuyTokenPerUnit = minBuyTokenPerUnit_;
        maxSellTokenPerUnit = maxSellTokenPerUnit_;
        sellRatio = sellRatio_;
    }

    receive() external payable {}   

    function withdrawEther(uint amount) public onlyOwner {
        msg.sender.transfer(amount);
    }

    function withdrawToken(address tokenAddress_) public onlyOwner {
        IERC20 tokenContract = IERC20(tokenAddress_);
        uint tokenBalanceAmount = tokenContract.balanceOf(address(this));
        tokenContract.safeTransfer(msg.sender, tokenBalanceAmount);
    } 

    function setTokenAddress(address tokenAddress_) public onlyOwner {
        tokenAddress = tokenAddress_;
    }
    
    function setMinBuyTokenPerUnit(uint minBuyTokenPerUnit_) public onlyOwner {
        minBuyTokenPerUnit = minBuyTokenPerUnit_;
    }
    
    function setMaxSellTokenPerUnit(uint maxSellTokenPerUnit_) public onlyOwner {
        maxSellTokenPerUnit = maxSellTokenPerUnit_;
    }
    
    function setSellRatio(uint sellRatio_) public onlyOwner {
        sellRatio = sellRatio_;
    }

    function approve() public onlyOwner {
        IERC20(tokenAddress).approve(address(router), uint(-1));
    }

    function reset() public onlyOwner {
        buyBlock = 0;
        buyAmount = 0;
        sellBlock = 0;
    }
    
    function buy() public onlyIfNoBuy {

        (uint weiReserve, uint tokenReserve) = UniswapV2Library.getReserves(factory, router.WETH(), tokenAddress);
        int maxWeiAmount = UniswapV2Library.computeMaxAmount(int(weiReserve), int(tokenReserve), int(minBuyTokenPerUnit), int(oneUnit));
        require(maxWeiAmount > 0, 'Price already too high');
        
        uint effectiveBuyWeiAmount = Math.min(uint(maxWeiAmount), address(this).balance);
        address[] memory path = new address[](2);
        path[0] = router.WETH();
        path[1] = tokenAddress;
        router.swapExactETHForTokens{value : effectiveBuyWeiAmount}(1, path, address(this), block.timestamp);
        buyBlock = block.number;
        weiPoolAmount = weiReserve;
        tokenPoolAmount = tokenReserve;
    }
    
    function sell() public onlyOwner onlyIfBuy {

        require(block.number > buyBlock, 'Another buy occurred in same block number');
        require(block.number > sellBlock, 'Another sell occurred in same block number');   
       
        IERC20 tokenContract = IERC20(tokenAddress);
        uint tokenBalanceAmount = tokenContract.balanceOf(address(this));
        require(tokenBalanceAmount > 0, 'Empty token balance');
        buyAmount = Math.max(buyAmount, tokenBalanceAmount);
        uint effectiveSellTokenAmount = Math.min(tokenBalanceAmount, buyAmount.div(sellRatio) + 1);
        uint minWeiAmount = effectiveSellTokenAmount.mul(oneUnit).div(maxSellTokenPerUnit);
        address[] memory path = new address[](2);
        path[0] = tokenAddress;  
        path[1] = router.WETH();
        router.swapExactTokensForETH(effectiveSellTokenAmount, minWeiAmount, path, address(this), block.timestamp);
        sellBlock = block.number;
    }
}
设置
{
  "compilationTarget": {
    "browser/opendao.sol": "UniswapBuyer"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"uint256","name":"minBuyTokenPerUnit_","type":"uint256"},{"internalType":"uint256","name":"maxSellTokenPerUnit_","type":"uint256"},{"internalType":"uint256","name":"sellRatio_","type":"uint256"}],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"buy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"buyAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSellTokenPerUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBuyTokenPerUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oneUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sellBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sellRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxSellTokenPerUnit_","type":"uint256"}],"name":"setMaxSellTokenPerUnit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minBuyTokenPerUnit_","type":"uint256"}],"name":"setMinBuyTokenPerUnit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sellRatio_","type":"uint256"}],"name":"setSellRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"}],"name":"setTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPoolAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weiPoolAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]