pragma solidity 0.4.18;
// File: contracts/ERC20Interface.sol
// https://github.com/ethereum/EIPs/issues/20
interface ERC20 {
function totalSupply() public view returns (uint supply);
function balanceOf(address _owner) public view returns (uint balance);
function transfer(address _to, uint _value) public returns (bool success);
function transferFrom(address _from, address _to, uint _value) public returns (bool success);
function approve(address _spender, uint _value) public returns (bool success);
function allowance(address _owner, address _spender) public view returns (uint remaining);
function decimals() public view returns(uint digits);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
// File: contracts/KyberReserveInterface.sol
/// @title Kyber Reserve contract
interface KyberReserveInterface {
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool);
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
}
// File: contracts/Utils.sol
/// @title Kyber constants contract
contract Utils {
ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
uint constant internal PRECISION = (10**18);
uint constant internal MAX_QTY = (10**28); // 10B tokens
uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH
uint constant internal MAX_DECIMALS = 18;
uint constant internal ETH_DECIMALS = 18;
mapping(address=>uint) internal decimals;
function setDecimals(ERC20 token) internal {
if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
else decimals[token] = token.decimals();
}
function getDecimals(ERC20 token) internal view returns(uint) {
if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
uint tokenDecimals = decimals[token];
// technically, there might be token with decimals 0
// moreover, very possible that old tokens have decimals 0
// these tokens will just have higher gas fees.
if(tokenDecimals == 0) return token.decimals();
return tokenDecimals;
}
function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(srcQty <= MAX_QTY);
require(rate <= MAX_RATE);
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
}
}
function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(dstQty <= MAX_QTY);
require(rate <= MAX_RATE);
//source quantity is rounded up. to avoid dest quantity being too low.
uint numerator;
uint denominator;
if (srcDecimals >= dstDecimals) {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
denominator = rate;
} else {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty);
denominator = (rate * (10**(dstDecimals - srcDecimals)));
}
return (numerator + denominator - 1) / denominator; //avoid rounding down errors
}
}
// File: contracts/Utils2.sol
contract Utils2 is Utils {
/// @dev get the balance of a user.
/// @param token The token type
/// @return The balance
function getBalance(ERC20 token, address user) public view returns(uint) {
if (token == ETH_TOKEN_ADDRESS)
return user.balance;
else
return token.balanceOf(user);
}
function getDecimalsSafe(ERC20 token) internal returns(uint) {
if (decimals[token] == 0) {
setDecimals(token);
}
return decimals[token];
}
function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {
return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {
return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)
internal pure returns(uint)
{
require(srcAmount <= MAX_QTY);
require(destAmount <= MAX_QTY);
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);
}
}
}
// File: contracts/PermissionGroups.sol
contract PermissionGroups {
address public admin;
address public pendingAdmin;
mapping(address=>bool) internal operators;
mapping(address=>bool) internal alerters;
address[] internal operatorsGroup;
address[] internal alertersGroup;
uint constant internal MAX_GROUP_SIZE = 50;
function PermissionGroups() public {
admin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == admin);
_;
}
modifier onlyOperator() {
require(operators[msg.sender]);
_;
}
modifier onlyAlerter() {
require(alerters[msg.sender]);
_;
}
function getOperators () external view returns(address[]) {
return operatorsGroup;
}
function getAlerters () external view returns(address[]) {
return alertersGroup;
}
event TransferAdminPending(address pendingAdmin);
/**
* @dev Allows the current admin to set the pendingAdmin address.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdmin(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(pendingAdmin);
pendingAdmin = newAdmin;
}
/**
* @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdminQuickly(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(newAdmin);
AdminClaimed(newAdmin, admin);
admin = newAdmin;
}
event AdminClaimed( address newAdmin, address previousAdmin);
/**
* @dev Allows the pendingAdmin address to finalize the change admin process.
*/
function claimAdmin() public {
require(pendingAdmin == msg.sender);
AdminClaimed(pendingAdmin, admin);
admin = pendingAdmin;
pendingAdmin = address(0);
}
event AlerterAdded (address newAlerter, bool isAdd);
function addAlerter(address newAlerter) public onlyAdmin {
require(!alerters[newAlerter]); // prevent duplicates.
require(alertersGroup.length < MAX_GROUP_SIZE);
AlerterAdded(newAlerter, true);
alerters[newAlerter] = true;
alertersGroup.push(newAlerter);
}
function removeAlerter (address alerter) public onlyAdmin {
require(alerters[alerter]);
alerters[alerter] = false;
for (uint i = 0; i < alertersGroup.length; ++i) {
if (alertersGroup[i] == alerter) {
alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
alertersGroup.length--;
AlerterAdded(alerter, false);
break;
}
}
}
event OperatorAdded(address newOperator, bool isAdd);
function addOperator(address newOperator) public onlyAdmin {
require(!operators[newOperator]); // prevent duplicates.
require(operatorsGroup.length < MAX_GROUP_SIZE);
OperatorAdded(newOperator, true);
operators[newOperator] = true;
operatorsGroup.push(newOperator);
}
function removeOperator (address operator) public onlyAdmin {
require(operators[operator]);
operators[operator] = false;
for (uint i = 0; i < operatorsGroup.length; ++i) {
if (operatorsGroup[i] == operator) {
operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
operatorsGroup.length -= 1;
OperatorAdded(operator, false);
break;
}
}
}
}
// File: contracts/Withdrawable.sol
/**
* @title Contracts that should be able to recover tokens or ethers
* @author Ilan Doron
* @dev This allows to recover any tokens or Ethers received in a contract.
* This will prevent any accidental loss of tokens.
*/
contract Withdrawable is PermissionGroups {
event TokenWithdraw(ERC20 token, uint amount, address sendTo);
/**
* @dev Withdraw all ERC20 compatible tokens
* @param token ERC20 The address of the token contract
*/
function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
require(token.transfer(sendTo, amount));
TokenWithdraw(token, amount, sendTo);
}
event EtherWithdraw(uint amount, address sendTo);
/**
* @dev Withdraw Ethers
*/
function withdrawEther(uint amount, address sendTo) external onlyAdmin {
sendTo.transfer(amount);
EtherWithdraw(amount, sendTo);
}
}
// File: contracts/reserves/bridgeReserves/uniswap/KyberUniswapReserve.sol
interface UniswapExchange {
function ethToTokenSwapInput(
uint256 min_tokens,
uint256 deadline
)
external
payable
returns (uint256 tokens_bought);
function tokenToEthSwapInput(
uint256 tokens_sold,
uint256 min_eth,
uint256 deadline
)
external
returns (uint256 eth_bought);
function getEthToTokenInputPrice(
uint256 eth_sold
)
external
view
returns (uint256 tokens_bought);
function getTokenToEthInputPrice(
uint256 tokens_sold
)
external
view
returns (uint256 eth_bought);
}
interface UniswapFactory {
function getExchange(address token) external view returns (address exchange);
}
/*
* A reserve that connects to Uniswap.
*
* This reserve makes use of an internal inventory for locally filling orders
* using the reserve's inventory when certain conditions are met.
* Conditions are:
* - After trading the inventory will remain within defined limits.
* - Uniswap prices do not display internal arbitrage.
* - Uniswap ask and bid prices meet minimum spread requirements.
*
* An additional premium may be added to the converted price for optional
* promotions.
*/
contract KyberUniswapReserve is KyberReserveInterface, Withdrawable, Utils2 {
// Parts per 10000
uint public constant DEFAULT_FEE_BPS = 25;
UniswapFactory public uniswapFactory;
address public kyberNetwork;
uint public feeBps = DEFAULT_FEE_BPS;
// Uniswap exchange contract for every listed token
// token -> exchange
mapping (address => address) public tokenExchange;
// Internal inventory balance limits
// token -> limit
mapping (address => uint) public internalInventoryMin;
mapping (address => uint) public internalInventoryMax;
// Minimum spread in BPS required for using internal inventory
// token -> limit
mapping (address => uint) public internalActivationMinSpreadBps;
// Premium BPS added to internal price (making it better).
// token -> limit
mapping (address => uint) public internalPricePremiumBps;
bool public tradeEnabled = true;
/**
Constructor
*/
function KyberUniswapReserve(
UniswapFactory _uniswapFactory,
address _admin,
address _kyberNetwork
)
public
{
require(address(_uniswapFactory) != 0);
require(_admin != 0);
require(_kyberNetwork != 0);
uniswapFactory = _uniswapFactory;
admin = _admin;
kyberNetwork = _kyberNetwork;
}
function() public payable {
// anyone can deposit ether
}
/**
Returns dest quantity / source quantity.
Last bit of the rate indicates whether to use internal inventory:
0 - use uniswap
1 - use internal inventory
*/
function getConversionRate(
ERC20 src,
ERC20 dest,
uint srcQty,
uint blockNumber
)
public
view
returns(uint)
{
// This makes the UNUSED warning go away.
blockNumber;
if (!isValidTokens(src, dest)) return 0;
if (!tradeEnabled) return 0;
if (srcQty == 0) return 0;
ERC20 token;
if (src == ETH_TOKEN_ADDRESS) {
token = dest;
} else if (dest == ETH_TOKEN_ADDRESS) {
token = src;
} else {
// Should never arrive here - isValidTokens requires one side to be ETH
revert();
}
uint convertedQuantity;
uint rateSrcDest;
uint rateDestSrc;
(convertedQuantity, rateSrcDest) = calcUniswapConversion(src, dest, srcQty);
if (convertedQuantity == 0) return 0;
(, rateDestSrc) = calcUniswapConversion(dest, src, convertedQuantity);
uint quantityWithPremium = addPremium(token, convertedQuantity);
bool useInternalInventory = shouldUseInternalInventory(
src, /* srcToken */
srcQty, /* srcAmount */
dest, /* destToken */
quantityWithPremium, /* destAmount */
rateSrcDest, /* rateSrcDest */
rateDestSrc /* rateDestSrc */
);
uint rate;
if (useInternalInventory) {
// If using internal inventory add premium to converted quantity
rate = calcRateFromQty(
srcQty, /* srcAmount */
quantityWithPremium, /* destAmount */
getDecimals(src), /* srcDecimals */
getDecimals(dest) /* dstDecimals */
);
} else {
// Use rate calculated from uniswap quantities after fees
rate = rateSrcDest;
}
return applyInternalInventoryHintToRate(rate, useInternalInventory);
}
function applyInternalInventoryHintToRate(
uint rate,
bool useInternalInventory
)
internal
pure
returns(uint)
{
return rate % 2 == (useInternalInventory ? 1 : 0)
? rate
: rate - 1;
}
event TradeExecute(
address indexed sender,
address src,
uint srcAmount,
address destToken,
uint destAmount,
address destAddress,
bool useInternalInventory
);
/**
conversionRate: expected conversion rate should be >= this value.
*/
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool)
{
require(tradeEnabled);
require(msg.sender == kyberNetwork);
require(isValidTokens(srcToken, destToken));
if (validate) {
require(conversionRate > 0);
if (srcToken == ETH_TOKEN_ADDRESS)
require(msg.value == srcAmount);
else
require(msg.value == 0);
}
// Making sure srcAmount has been transfered to the reserve.
// If srcToken is ETH the value has already been transfered by calling
// the function.
if (srcToken != ETH_TOKEN_ADDRESS)
require(srcToken.transferFrom(msg.sender, address(this), srcAmount));
uint expectedDestAmount = calcDestAmount(
srcToken, /* src */
destToken, /* dest */
srcAmount, /* srcAmount */
conversionRate /* rate */
);
bool useInternalInventory = conversionRate % 2 == 1;
uint destAmount;
UniswapExchange exchange;
if (srcToken == ETH_TOKEN_ADDRESS) {
if (!useInternalInventory) {
// Deduct fees (in ETH) before converting
uint quantity = deductFee(srcAmount);
exchange = UniswapExchange(tokenExchange[address(destToken)]);
destAmount = exchange.ethToTokenSwapInput.value(quantity)(
1, /* min_tokens: uniswap requires it to be > 0 */
2 ** 255 /* deadline */
);
require(destAmount >= expectedDestAmount);
}
// Transfer user-expected dest amount
require(destToken.transfer(destAddress, expectedDestAmount));
} else {
if (!useInternalInventory) {
exchange = UniswapExchange(tokenExchange[address(srcToken)]);
destAmount = exchange.tokenToEthSwapInput(
srcAmount,
1, /* min_eth: uniswap requires it to be > 0 */
2 ** 255 /* deadline */
);
// Deduct fees (in ETH) after converting
destAmount = deductFee(destAmount);
require(destAmount >= expectedDestAmount);
}
// Transfer user-expected dest amount
destAddress.transfer(expectedDestAmount);
}
TradeExecute(
msg.sender, /* sender */
srcToken, /* src */
srcAmount, /* srcAmount */
destToken, /* destToken */
expectedDestAmount, /* destAmount */
destAddress, /* destAddress */
useInternalInventory /* useInternalInventory */
);
return true;
}
event FeeUpdated(
uint bps
);
function setFee(
uint bps
)
public
onlyAdmin
{
require(bps <= 10000);
feeBps = bps;
FeeUpdated(bps);
}
event InternalActivationConfigUpdated(
ERC20 token,
uint minSpreadBps,
uint premiumBps
);
function setInternalActivationConfig(
ERC20 token,
uint minSpreadBps,
uint premiumBps
)
public
onlyAdmin
{
require(tokenExchange[address(token)] != address(0));
require(minSpreadBps <= 1000); // min spread <= 10%
require(premiumBps <= 500); // premium <= 5%
internalActivationMinSpreadBps[address(token)] = minSpreadBps;
internalPricePremiumBps[address(token)] = premiumBps;
InternalActivationConfigUpdated(token, minSpreadBps, premiumBps);
}
event InternalInventoryLimitsUpdated(
ERC20 token,
uint minBalance,
uint maxBalance
);
function setInternalInventoryLimits(
ERC20 token,
uint minBalance,
uint maxBalance
)
public
onlyOperator
{
require(tokenExchange[address(token)] != address(0));
internalInventoryMin[address(token)] = minBalance;
internalInventoryMax[address(token)] = maxBalance;
InternalInventoryLimitsUpdated(token, minBalance, maxBalance);
}
event TokenListed(
ERC20 token,
UniswapExchange exchange
);
function listToken(ERC20 token)
public
onlyAdmin
{
require(address(token) != 0);
UniswapExchange uniswapExchange = UniswapExchange(
uniswapFactory.getExchange(token)
);
tokenExchange[address(token)] = address(uniswapExchange);
setDecimals(token);
require(token.approve(uniswapExchange, 2 ** 255));
// internal inventory disabled by default
internalInventoryMin[address(token)] = 2 ** 255;
internalInventoryMax[address(token)] = 0;
internalActivationMinSpreadBps[address(token)] = 0;
internalPricePremiumBps[address(token)] = 0;
TokenListed(token, uniswapExchange);
}
event TokenDelisted(ERC20 token);
function delistToken(ERC20 token)
public
onlyAdmin
{
require(tokenExchange[address(token)] != address(0));
delete tokenExchange[address(token)];
delete internalInventoryMin[address(token)];
delete internalInventoryMax[address(token)];
delete internalActivationMinSpreadBps[address(token)];
delete internalPricePremiumBps[address(token)];
TokenDelisted(token);
}
function isValidTokens(
ERC20 src,
ERC20 dest
)
public
view
returns(bool)
{
return (
(
src == ETH_TOKEN_ADDRESS &&
tokenExchange[address(dest)] != address(0)
) ||
(
tokenExchange[address(src)] != address(0) &&
dest == ETH_TOKEN_ADDRESS
)
);
}
event TradeEnabled(
bool enable
);
function enableTrade()
public
onlyAdmin
returns(bool)
{
tradeEnabled = true;
TradeEnabled(true);
return true;
}
function disableTrade()
public
onlyAlerter
returns(bool)
{
tradeEnabled = false;
TradeEnabled(false);
return true;
}
event KyberNetworkSet(
address kyberNetwork
);
function setKyberNetwork(
address _kyberNetwork
)
public
onlyAdmin
{
require(_kyberNetwork != 0);
kyberNetwork = _kyberNetwork;
KyberNetworkSet(kyberNetwork);
}
/*
* Uses amounts and rates to check if the reserve's internal inventory can
* be used directly.
*
* rateEthToToken and rateTokenToEth are in kyber rate format meaning
* rate as numerator and 1e18 as denominator.
*/
function shouldUseInternalInventory(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
uint destAmount,
uint rateSrcDest,
uint rateDestSrc
)
public
view
returns(bool)
{
require(srcAmount < MAX_QTY);
require(destAmount < MAX_QTY);
// Check for internal inventory balance limitations
ERC20 token;
if (srcToken == ETH_TOKEN_ADDRESS) {
token = destToken;
uint tokenBalance = token.balanceOf(this);
if (
tokenBalance < destAmount ||
tokenBalance - destAmount < internalInventoryMin[token]
) {
return false;
}
} else {
token = srcToken;
if (this.balance < destAmount) return false;
if (token.balanceOf(this) + srcAmount > internalInventoryMax[token]) {
return false;
}
}
uint normalizedDestSrc = 10 ** 36 / rateDestSrc;
// Check for arbitrage
if (rateSrcDest > normalizedDestSrc) return false;
uint activationSpread = internalActivationMinSpreadBps[token];
uint spread = uint(calculateSpreadBps(normalizedDestSrc, rateSrcDest));
return spread >= activationSpread;
}
/*
* Spread calculation is (ask - bid) / ((ask + bid) / 2).
* We multiply by 10000 to get result in BPS.
*
* Note: if askRate > bidRate result will be negative indicating
* internal arbitrage.
*/
function calculateSpreadBps(
uint _askRate,
uint _bidRate
)
public
pure
returns(int)
{
int askRate = int(_askRate);
int bidRate = int(_bidRate);
return 10000 * 2 * (askRate - bidRate) / (askRate + bidRate);
}
function deductFee(
uint amount
)
public
view
returns(uint)
{
return amount * (10000 - feeBps) / 10000;
}
function addPremium(
ERC20 token,
uint amount
)
public
view
returns(uint)
{
require(amount <= MAX_QTY);
return amount * (10000 + internalPricePremiumBps[token]) / 10000;
}
function calcUniswapConversion(
ERC20 src,
ERC20 dest,
uint srcQty
)
internal
view
returns(uint destQty, uint rate)
{
UniswapExchange exchange;
if (src == ETH_TOKEN_ADDRESS) {
exchange = UniswapExchange(tokenExchange[address(dest)]);
destQty = exchange.getEthToTokenInputPrice(
deductFee(srcQty)
);
} else {
exchange = UniswapExchange(tokenExchange[address(src)]);
destQty = deductFee(
exchange.getTokenToEthInputPrice(srcQty)
);
}
rate = calcRateFromQty(
srcQty, /* srcAmount */
destQty, /* destAmount */
getDecimals(src), /* srcDecimals */
getDecimals(dest) /* dstDecimals */
);
}
}
{
"compilationTarget": {
"KyberUniswapReserve.sol": "KyberUniswapReserve"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"enableTrade","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"alerter","type":"address"}],"name":"removeAlerter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"tokenExchange","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"internalPricePremiumBps","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"listToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeBps","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOperators","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"DEFAULT_FEE_BPS","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"addPremium","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"sendTo","type":"address"}],"name":"withdrawToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAlerter","type":"address"}],"name":"addAlerter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"internalInventoryMin","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_kyberNetwork","type":"address"}],"name":"setKyberNetwork","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"srcToken","type":"address"},{"name":"srcAmount","type":"uint256"},{"name":"destToken","type":"address"},{"name":"destAmount","type":"uint256"},{"name":"rateSrcDest","type":"uint256"},{"name":"rateDestSrc","type":"uint256"}],"name":"shouldUseInternalInventory","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"disableTrade","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"bps","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"srcToken","type":"address"},{"name":"srcAmount","type":"uint256"},{"name":"destToken","type":"address"},{"name":"destAddress","type":"address"},{"name":"conversionRate","type":"uint256"},{"name":"validate","type":"bool"}],"name":"trade","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"transferAdminQuickly","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"dest","type":"address"}],"name":"isValidTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"minSpreadBps","type":"uint256"},{"name":"premiumBps","type":"uint256"}],"name":"setInternalActivationConfig","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getAlerters","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"src","type":"address"},{"name":"dest","type":"address"},{"name":"srcQty","type":"uint256"},{"name":"blockNumber","type":"uint256"}],"name":"getConversionRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOperator","type":"address"}],"name":"addOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"internalInventoryMax","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"minBalance","type":"uint256"},{"name":"maxBalance","type":"uint256"}],"name":"setInternalInventoryLimits","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],"name":"deductFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"delistToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"operator","type":"address"}],"name":"removeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetwork","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_askRate","type":"uint256"},{"name":"_bidRate","type":"uint256"}],"name":"calculateSpreadBps","outputs":[{"name":"","type":"int256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"internalActivationMinSpreadBps","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"sendTo","type":"address"}],"name":"withdrawEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"},{"name":"user","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tradeEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_uniswapFactory","type":"address"},{"name":"_admin","type":"address"},{"name":"_kyberNetwork","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"src","type":"address"},{"indexed":false,"name":"srcAmount","type":"uint256"},{"indexed":false,"name":"destToken","type":"address"},{"indexed":false,"name":"destAmount","type":"uint256"},{"indexed":false,"name":"destAddress","type":"address"},{"indexed":false,"name":"useInternalInventory","type":"bool"}],"name":"TradeExecute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bps","type":"uint256"}],"name":"FeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"minSpreadBps","type":"uint256"},{"indexed":false,"name":"premiumBps","type":"uint256"}],"name":"InternalActivationConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"minBalance","type":"uint256"},{"indexed":false,"name":"maxBalance","type":"uint256"}],"name":"InternalInventoryLimitsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"exchange","type":"address"}],"name":"TokenListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"}],"name":"TokenDelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"enable","type":"bool"}],"name":"TradeEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"kyberNetwork","type":"address"}],"name":"KyberNetworkSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"sendTo","type":"address"}],"name":"TokenWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"sendTo","type":"address"}],"name":"EtherWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pendingAdmin","type":"address"}],"name":"TransferAdminPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAdmin","type":"address"},{"indexed":false,"name":"previousAdmin","type":"address"}],"name":"AdminClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newAlerter","type":"address"},{"indexed":false,"name":"isAdd","type":"bool"}],"name":"AlerterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOperator","type":"address"},{"indexed":false,"name":"isAdd","type":"bool"}],"name":"OperatorAdded","type":"event"}]