2020-05-08 09:11:46 +00:00
|
|
|
pragma solidity ^0.6.0;
|
|
|
|
|
2020-05-08 21:11:13 +00:00
|
|
|
// import files from common directory
|
|
|
|
import { TokenInterface , MemoryInterface, EventInterface} from "../common/interfaces.sol";
|
|
|
|
import { Stores } from "../common/stores.sol";
|
|
|
|
import { DSMath } from "../common/math.sol";
|
|
|
|
|
2020-07-29 13:51:14 +00:00
|
|
|
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
|
|
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
|
|
2020-05-08 09:11:46 +00:00
|
|
|
interface OneInchInterace {
|
|
|
|
function swap(
|
|
|
|
TokenInterface fromToken,
|
|
|
|
TokenInterface toToken,
|
|
|
|
uint256 fromTokenAmount,
|
|
|
|
uint256 minReturnAmount,
|
|
|
|
uint256 guaranteedAmount,
|
|
|
|
address payable referrer,
|
|
|
|
address[] calldata callAddresses,
|
|
|
|
bytes calldata callDataConcat,
|
|
|
|
uint256[] calldata starts,
|
|
|
|
uint256[] calldata gasLimitsAndValues
|
|
|
|
)
|
|
|
|
external
|
|
|
|
payable
|
|
|
|
returns (uint256 returnAmount);
|
|
|
|
}
|
|
|
|
|
2020-05-08 21:11:13 +00:00
|
|
|
contract OneHelpers is Stores, DSMath {
|
2020-07-29 13:51:14 +00:00
|
|
|
using SafeERC20 for IERC20;
|
2020-05-08 09:11:46 +00:00
|
|
|
/**
|
2020-05-08 21:11:13 +00:00
|
|
|
* @dev Return 1Inch Address
|
2020-05-08 09:11:46 +00:00
|
|
|
*/
|
|
|
|
function getOneInchAddress() internal pure returns (address) {
|
|
|
|
return 0x11111254369792b2Ca5d084aB5eEA397cA8fa48B;
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:53:56 +00:00
|
|
|
/**
|
2020-07-24 12:52:04 +00:00
|
|
|
* @dev Return 1inch Token Taker Address
|
2020-05-10 21:53:56 +00:00
|
|
|
*/
|
2020-07-24 12:58:26 +00:00
|
|
|
function getOneInchTokenTaker() internal pure returns (address payable) {
|
2020-05-10 21:53:56 +00:00
|
|
|
return 0xE4C9194962532fEB467DCe8b3d42419641c6eD2E;
|
|
|
|
}
|
|
|
|
|
2020-05-09 20:28:08 +00:00
|
|
|
/**
|
2020-07-24 12:52:04 +00:00
|
|
|
* @dev Return 1inch swap function sig
|
2020-05-09 20:28:08 +00:00
|
|
|
*/
|
2020-07-24 12:58:26 +00:00
|
|
|
function getOneInchSig() internal pure returns (bytes4) {
|
2020-05-09 20:28:08 +00:00
|
|
|
return 0xf88309d7;
|
|
|
|
}
|
|
|
|
|
2020-07-24 11:12:04 +00:00
|
|
|
function getReferralAddr() internal pure returns (address) {
|
2020-07-30 13:20:25 +00:00
|
|
|
return 0xa7615CD307F323172331865181DC8b80a2834324; // TODO - change address
|
2020-07-24 11:12:04 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 09:11:46 +00:00
|
|
|
function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
|
|
|
|
amt = (_amt / 10 ** (18 - _dec));
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertTo18(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
|
|
|
|
amt = mul(_amt, 10 ** (18 - _dec));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTokenBal(TokenInterface token) internal view returns(uint _amt) {
|
2020-05-08 21:11:13 +00:00
|
|
|
_amt = address(token) == getEthAddr() ? address(this).balance : token.balanceOf(address(this));
|
2020-05-08 09:11:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getTokensDec(TokenInterface buyAddr, TokenInterface sellAddr) internal view returns(uint buyDec, uint sellDec) {
|
2020-05-08 21:11:13 +00:00
|
|
|
buyDec = address(buyAddr) == getEthAddr() ? 18 : buyAddr.decimals();
|
|
|
|
sellDec = address(sellAddr) == getEthAddr() ? 18 : sellAddr.decimals();
|
2020-05-08 09:11:46 +00:00
|
|
|
}
|
2020-07-24 11:59:21 +00:00
|
|
|
|
2020-07-29 13:51:14 +00:00
|
|
|
function _transfer(address payable to, IERC20 token, uint _amt) internal {
|
|
|
|
address(token) == getEthAddr() ?
|
|
|
|
to.transfer(_amt) :
|
|
|
|
token.safeTransfer(to, _amt);
|
|
|
|
}
|
|
|
|
|
|
|
|
function takeFee(
|
|
|
|
address token,
|
|
|
|
uint amount,
|
|
|
|
address feeCollector,
|
|
|
|
uint feePercent
|
|
|
|
) internal returns (uint leftAmt, uint feeAmount){
|
|
|
|
if (feeCollector != address(0)) {
|
|
|
|
feeAmount = wmul(amount, feePercent);
|
|
|
|
leftAmt = sub(amount, feeAmount);
|
|
|
|
uint feeCollectorAmt = wmul(feeAmount, 3 * 10 ** 17);
|
|
|
|
uint restAmt = sub(feeAmount, feeCollectorAmt);
|
|
|
|
_transfer(payable(feeCollector), IERC20(token), feeCollectorAmt);
|
2020-07-30 13:20:25 +00:00
|
|
|
_transfer(payable(getReferralAddr()), IERC20(token), restAmt);
|
2020-07-29 13:51:14 +00:00
|
|
|
} else {
|
|
|
|
leftAmt = amount;
|
|
|
|
}
|
|
|
|
}
|
2020-05-08 09:11:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
contract Resolver is OneHelpers {
|
2020-05-09 20:28:08 +00:00
|
|
|
function checkOneInchSig(bytes memory callData) internal pure returns(bool isOk) {
|
|
|
|
bytes memory _data = callData;
|
|
|
|
bytes4 sig;
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
|
|
assembly {
|
|
|
|
sig := mload(add(_data, 32))
|
|
|
|
}
|
2020-07-24 12:58:26 +00:00
|
|
|
isOk = sig == getOneInchSig();
|
2020-05-09 20:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
struct OneInchData {
|
2020-07-30 13:20:25 +00:00
|
|
|
TokenInterface sellToken;
|
|
|
|
TokenInterface buyToken;
|
|
|
|
uint _sellAmt;
|
|
|
|
uint _buyAmt;
|
|
|
|
uint unitAmt;
|
2020-07-31 07:31:21 +00:00
|
|
|
bytes callData;
|
2020-07-30 13:20:25 +00:00
|
|
|
address feeCollector;
|
|
|
|
uint256 feeAmount;
|
2020-07-24 11:12:04 +00:00
|
|
|
}
|
|
|
|
|
2020-05-09 00:40:44 +00:00
|
|
|
function oneInchSwap(
|
2020-07-31 07:31:21 +00:00
|
|
|
OneInchData memory oneInchData,
|
2020-05-09 00:40:44 +00:00
|
|
|
uint ethAmt
|
|
|
|
) internal returns (uint buyAmt) {
|
2020-07-31 07:31:21 +00:00
|
|
|
TokenInterface buyToken = oneInchData.buyToken;
|
|
|
|
(uint _buyDec, uint _sellDec) = getTokensDec(buyToken, oneInchData.sellToken);
|
|
|
|
uint _sellAmt18 = convertTo18(_sellDec, oneInchData._sellAmt);
|
|
|
|
uint _slippageAmt = convert18ToDec(_buyDec, wmul(oneInchData.unitAmt, _sellAmt18));
|
|
|
|
|
|
|
|
uint initalBal = getTokenBal(buyToken);
|
2020-05-09 00:40:44 +00:00
|
|
|
|
|
|
|
// solium-disable-next-line security/no-call-value
|
2020-07-31 07:31:21 +00:00
|
|
|
(bool success, ) = address(getOneInchAddress()).call.value(ethAmt)(oneInchData.callData);
|
2020-05-09 00:40:44 +00:00
|
|
|
if (!success) revert("1Inch-swap-failed");
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
uint finalBal = getTokenBal(buyToken);
|
|
|
|
|
2020-05-09 00:40:44 +00:00
|
|
|
buyAmt = sub(finalBal, initalBal);
|
|
|
|
|
|
|
|
require(_slippageAmt <= buyAmt, "Too much slippage");
|
|
|
|
}
|
2020-07-30 13:20:25 +00:00
|
|
|
|
2020-05-08 09:11:46 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
contract OneInchEventResolver is Resolver {
|
2020-05-08 10:36:46 +00:00
|
|
|
event LogSell(
|
2020-05-08 09:11:46 +00:00
|
|
|
address indexed buyToken,
|
|
|
|
address indexed sellToken,
|
|
|
|
uint256 buyAmt,
|
|
|
|
uint256 sellAmt,
|
|
|
|
uint256 getId,
|
|
|
|
uint256 setId
|
|
|
|
);
|
|
|
|
|
2020-07-30 13:20:25 +00:00
|
|
|
event LogSellFee(
|
|
|
|
address indexed buyToken,
|
|
|
|
address indexed sellToken,
|
2020-07-30 11:56:39 +00:00
|
|
|
uint256 buyAmt,
|
|
|
|
uint256 sellAmt,
|
2020-07-30 13:20:25 +00:00
|
|
|
address indexed collector,
|
|
|
|
uint256 fee,
|
|
|
|
uint256 getId,
|
|
|
|
uint256 setId
|
|
|
|
);
|
|
|
|
|
|
|
|
function emitLogSell(
|
2020-07-31 07:31:21 +00:00
|
|
|
OneInchData memory oneInchData,
|
2020-07-30 11:56:39 +00:00
|
|
|
uint256 setId
|
|
|
|
) internal {
|
|
|
|
bytes32 _eventCode;
|
|
|
|
bytes memory _eventParam;
|
2020-07-31 07:31:21 +00:00
|
|
|
if (oneInchData.feeCollector == address(0)) {
|
2020-07-30 13:20:25 +00:00
|
|
|
emit LogSell(
|
2020-07-31 07:31:21 +00:00
|
|
|
address(oneInchData.buyToken),
|
|
|
|
address(oneInchData.sellToken),
|
|
|
|
oneInchData._buyAmt,
|
|
|
|
oneInchData._sellAmt,
|
|
|
|
0,
|
2020-07-30 13:20:25 +00:00
|
|
|
setId
|
|
|
|
);
|
2020-07-30 11:56:39 +00:00
|
|
|
_eventCode = keccak256("LogSell(address,address,uint256,uint256,uint256,uint256)");
|
2020-07-30 13:20:25 +00:00
|
|
|
_eventParam = abi.encode(
|
2020-07-31 07:31:21 +00:00
|
|
|
address(oneInchData.buyToken),
|
|
|
|
address(oneInchData.sellToken),
|
|
|
|
oneInchData._buyAmt,
|
|
|
|
oneInchData._sellAmt,
|
|
|
|
0,
|
2020-07-30 13:20:25 +00:00
|
|
|
setId
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
emit LogSellFee(
|
2020-07-31 07:31:21 +00:00
|
|
|
address(oneInchData.buyToken),
|
|
|
|
address(oneInchData.sellToken),
|
|
|
|
oneInchData._buyAmt,
|
|
|
|
oneInchData._sellAmt,
|
|
|
|
oneInchData.feeCollector,
|
|
|
|
oneInchData.feeAmount,
|
|
|
|
0,
|
2020-07-30 13:20:25 +00:00
|
|
|
setId
|
|
|
|
);
|
|
|
|
_eventCode = keccak256("LogSellFee(address,address,uint256,uint256,uint256,uint256)");
|
|
|
|
_eventParam = abi.encode(
|
2020-07-31 07:31:21 +00:00
|
|
|
address(oneInchData.buyToken),
|
|
|
|
address(oneInchData.sellToken),
|
|
|
|
oneInchData._buyAmt,
|
|
|
|
oneInchData._sellAmt,
|
|
|
|
oneInchData.feeCollector,
|
|
|
|
oneInchData.feeAmount,
|
|
|
|
0,
|
2020-07-30 13:20:25 +00:00
|
|
|
setId
|
|
|
|
);
|
2020-07-30 11:56:39 +00:00
|
|
|
}
|
|
|
|
emitEvent(_eventCode, _eventParam);
|
|
|
|
}
|
2020-07-31 07:31:21 +00:00
|
|
|
}
|
2020-07-30 11:56:39 +00:00
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
contract OneInchResolverHelpers is OneInchEventResolver {
|
|
|
|
function _sell (
|
|
|
|
OneInchData memory oneInchData,
|
|
|
|
uint feePercent,
|
|
|
|
uint setId
|
2020-07-30 11:56:39 +00:00
|
|
|
) internal {
|
2020-07-31 07:31:21 +00:00
|
|
|
TokenInterface _buyAddr = oneInchData.buyToken;
|
|
|
|
TokenInterface _sellAddr = oneInchData.sellToken;
|
2020-07-30 13:20:25 +00:00
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
uint ethAmt;
|
|
|
|
if (address(_sellAddr) == getEthAddr()) {
|
|
|
|
ethAmt = oneInchData._sellAmt;
|
2020-07-30 13:20:25 +00:00
|
|
|
} else {
|
2020-07-31 07:31:21 +00:00
|
|
|
TokenInterface(_sellAddr).approve(getOneInchTokenTaker(), oneInchData._sellAmt);
|
2020-07-30 11:56:39 +00:00
|
|
|
}
|
2020-05-09 07:26:03 +00:00
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
require(checkOneInchSig(oneInchData.callData), "Not-swap-function");
|
2020-07-30 13:20:25 +00:00
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
uint buyAmt = oneInchSwap(oneInchData, ethAmt);
|
2020-07-30 13:20:25 +00:00
|
|
|
|
|
|
|
(uint feeAmount, uint leftBuyAmt) = takeFee(
|
2020-07-31 07:31:21 +00:00
|
|
|
address(_buyAddr),
|
|
|
|
buyAmt,
|
|
|
|
oneInchData.feeCollector,
|
2020-07-30 13:20:25 +00:00
|
|
|
feePercent
|
|
|
|
);
|
|
|
|
setUint(setId, leftBuyAmt);
|
2020-07-31 07:31:21 +00:00
|
|
|
oneInchData.feeAmount = feeAmount;
|
2020-07-30 13:20:25 +00:00
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
emitLogSell(oneInchData, setId);
|
2020-07-30 13:20:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
contract OneInchResolver is OneInchResolverHelpers {
|
2020-05-11 00:18:40 +00:00
|
|
|
/**
|
2020-07-31 07:31:21 +00:00
|
|
|
* @dev Sell ETH/ERC20_Token using 1inch.
|
2020-05-11 00:18:40 +00:00
|
|
|
* @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.
|
2020-07-31 07:31:21 +00:00
|
|
|
* @param callData Data from 1inch API.
|
2020-05-11 00:18:40 +00:00
|
|
|
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
|
|
|
*/
|
2020-05-08 09:11:46 +00:00
|
|
|
function sell(
|
|
|
|
address buyAddr,
|
|
|
|
address sellAddr,
|
|
|
|
uint sellAmt,
|
|
|
|
uint unitAmt,
|
2020-07-31 07:31:21 +00:00
|
|
|
bytes calldata callData,
|
2020-05-08 09:11:46 +00:00
|
|
|
uint setId
|
2020-05-09 20:28:08 +00:00
|
|
|
) external payable {
|
2020-07-31 07:31:21 +00:00
|
|
|
OneInchData memory oneInchData = OneInchData({
|
2020-07-30 13:20:25 +00:00
|
|
|
buyToken: TokenInterface(buyAddr),
|
|
|
|
sellToken: TokenInterface(sellAddr),
|
|
|
|
unitAmt: unitAmt,
|
2020-07-31 07:31:21 +00:00
|
|
|
callData: callData,
|
2020-07-30 13:20:25 +00:00
|
|
|
feeCollector: address(0),
|
|
|
|
_sellAmt: sellAmt,
|
|
|
|
_buyAmt: 0,
|
|
|
|
feeAmount: 0
|
|
|
|
});
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
_sell(oneInchData, 0, setId);
|
2020-07-30 13:20:25 +00:00
|
|
|
}
|
2020-05-09 20:28:08 +00:00
|
|
|
|
2020-07-30 13:20:25 +00:00
|
|
|
/**
|
2020-07-31 07:31:21 +00:00
|
|
|
* @dev Sell ETH/ERC20_Token using 1inch.
|
2020-07-30 13:20:25 +00:00
|
|
|
* @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.
|
2020-07-31 07:31:21 +00:00
|
|
|
* @param callData Data from 1inch API.
|
2020-07-30 13:20:25 +00:00
|
|
|
* @param feeCollector Fee amount to transfer.
|
|
|
|
* @param feePercent Fee percentage on buyAmt.
|
|
|
|
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
|
|
|
*/
|
|
|
|
function sellFee(
|
|
|
|
address buyAddr,
|
|
|
|
address sellAddr,
|
|
|
|
uint sellAmt,
|
|
|
|
uint unitAmt,
|
2020-07-31 07:31:21 +00:00
|
|
|
bytes calldata callData,
|
2020-07-30 13:20:25 +00:00
|
|
|
address feeCollector,
|
|
|
|
uint feePercent,
|
|
|
|
uint setId
|
|
|
|
) external payable {
|
|
|
|
require(feePercent > 0 && feePercent <= 2*10*16, "Fee more than 2%");
|
|
|
|
require(feeCollector != address(0), "feeCollector is not vaild address");
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
OneInchData memory oneInchData = OneInchData({
|
2020-07-30 13:20:25 +00:00
|
|
|
buyToken: TokenInterface(buyAddr),
|
|
|
|
sellToken: TokenInterface(sellAddr),
|
|
|
|
_sellAmt: sellAmt,
|
|
|
|
unitAmt: unitAmt,
|
2020-07-31 07:31:21 +00:00
|
|
|
callData: callData,
|
2020-07-30 13:20:25 +00:00
|
|
|
feeCollector: feeCollector,
|
|
|
|
_buyAmt: 0,
|
|
|
|
feeAmount: 0
|
|
|
|
});
|
|
|
|
|
2020-07-31 07:31:21 +00:00
|
|
|
_sell(oneInchData, feePercent, setId);
|
2020-07-24 11:12:04 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-30 13:20:25 +00:00
|
|
|
|
2020-07-24 11:12:04 +00:00
|
|
|
contract ConnectOne is OneInchResolver {
|
2020-07-31 07:31:21 +00:00
|
|
|
string public name = "1Inch-v1";
|
2020-05-08 09:11:46 +00:00
|
|
|
}
|