1
0
mirror of https://github.com/dap-ps/discover.git synced 2025-02-07 23:15:09 +00:00
discover/contracts/DiscoverKyberSwap.sol
2020-03-20 08:04:05 -04:00

146 lines
5.5 KiB
Solidity

pragma solidity ^0.5.2;
import "./kyber/KyberNetworkProxy.sol";
import "./Discover.sol";
import "./token/ERC20Token.sol";
contract DiscoverKyberSwap is Controlled {
address public SNT;
address public ETH;
KyberNetworkProxy public kyberProxy;
Discover public discover;
address public walletId;
uint public maxSlippage;
/**
* @param _discover Discover contract address
* @param _kyberProxy Kyber Network Proxy address
* @param _ETH Kyber ETH address
* @param _SNT Kyber SNT address
* @param _walletId Wallet for Kyber network fees
* @param _maxSlippage Max slippage rate
*/
constructor(address _discover, address _kyberProxy, address _ETH, address _SNT, address _walletId, uint _maxSlippage) public {
require(_maxSlippage < 100);
discover = Discover(_discover);
kyberProxy = KyberNetworkProxy(_kyberProxy);
ETH = _ETH;
SNT = _SNT;
walletId = _walletId;
maxSlippage = _maxSlippage;
}
/**
* @notice Gets the conversion rate for the destToken given the srcQty.
* @param srcToken source token contract address
* @param srcQty amount of source tokens
* @return exchange rate
*/
function getConversionRates(address srcToken, uint srcQty) public view returns (uint expectedRate, uint slippageRate)
{
if(srcToken == address(0)){
srcToken = ETH;
}
(expectedRate, slippageRate) = kyberProxy.getExpectedRate(srcToken, SNT, srcQty);
require(expectedRate > 0);
}
/**
* @notice Upvote in discover
* @dev Requires a msg.value if using ETH
* @param _id Id to upvote
* @param _token Token to convert to SNT (see https://developer.kyber.network/docs/Environments-Intro/). Address 0 can be used for ETH too
* @param _amount Amount of tokens/eth to convert
*/
function upvote(bytes32 _id, address _token, uint _amount) public payable {
uint sntAmount = _tradeTokens(_token, _amount);
discover.upvote(_id, sntAmount);
}
/**
* @notice Downvote in discover
* @dev Requires a msg.value if using ETH
* @param _id Id to upvote
* @param _token Token to convert to SNT (see https://developer.kyber.network/docs/Environments-Intro/). Address 0 can be used for ETH too
* @param _amount Amount of tokens/eth to convert
*/
function downvote(bytes32 _id, address _token, uint _amount) public payable {
uint sntAmount = _tradeTokens(_token, _amount);
discover.downvote(_id, sntAmount);
}
/**
* @dev Trades tokens/ETH to SNT using Kyber
* @param _token Token to convert to SNT (see https://developer.kyber.network/docs/Environments-Intro/). Address 0 can be used for ETH too
* @param _amount Amount of tokens/eth to convert
* @return Amount of SNT received from the conversion
*/
function _tradeTokens(address _token, uint _amount) internal returns(uint sntAmount) {
uint minConversionRate;
uint slippageRate;
uint slippagePercent;
ERC20Token sntToken = ERC20Token(SNT);
if (_token == address(0) || _token == ETH) {
require(msg.value == _amount, "Not enough ETH");
(minConversionRate, slippageRate) = getConversionRates(ETH, _amount);
slippagePercent = 100 - ((slippageRate * 100) / minConversionRate);
require(slippagePercent <= maxSlippage);
sntAmount = kyberProxy.trade.value(_amount)(ETH, _amount, SNT, address(this), 0 - uint256(1), minConversionRate, walletId);
} else {
ERC20Token t = ERC20Token(_token);
// Initially transfer the tokens from the user to this contract
require(t.transferFrom(msg.sender, address(this), _amount));
if (_token != SNT) {
// Mitigate ERC20 Approve front-running attack, by initially setting allowance to 0
require(t.approve(address(kyberProxy), 0), "Could not reset token approval");
// Set the spender's token allowance to tokenQty
require(t.approve(address(kyberProxy), _amount), "Could not approve token amount");
(minConversionRate, slippageRate) = getConversionRates(_token, _amount);
slippagePercent = 100 - ((slippageRate * 100) / minConversionRate);
require(slippagePercent <= maxSlippage);
sntAmount = kyberProxy.trade(_token, _amount, SNT, address(this), 0 - uint256(1), minConversionRate, walletId);
} else {
sntAmount = _amount;
}
}
require(sntAmount != 0, "Not enough SNT for vote");
require(sntToken.approve(address(discover), 0), "Could not reset SNT approval");
require(sntToken.approve(address(discover), sntAmount), "Could not approve SNT amount");
}
event WalletIdChanged(address sender, address prevWalletId, address newWalletId);
/**
* @dev Changes the walletId address (for the fee sharing program)
* @param _walletId New walletId address
*/
function setWalletId(address _walletId) external onlyController {
emit WalletIdChanged(msg.sender, walletId, _walletId);
walletId = _walletId;
}
event SlippageUpdated(uint maxSlippage);
/**
* @param _maxSlippage most slippage as a percentage
*/
function setSlippage(uint _maxSlippage) public onlyController {
require(_maxSlippage < 100);
maxSlippage = _maxSlippage;
emit SlippageUpdated(_maxSlippage);
}
}