pragma solidity ^0.6.0; interface OasisInterface { function getMinSell(TokenInterface pay_gem) external view returns (uint); function getBuyAmount(address dest, address src, uint srcAmt) external view returns(uint); function getPayAmount(address src, address dest, uint destAmt) external view returns (uint); function sellAllAmount( address src, uint srcAmt, address dest, uint minDest ) external returns (uint destAmt); function buyAllAmount( address dest, uint destAmt, address src, uint maxSrc ) external returns (uint srcAmt); function getBestOffer(TokenInterface sell_gem, TokenInterface buy_gem) external view returns(uint); } interface TokenInterface { function allowance(address, address) external view returns (uint); function balanceOf(address) external view returns (uint); function decimals() external view returns (uint); function approve(address, uint) external; function transfer(address, uint) external returns (bool); function transferFrom(address, address, uint) external returns (bool); function deposit() external payable; function withdraw(uint) external; } interface AccountInterface { function isAuth(address _user) external view returns (bool); } interface MemoryInterface { function getUint(uint _id) external returns (uint _num); function setUint(uint _id, uint _val) external; } interface EventInterface { function emitEvent(uint _connectorType, uint _connectorID, bytes32 _eventCode, bytes calldata _eventData) external; } contract DSMath { function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x, "math-not-safe"); } function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x, "math-not-safe"); } function sub(uint x, uint y) internal pure returns (uint z) { require((z = x - y) <= x, "sub-overflow"); } uint constant WAD = 10 ** 18; function wmul(uint x, uint y) internal pure returns (uint z) { z = add(mul(x, y), WAD / 2) / WAD; } function wdiv(uint x, uint y) internal pure returns (uint z) { z = add(mul(x, WAD), y / 2) / y; } } contract Helpers is DSMath { /** * @dev Return ethereum address */ function getAddressETH() internal pure returns (address) { return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH Address } /** * @dev Return Memory Variable Address */ function getMemoryAddr() internal pure returns (address) { return 0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F; // InstaMemory Address } /** * @dev Return InstaEvent Address. */ function getEventAddr() internal pure returns (address) { return 0x2af7ea6Cb911035f3eb1ED895Cb6692C39ecbA97; // InstaEvent Address } /** * @dev Get Uint value from InstaMemory Contract. */ function getUint(uint getId, uint val) internal returns (uint returnVal) { returnVal = getId == 0 ? val : MemoryInterface(getMemoryAddr()).getUint(getId); } /** * @dev Set Uint value in InstaMemory Contract. */ function setUint(uint setId, uint val) internal { if (setId != 0) MemoryInterface(getMemoryAddr()).setUint(setId, val); } /** * @dev Connector Details */ function connectorID() public pure returns(uint _type, uint _id) { (_type, _id) = (1, 11); } } contract OasisHelpers is Helpers { /** * @dev Return WETH address */ function getAddressWETH() internal pure returns (address) { return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; } /** * @dev Return Oasis Address */ function getOasisAddr() internal pure returns (address) { return 0x794e6e91555438aFc3ccF1c5076A74F42133d08D; } function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) { amt = (_amt / 10 ** (18 - _dec)); } function changeEthAddress(address buy, address sell) internal pure returns(TokenInterface _buy, TokenInterface _sell){ _buy = buy == getAddressETH() ? TokenInterface(getAddressWETH()) : TokenInterface(buy); _sell = sell == getAddressETH() ? TokenInterface(getAddressWETH()) : TokenInterface(sell); } function convertEthToWeth(TokenInterface token, uint amount) internal { if(address(token) == getAddressWETH()) token.deposit{value: amount}(); } function convertWethToEth(TokenInterface token, uint amount) internal { if(address(token) == getAddressWETH()) { token.approve(getAddressWETH(), amount); token.withdraw(amount); } } } contract OasisResolver is OasisHelpers { event LogBuy( address indexed buyToken, address indexed sellToken, uint256 buyAmt, uint256 sellAmt, uint256 getId, uint256 setId ); event LogSell( address indexed buyToken, address indexed sellToken, uint256 buyAmt, uint256 sellAmt, uint256 getId, uint256 setId ); /** * @dev Buy ETH/ERC20_Token. * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param buyAmt buying token amount. * @param unitAmt unit amount of sellAmt/buyAmt with slippage. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. */ function buy( address buyAddr, address sellAddr, uint buyAmt, uint unitAmt, uint getId, uint setId ) external payable { uint _buyAmt = getUint(getId, buyAmt); (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); uint _slippageAmt = convert18ToDec(_sellAddr.decimals(), wmul(unitAmt, _buyAmt)); OasisInterface oasisContract = OasisInterface(getOasisAddr()); require(oasisContract.getBestOffer(_sellAddr, _buyAddr) != 0, "no-offer"); require(oasisContract.getMinSell(_sellAddr) <= _slippageAmt, "less-than-min-pay-amt"); uint _expectedAmt = oasisContract.getPayAmount(address(_sellAddr), address(_buyAddr), _buyAmt); require(_slippageAmt >= _expectedAmt, "Too much slippage"); convertEthToWeth(_sellAddr, _expectedAmt); _sellAddr.approve(getOasisAddr(), _expectedAmt); uint _sellAmt = oasisContract.buyAllAmount( address(_buyAddr), _buyAmt, address(_sellAddr), _slippageAmt ); convertWethToEth(_buyAddr, _buyAmt); setUint(setId, _sellAmt); emit LogBuy(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); bytes32 _eventCode = keccak256("LogBuy(address,address,uint256,uint256,uint256,uint256)"); bytes memory _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); (uint _type, uint _id) = connectorID(); EventInterface(getEventAddr()).emitEvent(_type, _id, _eventCode, _eventParam); } /** * @dev Sell ETH/ERC20_Token. * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param sellAmt selling token amount. * @param unitAmt unit amount of buyAmt/sellAmt with slippage. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. */ function sell( address buyAddr, address sellAddr, uint sellAmt, uint unitAmt, uint getId, uint setId ) external payable { uint _sellAmt = getUint(getId, sellAmt); (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); if (_sellAmt == uint(-1)) { _sellAmt = sellAddr == getAddressETH() ? address(this).balance : _buyAddr.balanceOf(address(this)); } uint _slippageAmt = convert18ToDec(_buyAddr.decimals(), wmul(unitAmt, _sellAmt)); OasisInterface oasisContract = OasisInterface(getOasisAddr()); require(oasisContract.getBestOffer(_sellAddr, _buyAddr) != 0, "no-offer"); require(oasisContract.getMinSell(_sellAddr) <= _sellAmt, "less-than-min-pay-amt"); uint _expectedAmt = oasisContract.getBuyAmount(address(_buyAddr), address(_sellAddr), _sellAmt); require(_slippageAmt <= _expectedAmt, "Too much slippage"); convertEthToWeth(_sellAddr, _sellAmt); _sellAddr.approve(getOasisAddr(), _sellAmt); uint _buyAmt = oasisContract.sellAllAmount( address(_sellAddr), _sellAmt, address(_buyAddr), _slippageAmt ); convertWethToEth(_buyAddr, _buyAmt); setUint(setId, _buyAmt); emit LogSell(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); bytes32 _eventCode = keccak256("LogSell(address,address,uint256,uint256,uint256,uint256)"); bytes memory _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); (uint _type, uint _id) = connectorID(); EventInterface(getEventAddr()).emitEvent(_type, _id, _eventCode, _eventParam); } } contract ConnectOasis is OasisResolver { string public name = "Oasis-v1.1"; }