Transaction Hash:
Block:
10258645 at Jun-13-2020 05:19:44 PM +UTC
Transaction Fee:
0.00817966 ETH
$16.02
Gas Used:
408,983 Gas / 20 Gwei
Emitted Events:
| 11 |
EthLendToken.Transfer( _from=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, _to=KyberNetwork, _value=3604148702765674280811 )
|
| 12 |
EthLendToken.Transfer( _from=KyberNetwork, _to=KyberReserve, _value=3604148702765674280811 )
|
| 13 |
KyberNetwork.EtherReceival( sender=KyberReserve, amount=1412894614423318504 )
|
| 14 |
KyberReserve.TradeExecute( origin=KyberNetwork, src=EthLendToken, srcAmount=3604148702765674280811, destToken=0xEeeeeEee...eeeeeEEeE, destAmount=1412894614423318504, destAddress=KyberNetwork )
|
| 15 |
RepublicToken.Transfer( from=KyberReserve, to=KyberNetwork, value=3177187964488720040436 )
|
| 16 |
KyberReserve.TradeExecute( origin=KyberNetwork, src=0xEeeeeEee...eeeeeEEeE, srcAmount=1412894614423318504, destToken=RepublicToken, destAmount=3177187964488720040436, destAddress=KyberNetwork )
|
| 17 |
RepublicToken.Transfer( from=KyberNetwork, to=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, value=3177187964488720040436 )
|
| 18 |
FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0x440bBd6a...16874faa9, walletFee=202189408791872350 )
|
| 19 |
FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=471775287181035486 )
|
| 20 |
FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0x440bBd6a...16874faa9, walletFee=202189408791872350 )
|
| 21 |
FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=471775287181035486 )
|
| 22 |
KyberNetwork.KyberTrade( trader=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, src=EthLendToken, dest=RepublicToken, srcAmount=3604148702765674280811, dstAmount=3177187964488720040436, destAddress=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, ethWeiValue=1412894614423318504, reserve1=KyberReserve, reserve2=KyberReserve, hint=0x5045524D )
|
| 23 |
KyberNetworkProxy.ExecuteTrade( trader=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, src=EthLendToken, dest=RepublicToken, actualSrcAmount=3604148702765674280811, actualDestAmount=3177187964488720040436 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x2f4B3134...55a906963 | |||||
| 0x408e4187...656052a38 | |||||
| 0x45eb33D0...aE596EA57 | (Kyber: Reserve REN) | 210.626324740573305813 Eth | 212.039219354996624317 Eth | 1.412894614423318504 | |
|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 58.630260701603742049 Eth | 58.638440361603742049 Eth | 0.00817966 | |
| 0x63825c17...bD36A0D8F | (Kyber: Reserve) | 3,640.114768167976945754 Eth | 3,638.70187355355362725 Eth | 1.412894614423318504 | |
| 0x65bF64Ff...77ced86Cd | (Kyber: Contract) | ||||
| 0x798AbDA6...d3d11191B | (Kyber: Conversion Rates) | ||||
| 0x8007aa43...E5fF626d1 | (Kyber: Fee Burner) | ||||
| 0x80fB784B...9931aab03 | |||||
| 0xd1A83A93...aC0465e65 |
45.706453376556338371 Eth
Nonce: 447
|
45.698273716556338371 Eth
Nonce: 448
| 0.00817966 |
Execution Trace
KyberNetworkProxy.tradeWithHint( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, dest=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=855090225103408578, walletId=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9, hint=0x5045524D ) => ( 3177187964488720040436 )
-
EthLendToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( 3604148702765674280811 )
-
RepublicToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( balance=0 )
-
EthLendToken.transferFrom( _from=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, _to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _value=3604148702765674280811 ) => ( True )
KyberNetwork.tradeWithHint( trader=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, dest=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=855090225103408578, walletId=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9, hint=0x5045524D ) => ( 3177187964488720040436 )-
EthLendToken.balanceOf( _owner=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd ) => ( 3604148702765674863704 )
KyberReserve.getConversionRate( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=3604148702765674280811, blockNumber=10258645 ) => ( 392018956748128 )-
ConversionRates.getRate( token=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, currentBlockNumber=10258645, buy=False, qty=3604148702765674280811 ) => ( 392018956748128 ) -
SanityRates.getSanityRate( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ) => ( 439826750000000 )
-
KyberReserve.getConversionRate( src=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, dest=0x408e41876cCCDC0F92210600ef50372656052a38, srcQty=1412894614423318504, blockNumber=10258645 ) => ( 2248708383523358927314 )-
LiquidityConversionRates.getRate( conversionToken=0x408e41876cCCDC0F92210600ef50372656052a38, currentBlockNumber=10258645, buy=True, qtyInSrcWei=1412894614423318504 ) => ( 2248708383523358927314 ) -
RepublicToken.balanceOf( _owner=0x45eb33D008801d547990cAF3b63B4F8aE596EA57 ) => ( balance=516997733618317278783239 )
-
RepublicToken.allowance( _owner=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, _spender=0x45eb33D008801d547990cAF3b63B4F8aE596EA57 ) => ( 57896044618658097711785492504343953926634992332820221954033810829822162136671 )
-
KyberReserve.trade( srcToken=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, destToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=392018956748128, validate=True ) => ( True )-
ConversionRates.recordImbalance( token=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, buyAmount=-3604148702765674280811, rateUpdateBlock=0, currentBlock=10258645 ) -
EthLendToken.transferFrom( _from=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _to=0x63825c174ab367968EC60f061753D3bbD36A0D8F, _value=3604148702765674280811 ) => ( True )
- ETH 1.412894614423318504
KyberNetwork.CALL( )
-
ETH 1.412894614423318504
KyberReserve.trade( srcToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcAmount=1412894614423318504, destToken=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=2248708383523358927314, validate=True ) => ( True )-
LiquidityConversionRates.recordImbalance( conversionToken=0x408e41876cCCDC0F92210600ef50372656052a38, buyAmountInTwei=3177187964488720040436, rateUpdateBlock=0, currentBlock=10258645 ) -
RepublicToken.transferFrom( _from=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, _to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _value=3177187964488720040436 ) => ( True )
-
-
RepublicToken.transfer( _to=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, _value=3177187964488720040436 ) => ( True )
-
FeeBurner.handleFees( tradeWeiAmount=1412894614423318504, reserve=0x63825c174ab367968EC60f061753D3bbD36A0D8F, wallet=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9 ) => ( True ) -
FeeBurner.handleFees( tradeWeiAmount=1412894614423318504, reserve=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, wallet=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9 ) => ( True )
-
-
EthLendToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( 0 )
-
RepublicToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( balance=3177187964488720040436 )
tradeWithHint[KyberNetworkProxy (ln:462)]
getBalance[KyberNetworkProxy (ln:480)]getBalance[KyberNetworkProxy (ln:481)]transferFrom[KyberNetworkProxy (ln:486)]value[KyberNetworkProxy (ln:489)]calculateTradeOutcome[KyberNetworkProxy (ln:501)]getBalance[KyberNetworkProxy (ln:568)]getBalance[KyberNetworkProxy (ln:569)]calcRateFromQty[KyberNetworkProxy (ln:578)]getDecimalsSafe[KyberNetworkProxy (ln:581)]getDecimalsSafe[KyberNetworkProxy (ln:582)]
ExecuteTrade[KyberNetworkProxy (ln:513)]
File 1 of 10: KyberNetworkProxy
File 2 of 10: KyberNetwork
File 3 of 10: EthLendToken
File 4 of 10: KyberReserve
File 5 of 10: KyberReserve
File 6 of 10: RepublicToken
File 7 of 10: FeeBurner
File 8 of 10: ConversionRates
File 9 of 10: SanityRates
File 10 of 10: LiquidityConversionRates
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/KyberNetworkInterface.sol
/// @title Kyber Network interface
interface KyberNetworkInterface {
function maxGasPrice() public view returns(uint);
function getUserCapInWei(address user) public view returns(uint);
function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);
function enabled() public view returns(bool);
function info(bytes32 id) public view returns(uint);
function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view
returns (uint expectedRate, uint slippageRate);
function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,
uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);
}
// File: contracts/KyberNetworkProxyInterface.sol
/// @title Kyber Network interface
interface KyberNetworkProxyInterface {
function maxGasPrice() public view returns(uint);
function getUserCapInWei(address user) public view returns(uint);
function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);
function enabled() public view returns(bool);
function info(bytes32 id) public view returns(uint);
function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view
returns (uint expectedRate, uint slippageRate);
function tradeWithHint(ERC20 src, uint srcAmount, ERC20 dest, address destAddress, uint maxDestAmount,
uint minConversionRate, address walletId, bytes hint) public payable returns(uint);
}
// File: contracts/SimpleNetworkInterface.sol
/// @title simple interface for Kyber Network
interface SimpleNetworkInterface {
function swapTokenToToken(ERC20 src, uint srcAmount, ERC20 dest, uint minConversionRate) public returns(uint);
function swapEtherToToken(ERC20 token, uint minConversionRate) public payable returns(uint);
function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) public 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/KyberNetworkProxy.sol
////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @title Kyber Network proxy for main contract
contract KyberNetworkProxy is KyberNetworkProxyInterface, SimpleNetworkInterface, Withdrawable, Utils2 {
KyberNetworkInterface public kyberNetworkContract;
function KyberNetworkProxy(address _admin) public {
require(_admin != address(0));
admin = _admin;
}
/// @notice use token address ETH_TOKEN_ADDRESS for ether
/// @dev makes a trade between src and dest token and send dest token to destAddress
/// @param src Src token
/// @param srcAmount amount of src tokens
/// @param dest Destination token
/// @param destAddress Address to send tokens to
/// @param maxDestAmount A limit on the amount of dest tokens
/// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
/// @param walletId is the wallet ID to send part of the fees
/// @return amount of actual dest tokens
function trade(
ERC20 src,
uint srcAmount,
ERC20 dest,
address destAddress,
uint maxDestAmount,
uint minConversionRate,
address walletId
)
public
payable
returns(uint)
{
bytes memory hint;
return tradeWithHint(
src,
srcAmount,
dest,
destAddress,
maxDestAmount,
minConversionRate,
walletId,
hint
);
}
/// @dev makes a trade between src and dest token and send dest tokens to msg sender
/// @param src Src token
/// @param srcAmount amount of src tokens
/// @param dest Destination token
/// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
/// @return amount of actual dest tokens
function swapTokenToToken(
ERC20 src,
uint srcAmount,
ERC20 dest,
uint minConversionRate
)
public
returns(uint)
{
bytes memory hint;
return tradeWithHint(
src,
srcAmount,
dest,
msg.sender,
MAX_QTY,
minConversionRate,
0,
hint
);
}
/// @dev makes a trade from Ether to token. Sends token to msg sender
/// @param token Destination token
/// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
/// @return amount of actual dest tokens
function swapEtherToToken(ERC20 token, uint minConversionRate) public payable returns(uint) {
bytes memory hint;
return tradeWithHint(
ETH_TOKEN_ADDRESS,
msg.value,
token,
msg.sender,
MAX_QTY,
minConversionRate,
0,
hint
);
}
/// @dev makes a trade from token to Ether, sends Ether to msg sender
/// @param token Src token
/// @param srcAmount amount of src tokens
/// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
/// @return amount of actual dest tokens
function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) public returns(uint) {
bytes memory hint;
return tradeWithHint(
token,
srcAmount,
ETH_TOKEN_ADDRESS,
msg.sender,
MAX_QTY,
minConversionRate,
0,
hint
);
}
struct UserBalance {
uint srcBalance;
uint destBalance;
}
event ExecuteTrade(address indexed trader, ERC20 src, ERC20 dest, uint actualSrcAmount, uint actualDestAmount);
/// @notice use token address ETH_TOKEN_ADDRESS for ether
/// @dev makes a trade between src and dest token and send dest token to destAddress
/// @param src Src token
/// @param srcAmount amount of src tokens
/// @param dest Destination token
/// @param destAddress Address to send tokens to
/// @param maxDestAmount A limit on the amount of dest tokens
/// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
/// @param walletId is the wallet ID to send part of the fees
/// @param hint will give hints for the trade.
/// @return amount of actual dest tokens
function tradeWithHint(
ERC20 src,
uint srcAmount,
ERC20 dest,
address destAddress,
uint maxDestAmount,
uint minConversionRate,
address walletId,
bytes hint
)
public
payable
returns(uint)
{
require(src == ETH_TOKEN_ADDRESS || msg.value == 0);
UserBalance memory userBalanceBefore;
userBalanceBefore.srcBalance = getBalance(src, msg.sender);
userBalanceBefore.destBalance = getBalance(dest, destAddress);
if (src == ETH_TOKEN_ADDRESS) {
userBalanceBefore.srcBalance += msg.value;
} else {
require(src.transferFrom(msg.sender, kyberNetworkContract, srcAmount));
}
uint reportedDestAmount = kyberNetworkContract.tradeWithHint.value(msg.value)(
msg.sender,
src,
srcAmount,
dest,
destAddress,
maxDestAmount,
minConversionRate,
walletId,
hint
);
TradeOutcome memory tradeOutcome = calculateTradeOutcome(
userBalanceBefore.srcBalance,
userBalanceBefore.destBalance,
src,
dest,
destAddress
);
require(reportedDestAmount == tradeOutcome.userDeltaDestAmount);
require(tradeOutcome.userDeltaDestAmount <= maxDestAmount);
require(tradeOutcome.actualRate >= minConversionRate);
ExecuteTrade(msg.sender, src, dest, tradeOutcome.userDeltaSrcAmount, tradeOutcome.userDeltaDestAmount);
return tradeOutcome.userDeltaDestAmount;
}
event KyberNetworkSet(address newNetworkContract, address oldNetworkContract);
function setKyberNetworkContract(KyberNetworkInterface _kyberNetworkContract) public onlyAdmin {
require(_kyberNetworkContract != address(0));
KyberNetworkSet(_kyberNetworkContract, kyberNetworkContract);
kyberNetworkContract = _kyberNetworkContract;
}
function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty)
public view
returns(uint expectedRate, uint slippageRate)
{
return kyberNetworkContract.getExpectedRate(src, dest, srcQty);
}
function getUserCapInWei(address user) public view returns(uint) {
return kyberNetworkContract.getUserCapInWei(user);
}
function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) {
return kyberNetworkContract.getUserCapInTokenWei(user, token);
}
function maxGasPrice() public view returns(uint) {
return kyberNetworkContract.maxGasPrice();
}
function enabled() public view returns(bool) {
return kyberNetworkContract.enabled();
}
function info(bytes32 field) public view returns(uint) {
return kyberNetworkContract.info(field);
}
struct TradeOutcome {
uint userDeltaSrcAmount;
uint userDeltaDestAmount;
uint actualRate;
}
function calculateTradeOutcome (uint srcBalanceBefore, uint destBalanceBefore, ERC20 src, ERC20 dest,
address destAddress)
internal returns(TradeOutcome outcome)
{
uint userSrcBalanceAfter;
uint userDestBalanceAfter;
userSrcBalanceAfter = getBalance(src, msg.sender);
userDestBalanceAfter = getBalance(dest, destAddress);
//protect from underflow
require(userDestBalanceAfter > destBalanceBefore);
require(srcBalanceBefore > userSrcBalanceAfter);
outcome.userDeltaDestAmount = userDestBalanceAfter - destBalanceBefore;
outcome.userDeltaSrcAmount = srcBalanceBefore - userSrcBalanceAfter;
outcome.actualRate = calcRateFromQty(
outcome.userDeltaSrcAmount,
outcome.userDeltaDestAmount,
getDecimalsSafe(src),
getDecimalsSafe(dest)
);
}
}File 2 of 10: KyberNetwork
{"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n function totalSupply() public view returns (uint supply);\n function balanceOf(address _owner) public view returns (uint balance);\n function transfer(address _to, uint _value) public returns (bool success);\n function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n function approve(address _spender, uint _value) public returns (bool success);\n function allowance(address _owner, address _spender) public view returns (uint remaining);\n function decimals() public view returns(uint digits);\n event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"ExpectedRateInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ExpectedRateInterface {\n function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty, bool usePermissionless) public view\n returns (uint expectedRate, uint slippageRate);\n}\n"},"FeeBurnerInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ninterface FeeBurnerInterface {\n function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool);\n function setReserveData(address reserve, uint feesInBps, address kncWallet) public;\n}\n"},"KyberNetwork.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./KyberReserveInterface.sol\";\nimport \"./KyberNetworkInterface.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./Utils3.sol\";\nimport \"./WhiteListInterface.sol\";\nimport \"./ExpectedRateInterface.sol\";\nimport \"./FeeBurnerInterface.sol\";\n\n\n/**\n * @title Helps contracts guard against reentrancy attacks.\n */\ncontract ReentrancyGuard {\n\n /// @dev counter to allow mutex lock with only one SSTORE operation\n uint256 private guardCounter = 1;\n\n /**\n * @dev Prevents a function from calling itself, directly or indirectly.\n * Calling one `nonReentrant` function from\n * another is not supported. Instead, you can implement a\n * `private` function doing the actual work, and an `external`\n * wrapper marked as `nonReentrant`.\n */\n modifier nonReentrant() {\n guardCounter += 1;\n uint256 localCounter = guardCounter;\n _;\n require(localCounter == guardCounter);\n }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////\n/// @title Kyber Network main contract\ncontract KyberNetwork is Withdrawable, Utils3, KyberNetworkInterface, ReentrancyGuard {\n\n bytes public constant PERM_HINT = \"PERM\";\n uint public constant PERM_HINT_GET_RATE = 1 \u003c\u003c 255; // for get rate. bit mask hint.\n\n uint public negligibleRateDiff = 10; // basic rate steps will be in 0.01%\n KyberReserveInterface[] public reserves;\n mapping(address=\u003eReserveType) public reserveType;\n WhiteListInterface public whiteListContract;\n ExpectedRateInterface public expectedRateContract;\n FeeBurnerInterface public feeBurnerContract;\n address public kyberNetworkProxyContract;\n uint public maxGasPriceValue = 50 * 1000 * 1000 * 1000; // 50 gwei\n bool public isEnabled = false; // network is enabled\n mapping(bytes32=\u003euint) public infoFields; // this is only a UI field for external app.\n\n mapping(address=\u003eaddress[]) public reservesPerTokenSrc; //reserves supporting token to eth\n mapping(address=\u003eaddress[]) public reservesPerTokenDest;//reserves support eth to token\n\n enum ReserveType {NONE, PERMISSIONED, PERMISSIONLESS}\n bytes internal constant EMPTY_HINT = \"\";\n\n function KyberNetwork(address _admin) public {\n require(_admin != address(0));\n admin = _admin;\n }\n\n event EtherReceival(address indexed sender, uint amount);\n\n /* solhint-disable no-complex-fallback */\n function() public payable {\n EtherReceival(msg.sender, msg.value);\n }\n /* solhint-enable no-complex-fallback */\n\n struct TradeInput {\n address trader;\n ERC20 src;\n uint srcAmount;\n ERC20 dest;\n address destAddress;\n uint maxDestAmount;\n uint minConversionRate;\n address walletId;\n bytes hint;\n }\n\n function tradeWithHint(\n address trader,\n ERC20 src,\n uint srcAmount,\n ERC20 dest,\n address destAddress,\n uint maxDestAmount,\n uint minConversionRate,\n address walletId,\n bytes hint\n )\n public\n nonReentrant\n payable\n returns(uint)\n {\n require(msg.sender == kyberNetworkProxyContract);\n require((hint.length == 0) || (hint.length == 4));\n\n TradeInput memory tradeInput;\n\n tradeInput.trader = trader;\n tradeInput.src = src;\n tradeInput.srcAmount = srcAmount;\n tradeInput.dest = dest;\n tradeInput.destAddress = destAddress;\n tradeInput.maxDestAmount = maxDestAmount;\n tradeInput.minConversionRate = minConversionRate;\n tradeInput.walletId = walletId;\n tradeInput.hint = hint;\n\n return trade(tradeInput);\n }\n\n event AddReserveToNetwork(KyberReserveInterface indexed reserve, bool add, bool isPermissionless);\n\n /// @notice can be called only by operator\n /// @dev add or deletes a reserve to/from the network.\n /// @param reserve The reserve address.\n /// @param isPermissionless is the new reserve from permissionless type.\n function addReserve(KyberReserveInterface reserve, bool isPermissionless) public onlyOperator\n returns(bool)\n {\n require(reserveType[reserve] == ReserveType.NONE);\n reserves.push(reserve);\n\n reserveType[reserve] = isPermissionless ? ReserveType.PERMISSIONLESS : ReserveType.PERMISSIONED;\n\n AddReserveToNetwork(reserve, true, isPermissionless);\n\n return true;\n }\n\n event RemoveReserveFromNetwork(KyberReserveInterface reserve);\n\n /// @notice can be called only by operator\n /// @dev removes a reserve from Kyber network.\n /// @param reserve The reserve address.\n /// @param index in reserve array.\n function removeReserve(KyberReserveInterface reserve, uint index) public onlyOperator\n returns(bool)\n {\n\n require(reserveType[reserve] != ReserveType.NONE);\n require(reserves[index] == reserve);\n\n reserveType[reserve] = ReserveType.NONE;\n reserves[index] = reserves[reserves.length - 1];\n reserves.length--;\n\n RemoveReserveFromNetwork(reserve);\n\n return true;\n }\n\n event ListReservePairs(address indexed reserve, ERC20 src, ERC20 dest, bool add);\n\n /// @notice can be called only by operator\n /// @dev allow or prevent a specific reserve to trade a pair of tokens\n /// @param reserve The reserve address.\n /// @param token token address\n /// @param ethToToken will it support ether to token trade\n /// @param tokenToEth will it support token to ether trade\n /// @param add If true then list this pair, otherwise unlist it.\n function listPairForReserve(address reserve, ERC20 token, bool ethToToken, bool tokenToEth, bool add)\n public\n onlyOperator\n returns(bool)\n {\n require(reserveType[reserve] != ReserveType.NONE);\n\n if (ethToToken) {\n listPairs(reserve, token, false, add);\n\n ListReservePairs(reserve, ETH_TOKEN_ADDRESS, token, add);\n }\n\n if (tokenToEth) {\n listPairs(reserve, token, true, add);\n\n if (add) {\n require(token.approve(reserve, 2**255)); // approve infinity\n } else {\n require(token.approve(reserve, 0));\n }\n\n ListReservePairs(reserve, token, ETH_TOKEN_ADDRESS, add);\n }\n\n setDecimals(token);\n\n return true;\n }\n\n event WhiteListContractSet(WhiteListInterface newContract, WhiteListInterface currentContract);\n\n ///@param whiteList can be empty\n function setWhiteList(WhiteListInterface whiteList) public onlyAdmin {\n WhiteListContractSet(whiteList, whiteListContract);\n whiteListContract = whiteList;\n }\n\n event ExpectedRateContractSet(ExpectedRateInterface newContract, ExpectedRateInterface currentContract);\n\n function setExpectedRate(ExpectedRateInterface expectedRate) public onlyAdmin {\n require(expectedRate != address(0));\n\n ExpectedRateContractSet(expectedRate, expectedRateContract);\n expectedRateContract = expectedRate;\n }\n\n event FeeBurnerContractSet(FeeBurnerInterface newContract, FeeBurnerInterface currentContract);\n\n function setFeeBurner(FeeBurnerInterface feeBurner) public onlyAdmin {\n require(feeBurner != address(0));\n\n FeeBurnerContractSet(feeBurner, feeBurnerContract);\n feeBurnerContract = feeBurner;\n }\n\n event KyberNetwrokParamsSet(uint maxGasPrice, uint negligibleRateDiff);\n\n function setParams(\n uint _maxGasPrice,\n uint _negligibleRateDiff\n )\n public\n onlyAdmin\n {\n require(_negligibleRateDiff \u003c= 100 * 100); // at most 100%\n\n maxGasPriceValue = _maxGasPrice;\n negligibleRateDiff = _negligibleRateDiff;\n KyberNetwrokParamsSet(maxGasPriceValue, negligibleRateDiff);\n }\n\n event KyberNetworkSetEnable(bool isEnabled);\n\n function setEnable(bool _enable) public onlyAdmin {\n if (_enable) {\n require(feeBurnerContract != address(0));\n require(expectedRateContract != address(0));\n require(kyberNetworkProxyContract != address(0));\n }\n isEnabled = _enable;\n\n KyberNetworkSetEnable(isEnabled);\n }\n\n function setInfo(bytes32 field, uint value) public onlyOperator {\n infoFields[field] = value;\n }\n\n event KyberProxySet(address proxy, address sender);\n\n function setKyberProxy(address networkProxy) public onlyAdmin {\n require(networkProxy != address(0));\n kyberNetworkProxyContract = networkProxy;\n KyberProxySet(kyberNetworkProxyContract, msg.sender);\n }\n\n /// @dev returns number of reserves\n /// @return number of reserves\n function getNumReserves() public view returns(uint) {\n return reserves.length;\n }\n\n /// @notice should be called off chain\n /// @dev get an array of all reserves\n /// @return An array of all reserves\n function getReserves() public view returns(KyberReserveInterface[]) {\n return reserves;\n }\n\n function maxGasPrice() public view returns(uint) {\n return maxGasPriceValue;\n }\n\n function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty)\n public view\n returns(uint expectedRate, uint slippageRate)\n {\n require(expectedRateContract != address(0));\n if (src == dest) return (0,0);\n bool includePermissionless = true;\n\n if (srcQty \u0026 PERM_HINT_GET_RATE \u003e 0) {\n includePermissionless = false;\n srcQty = srcQty \u0026 ~PERM_HINT_GET_RATE;\n }\n\n return expectedRateContract.getExpectedRate(src, dest, srcQty, includePermissionless);\n }\n\n function getExpectedRateOnlyPermission(ERC20 src, ERC20 dest, uint srcQty)\n public view\n returns(uint expectedRate, uint slippageRate)\n {\n require(expectedRateContract != address(0));\n if (src == dest) return (0,0);\n return expectedRateContract.getExpectedRate(src, dest, srcQty, false);\n }\n\n function getUserCapInWei(address user) public view returns(uint) {\n if (whiteListContract == address(0)) return (2 ** 255);\n return whiteListContract.getUserCapInWei(user);\n }\n\n function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) {\n //future feature\n user;\n token;\n require(false);\n }\n\n struct BestRateResult {\n uint rate;\n address reserve1;\n address reserve2;\n uint weiAmount;\n uint rateSrcToEth;\n uint rateEthToDest;\n uint destAmount;\n }\n\n /// @notice use token address ETH_TOKEN_ADDRESS for ether\n /// @dev best conversion rate for a pair of tokens, if number of reserves have small differences. randomize\n /// @param src Src token\n /// @param dest Destination token\n /// @return obsolete - used to return best reserve index. not relevant anymore for this API.\n function findBestRate(ERC20 src, ERC20 dest, uint srcAmount) public view returns(uint obsolete, uint rate) {\n BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, EMPTY_HINT);\n return(0, result.rate);\n }\n\n function findBestRateOnlyPermission(ERC20 src, ERC20 dest, uint srcAmount)\n public\n view\n returns(uint obsolete, uint rate)\n {\n BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, PERM_HINT);\n return(0, result.rate);\n }\n\n function enabled() public view returns(bool) {\n return isEnabled;\n }\n\n function info(bytes32 field) public view returns(uint) {\n return infoFields[field];\n }\n\n /* solhint-disable code-complexity */\n // Regarding complexity. Below code follows the required algorithm for choosing a reserve.\n // It has been tested, reviewed and found to be clear enough.\n //@dev this function always src or dest are ether. can\u0027t do token to token\n function searchBestRate(ERC20 src, ERC20 dest, uint srcAmount, bool usePermissionless)\n public\n view\n returns(address, uint)\n {\n uint bestRate = 0;\n uint bestReserve = 0;\n uint numRelevantReserves = 0;\n\n //return 1 for ether to ether\n if (src == dest) return (reserves[bestReserve], PRECISION);\n\n address[] memory reserveArr;\n\n reserveArr = src == ETH_TOKEN_ADDRESS ? reservesPerTokenDest[dest] : reservesPerTokenSrc[src];\n\n if (reserveArr.length == 0) return (reserves[bestReserve], bestRate);\n\n uint[] memory rates = new uint[](reserveArr.length);\n uint[] memory reserveCandidates = new uint[](reserveArr.length);\n\n for (uint i = 0; i \u003c reserveArr.length; i++) {\n //list all reserves that have this token.\n if (!usePermissionless \u0026\u0026 reserveType[reserveArr[i]] == ReserveType.PERMISSIONLESS) {\n continue;\n }\n\n rates[i] = (KyberReserveInterface(reserveArr[i])).getConversionRate(src, dest, srcAmount, block.number);\n\n if (rates[i] \u003e bestRate) {\n //best rate is highest rate\n bestRate = rates[i];\n }\n }\n\n if (bestRate \u003e 0) {\n uint smallestRelevantRate = (bestRate * 10000) / (10000 + negligibleRateDiff);\n\n for (i = 0; i \u003c reserveArr.length; i++) {\n if (rates[i] \u003e= smallestRelevantRate) {\n reserveCandidates[numRelevantReserves++] = i;\n }\n }\n\n if (numRelevantReserves \u003e 1) {\n //when encountering small rate diff from bestRate. draw from relevant reserves\n bestReserve = reserveCandidates[uint(block.blockhash(block.number-1)) % numRelevantReserves];\n } else {\n bestReserve = reserveCandidates[0];\n }\n\n bestRate = rates[bestReserve];\n }\n\n return (reserveArr[bestReserve], bestRate);\n }\n /* solhint-enable code-complexity */\n\n function getReservesRates(ERC20 token, uint optionalAmount) public view\n returns(address[] buyReserves, uint[] buyRates, address[] sellReserves, uint[] sellRates)\n {\n uint amount = optionalAmount \u003e 0 ? optionalAmount : 1000;\n ERC20 ETH = ETH_TOKEN_ADDRESS;\n\n buyReserves = reservesPerTokenDest[token];\n buyRates = new uint[](buyReserves.length);\n\n for (uint i = 0; i \u003c buyReserves.length; i++) {\n buyRates[i] = (KyberReserveInterface(buyReserves[i])).getConversionRate(ETH, token, amount, block.number);\n }\n\n sellReserves = reservesPerTokenSrc[token];\n sellRates = new uint[](sellReserves.length);\n\n for (i = 0; i \u003c sellReserves.length; i++) {\n sellRates[i] = (KyberReserveInterface(sellReserves[i])).getConversionRate(token, ETH, amount, block.number);\n }\n }\n\n function findBestRateTokenToToken(ERC20 src, ERC20 dest, uint srcAmount, bytes hint) internal view\n returns(BestRateResult result)\n {\n //by default we use permission less reserves\n bool usePermissionless = true;\n\n // if hint in first 4 bytes == \u0027PERM\u0027 only permissioned reserves will be used.\n if ((hint.length \u003e= 4) \u0026\u0026 (keccak256(hint[0], hint[1], hint[2], hint[3]) == keccak256(PERM_HINT))) {\n usePermissionless = false;\n }\n\n uint srcDecimals = getDecimals(src);\n uint destDecimals = getDecimals(dest);\n\n (result.reserve1, result.rateSrcToEth) =\n searchBestRate(src, ETH_TOKEN_ADDRESS, srcAmount, usePermissionless);\n\n result.weiAmount = calcDestAmountWithDecimals(srcDecimals, ETH_DECIMALS, srcAmount, result.rateSrcToEth);\n //if weiAmount is zero, return zero rate to avoid revert in ETH -\u003e token call\n if (result.weiAmount == 0) {\n result.rate = 0;\n return;\n }\n \n (result.reserve2, result.rateEthToDest) =\n searchBestRate(ETH_TOKEN_ADDRESS, dest, result.weiAmount, usePermissionless);\n\n result.destAmount = calcDestAmountWithDecimals(ETH_DECIMALS, destDecimals, result.weiAmount, result.rateEthToDest);\n\n result.rate = calcRateFromQty(srcAmount, result.destAmount, srcDecimals, destDecimals);\n }\n\n function listPairs(address reserve, ERC20 token, bool isTokenToEth, bool add) internal {\n uint i;\n address[] storage reserveArr = reservesPerTokenDest[token];\n\n if (isTokenToEth) {\n reserveArr = reservesPerTokenSrc[token];\n }\n\n for (i = 0; i \u003c reserveArr.length; i++) {\n if (reserve == reserveArr[i]) {\n if (add) {\n break; //already added\n } else {\n //remove\n reserveArr[i] = reserveArr[reserveArr.length - 1];\n reserveArr.length--;\n break;\n }\n }\n }\n\n if (add \u0026\u0026 i == reserveArr.length) {\n //if reserve wasn\u0027t found add it\n reserveArr.push(reserve);\n }\n }\n\n event KyberTrade(address indexed trader, ERC20 src, ERC20 dest, uint srcAmount, uint dstAmount,\n address destAddress, uint ethWeiValue, address reserve1, address reserve2, bytes hint);\n\n /* solhint-disable function-max-lines */\n // Most of the lines here are functions calls spread over multiple lines. We find this function readable enough\n /// @notice use token address ETH_TOKEN_ADDRESS for ether\n /// @dev trade api for kyber network.\n /// @param tradeInput structure of trade inputs\n function trade(TradeInput tradeInput) internal returns(uint) {\n require(isEnabled);\n require(tx.gasprice \u003c= maxGasPriceValue);\n require(validateTradeInput(tradeInput.src, tradeInput.srcAmount, tradeInput.dest, tradeInput.destAddress));\n\n BestRateResult memory rateResult =\n findBestRateTokenToToken(tradeInput.src, tradeInput.dest, tradeInput.srcAmount, tradeInput.hint);\n\n require(rateResult.rate \u003e 0);\n require(rateResult.rate \u003c MAX_RATE);\n require(rateResult.rate \u003e= tradeInput.minConversionRate);\n\n uint actualDestAmount;\n uint weiAmount;\n uint actualSrcAmount;\n\n (actualSrcAmount, weiAmount, actualDestAmount) = calcActualAmounts(tradeInput.src,\n tradeInput.dest,\n tradeInput.srcAmount,\n tradeInput.maxDestAmount,\n rateResult);\n\n require(getUserCapInWei(tradeInput.trader) \u003e= weiAmount);\n require(handleChange(tradeInput.src, tradeInput.srcAmount, actualSrcAmount, tradeInput.trader));\n\n require(doReserveTrade( //src to ETH\n tradeInput.src,\n actualSrcAmount,\n ETH_TOKEN_ADDRESS,\n this,\n weiAmount,\n KyberReserveInterface(rateResult.reserve1),\n rateResult.rateSrcToEth,\n true));\n\n require(doReserveTrade( //Eth to dest\n ETH_TOKEN_ADDRESS,\n weiAmount,\n tradeInput.dest,\n tradeInput.destAddress,\n actualDestAmount,\n KyberReserveInterface(rateResult.reserve2),\n rateResult.rateEthToDest,\n true));\n\n if (tradeInput.src != ETH_TOKEN_ADDRESS) //\"fake\" trade. (ether to ether) - don\u0027t burn.\n require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve1, tradeInput.walletId));\n if (tradeInput.dest != ETH_TOKEN_ADDRESS) //\"fake\" trade. (ether to ether) - don\u0027t burn.\n require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve2, tradeInput.walletId));\n\n KyberTrade({\n trader: tradeInput.trader,\n src: tradeInput.src,\n dest: tradeInput.dest,\n srcAmount: actualSrcAmount,\n dstAmount: actualDestAmount,\n destAddress: tradeInput.destAddress,\n ethWeiValue: weiAmount,\n reserve1: (tradeInput.src == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve1,\n reserve2: (tradeInput.dest == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve2,\n hint: tradeInput.hint\n });\n\n return actualDestAmount;\n }\n /* solhint-enable function-max-lines */\n\n function calcActualAmounts (ERC20 src, ERC20 dest, uint srcAmount, uint maxDestAmount, BestRateResult rateResult)\n internal view returns(uint actualSrcAmount, uint weiAmount, uint actualDestAmount)\n {\n if (rateResult.destAmount \u003e maxDestAmount) {\n actualDestAmount = maxDestAmount;\n weiAmount = calcSrcAmount(ETH_TOKEN_ADDRESS, dest, actualDestAmount, rateResult.rateEthToDest);\n actualSrcAmount = calcSrcAmount(src, ETH_TOKEN_ADDRESS, weiAmount, rateResult.rateSrcToEth);\n require(actualSrcAmount \u003c= srcAmount);\n } else {\n actualDestAmount = rateResult.destAmount;\n actualSrcAmount = srcAmount;\n weiAmount = rateResult.weiAmount;\n }\n }\n\n /// @notice use token address ETH_TOKEN_ADDRESS for ether\n /// @dev do one trade with a reserve\n /// @param src Src token\n /// @param amount amount of src tokens\n /// @param dest Destination token\n /// @param destAddress Address to send tokens to\n /// @param reserve Reserve to use\n /// @param validate If true, additional validations are applicable\n /// @return true if trade is successful\n function doReserveTrade(\n ERC20 src,\n uint amount,\n ERC20 dest,\n address destAddress,\n uint expectedDestAmount,\n KyberReserveInterface reserve,\n uint conversionRate,\n bool validate\n )\n internal\n returns(bool)\n {\n uint callValue = 0;\n\n if (src == dest) {\n //this is for a \"fake\" trade when both src and dest are ethers.\n if (destAddress != (address(this)))\n destAddress.transfer(amount);\n return true;\n }\n\n if (src == ETH_TOKEN_ADDRESS) {\n callValue = amount;\n }\n\n // reserve sends tokens/eth to network. network sends it to destination\n require(reserve.trade.value(callValue)(src, amount, dest, this, conversionRate, validate));\n\n if (destAddress != address(this)) {\n //for token to token dest address is network. and Ether / token already here...\n if (dest == ETH_TOKEN_ADDRESS) {\n destAddress.transfer(expectedDestAmount);\n } else {\n require(dest.transfer(destAddress, expectedDestAmount));\n }\n }\n\n return true;\n }\n\n /// when user sets max dest amount we could have too many source tokens == change. so we send it back to user.\n function handleChange (ERC20 src, uint srcAmount, uint requiredSrcAmount, address trader) internal returns (bool) {\n\n if (requiredSrcAmount \u003c srcAmount) {\n //if there is \"change\" send back to trader\n if (src == ETH_TOKEN_ADDRESS) {\n trader.transfer(srcAmount - requiredSrcAmount);\n } else {\n require(src.transfer(trader, (srcAmount - requiredSrcAmount)));\n }\n }\n\n return true;\n }\n\n /// @notice use token address ETH_TOKEN_ADDRESS for ether\n /// @dev checks that user sent ether/tokens to contract before trade\n /// @param src Src token\n /// @param srcAmount amount of src tokens\n /// @return true if tradeInput is valid\n function validateTradeInput(ERC20 src, uint srcAmount, ERC20 dest, address destAddress)\n internal\n view\n returns(bool)\n {\n require(srcAmount \u003c= MAX_QTY);\n require(srcAmount != 0);\n require(destAddress != address(0));\n require(src != dest);\n\n if (src == ETH_TOKEN_ADDRESS) {\n require(msg.value == srcAmount);\n } else {\n require(msg.value == 0);\n //funds should have been moved to this contract already.\n require(src.balanceOf(this) \u003e= srcAmount);\n }\n\n return true;\n }\n}\n"},"KyberNetworkInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber Network interface\ninterface KyberNetworkInterface {\n function maxGasPrice() public view returns(uint);\n function getUserCapInWei(address user) public view returns(uint);\n function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);\n function enabled() public view returns(bool);\n function info(bytes32 id) public view returns(uint);\n\n function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view\n returns (uint expectedRate, uint slippageRate);\n\n function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,\n uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);\n}\n"},"KyberReserveInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n/// @title Kyber Reserve contract\ninterface KyberReserveInterface {\n\n function trade(\n ERC20 srcToken,\n uint srcAmount,\n ERC20 destToken,\n address destAddress,\n uint conversionRate,\n bool validate\n )\n public\n payable\n returns(bool);\n\n function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n address public admin;\n address public pendingAdmin;\n mapping(address=\u003ebool) internal operators;\n mapping(address=\u003ebool) internal alerters;\n address[] internal operatorsGroup;\n address[] internal alertersGroup;\n uint constant internal MAX_GROUP_SIZE = 50;\n\n function PermissionGroups() public {\n admin = msg.sender;\n }\n\n modifier onlyAdmin() {\n require(msg.sender == admin);\n _;\n }\n\n modifier onlyOperator() {\n require(operators[msg.sender]);\n _;\n }\n\n modifier onlyAlerter() {\n require(alerters[msg.sender]);\n _;\n }\n\n function getOperators () external view returns(address[]) {\n return operatorsGroup;\n }\n\n function getAlerters () external view returns(address[]) {\n return alertersGroup;\n }\n\n event TransferAdminPending(address pendingAdmin);\n\n /**\n * @dev Allows the current admin to set the pendingAdmin address.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdmin(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(pendingAdmin);\n pendingAdmin = newAdmin;\n }\n\n /**\n * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdminQuickly(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(newAdmin);\n AdminClaimed(newAdmin, admin);\n admin = newAdmin;\n }\n\n event AdminClaimed( address newAdmin, address previousAdmin);\n\n /**\n * @dev Allows the pendingAdmin address to finalize the change admin process.\n */\n function claimAdmin() public {\n require(pendingAdmin == msg.sender);\n AdminClaimed(pendingAdmin, admin);\n admin = pendingAdmin;\n pendingAdmin = address(0);\n }\n\n event AlerterAdded (address newAlerter, bool isAdd);\n\n function addAlerter(address newAlerter) public onlyAdmin {\n require(!alerters[newAlerter]); // prevent duplicates.\n require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n AlerterAdded(newAlerter, true);\n alerters[newAlerter] = true;\n alertersGroup.push(newAlerter);\n }\n\n function removeAlerter (address alerter) public onlyAdmin {\n require(alerters[alerter]);\n alerters[alerter] = false;\n\n for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n if (alertersGroup[i] == alerter) {\n alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n alertersGroup.length--;\n AlerterAdded(alerter, false);\n break;\n }\n }\n }\n\n event OperatorAdded(address newOperator, bool isAdd);\n\n function addOperator(address newOperator) public onlyAdmin {\n require(!operators[newOperator]); // prevent duplicates.\n require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n OperatorAdded(newOperator, true);\n operators[newOperator] = true;\n operatorsGroup.push(newOperator);\n }\n\n function removeOperator (address operator) public onlyAdmin {\n require(operators[operator]);\n operators[operator] = false;\n\n for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n if (operatorsGroup[i] == operator) {\n operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n operatorsGroup.length -= 1;\n OperatorAdded(operator, false);\n break;\n }\n }\n }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n uint constant internal PRECISION = (10**18);\n uint constant internal MAX_QTY = (10**28); // 10B tokens\n uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH\n uint constant internal MAX_DECIMALS = 18;\n uint constant internal ETH_DECIMALS = 18;\n mapping(address=\u003euint) internal decimals;\n\n function setDecimals(ERC20 token) internal {\n if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n else decimals[token] = token.decimals();\n }\n\n function getDecimals(ERC20 token) internal view returns(uint) {\n if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n uint tokenDecimals = decimals[token];\n // technically, there might be token with decimals 0\n // moreover, very possible that old tokens have decimals 0\n // these tokens will just have higher gas fees.\n if(tokenDecimals == 0) return token.decimals();\n\n return tokenDecimals;\n }\n\n function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(srcQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n\n if (dstDecimals \u003e= srcDecimals) {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n } else {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n }\n }\n\n function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(dstQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n \n //source quantity is rounded up. to avoid dest quantity being too low.\n uint numerator;\n uint denominator;\n if (srcDecimals \u003e= dstDecimals) {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n denominator = rate;\n } else {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty);\n denominator = (rate * (10**(dstDecimals - srcDecimals)));\n }\n return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n }\n}\n"},"Utils2.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils.sol\";\n\n\ncontract Utils2 is Utils {\n\n /// @dev get the balance of a user.\n /// @param token The token type\n /// @return The balance\n function getBalance(ERC20 token, address user) public view returns(uint) {\n if (token == ETH_TOKEN_ADDRESS)\n return user.balance;\n else\n return token.balanceOf(user);\n }\n\n function getDecimalsSafe(ERC20 token) internal returns(uint) {\n\n if (decimals[token] == 0) {\n setDecimals(token);\n }\n\n return decimals[token];\n }\n\n function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {\n return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);\n }\n\n function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {\n return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);\n }\n\n function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)\n internal pure returns(uint)\n {\n require(srcAmount \u003c= MAX_QTY);\n require(destAmount \u003c= MAX_QTY);\n\n if (dstDecimals \u003e= srcDecimals) {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));\n } else {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);\n }\n }\n}\n"},"Utils3.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils2.sol\";\n\n\ncontract Utils3 is Utils2 {\n\n function calcDestAmountWithDecimals(uint srcDecimals, uint destDecimals, uint srcAmount, uint rate) internal pure returns(uint) {\n return calcDstQty(srcAmount, srcDecimals, destDecimals, rate);\n }\n\n}\n"},"WhiteListInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract WhiteListInterface {\n function getUserCapInWei(address user) external view returns (uint userCapWei);\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n /**\n * @dev Withdraw all ERC20 compatible tokens\n * @param token ERC20 The address of the token contract\n */\n function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n require(token.transfer(sendTo, amount));\n TokenWithdraw(token, amount, sendTo);\n }\n\n event EtherWithdraw(uint amount, address sendTo);\n\n /**\n * @dev Withdraw Ethers\n */\n function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n sendTo.transfer(amount);\n EtherWithdraw(amount, sendTo);\n }\n}\n"}}File 3 of 10: EthLendToken
pragma solidity ^0.4.16;
contract SafeMath {
function safeMul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
}
// Standard token interface (ERC 20)
// https://github.com/ethereum/EIPs/issues/20
contract Token is SafeMath {
// Functions:
/// @return total amount of tokens
function totalSupply() constant returns (uint256 supply);
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) constant returns (uint256 balance);
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
function transfer(address _to, uint256 _value) returns(bool);
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) returns(bool);
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
// Events:
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract StdToken is Token {
// Fields:
mapping(address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint public supply = 0;
// Functions:
function transfer(address _to, uint256 _value) returns(bool) {
require(balances[msg.sender] >= _value);
require(balances[_to] + _value > balances[_to]);
balances[msg.sender] = safeSub(balances[msg.sender],_value);
balances[_to] = safeAdd(balances[_to],_value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) returns(bool){
require(balances[_from] >= _value);
require(allowed[_from][msg.sender] >= _value);
require(balances[_to] + _value > balances[_to]);
balances[_to] = safeAdd(balances[_to],_value);
balances[_from] = safeSub(balances[_from],_value);
allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender],_value);
Transfer(_from, _to, _value);
return true;
}
function totalSupply() constant returns (uint256) {
return supply;
}
function balanceOf(address _owner) constant returns (uint256) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) returns (bool) {
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require((_value == 0) || (allowed[msg.sender][_spender] == 0));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint256) {
return allowed[_owner][_spender];
}
}
contract EthLendToken is StdToken
{
/// Fields:
string public constant name = "EthLend Token";
string public constant symbol = "LEND";
uint public constant decimals = 18;
// this includes DEVELOPERS_BONUS
uint public constant TOTAL_SUPPLY = 1300000000 * (1 ether / 1 wei);
uint public constant DEVELOPERS_BONUS = 300000000 * (1 ether / 1 wei);
uint public constant PRESALE_PRICE = 30000; // per 1 Ether
uint public constant PRESALE_MAX_ETH = 2000;
// 60 mln tokens sold during presale
uint public constant PRESALE_TOKEN_SUPPLY_LIMIT = PRESALE_PRICE * PRESALE_MAX_ETH * (1 ether / 1 wei);
uint public constant ICO_PRICE1 = 27500; // per 1 Ether
uint public constant ICO_PRICE2 = 26250; // per 1 Ether
uint public constant ICO_PRICE3 = 25000; // per 1 Ether
// 1bln - this includes presale tokens
uint public constant TOTAL_SOLD_TOKEN_SUPPLY_LIMIT = 1000000000* (1 ether / 1 wei);
enum State{
Init,
Paused,
PresaleRunning,
PresaleFinished,
ICORunning,
ICOFinished
}
State public currentState = State.Init;
bool public enableTransfers = false;
address public teamTokenBonus = 0;
// Gathered funds can be withdrawn only to escrow's address.
address public escrow = 0;
// Token manager has exclusive priveleges to call administrative
// functions on this contract.
address public tokenManager = 0;
uint public presaleSoldTokens = 0;
uint public icoSoldTokens = 0;
uint public totalSoldTokens = 0;
/// Modifiers:
modifier onlyTokenManager()
{
require(msg.sender==tokenManager);
_;
}
modifier onlyInState(State state)
{
require(state==currentState);
_;
}
/// Events:
event LogBuy(address indexed owner, uint value);
event LogBurn(address indexed owner, uint value);
/// Functions:
/// @dev Constructor
/// @param _tokenManager Token manager address.
function EthLendToken(address _tokenManager, address _escrow, address _teamTokenBonus)
{
tokenManager = _tokenManager;
teamTokenBonus = _teamTokenBonus;
escrow = _escrow;
// send team bonus immediately
uint teamBonus = DEVELOPERS_BONUS;
balances[_teamTokenBonus] += teamBonus;
supply+= teamBonus;
assert(PRESALE_TOKEN_SUPPLY_LIMIT==60000000 * (1 ether / 1 wei));
assert(TOTAL_SOLD_TOKEN_SUPPLY_LIMIT==1000000000 * (1 ether / 1 wei));
}
function buyTokens() public payable
{
require(currentState==State.PresaleRunning || currentState==State.ICORunning);
if(currentState==State.PresaleRunning){
return buyTokensPresale();
}else{
return buyTokensICO();
}
}
function buyTokensPresale() public payable onlyInState(State.PresaleRunning)
{
// min - 1 ETH
require(msg.value >= (1 ether / 1 wei));
uint newTokens = msg.value * PRESALE_PRICE;
require(presaleSoldTokens + newTokens <= PRESALE_TOKEN_SUPPLY_LIMIT);
balances[msg.sender] += newTokens;
supply+= newTokens;
presaleSoldTokens+= newTokens;
totalSoldTokens+= newTokens;
LogBuy(msg.sender, newTokens);
}
function buyTokensICO() public payable onlyInState(State.ICORunning)
{
// min - 0.01 ETH
require(msg.value >= ((1 ether / 1 wei) / 100));
uint newTokens = msg.value * getPrice();
require(totalSoldTokens + newTokens <= TOTAL_SOLD_TOKEN_SUPPLY_LIMIT);
balances[msg.sender] += newTokens;
supply+= newTokens;
icoSoldTokens+= newTokens;
totalSoldTokens+= newTokens;
LogBuy(msg.sender, newTokens);
}
function getPrice()constant returns(uint)
{
if(currentState==State.ICORunning){
if(icoSoldTokens<(200000000 * (1 ether / 1 wei))){
return ICO_PRICE1;
}
if(icoSoldTokens<(300000000 * (1 ether / 1 wei))){
return ICO_PRICE2;
}
return ICO_PRICE3;
}else{
return PRESALE_PRICE;
}
}
function setState(State _nextState) public onlyTokenManager
{
//setState() method call shouldn't be entertained after ICOFinished
require(currentState != State.ICOFinished);
currentState = _nextState;
// enable/disable transfers
//enable transfers only after ICOFinished, disable otherwise
enableTransfers = (currentState==State.ICOFinished);
}
function withdrawEther() public onlyTokenManager
{
if(this.balance > 0)
{
require(escrow.send(this.balance));
}
}
/// Overrides:
function transfer(address _to, uint256 _value) returns(bool){
require(enableTransfers);
return super.transfer(_to,_value);
}
function transferFrom(address _from, address _to, uint256 _value) returns(bool){
require(enableTransfers);
return super.transferFrom(_from,_to,_value);
}
function approve(address _spender, uint256 _value) returns (bool) {
require(enableTransfers);
return super.approve(_spender,_value);
}
/// Setters/getters
function setTokenManager(address _mgr) public onlyTokenManager
{
tokenManager = _mgr;
}
// Default fallback function
function() payable
{
buyTokens();
}
}File 4 of 10: KyberReserve
pragma solidity 0.4.18;
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
}
}
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;
}
}
}
}
interface ConversionRatesInterface {
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public;
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}
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);
}
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);
}
interface SanityRatesInterface {
function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
}
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);
}
}
contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {
address public kyberNetwork;
bool public tradeEnabled;
ConversionRatesInterface public conversionRatesContract;
SanityRatesInterface public sanityRatesContract;
mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
require(_admin != address(0));
require(_ratesContract != address(0));
require(_kyberNetwork != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _ratesContract;
admin = _admin;
tradeEnabled = true;
}
event DepositToken(ERC20 token, uint amount);
function() public payable {
DepositToken(ETH_TOKEN_ADDRESS, msg.value);
}
event TradeExecute(
address indexed origin,
address src,
uint srcAmount,
address destToken,
uint destAmount,
address destAddress
);
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(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
return true;
}
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 WithdrawAddressApproved(ERC20 token, address addr, bool approve);
function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
approvedWithdrawAddresses[keccak256(token, addr)] = approve;
WithdrawAddressApproved(token, addr, approve);
setDecimals(token);
}
event WithdrawFunds(ERC20 token, uint amount, address destination);
function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
require(approvedWithdrawAddresses[keccak256(token, destination)]);
if (token == ETH_TOKEN_ADDRESS) {
destination.transfer(amount);
} else {
require(token.transfer(destination, amount));
}
WithdrawFunds(token, amount, destination);
return true;
}
event SetContractAddresses(address network, address rate, address sanity);
function setContracts(address _kyberNetwork, ConversionRatesInterface _conversionRates, SanityRatesInterface _sanityRates)
public
onlyAdmin
{
require(_kyberNetwork != address(0));
require(_conversionRates != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _conversionRates;
sanityRatesContract = _sanityRates;
SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
}
////////////////////////////////////////////////////////////////////////////
/// status functions ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
function getBalance(ERC20 token) public view returns(uint) {
if (token == ETH_TOKEN_ADDRESS)
return this.balance;
else
return token.balanceOf(this);
}
function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
}
function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
}
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
ERC20 token;
bool buy;
if (!tradeEnabled) return 0;
if (ETH_TOKEN_ADDRESS == src) {
buy = true;
token = dest;
} else if (ETH_TOKEN_ADDRESS == dest) {
buy = false;
token = src;
} else {
return 0; // pair is not listed
}
uint rate = conversionRatesContract.getRate(token, blockNumber, buy, srcQty);
uint destQty = getDestQty(src, dest, srcQty, rate);
if (getBalance(dest) < destQty) return 0;
if (sanityRatesContract != address(0)) {
uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
if (rate > sanityRate) return 0;
}
return rate;
}
/// @dev do a trade
/// @param srcToken Src token
/// @param srcAmount Amount of src token
/// @param destToken Destination token
/// @param destAddress Destination address to send tokens to
/// @param validate If true, additional validations are applicable
/// @return true iff trade is successful
function doTrade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
internal
returns(bool)
{
// can skip validation if done at kyber network level
if (validate) {
require(conversionRate > 0);
if (srcToken == ETH_TOKEN_ADDRESS)
require(msg.value == srcAmount);
else
require(msg.value == 0);
}
uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
// sanity check
require(destAmount > 0);
// add to imbalance
ERC20 token;
int buy;
if (srcToken == ETH_TOKEN_ADDRESS) {
buy = int(destAmount);
token = destToken;
} else {
buy = -1 * int(srcAmount);
token = srcToken;
}
conversionRatesContract.recordImbalance(
token,
buy,
0,
block.number
);
// collect src tokens
if (srcToken != ETH_TOKEN_ADDRESS) {
require(srcToken.transferFrom(msg.sender, this, srcAmount));
}
// send dest tokens
if (destToken == ETH_TOKEN_ADDRESS) {
destAddress.transfer(destAmount);
} else {
require(destToken.transfer(destAddress, destAmount));
}
TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
return true;
}
}File 5 of 10: KyberReserve
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/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/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/ConversionRatesInterface.sol
interface ConversionRatesInterface {
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public;
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}
// File: contracts/SanityRatesInterface.sol
interface SanityRatesInterface {
function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
}
// 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/KyberReserve.sol
/// @title Kyber Reserve contract
contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {
address public kyberNetwork;
bool public tradeEnabled;
ConversionRatesInterface public conversionRatesContract;
SanityRatesInterface public sanityRatesContract;
mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
mapping(address=>address) public tokenWallet;
function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
require(_admin != address(0));
require(_ratesContract != address(0));
require(_kyberNetwork != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _ratesContract;
admin = _admin;
tradeEnabled = true;
}
event DepositToken(ERC20 token, uint amount);
function() public payable {
DepositToken(ETH_TOKEN_ADDRESS, msg.value);
}
event TradeExecute(
address indexed origin,
address src,
uint srcAmount,
address destToken,
uint destAmount,
address destAddress
);
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(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
return true;
}
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 WithdrawAddressApproved(ERC20 token, address addr, bool approve);
function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
approvedWithdrawAddresses[keccak256(token, addr)] = approve;
WithdrawAddressApproved(token, addr, approve);
setDecimals(token);
if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) {
tokenWallet[token] = this; // by default
require(token.approve(this, 2 ** 255));
}
}
event NewTokenWallet(ERC20 token, address wallet);
function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {
require(wallet != address(0x0));
tokenWallet[token] = wallet;
NewTokenWallet(token, wallet);
}
event WithdrawFunds(ERC20 token, uint amount, address destination);
function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
require(approvedWithdrawAddresses[keccak256(token, destination)]);
if (token == ETH_TOKEN_ADDRESS) {
destination.transfer(amount);
} else {
require(token.transferFrom(tokenWallet[token], destination, amount));
}
WithdrawFunds(token, amount, destination);
return true;
}
event SetContractAddresses(address network, address rate, address sanity);
function setContracts(
address _kyberNetwork,
ConversionRatesInterface _conversionRates,
SanityRatesInterface _sanityRates
)
public
onlyAdmin
{
require(_kyberNetwork != address(0));
require(_conversionRates != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _conversionRates;
sanityRatesContract = _sanityRates;
SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
}
////////////////////////////////////////////////////////////////////////////
/// status functions ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
function getBalance(ERC20 token) public view returns(uint) {
if (token == ETH_TOKEN_ADDRESS)
return this.balance;
else {
address wallet = tokenWallet[token];
uint balanceOfWallet = token.balanceOf(wallet);
uint allowanceOfWallet = token.allowance(wallet, this);
return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;
}
}
function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
}
function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
}
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
ERC20 token;
bool isBuy;
if (!tradeEnabled) return 0;
if (ETH_TOKEN_ADDRESS == src) {
isBuy = true;
token = dest;
} else if (ETH_TOKEN_ADDRESS == dest) {
isBuy = false;
token = src;
} else {
return 0; // pair is not listed
}
uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);
uint destQty = getDestQty(src, dest, srcQty, rate);
if (getBalance(dest) < destQty) return 0;
if (sanityRatesContract != address(0)) {
uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
if (rate > sanityRate) return 0;
}
return rate;
}
/// @dev do a trade
/// @param srcToken Src token
/// @param srcAmount Amount of src token
/// @param destToken Destination token
/// @param destAddress Destination address to send tokens to
/// @param validate If true, additional validations are applicable
/// @return true iff trade is successful
function doTrade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
internal
returns(bool)
{
// can skip validation if done at kyber network level
if (validate) {
require(conversionRate > 0);
if (srcToken == ETH_TOKEN_ADDRESS)
require(msg.value == srcAmount);
else
require(msg.value == 0);
}
uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
// sanity check
require(destAmount > 0);
// add to imbalance
ERC20 token;
int tradeAmount;
if (srcToken == ETH_TOKEN_ADDRESS) {
tradeAmount = int(destAmount);
token = destToken;
} else {
tradeAmount = -1 * int(srcAmount);
token = srcToken;
}
conversionRatesContract.recordImbalance(
token,
tradeAmount,
0,
block.number
);
// collect src tokens
if (srcToken != ETH_TOKEN_ADDRESS) {
require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));
}
// send dest tokens
if (destToken == ETH_TOKEN_ADDRESS) {
destAddress.transfer(destAmount);
} else {
require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));
}
TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
return true;
}
}File 6 of 10: RepublicToken
pragma solidity ^0.4.13;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
contract BurnableToken is StandardToken {
event Burn(address indexed burner, uint256 value);
/**
* @dev Burns a specific amount of tokens.
* @param _value The amount of token to be burned.
*/
function burn(uint256 _value) public {
require(_value > 0);
require(_value <= balances[msg.sender]);
// no need to require value <= totalSupply, since that would imply the
// sender's balance is greater than the totalSupply, which *should* be an assertion failure
address burner = msg.sender;
balances[burner] = balances[burner].sub(_value);
totalSupply = totalSupply.sub(_value);
Burn(burner, _value);
}
}
contract PausableToken is StandardToken, Pausable {
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
return super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {
return super.transferFrom(_from, _to, _value);
}
function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
return super.approve(_spender, _value);
}
function increaseApproval(address _spender, uint _addedValue) public whenNotPaused returns (bool success) {
return super.increaseApproval(_spender, _addedValue);
}
function decreaseApproval(address _spender, uint _subtractedValue) public whenNotPaused returns (bool success) {
return super.decreaseApproval(_spender, _subtractedValue);
}
}
contract RepublicToken is PausableToken, BurnableToken {
string public constant name = "Republic Token";
string public constant symbol = "REN";
uint8 public constant decimals = 18;
uint256 public constant INITIAL_SUPPLY = 1000000000 * 10**uint256(decimals);
/**
* @dev RepublicToken Constructor
*/
function RepublicToken() public {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
function transferTokens(address beneficiary, uint256 amount) public onlyOwner returns (bool) {
require(amount > 0);
balances[owner] = balances[owner].sub(amount);
balances[beneficiary] = balances[beneficiary].add(amount);
Transfer(owner, beneficiary, amount);
return true;
}
}File 7 of 10: FeeBurner
{"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n function totalSupply() public view returns (uint supply);\n function balanceOf(address _owner) public view returns (uint balance);\n function transfer(address _to, uint _value) public returns (bool success);\n function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n function approve(address _spender, uint _value) public returns (bool success);\n function allowance(address _owner, address _spender) public view returns (uint remaining);\n function decimals() public view returns(uint digits);\n event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"FeeBurner.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./FeeBurnerInterface.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./Utils3.sol\";\nimport \"./KyberNetworkInterface.sol\";\n\n\ninterface BurnableToken {\n function transferFrom(address _from, address _to, uint _value) public returns (bool);\n function burnFrom(address _from, uint256 _value) public returns (bool);\n}\n\n\ncontract FeeBurner is Withdrawable, FeeBurnerInterface, Utils3 {\n\n mapping(address=\u003euint) public reserveFeesInBps;\n mapping(address=\u003eaddress) public reserveKNCWallet; //wallet holding knc per reserve. from here burn and send fees.\n mapping(address=\u003euint) public walletFeesInBps; // wallet that is the source of tx is entitled so some fees.\n mapping(address=\u003euint) public reserveFeeToBurn;\n mapping(address=\u003euint) public feePayedPerReserve; // track burned fees and sent wallet fees per reserve.\n mapping(address=\u003emapping(address=\u003euint)) public reserveFeeToWallet;\n address public taxWallet;\n uint public taxFeeBps = 0; // burned fees are taxed. % out of burned fees.\n\n BurnableToken public knc;\n KyberNetworkInterface public kyberNetwork;\n uint public kncPerEthRatePrecision = 600 * PRECISION; //--\u003e 1 ether = 600 knc tokens\n\n function FeeBurner(\n address _admin,\n BurnableToken _kncToken,\n KyberNetworkInterface _kyberNetwork,\n uint _initialKncToEthRatePrecision\n )\n public\n {\n require(_admin != address(0));\n require(_kncToken != address(0));\n require(_kyberNetwork != address(0));\n require(_initialKncToEthRatePrecision != 0);\n\n kyberNetwork = _kyberNetwork;\n admin = _admin;\n knc = _kncToken;\n kncPerEthRatePrecision = _initialKncToEthRatePrecision;\n }\n\n event ReserveDataSet(address reserve, uint feeInBps, address kncWallet);\n\n function setReserveData(address reserve, uint feesInBps, address kncWallet) public onlyOperator {\n require(feesInBps \u003c 100); // make sure it is always \u003c 1%\n require(kncWallet != address(0));\n reserveFeesInBps[reserve] = feesInBps;\n reserveKNCWallet[reserve] = kncWallet;\n ReserveDataSet(reserve, feesInBps, kncWallet);\n }\n\n event WalletFeesSet(address wallet, uint feesInBps);\n\n function setWalletFees(address wallet, uint feesInBps) public onlyAdmin {\n require(feesInBps \u003c 10000); // under 100%\n walletFeesInBps[wallet] = feesInBps;\n WalletFeesSet(wallet, feesInBps);\n }\n\n event TaxFeesSet(uint feesInBps);\n\n function setTaxInBps(uint _taxFeeBps) public onlyAdmin {\n require(_taxFeeBps \u003c 10000); // under 100%\n taxFeeBps = _taxFeeBps;\n TaxFeesSet(_taxFeeBps);\n }\n\n event TaxWalletSet(address taxWallet);\n\n function setTaxWallet(address _taxWallet) public onlyAdmin {\n require(_taxWallet != address(0));\n taxWallet = _taxWallet;\n TaxWalletSet(_taxWallet);\n }\n\n event KNCRateSet(uint ethToKncRatePrecision, uint kyberEthKnc, uint kyberKncEth, address updater);\n\n function setKNCRate() public {\n //query kyber for knc rate sell and buy\n uint kyberEthKncRate;\n uint kyberKncEthRate;\n (kyberEthKncRate, ) = kyberNetwork.getExpectedRate(ETH_TOKEN_ADDRESS, ERC20(knc), (10 ** 18));\n (kyberKncEthRate, ) = kyberNetwork.getExpectedRate(ERC20(knc), ETH_TOKEN_ADDRESS, (10 ** 18));\n\n //check \"reasonable\" spread == diff not too big. rate wasn\u0027t tampered.\n require(kyberEthKncRate * kyberKncEthRate \u003c PRECISION ** 2 * 2);\n require(kyberEthKncRate * kyberKncEthRate \u003e PRECISION ** 2 / 2);\n\n require(kyberEthKncRate \u003c= MAX_RATE);\n kncPerEthRatePrecision = kyberEthKncRate;\n KNCRateSet(kncPerEthRatePrecision, kyberEthKncRate, kyberKncEthRate, msg.sender);\n }\n\n event AssignFeeToWallet(address reserve, address wallet, uint walletFee);\n event AssignBurnFees(address reserve, uint burnFee);\n\n function handleFees(uint tradeWeiAmount, address reserve, address wallet) public returns(bool) {\n require(msg.sender == address(kyberNetwork));\n require(tradeWeiAmount \u003c= MAX_QTY);\n\n // MAX_DECIMALS = 18 = KNC_DECIMALS, use this value instead of calling getDecimals() to save gas\n uint kncAmount = calcDestAmountWithDecimals(ETH_DECIMALS, MAX_DECIMALS, tradeWeiAmount, kncPerEthRatePrecision);\n uint fee = kncAmount * reserveFeesInBps[reserve] / 10000;\n\n uint walletFee = fee * walletFeesInBps[wallet] / 10000;\n require(fee \u003e= walletFee);\n uint feeToBurn = fee - walletFee;\n\n if (walletFee \u003e 0) {\n reserveFeeToWallet[reserve][wallet] += walletFee;\n AssignFeeToWallet(reserve, wallet, walletFee);\n }\n\n if (feeToBurn \u003e 0) {\n AssignBurnFees(reserve, feeToBurn);\n reserveFeeToBurn[reserve] += feeToBurn;\n }\n\n return true;\n }\n\n event BurnAssignedFees(address indexed reserve, address sender, uint quantity);\n\n event SendTaxFee(address indexed reserve, address sender, address taxWallet, uint quantity);\n\n // this function is callable by anyone\n function burnReserveFees(address reserve) public {\n uint burnAmount = reserveFeeToBurn[reserve];\n uint taxToSend = 0;\n require(burnAmount \u003e 2);\n reserveFeeToBurn[reserve] = 1; // leave 1 twei to avoid spikes in gas fee\n if (taxWallet != address(0) \u0026\u0026 taxFeeBps != 0) {\n taxToSend = (burnAmount - 1) * taxFeeBps / 10000;\n require(burnAmount - 1 \u003e taxToSend);\n burnAmount -= taxToSend;\n if (taxToSend \u003e 0) {\n require(knc.transferFrom(reserveKNCWallet[reserve], taxWallet, taxToSend));\n SendTaxFee(reserve, msg.sender, taxWallet, taxToSend);\n }\n }\n require(knc.burnFrom(reserveKNCWallet[reserve], burnAmount - 1));\n\n //update reserve \"payments\" so far\n feePayedPerReserve[reserve] += (taxToSend + burnAmount - 1);\n\n BurnAssignedFees(reserve, msg.sender, (burnAmount - 1));\n }\n\n event SendWalletFees(address indexed wallet, address reserve, address sender);\n\n // this function is callable by anyone\n function sendFeeToWallet(address wallet, address reserve) public {\n uint feeAmount = reserveFeeToWallet[reserve][wallet];\n require(feeAmount \u003e 1);\n reserveFeeToWallet[reserve][wallet] = 1; // leave 1 twei to avoid spikes in gas fee\n require(knc.transferFrom(reserveKNCWallet[reserve], wallet, feeAmount - 1));\n\n feePayedPerReserve[reserve] += (feeAmount - 1);\n SendWalletFees(wallet, reserve, msg.sender);\n }\n}\n"},"FeeBurnerInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ninterface FeeBurnerInterface {\n function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool);\n function setReserveData(address reserve, uint feesInBps, address kncWallet) public;\n}\n"},"KyberNetworkInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber Network interface\ninterface KyberNetworkInterface {\n function maxGasPrice() public view returns(uint);\n function getUserCapInWei(address user) public view returns(uint);\n function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);\n function enabled() public view returns(bool);\n function info(bytes32 id) public view returns(uint);\n\n function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view\n returns (uint expectedRate, uint slippageRate);\n\n function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,\n uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n address public admin;\n address public pendingAdmin;\n mapping(address=\u003ebool) internal operators;\n mapping(address=\u003ebool) internal alerters;\n address[] internal operatorsGroup;\n address[] internal alertersGroup;\n uint constant internal MAX_GROUP_SIZE = 50;\n\n function PermissionGroups() public {\n admin = msg.sender;\n }\n\n modifier onlyAdmin() {\n require(msg.sender == admin);\n _;\n }\n\n modifier onlyOperator() {\n require(operators[msg.sender]);\n _;\n }\n\n modifier onlyAlerter() {\n require(alerters[msg.sender]);\n _;\n }\n\n function getOperators () external view returns(address[]) {\n return operatorsGroup;\n }\n\n function getAlerters () external view returns(address[]) {\n return alertersGroup;\n }\n\n event TransferAdminPending(address pendingAdmin);\n\n /**\n * @dev Allows the current admin to set the pendingAdmin address.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdmin(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(pendingAdmin);\n pendingAdmin = newAdmin;\n }\n\n /**\n * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdminQuickly(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(newAdmin);\n AdminClaimed(newAdmin, admin);\n admin = newAdmin;\n }\n\n event AdminClaimed( address newAdmin, address previousAdmin);\n\n /**\n * @dev Allows the pendingAdmin address to finalize the change admin process.\n */\n function claimAdmin() public {\n require(pendingAdmin == msg.sender);\n AdminClaimed(pendingAdmin, admin);\n admin = pendingAdmin;\n pendingAdmin = address(0);\n }\n\n event AlerterAdded (address newAlerter, bool isAdd);\n\n function addAlerter(address newAlerter) public onlyAdmin {\n require(!alerters[newAlerter]); // prevent duplicates.\n require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n AlerterAdded(newAlerter, true);\n alerters[newAlerter] = true;\n alertersGroup.push(newAlerter);\n }\n\n function removeAlerter (address alerter) public onlyAdmin {\n require(alerters[alerter]);\n alerters[alerter] = false;\n\n for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n if (alertersGroup[i] == alerter) {\n alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n alertersGroup.length--;\n AlerterAdded(alerter, false);\n break;\n }\n }\n }\n\n event OperatorAdded(address newOperator, bool isAdd);\n\n function addOperator(address newOperator) public onlyAdmin {\n require(!operators[newOperator]); // prevent duplicates.\n require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n OperatorAdded(newOperator, true);\n operators[newOperator] = true;\n operatorsGroup.push(newOperator);\n }\n\n function removeOperator (address operator) public onlyAdmin {\n require(operators[operator]);\n operators[operator] = false;\n\n for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n if (operatorsGroup[i] == operator) {\n operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n operatorsGroup.length -= 1;\n OperatorAdded(operator, false);\n break;\n }\n }\n }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n uint constant internal PRECISION = (10**18);\n uint constant internal MAX_QTY = (10**28); // 10B tokens\n uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH\n uint constant internal MAX_DECIMALS = 18;\n uint constant internal ETH_DECIMALS = 18;\n mapping(address=\u003euint) internal decimals;\n\n function setDecimals(ERC20 token) internal {\n if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n else decimals[token] = token.decimals();\n }\n\n function getDecimals(ERC20 token) internal view returns(uint) {\n if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n uint tokenDecimals = decimals[token];\n // technically, there might be token with decimals 0\n // moreover, very possible that old tokens have decimals 0\n // these tokens will just have higher gas fees.\n if(tokenDecimals == 0) return token.decimals();\n\n return tokenDecimals;\n }\n\n function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(srcQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n\n if (dstDecimals \u003e= srcDecimals) {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n } else {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n }\n }\n\n function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(dstQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n \n //source quantity is rounded up. to avoid dest quantity being too low.\n uint numerator;\n uint denominator;\n if (srcDecimals \u003e= dstDecimals) {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n denominator = rate;\n } else {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty);\n denominator = (rate * (10**(dstDecimals - srcDecimals)));\n }\n return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n }\n}\n"},"Utils2.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils.sol\";\n\n\ncontract Utils2 is Utils {\n\n /// @dev get the balance of a user.\n /// @param token The token type\n /// @return The balance\n function getBalance(ERC20 token, address user) public view returns(uint) {\n if (token == ETH_TOKEN_ADDRESS)\n return user.balance;\n else\n return token.balanceOf(user);\n }\n\n function getDecimalsSafe(ERC20 token) internal returns(uint) {\n\n if (decimals[token] == 0) {\n setDecimals(token);\n }\n\n return decimals[token];\n }\n\n function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {\n return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);\n }\n\n function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {\n return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);\n }\n\n function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)\n internal pure returns(uint)\n {\n require(srcAmount \u003c= MAX_QTY);\n require(destAmount \u003c= MAX_QTY);\n\n if (dstDecimals \u003e= srcDecimals) {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));\n } else {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);\n }\n }\n}\n"},"Utils3.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils2.sol\";\n\n\ncontract Utils3 is Utils2 {\n\n function calcDestAmountWithDecimals(uint srcDecimals, uint destDecimals, uint srcAmount, uint rate) internal pure returns(uint) {\n return calcDstQty(srcAmount, srcDecimals, destDecimals, rate);\n }\n\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n /**\n * @dev Withdraw all ERC20 compatible tokens\n * @param token ERC20 The address of the token contract\n */\n function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n require(token.transfer(sendTo, amount));\n TokenWithdraw(token, amount, sendTo);\n }\n\n event EtherWithdraw(uint amount, address sendTo);\n\n /**\n * @dev Withdraw Ethers\n */\n function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n sendTo.transfer(amount);\n EtherWithdraw(amount, sendTo);\n }\n}\n"}}File 8 of 10: ConversionRates
pragma solidity 0.4.18;
interface ConversionRatesInterface {
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public;
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}
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);
}
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;
}
}
}
}
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);
}
}
contract VolumeImbalanceRecorder is Withdrawable {
uint constant internal SLIDING_WINDOW_SIZE = 5;
uint constant internal POW_2_64 = 2 ** 64;
struct TokenControlInfo {
uint minimalRecordResolution; // can be roughly 1 cent
uint maxPerBlockImbalance; // in twei resolution
uint maxTotalImbalance; // max total imbalance (between rate updates)
// before halting trade
}
mapping(address => TokenControlInfo) internal tokenControlInfo;
struct TokenImbalanceData {
int lastBlockBuyUnitsImbalance;
uint lastBlock;
int totalBuyUnitsImbalance;
uint lastRateUpdateBlock;
}
mapping(address => mapping(uint=>uint)) public tokenImbalanceData;
function VolumeImbalanceRecorder(address _admin) public {
require(_admin != address(0));
admin = _admin;
}
function setTokenControlInfo(
ERC20 token,
uint minimalRecordResolution,
uint maxPerBlockImbalance,
uint maxTotalImbalance
)
public
onlyAdmin
{
tokenControlInfo[token] =
TokenControlInfo(
minimalRecordResolution,
maxPerBlockImbalance,
maxTotalImbalance
);
}
function getTokenControlInfo(ERC20 token) public view returns(uint, uint, uint) {
return (tokenControlInfo[token].minimalRecordResolution,
tokenControlInfo[token].maxPerBlockImbalance,
tokenControlInfo[token].maxTotalImbalance);
}
function addImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
internal
{
uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE;
int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution));
int prevImbalance = 0;
TokenImbalanceData memory currentBlockData =
decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]);
// first scenario - this is not the first tx in the current block
if (currentBlockData.lastBlock == currentBlock) {
if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) {
// just increase imbalance
currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;
currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount;
} else {
// imbalance was changed in the middle of the block
prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock);
currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;
currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;
currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);
}
} else {
// first tx in the current block
int currentBlockImbalance;
(prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock);
currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount;
currentBlockData.lastBlock = uint(currentBlock);
currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);
currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;
}
tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData);
}
function setGarbageToVolumeRecorder(ERC20 token) internal {
for (uint i = 0; i < SLIDING_WINDOW_SIZE; i++) {
tokenImbalanceData[token][i] = 0x1;
}
}
function getImbalanceInRange(ERC20 token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) {
// check the imbalance in the sliding window
require(startBlock <= endBlock);
buyImbalance = 0;
for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) {
TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);
if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) {
buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance);
}
}
}
function getImbalanceSinceRateUpdate(ERC20 token, uint rateUpdateBlock, uint currentBlock)
internal view
returns(int buyImbalance, int currentBlockImbalance)
{
buyImbalance = 0;
currentBlockImbalance = 0;
uint latestBlock = 0;
int imbalanceInRange = 0;
uint startBlock = rateUpdateBlock;
uint endBlock = currentBlock;
for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) {
TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);
if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) {
imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance;
}
if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue;
if (perBlockData.lastBlock < latestBlock) continue;
latestBlock = perBlockData.lastBlock;
buyImbalance = perBlockData.totalBuyUnitsImbalance;
if (uint(perBlockData.lastBlock) == currentBlock) {
currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance;
}
}
if (buyImbalance == 0) {
buyImbalance = imbalanceInRange;
}
}
function getImbalance(ERC20 token, uint rateUpdateBlock, uint currentBlock)
internal view
returns(int totalImbalance, int currentBlockImbalance)
{
int resolution = int(tokenControlInfo[token].minimalRecordResolution);
(totalImbalance, currentBlockImbalance) =
getImbalanceSinceRateUpdate(
token,
rateUpdateBlock,
currentBlock);
totalImbalance *= resolution;
currentBlockImbalance *= resolution;
}
function getMaxPerBlockImbalance(ERC20 token) internal view returns(uint) {
return tokenControlInfo[token].maxPerBlockImbalance;
}
function getMaxTotalImbalance(ERC20 token) internal view returns(uint) {
return tokenControlInfo[token].maxTotalImbalance;
}
function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) {
// check for overflows
require(data.lastBlockBuyUnitsImbalance < int(POW_2_64 / 2));
require(data.lastBlockBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2));
require(data.lastBlock < POW_2_64);
require(data.totalBuyUnitsImbalance < int(POW_2_64 / 2));
require(data.totalBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2));
require(data.lastRateUpdateBlock < POW_2_64);
// do encoding
uint result = uint(data.lastBlockBuyUnitsImbalance) & (POW_2_64 - 1);
result |= data.lastBlock * POW_2_64;
result |= (uint(data.totalBuyUnitsImbalance) & (POW_2_64 - 1)) * POW_2_64 * POW_2_64;
result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64;
return result;
}
function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) {
TokenImbalanceData memory data;
data.lastBlockBuyUnitsImbalance = int(int64(input & (POW_2_64 - 1)));
data.lastBlock = uint(uint64((input / POW_2_64) & (POW_2_64 - 1)));
data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) & (POW_2_64 - 1)));
data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64))));
return data;
}
}
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
}
}
contract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils {
// bps - basic rate steps. one step is 1 / 10000 of the rate.
struct StepFunction {
int[] x; // quantity for each step. Quantity of each step includes previous steps.
int[] y; // rate change per quantity step in bps.
}
struct TokenData {
bool listed; // was added to reserve
bool enabled; // whether trade is enabled
// position in the compact data
uint compactDataArrayIndex;
uint compactDataFieldIndex;
// rate data. base and changes according to quantity and reserve balance.
// generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction.
uint baseBuyRate; // in PRECISION units. see KyberConstants
uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate
StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate.
StepFunction sellRateQtyStepFunction;// in bps. higher the qua
StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate.
StepFunction sellRateImbalanceStepFunction;
}
/*
this is the data for tokenRatesCompactData
but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write
so we represent it as bytes32 and do the byte tricks ourselves.
struct TokenRatesCompactData {
bytes14 buy; // change buy rate of token from baseBuyRate in 10 bps
bytes14 sell; // change sell rate of token from baseSellRate in 10 bps
uint32 blockNumber;
} */
uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks
ERC20[] internal listedTokens;
mapping(address=>TokenData) internal tokenData;
bytes32[] internal tokenRatesCompactData;
uint public numTokensInCurrentCompactData = 0;
address public reserveContract;
uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14;
uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA));
uint constant internal MAX_STEPS_IN_FUNCTION = 10;
int constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B %
int constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100%
function ConversionRates(address _admin) public VolumeImbalanceRecorder(_admin)
{ } // solhint-disable-line no-empty-blocks
function addToken(ERC20 token) public onlyAdmin {
require(!tokenData[token].listed);
tokenData[token].listed = true;
listedTokens.push(token);
if (numTokensInCurrentCompactData == 0) {
tokenRatesCompactData.length++; // add new structure
}
tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1;
tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData;
numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA;
setGarbageToVolumeRecorder(token);
setDecimals(token);
}
function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator {
require(buy.length == sell.length);
require(indices.length == buy.length);
require(blockNumber <= 0xFFFFFFFF);
uint bytes14Offset = BYTES_14_OFFSET;
for (uint i = 0; i < indices.length; i++) {
require(indices[i] < tokenRatesCompactData.length);
uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset));
tokenRatesCompactData[indices[i]] = bytes32(data);
}
}
function setBaseRate(
ERC20[] tokens,
uint[] baseBuy,
uint[] baseSell,
bytes14[] buy,
bytes14[] sell,
uint blockNumber,
uint[] indices
)
public
onlyOperator
{
require(tokens.length == baseBuy.length);
require(tokens.length == baseSell.length);
require(sell.length == buy.length);
require(sell.length == indices.length);
for (uint ind = 0; ind < tokens.length; ind++) {
require(tokenData[tokens[ind]].listed);
tokenData[tokens[ind]].baseBuyRate = baseBuy[ind];
tokenData[tokens[ind]].baseSellRate = baseSell[ind];
}
setCompactData(buy, sell, blockNumber, indices);
}
function setQtyStepFunction(
ERC20 token,
int[] xBuy,
int[] yBuy,
int[] xSell,
int[] ySell
)
public
onlyOperator
{
require(xBuy.length == yBuy.length);
require(xSell.length == ySell.length);
require(xBuy.length <= MAX_STEPS_IN_FUNCTION);
require(xSell.length <= MAX_STEPS_IN_FUNCTION);
require(tokenData[token].listed);
tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy);
tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell);
}
function setImbalanceStepFunction(
ERC20 token,
int[] xBuy,
int[] yBuy,
int[] xSell,
int[] ySell
)
public
onlyOperator
{
require(xBuy.length == yBuy.length);
require(xSell.length == ySell.length);
require(xBuy.length <= MAX_STEPS_IN_FUNCTION);
require(xSell.length <= MAX_STEPS_IN_FUNCTION);
require(tokenData[token].listed);
tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy);
tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell);
}
function setValidRateDurationInBlocks(uint duration) public onlyAdmin {
validRateDurationInBlocks = duration;
}
function enableTokenTrade(ERC20 token) public onlyAdmin {
require(tokenData[token].listed);
require(tokenControlInfo[token].minimalRecordResolution != 0);
tokenData[token].enabled = true;
}
function disableTokenTrade(ERC20 token) public onlyAlerter {
require(tokenData[token].listed);
tokenData[token].enabled = false;
}
function setReserveAddress(address reserve) public onlyAdmin {
reserveContract = reserve;
}
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public
{
require(msg.sender == reserveContract);
if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token);
return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock);
}
/* solhint-disable function-max-lines */
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) {
// check if trade is enabled
if (!tokenData[token].enabled) return 0;
if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set
// get rate update block
bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];
uint updateRateBlock = getLast4Bytes(compactData);
if (currentBlockNumber >= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired
// check imbalance
int totalImbalance;
int blockImbalance;
(totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber);
// calculate actual rate
int imbalanceQty;
int extraBps;
int8 rateUpdate;
uint rate;
if (buy) {
// start with base rate
rate = tokenData[token].baseBuyRate;
// add rate update
rateUpdate = getRateByteFromCompactData(compactData, token, true);
extraBps = int(rateUpdate) * 10;
rate = addBps(rate, extraBps);
// compute token qty
qty = getTokenQty(token, rate, qty);
imbalanceQty = int(qty);
totalImbalance += imbalanceQty;
// add qty overhead
extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty));
rate = addBps(rate, extraBps);
// add imbalance overhead
extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance);
rate = addBps(rate, extraBps);
} else {
// start with base rate
rate = tokenData[token].baseSellRate;
// add rate update
rateUpdate = getRateByteFromCompactData(compactData, token, false);
extraBps = int(rateUpdate) * 10;
rate = addBps(rate, extraBps);
// compute token qty
imbalanceQty = -1 * int(qty);
totalImbalance += imbalanceQty;
// add qty overhead
extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty));
rate = addBps(rate, extraBps);
// add imbalance overhead
extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance);
rate = addBps(rate, extraBps);
}
if (abs(totalImbalance) >= getMaxTotalImbalance(token)) return 0;
if (abs(blockImbalance + imbalanceQty) >= getMaxPerBlockImbalance(token)) return 0;
return rate;
}
/* solhint-enable function-max-lines */
function getBasicRate(ERC20 token, bool buy) public view returns(uint) {
if (buy)
return tokenData[token].baseBuyRate;
else
return tokenData[token].baseSellRate;
}
function getCompactData(ERC20 token) public view returns(uint, uint, byte, byte) {
require(tokenData[token].listed);
uint arrayIndex = tokenData[token].compactDataArrayIndex;
uint fieldOffset = tokenData[token].compactDataFieldIndex;
return (
arrayIndex,
fieldOffset,
byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)),
byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false))
);
}
function getTokenBasicData(ERC20 token) public view returns(bool, bool) {
return (tokenData[token].listed, tokenData[token].enabled);
}
/* solhint-disable code-complexity */
function getStepFunctionData(ERC20 token, uint command, uint param) public view returns(int) {
if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length);
if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param];
if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length);
if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param];
if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length);
if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param];
if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length);
if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param];
if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length);
if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param];
if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length);
if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param];
if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length);
if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param];
if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length);
if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param];
revert();
}
/* solhint-enable code-complexity */
function getRateUpdateBlock(ERC20 token) public view returns(uint) {
bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];
return getLast4Bytes(compactData);
}
function getListedTokens() public view returns(ERC20[]) {
return listedTokens;
}
function getTokenQty(ERC20 token, uint ethQty, uint rate) internal view returns(uint) {
uint dstDecimals = getDecimals(token);
uint srcDecimals = ETH_DECIMALS;
return calcDstQty(ethQty, srcDecimals, dstDecimals, rate);
}
function getLast4Bytes(bytes32 b) internal pure returns(uint) {
// cannot trust compiler with not turning bit operations into EXP opcode
return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET);
}
function getRateByteFromCompactData(bytes32 data, ERC20 token, bool buy) internal view returns(int8) {
uint fieldOffset = tokenData[token].compactDataFieldIndex;
uint byteOffset;
if (buy)
byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset;
else
byteOffset = 4 + fieldOffset;
return int8(data[byteOffset]);
}
function executeStepFunction(StepFunction f, int x) internal pure returns(int) {
uint len = f.y.length;
for (uint ind = 0; ind < len; ind++) {
if (x <= f.x[ind]) return f.y[ind];
}
return f.y[len-1];
}
function addBps(uint rate, int bps) internal pure returns(uint) {
require(rate <= MAX_RATE);
require(bps >= MIN_BPS_ADJUSTMENT);
require(bps <= MAX_BPS_ADJUSTMENT);
uint maxBps = 100 * 100;
return (rate * uint(int(maxBps) + bps)) / maxBps;
}
function abs(int x) internal pure returns(uint) {
if (x < 0)
return uint(-1 * x);
else
return uint(x);
}
}File 9 of 10: SanityRates
pragma solidity 0.4.18;
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);
}
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;
}
}
}
}
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
}
}
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);
}
}
interface SanityRatesInterface {
function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
}
contract SanityRates is SanityRatesInterface, Withdrawable, Utils {
mapping(address=>uint) public tokenRate;
mapping(address=>uint) public reasonableDiffInBps;
function SanityRates(address _admin) public {
require(_admin != address(0));
admin = _admin;
}
function setReasonableDiff(ERC20[] srcs, uint[] diff) public onlyAdmin {
require(srcs.length == diff.length);
for (uint i = 0; i < srcs.length; i++) {
require(diff[i] <= 100 * 100);
reasonableDiffInBps[srcs[i]] = diff[i];
}
}
function setSanityRates(ERC20[] srcs, uint[] rates) public onlyOperator {
require(srcs.length == rates.length);
for (uint i = 0; i < srcs.length; i++) {
require(rates[i] <= MAX_RATE);
tokenRate[srcs[i]] = rates[i];
}
}
function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint) {
if (src != ETH_TOKEN_ADDRESS && dest != ETH_TOKEN_ADDRESS) return 0;
uint rate;
address token;
if (src == ETH_TOKEN_ADDRESS) {
rate = (PRECISION*PRECISION)/tokenRate[dest];
token = dest;
} else {
rate = tokenRate[src];
token = src;
}
return rate * (10000 + reasonableDiffInBps[token])/10000;
}
}File 10 of 10: LiquidityConversionRates
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/ConversionRatesInterface.sol
interface ConversionRatesInterface {
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public;
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}
// File: contracts/LiquidityFormula.sol
contract UtilMath {
uint public constant BIG_NUMBER = (uint(1)<<uint(200));
function checkMultOverflow(uint x, uint y) public pure returns(bool) {
if (y == 0) return false;
return (((x*y) / y) != x);
}
function compactFraction(uint p, uint q, uint precision) public pure returns (uint, uint) {
if (q < precision * precision) return (p, q);
return compactFraction(p/precision, q/precision, precision);
}
/* solhint-disable code-complexity */
function exp(uint p, uint q, uint precision) public pure returns (uint) {
uint n = 0;
uint nFact = 1;
uint currentP = 1;
uint currentQ = 1;
uint sum = 0;
uint prevSum = 0;
while (true) {
if (checkMultOverflow(currentP, precision)) return sum;
if (checkMultOverflow(currentQ, nFact)) return sum;
sum += (currentP * precision) / (currentQ * nFact);
if (sum == prevSum) return sum;
prevSum = sum;
n++;
if (checkMultOverflow(currentP, p)) return sum;
if (checkMultOverflow(currentQ, q)) return sum;
if (checkMultOverflow(nFact, n)) return sum;
currentP *= p;
currentQ *= q;
nFact *= n;
(currentP, currentQ) = compactFraction(currentP, currentQ, precision);
}
}
/* solhint-enable code-complexity */
function countLeadingZeros(uint p, uint q) public pure returns (uint) {
uint denomator = (uint(1)<<255);
for (int i = 255; i >= 0; i--) {
if ((q*denomator)/denomator != q) {
// overflow
denomator = denomator/2;
continue;
}
if (p/(q*denomator) > 0) return uint(i);
denomator = denomator/2;
}
return uint(-1);
}
// log2 for a number that it in [1,2)
function log2ForSmallNumber(uint x, uint numPrecisionBits) public pure returns (uint) {
uint res = 0;
uint one = (uint(1)<<numPrecisionBits);
uint two = 2 * one;
uint addition = one;
require((x >= one) && (x <= two));
require(numPrecisionBits < 125);
for (uint i = numPrecisionBits; i > 0; i--) {
x = (x*x) / one;
addition = addition/2;
if (x >= two) {
x = x/2;
res += addition;
}
}
return res;
}
function logBase2 (uint p, uint q, uint numPrecisionBits) public pure returns (uint) {
uint n = 0;
uint precision = (uint(1)<<numPrecisionBits);
if (p > q) {
n = countLeadingZeros(p, q);
}
require(!checkMultOverflow(p, precision));
require(!checkMultOverflow(n, precision));
require(!checkMultOverflow(uint(1)<<n, q));
uint y = p * precision / (q * (uint(1)<<n));
uint log2Small = log2ForSmallNumber(y, numPrecisionBits);
require(n*precision <= BIG_NUMBER);
require(log2Small <= BIG_NUMBER);
return n * precision + log2Small;
}
function ln(uint p, uint q, uint numPrecisionBits) public pure returns (uint) {
uint ln2Numerator = 6931471805599453094172;
uint ln2Denomerator = 10000000000000000000000;
uint log2x = logBase2(p, q, numPrecisionBits);
require(!checkMultOverflow(ln2Numerator, log2x));
return ln2Numerator * log2x / ln2Denomerator;
}
}
contract LiquidityFormula is UtilMath {
function pE(uint r, uint pMIn, uint e, uint precision) public pure returns (uint) {
uint expRE = exp(r*e, precision*precision, precision);
require(!checkMultOverflow(expRE, pMIn));
return pMIn*expRE / precision;
}
function deltaTFunc(uint r, uint pMIn, uint e, uint deltaE, uint precision) public pure returns (uint) {
uint pe = pE(r, pMIn, e, precision);
uint rpe = r * pe;
uint erdeltaE = exp(r*deltaE, precision*precision, precision);
require(erdeltaE >= precision);
require(!checkMultOverflow(erdeltaE - precision, precision));
require(!checkMultOverflow((erdeltaE - precision)*precision, precision));
require(!checkMultOverflow((erdeltaE - precision)*precision*precision, precision));
require(!checkMultOverflow(rpe, erdeltaE));
require(!checkMultOverflow(r, pe));
return (erdeltaE - precision) * precision * precision * precision / (rpe*erdeltaE);
}
function deltaEFunc(uint r, uint pMIn, uint e, uint deltaT, uint precision, uint numPrecisionBits)
public pure
returns (uint)
{
uint pe = pE(r, pMIn, e, precision);
uint rpe = r * pe;
uint lnPart = ln(precision*precision + rpe*deltaT/precision, precision*precision, numPrecisionBits);
require(!checkMultOverflow(r, pe));
require(!checkMultOverflow(precision, precision));
require(!checkMultOverflow(rpe, deltaT));
require(!checkMultOverflow(lnPart, precision));
return lnPart * precision / r;
}
}
// 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/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/LiquidityConversionRates.sol
contract LiquidityConversionRates is ConversionRatesInterface, LiquidityFormula, Withdrawable, Utils {
ERC20 public token;
address public reserveContract;
uint public numFpBits;
uint public formulaPrecision;
uint public rInFp;
uint public pMinInFp;
uint public maxEthCapBuyInFp;
uint public maxEthCapSellInFp;
uint public maxQtyInFp;
uint public feeInBps;
uint public collectedFeesInTwei = 0;
uint public maxBuyRateInPrecision;
uint public minBuyRateInPrecision;
uint public maxSellRateInPrecision;
uint public minSellRateInPrecision;
function LiquidityConversionRates(address _admin, ERC20 _token) public {
transferAdminQuickly(_admin);
token = _token;
setDecimals(token);
require(getDecimals(token) <= MAX_DECIMALS);
}
event ReserveAddressSet(address reserve);
function setReserveAddress(address reserve) public onlyAdmin {
reserveContract = reserve;
ReserveAddressSet(reserve);
}
event LiquidityParamsSet(
uint rInFp,
uint pMinInFp,
uint numFpBits,
uint maxCapBuyInFp,
uint maxEthCapSellInFp,
uint feeInBps,
uint formulaPrecision,
uint maxQtyInFp,
uint maxBuyRateInPrecision,
uint minBuyRateInPrecision,
uint maxSellRateInPrecision,
uint minSellRateInPrecision
);
function setLiquidityParams(
uint _rInFp,
uint _pMinInFp,
uint _numFpBits,
uint _maxCapBuyInWei,
uint _maxCapSellInWei,
uint _feeInBps,
uint _maxTokenToEthRateInPrecision,
uint _minTokenToEthRateInPrecision
) public onlyAdmin {
require(_numFpBits < 256);
require(formulaPrecision <= MAX_QTY);
require(_feeInBps < 10000);
require(_minTokenToEthRateInPrecision < _maxTokenToEthRateInPrecision);
rInFp = _rInFp;
pMinInFp = _pMinInFp;
formulaPrecision = uint(1)<<_numFpBits;
maxQtyInFp = fromWeiToFp(MAX_QTY);
numFpBits = _numFpBits;
maxEthCapBuyInFp = fromWeiToFp(_maxCapBuyInWei);
maxEthCapSellInFp = fromWeiToFp(_maxCapSellInWei);
feeInBps = _feeInBps;
maxBuyRateInPrecision = PRECISION * PRECISION / _minTokenToEthRateInPrecision;
minBuyRateInPrecision = PRECISION * PRECISION / _maxTokenToEthRateInPrecision;
maxSellRateInPrecision = _maxTokenToEthRateInPrecision;
minSellRateInPrecision = _minTokenToEthRateInPrecision;
LiquidityParamsSet(
rInFp,
pMinInFp,
numFpBits,
maxEthCapBuyInFp,
maxEthCapSellInFp,
feeInBps,
formulaPrecision,
maxQtyInFp,
maxBuyRateInPrecision,
minBuyRateInPrecision,
maxSellRateInPrecision,
minSellRateInPrecision
);
}
function recordImbalance(
ERC20 conversionToken,
int buyAmountInTwei,
uint rateUpdateBlock,
uint currentBlock
)
public
{
conversionToken;
rateUpdateBlock;
currentBlock;
require(msg.sender == reserveContract);
if (buyAmountInTwei > 0) {
// Buy case
collectedFeesInTwei += calcCollectedFee(abs(buyAmountInTwei));
} else {
// Sell case
collectedFeesInTwei += abs(buyAmountInTwei) * feeInBps / 10000;
}
}
event CollectedFeesReset(uint resetFeesInTwei);
function resetCollectedFees() public onlyAdmin {
uint resetFeesInTwei = collectedFeesInTwei;
collectedFeesInTwei = 0;
CollectedFeesReset(resetFeesInTwei);
}
function getRate(
ERC20 conversionToken,
uint currentBlockNumber,
bool buy,
uint qtyInSrcWei
) public view returns(uint) {
currentBlockNumber;
require(qtyInSrcWei <= MAX_QTY);
uint eInFp = fromWeiToFp(reserveContract.balance);
uint rateInPrecision = getRateWithE(conversionToken, buy, qtyInSrcWei, eInFp);
require(rateInPrecision <= MAX_RATE);
return rateInPrecision;
}
function getRateWithE(ERC20 conversionToken, bool buy, uint qtyInSrcWei, uint eInFp) public view returns(uint) {
uint deltaEInFp;
uint sellInputTokenQtyInFp;
uint deltaTInFp;
uint rateInPrecision;
require(qtyInSrcWei <= MAX_QTY);
require(eInFp <= maxQtyInFp);
if (conversionToken != token) return 0;
if (buy) {
// ETH goes in, token goes out
deltaEInFp = fromWeiToFp(qtyInSrcWei);
if (deltaEInFp > maxEthCapBuyInFp) return 0;
if (deltaEInFp == 0) {
rateInPrecision = buyRateZeroQuantity(eInFp);
} else {
rateInPrecision = buyRate(eInFp, deltaEInFp);
}
} else {
sellInputTokenQtyInFp = fromTweiToFp(qtyInSrcWei);
deltaTInFp = valueAfterReducingFee(sellInputTokenQtyInFp);
if (deltaTInFp == 0) {
rateInPrecision = sellRateZeroQuantity(eInFp);
deltaEInFp = 0;
} else {
(rateInPrecision, deltaEInFp) = sellRate(eInFp, sellInputTokenQtyInFp, deltaTInFp);
}
if (deltaEInFp > maxEthCapSellInFp) return 0;
}
rateInPrecision = rateAfterValidation(rateInPrecision, buy);
return rateInPrecision;
}
function rateAfterValidation(uint rateInPrecision, bool buy) public view returns(uint) {
uint minAllowRateInPrecision;
uint maxAllowedRateInPrecision;
if (buy) {
minAllowRateInPrecision = minBuyRateInPrecision;
maxAllowedRateInPrecision = maxBuyRateInPrecision;
} else {
minAllowRateInPrecision = minSellRateInPrecision;
maxAllowedRateInPrecision = maxSellRateInPrecision;
}
if ((rateInPrecision > maxAllowedRateInPrecision) || (rateInPrecision < minAllowRateInPrecision)) {
return 0;
} else if (rateInPrecision > MAX_RATE) {
return 0;
} else {
return rateInPrecision;
}
}
function buyRate(uint eInFp, uint deltaEInFp) public view returns(uint) {
uint deltaTInFp = deltaTFunc(rInFp, pMinInFp, eInFp, deltaEInFp, formulaPrecision);
require(deltaTInFp <= maxQtyInFp);
deltaTInFp = valueAfterReducingFee(deltaTInFp);
return deltaTInFp * PRECISION / deltaEInFp;
}
function buyRateZeroQuantity(uint eInFp) public view returns(uint) {
uint ratePreReductionInPrecision = formulaPrecision * PRECISION / pE(rInFp, pMinInFp, eInFp, formulaPrecision);
return valueAfterReducingFee(ratePreReductionInPrecision);
}
function sellRate(
uint eInFp,
uint sellInputTokenQtyInFp,
uint deltaTInFp
) public view returns(uint rateInPrecision, uint deltaEInFp) {
deltaEInFp = deltaEFunc(rInFp, pMinInFp, eInFp, deltaTInFp, formulaPrecision, numFpBits);
require(deltaEInFp <= maxQtyInFp);
rateInPrecision = deltaEInFp * PRECISION / sellInputTokenQtyInFp;
}
function sellRateZeroQuantity(uint eInFp) public view returns(uint) {
uint ratePreReductionInPrecision = pE(rInFp, pMinInFp, eInFp, formulaPrecision) * PRECISION / formulaPrecision;
return valueAfterReducingFee(ratePreReductionInPrecision);
}
function fromTweiToFp(uint qtyInTwei) public view returns(uint) {
require(qtyInTwei <= MAX_QTY);
return qtyInTwei * formulaPrecision / (10 ** getDecimals(token));
}
function fromWeiToFp(uint qtyInwei) public view returns(uint) {
require(qtyInwei <= MAX_QTY);
return qtyInwei * formulaPrecision / (10 ** ETH_DECIMALS);
}
function valueAfterReducingFee(uint val) public view returns(uint) {
require(val <= BIG_NUMBER);
return ((10000 - feeInBps) * val) / 10000;
}
function calcCollectedFee(uint val) public view returns(uint) {
require(val <= MAX_QTY);
return val * feeInBps / (10000 - feeInBps);
}
function abs(int val) public pure returns(uint) {
if (val < 0) {
return uint(val * (-1));
} else {
return uint(val);
}
}
}