pragma solidity >=0.4.22 <0.6.0;
pragma experimental ABIEncoderV2;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
contract LibMath {
using SafeMath for uint256;
function getPartialAmount(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
pure
returns (uint256 partialAmount)
{
partialAmount = numerator.mul(target).div(denominator);
}
function getFeeAmount(
uint256 numerator,
uint256 target
)
internal
pure
returns (uint256 feeAmount)
{
feeAmount = numerator.mul(target).div(1 ether); // todo: constants
}
}
contract LibOrder {
struct Order {
uint256 makerSellAmount;
uint256 makerBuyAmount;
uint256 takerSellAmount;
uint256 salt;
uint256 expiration;
address taker;
address maker;
address makerSellToken;
address makerBuyToken;
}
struct OrderInfo {
uint256 filledAmount;
bytes32 hash;
uint8 status;
}
struct OrderFill {
uint256 makerFillAmount;
uint256 takerFillAmount;
uint256 takerFeePaid;
uint256 exchangeFeeReceived;
uint256 referralFeeReceived;
uint256 makerFeeReceived;
}
enum OrderStatus {
INVALID_SIGNER,
INVALID_TAKER_AMOUNT,
INVALID_MAKER_AMOUNT,
FILLABLE,
EXPIRED,
FULLY_FILLED,
CANCELLED
}
function getHash(Order memory order)
public
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
order.maker,
order.makerSellToken,
order.makerSellAmount,
order.makerBuyToken,
order.makerBuyAmount,
order.salt,
order.expiration
)
);
}
function getPrefixedHash(Order memory order)
public
pure
returns (bytes32)
{
bytes32 orderHash = getHash(order);
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", orderHash));
}
}
contract LibSignatureValidator {
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solium-disable-next-line security/no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}
// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
// solium-disable-next-line arg-overflow
return ecrecover(hash, v, r, s);
}
}
}
contract IKyberNetworkProxy {
function getExpectedRate(address src, address dest, uint srcQty) public view
returns (uint expectedRate, uint slippageRate);
function trade(
address src,
uint srcAmount,
address dest,
address destAddress,
uint maxDestAmount,
uint minConversionRate,
address walletId
) public payable returns(uint256);
}
contract LibKyberData {
struct KyberData {
uint256 rate;
uint256 value;
address givenToken;
address receivedToken;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
contract IExchangeUpgradability {
uint8 public VERSION;
event FundsMigrated(address indexed user, address indexed newExchange);
function allowOrRestrictMigrations() external;
function migrateFunds(address[] calldata tokens) external;
function migrateEthers() private;
function migrateTokens(address[] memory tokens) private;
function importEthers(address user) external payable;
function importTokens(address tokenAddress, uint256 tokenAmount, address user) external;
}
contract LibCrowdsale {
using SafeMath for uint256;
struct Crowdsale {
uint256 startBlock;
uint256 endBlock;
uint256 hardCap;
uint256 leftAmount;
uint256 tokenRatio;
uint256 minContribution;
uint256 maxContribution;
uint256 weiRaised;
address wallet;
}
enum ContributionStatus {
CROWDSALE_NOT_OPEN,
MIN_CONTRIBUTION,
MAX_CONTRIBUTION,
HARDCAP_REACHED,
VALID
}
enum CrowdsaleStatus {
INVALID_START_BLOCK,
INVALID_END_BLOCK,
INVALID_TOKEN_RATIO,
INVALID_LEFT_AMOUNT,
VALID
}
function getCrowdsaleStatus(Crowdsale memory crowdsale)
public
view
returns (CrowdsaleStatus)
{
if(crowdsale.startBlock < block.number) {
return CrowdsaleStatus.INVALID_START_BLOCK;
}
if(crowdsale.endBlock < crowdsale.startBlock) {
return CrowdsaleStatus.INVALID_END_BLOCK;
}
if(crowdsale.tokenRatio == 0) {
return CrowdsaleStatus.INVALID_TOKEN_RATIO;
}
uint256 tokenForSale = crowdsale.hardCap.mul(crowdsale.tokenRatio);
if(tokenForSale != crowdsale.leftAmount) {
return CrowdsaleStatus.INVALID_LEFT_AMOUNT;
}
return CrowdsaleStatus.VALID;
}
function isOpened(uint256 startBlock, uint256 endBlock)
internal
view
returns (bool)
{
return (block.number >= startBlock && block.number <= endBlock);
}
function isFinished(uint256 endBlock)
internal
view
returns (bool)
{
return block.number > endBlock;
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > 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
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
contract ExchangeStorage is Ownable {
/**
* @dev The minimum fee rate that the maker will receive
* Note: 20% = 20 * 10^16
*/
uint256 constant internal minMakerFeeRate = 200000000000000000;
/**
* @dev The maximum fee rate that the maker will receive
* Note: 90% = 90 * 10^16
*/
uint256 constant internal maxMakerFeeRate = 900000000000000000;
/**
* @dev The minimum fee rate that the taker will pay
* Note: 0.1% = 0.1 * 10^16
*/
uint256 constant internal minTakerFeeRate = 1000000000000000;
/**
* @dev The maximum fee rate that the taker will pay
* Note: 1% = 1 * 10^16
*/
uint256 constant internal maxTakerFeeRate = 10000000000000000;
/**
* @dev The referrer will receive 10% from each taker fee.
* Note: 10% = 10 * 10^16
*/
uint256 constant internal referralFeeRate = 100000000000000000;
/**
* @dev The amount of percentage the maker will receive from each taker fee.
* Note: Initially: 50% = 50 * 10^16
*/
uint256 public makerFeeRate;
/**
* @dev The amount of percentage the will pay for taking an order.
* Note: Initially: 0.2% = 0.2 * 10^16
*/
uint256 public takerFeeRate;
/**
* @dev 2-level map: tokenAddress -> userAddress -> balance
*/
mapping(address => mapping(address => uint256)) internal balances;
/**
* @dev map: orderHash -> filled amount
*/
mapping(bytes32 => uint256) internal filled;
/**
* @dev map: orderHash -> isCancelled
*/
mapping(bytes32 => bool) internal cancelled;
/**
* @dev map: user -> userReferrer
*/
mapping(address => address) internal referrals;
/**
* @dev The address where all exchange fees (0,08%) are kept.
* Node: multisig wallet
*/
address public feeAccount;
/**
* @return return the balance of `token` for certain `user`
*/
function getBalance(
address user,
address token
)
public
view
returns (uint256)
{
return balances[token][user];
}
/**
* @return return the balance of multiple tokens for certain `user`
*/
function getBalances(
address user,
address[] memory token
)
public
view
returns(uint256[] memory balanceArray)
{
balanceArray = new uint256[](token.length);
for(uint256 index = 0; index < token.length; index++) {
balanceArray[index] = balances[token[index]][user];
}
}
/**
* @return return the filled amount of order specified by `orderHash`
*/
function getFill(
bytes32 orderHash
)
public
view
returns (uint256)
{
return filled[orderHash];
}
/**
* @return return the filled amount of multple orders specified by `orderHash` array
*/
function getFills(
bytes32[] memory orderHash
)
public
view
returns (uint256[] memory filledArray)
{
filledArray = new uint256[](orderHash.length);
for(uint256 index = 0; index < orderHash.length; index++) {
filledArray[index] = filled[orderHash[index]];
}
}
/**
* @return return true(false) if order specified by `orderHash` is(not) cancelled
*/
function getCancel(
bytes32 orderHash
)
public
view
returns (bool)
{
return cancelled[orderHash];
}
/**
* @return return array of true(false) if orders specified by `orderHash` array are(not) cancelled
*/
function getCancels(
bytes32[] memory orderHash
)
public
view
returns (bool[]memory cancelledArray)
{
cancelledArray = new bool[](orderHash.length);
for(uint256 index = 0; index < orderHash.length; index++) {
cancelledArray[index] = cancelled[orderHash[index]];
}
}
/**
* @return return the referrer address of `user`
*/
function getReferral(
address user
)
public
view
returns (address)
{
return referrals[user];
}
/**
* @return set new rate for the maker fee received
*/
function setMakerFeeRate(
uint256 newMakerFeeRate
)
external
onlyOwner
{
require(
newMakerFeeRate >= minMakerFeeRate &&
newMakerFeeRate <= maxMakerFeeRate,
"INVALID_MAKER_FEE_RATE"
);
makerFeeRate = newMakerFeeRate;
}
/**
* @return set new rate for the taker fee paid
*/
function setTakerFeeRate(
uint256 newTakerFeeRate
)
external
onlyOwner
{
require(
newTakerFeeRate >= minTakerFeeRate &&
newTakerFeeRate <= maxTakerFeeRate,
"INVALID_TAKER_FEE_RATE"
);
takerFeeRate = newTakerFeeRate;
}
/**
* @return set new fee account
*/
function setFeeAccount(
address newFeeAccount
)
external
onlyOwner
{
feeAccount = newFeeAccount;
}
}
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
contract Exchange is LibMath, LibOrder, LibSignatureValidator, ExchangeStorage {
using SafeMath for uint256;
/**
* @dev emitted when a trade is executed
*/
event Trade(
address indexed makerAddress, // Address that created the order
address indexed takerAddress, // Address that filled the order
bytes32 indexed orderHash, // Hash of the order
address makerFilledAsset, // Address of assets filled for maker
address takerFilledAsset, // Address of assets filled for taker
uint256 makerFilledAmount, // Amount of assets filled for maker
uint256 takerFilledAmount, // Amount of assets filled for taker
uint256 takerFeePaid, // Amount of fee paid by the taker
uint256 makerFeeReceived, // Amount of fee received by the maker
uint256 referralFeeReceived // Amount of fee received by the referrer
);
/**
* @dev emitted when a cancel order is executed
*/
event Cancel(
address indexed makerBuyToken, // Address of asset being bought.
address makerSellToken, // Address of asset being sold.
address indexed maker, // Address that created the order
bytes32 indexed orderHash // Hash of the order
);
/**
* @dev Compute the status of an order.
* Should be called before a contract execution is performet in order to not waste gas.
* @return OrderStatus.FILLABLE if the order is valid for taking.
* Note: See LibOrder.sol to see all statuses
*/
function getOrderInfo(
uint256 partialAmount,
Order memory order
)
public
view
returns (OrderInfo memory orderInfo)
{
// Compute the order hash
orderInfo.hash = getPrefixedHash(order);
// Fetch filled amount
orderInfo.filledAmount = filled[orderInfo.hash];
// Check taker balance
if(balances[order.makerBuyToken][order.taker] < order.takerSellAmount) {
orderInfo.status = uint8(OrderStatus.INVALID_TAKER_AMOUNT);
return orderInfo;
}
// Check maker balance
if(balances[order.makerSellToken][order.maker] < partialAmount) {
orderInfo.status = uint8(OrderStatus.INVALID_MAKER_AMOUNT);
return orderInfo;
}
// Check if order is filled
if (orderInfo.filledAmount.add(order.takerSellAmount) > order.makerBuyAmount) {
orderInfo.status = uint8(OrderStatus.FULLY_FILLED);
return orderInfo;
}
// Check for expiration
if (block.number >= order.expiration) {
orderInfo.status = uint8(OrderStatus.EXPIRED);
return orderInfo;
}
// Check if order has been cancelled
if (cancelled[orderInfo.hash]) {
orderInfo.status = uint8(OrderStatus.CANCELLED);
return orderInfo;
}
orderInfo.status = uint8(OrderStatus.FILLABLE);
return orderInfo;
}
/**
* @dev Execute a trade based on the input order and signature.
* Reverts if order is not valid
*/
function trade(
Order memory order,
bytes memory signature
)
public
{
bool result = _trade(order, signature);
require(result, "INVALID_TRADE");
}
/**
* @dev Execute a trade based on the input order and signature.
* If the order is valid returns true.
*/
function _trade(
Order memory order,
bytes memory signature
)
internal
returns(bool)
{
order.taker = msg.sender;
uint256 takerReceivedAmount = getPartialAmount(
order.makerSellAmount,
order.makerBuyAmount,
order.takerSellAmount
);
OrderInfo memory orderInfo = getOrderInfo(takerReceivedAmount, order);
uint8 status = assertTakeOrder(orderInfo.hash, orderInfo.status, order.maker, signature);
if(status != uint8(OrderStatus.FILLABLE)) {
return false;
}
OrderFill memory orderFill = getOrderFillResult(takerReceivedAmount, order);
executeTrade(order, orderFill);
filled[orderInfo.hash] = filled[orderInfo.hash].add(order.takerSellAmount);
emit Trade(
order.maker,
order.taker,
orderInfo.hash,
order.makerBuyToken,
order.makerSellToken,
orderFill.makerFillAmount,
orderFill.takerFillAmount,
orderFill.takerFeePaid,
orderFill.makerFeeReceived,
orderFill.referralFeeReceived
);
return true;
}
/**
* @dev Cancel an order if msg.sender is the order signer.
*/
function cancelSingleOrder(
Order memory order,
bytes memory signature
)
public
{
bytes32 orderHash = getPrefixedHash(order);
require(
recover(orderHash, signature) == msg.sender,
"INVALID_SIGNER"
);
require(
cancelled[orderHash] == false,
"ALREADY_CANCELLED"
);
cancelled[orderHash] = true;
emit Cancel(
order.makerBuyToken,
order.makerSellToken,
msg.sender,
orderHash
);
}
/**
* @dev Computation of the following properties based on the order input:
* takerFillAmount -> amount of assets received by the taker
* makerFillAmount -> amount of assets received by the maker
* takerFeePaid -> amount of fee paid by the taker (0.2% of takerFillAmount)
* makerFeeReceived -> amount of fee received by the maker (50% of takerFeePaid)
* referralFeeReceived -> amount of fee received by the taker referrer (10% of takerFeePaid)
* exchangeFeeReceived -> amount of fee received by the exchange (40% of takerFeePaid)
*/
function getOrderFillResult(
uint256 takerReceivedAmount,
Order memory order
)
internal
view
returns (OrderFill memory orderFill)
{
orderFill.takerFillAmount = takerReceivedAmount;
orderFill.makerFillAmount = order.takerSellAmount;
// 0.2% == 0.2*10^16
orderFill.takerFeePaid = getFeeAmount(
takerReceivedAmount,
takerFeeRate
);
// 50% of taker fee == 50*10^16
orderFill.makerFeeReceived = getFeeAmount(
orderFill.takerFeePaid,
makerFeeRate
);
// 10% of taker fee == 10*10^16
orderFill.referralFeeReceived = getFeeAmount(
orderFill.takerFeePaid,
referralFeeRate
);
// exchangeFee = (takerFeePaid - makerFeeReceived - referralFeeReceived)
orderFill.exchangeFeeReceived = orderFill.takerFeePaid.sub(
orderFill.makerFeeReceived).sub(
orderFill.referralFeeReceived);
}
/**
* @dev Throws when the order status is invalid or the signer is not valid.
*/
function assertTakeOrder(
bytes32 orderHash,
uint8 status,
address signer,
bytes memory signature
)
internal
pure
returns(uint8)
{
uint8 result = uint8(OrderStatus.FILLABLE);
if(recover(orderHash, signature) != signer) {
result = uint8(OrderStatus.INVALID_SIGNER);
}
if(status != uint8(OrderStatus.FILLABLE)) {
result = status;
}
return status;
}
/**
* @dev Updates the contract state i.e. user balances
*/
function executeTrade(
Order memory order,
OrderFill memory orderFill
)
private
{
uint256 makerGiveAmount = orderFill.takerFillAmount.sub(orderFill.makerFeeReceived);
uint256 takerFillAmount = orderFill.takerFillAmount.sub(orderFill.takerFeePaid);
address referrer = referrals[order.taker];
address feeAddress = feeAccount;
balances[order.makerSellToken][referrer] = balances[order.makerSellToken][referrer].add(orderFill.referralFeeReceived);
balances[order.makerSellToken][feeAddress] = balances[order.makerSellToken][feeAddress].add(orderFill.exchangeFeeReceived);
balances[order.makerBuyToken][order.taker] = balances[order.makerBuyToken][order.taker].sub(orderFill.makerFillAmount);
balances[order.makerBuyToken][order.maker] = balances[order.makerBuyToken][order.maker].add(orderFill.makerFillAmount);
balances[order.makerSellToken][order.taker] = balances[order.makerSellToken][order.taker].add(takerFillAmount);
balances[order.makerSellToken][order.maker] = balances[order.makerSellToken][order.maker].sub(makerGiveAmount);
}
}
contract ExchangeKyberProxy is Exchange, LibKyberData {
using SafeERC20 for IERC20;
/**
* @dev The precision used for calculating the amounts - 10*18
*/
uint256 constant internal PRECISION = 1000000000000000000;
/**
* @dev Max decimals allowed when calculating amounts.
*/
uint256 constant internal MAX_DECIMALS = 18;
/**
* @dev Decimals of Ether.
*/
uint256 constant internal ETH_DECIMALS = 18;
/**
* @dev The address that represents ETH in Kyber Network Contracts.
*/
address constant internal KYBER_ETH_TOKEN_ADDRESS =
address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
uint256 constant internal MAX_DEST_AMOUNT = 2**256 - 1;
/**
* @dev KyberNetworkProxy contract address
*/
IKyberNetworkProxy constant internal kyberNetworkContract =
IKyberNetworkProxy(0x818E6FECD516Ecc3849DAf6845e3EC868087B755);
/**
* @dev Swaps ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using KyberNetwork reserves.
*/
function kyberSwap(
uint256 givenAmount,
address givenToken,
address receivedToken,
bytes32 hash
)
public
payable
{
address taker = msg.sender;
KyberData memory kyberData = getSwapInfo(
givenAmount,
givenToken,
receivedToken,
taker
);
uint256 convertedAmount = kyberNetworkContract.trade.value(kyberData.value)(
kyberData.givenToken,
givenAmount,
kyberData.receivedToken,
taker,
MAX_DEST_AMOUNT,
kyberData.rate,
feeAccount
);
emit Trade(
address(kyberNetworkContract),
taker,
hash,
givenToken,
receivedToken,
givenAmount,
convertedAmount,
0,
0,
0
);
}
/**
* @dev Exchange ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using the internal
* balance mapping that keeps track of user's balances. It requires user to first invoke deposit function.
* The function relies on KyberNetworkProxy contract.
*/
function kyberTrade(
uint256 givenAmount,
address givenToken,
address receivedToken,
bytes32 hash
)
public
{
address taker = msg.sender;
KyberData memory kyberData = getTradeInfo(
givenAmount,
givenToken,
receivedToken
);
balances[givenToken][taker] = balances[givenToken][taker].sub(givenAmount);
uint256 convertedAmount = kyberNetworkContract.trade.value(kyberData.value)(
kyberData.givenToken,
givenAmount,
kyberData.receivedToken,
address(this),
MAX_DEST_AMOUNT,
kyberData.rate,
feeAccount
);
balances[receivedToken][taker] = balances[receivedToken][taker].add(convertedAmount);
emit Trade(
address(kyberNetworkContract),
taker,
hash,
givenToken,
receivedToken,
givenAmount,
convertedAmount,
0,
0,
0
);
}
/**
* @dev Helper function to determine what is being swapped.
*/
function getSwapInfo(
uint256 givenAmount,
address givenToken,
address receivedToken,
address taker
)
private
returns(KyberData memory)
{
KyberData memory kyberData;
uint256 givenTokenDecimals;
uint256 receivedTokenDecimals;
if(givenToken == address(0x0)) {
require(msg.value == givenAmount, "INVALID_ETH_VALUE");
kyberData.givenToken = KYBER_ETH_TOKEN_ADDRESS;
kyberData.receivedToken = receivedToken;
kyberData.value = givenAmount;
givenTokenDecimals = ETH_DECIMALS;
receivedTokenDecimals = IERC20(receivedToken).decimals();
} else if(receivedToken == address(0x0)) {
kyberData.givenToken = givenToken;
kyberData.receivedToken = KYBER_ETH_TOKEN_ADDRESS;
kyberData.value = 0;
givenTokenDecimals = IERC20(givenToken).decimals();
receivedTokenDecimals = ETH_DECIMALS;
IERC20(givenToken).safeTransferFrom(taker, address(this), givenAmount);
IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
} else {
kyberData.givenToken = givenToken;
kyberData.receivedToken = receivedToken;
kyberData.value = 0;
givenTokenDecimals = IERC20(givenToken).decimals();
receivedTokenDecimals = IERC20(receivedToken).decimals();
IERC20(givenToken).safeTransferFrom(taker, address(this), givenAmount);
IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
}
(kyberData.rate, ) = kyberNetworkContract.getExpectedRate(
kyberData.givenToken,
kyberData.receivedToken,
givenAmount
);
return kyberData;
}
/**
* @dev Helper function to determines what is being
swapped using the internal balance mapping.
*/
function getTradeInfo(
uint256 givenAmount,
address givenToken,
address receivedToken
)
private
returns(KyberData memory)
{
KyberData memory kyberData;
uint256 givenTokenDecimals;
uint256 receivedTokenDecimals;
if(givenToken == address(0x0)) {
kyberData.givenToken = KYBER_ETH_TOKEN_ADDRESS;
kyberData.receivedToken = receivedToken;
kyberData.value = givenAmount;
givenTokenDecimals = ETH_DECIMALS;
receivedTokenDecimals = IERC20(receivedToken).decimals();
} else if(receivedToken == address(0x0)) {
kyberData.givenToken = givenToken;
kyberData.receivedToken = KYBER_ETH_TOKEN_ADDRESS;
kyberData.value = 0;
givenTokenDecimals = IERC20(givenToken).decimals();
receivedTokenDecimals = ETH_DECIMALS;
IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
} else {
kyberData.givenToken = givenToken;
kyberData.receivedToken = receivedToken;
kyberData.value = 0;
givenTokenDecimals = IERC20(givenToken).decimals();
receivedTokenDecimals = IERC20(receivedToken).decimals();
IERC20(givenToken).safeApprove(address(kyberNetworkContract), givenAmount);
}
(kyberData.rate, ) = kyberNetworkContract.getExpectedRate(
kyberData.givenToken,
kyberData.receivedToken,
givenAmount
);
return kyberData;
}
function getExpectedRateBatch(
address[] memory givenTokens,
address[] memory receivedTokens,
uint256[] memory givenAmounts
)
public
view
returns(uint256[] memory, uint256[] memory)
{
uint256 size = givenTokens.length;
uint256[] memory expectedRates = new uint256[](size);
uint256[] memory slippageRates = new uint256[](size);
for(uint256 index = 0; index < size; index++) {
(expectedRates[index], slippageRates[index]) = kyberNetworkContract.getExpectedRate(
givenTokens[index],
receivedTokens[index],
givenAmounts[index]
);
}
return (expectedRates, slippageRates);
}
}
contract ExchangeBatchTrade is Exchange {
/**
* @dev Cancel an array of orders if msg.sender is the order signer.
*/
function cancelMultipleOrders(
Order[] memory orders,
bytes[] memory signatures
)
public
{
for (uint256 index = 0; index < orders.length; index++) {
cancelSingleOrder(
orders[index],
signatures[index]
);
}
}
/**
* @dev Execute multiple trades based on the input orders and signatures.
* Note: reverts of one or more trades fail.
*/
function takeAllOrRevert(
Order[] memory orders,
bytes[] memory signatures
)
public
{
for (uint256 index = 0; index < orders.length; index++) {
bool result = _trade(orders[index], signatures[index]);
require(result, "INVALID_TAKEALL");
}
}
/**
* @dev Execute multiple trades based on the input orders and signatures.
* Note: does not revert if one or more trades fail.
*/
function takeAllPossible(
Order[] memory orders,
bytes[] memory signatures
)
public
{
for (uint256 index = 0; index < orders.length; index++) {
_trade(orders[index], signatures[index]);
}
}
}
contract ExchangeMovements is ExchangeStorage {
using SafeERC20 for IERC20;
using SafeMath for uint256;
/**
* @dev emitted when a deposit is received
*/
event Deposit(
address indexed token,
address indexed user,
address indexed referral,
address beneficiary,
uint256 amount,
uint256 balance
);
/**
* @dev emitted when a withdraw is received
*/
event Withdraw(
address indexed token,
address indexed user,
uint256 amount,
uint256 balance
);
/**
* @dev emitted when a transfer is received
*/
event Transfer(
address indexed token,
address indexed user,
address indexed beneficiary,
uint256 amount,
uint256 userBalance,
uint256 beneficiaryBalance
);
/**
* @dev Updates the level 2 map `balances` based on the input
* Note: token address is (0x0) when the deposit is for ETH
*/
function deposit(
address token,
uint256 amount,
address beneficiary,
address referral
)
public
payable
{
uint256 value = amount;
address user = msg.sender;
if(token == address(0x0)) {
value = msg.value;
} else {
IERC20(token).safeTransferFrom(user, address(this), value);
}
balances[token][beneficiary] = balances[token][beneficiary].add(value);
if(referrals[user] == address(0x0)) {
referrals[user] = referral;
}
emit Deposit(
token,
user,
referrals[user],
beneficiary,
value,
balances[token][beneficiary]
);
}
/**
* @dev Updates the level 2 map `balances` based on the input
* Note: token address is (0x0) when the deposit is for ETH
*/
function withdraw(
address token,
uint amount
)
public
{
address payable user = msg.sender;
require(
balances[token][user] >= amount,
"INVALID_WITHDRAW"
);
balances[token][user] = balances[token][user].sub(amount);
if (token == address(0x0)) {
user.transfer(amount);
} else {
IERC20(token).safeTransfer(user, amount);
}
emit Withdraw(
token,
user,
amount,
balances[token][user]
);
}
/**
* @dev Transfer assets between two users inside the exchange. Updates the level 2 map `balances`
*/
function transfer(
address token,
address to,
uint256 amount
)
external
payable
{
address user = msg.sender;
require(
balances[token][user] >= amount,
"INVALID_TRANSFER"
);
balances[token][user] = balances[token][user].sub(amount);
balances[token][to] = balances[token][to].add(amount);
emit Transfer(
token,
user,
to,
amount,
balances[token][user],
balances[token][to]
);
}
}
contract ExchangeUpgradability is Ownable, ExchangeStorage {
using SafeERC20 for IERC20;
using SafeMath for uint256;
/**
* @dev version of the exchange
*/
uint8 constant public VERSION = 1;
/**
* @dev the address of the upgraded exchange contract
*/
address public newExchange;
/**
* @dev flag to allow migrating to an upgraded contract
*/
bool public migrationAllowed;
/**
* @dev emitted when funds are migrated
*/
event FundsMigrated(address indexed user, address indexed newExchange);
/**
* @dev Owner can set the address of the new version of the exchange contract.
*/
function setNewExchangeAddress(address exchange)
external
onlyOwner
{
newExchange = exchange;
}
/**
* @dev Enables/Disables the migrations. Can be called only by the owner.
*/
function allowOrRestrictMigrations()
external
onlyOwner
{
migrationAllowed = !migrationAllowed;
}
/**
* @dev Migrating assets of the caller to the new exchange contract
*/
function migrateFunds(address[] calldata tokens) external {
require(
false != migrationAllowed,
"MIGRATIONS_DISALLOWED"
);
require(
IExchangeUpgradability(newExchange).VERSION() > VERSION,
"INVALID_VERSION"
);
migrateEthers();
migrateTokens(tokens);
emit FundsMigrated(msg.sender, newExchange);
}
/**
* @dev Helper function to migrate user's Ethers. Should be called in migrateFunds() function.
*/
function migrateEthers() private {
address user = msg.sender;
uint256 etherAmount = balances[address(0x0)][user];
if (etherAmount > 0) {
balances[address(0x0)][user] = 0;
IExchangeUpgradability(newExchange).importEthers.value(etherAmount)(user);
}
}
/**
* @dev Helper function to migrate user's tokens. Should be called in migrateFunds() function.
*/
function migrateTokens(address[] memory tokens) private {
address user = msg.sender;
address exchange = newExchange;
for (uint256 index = 0; index < tokens.length; index++) {
address tokenAddress = tokens[index];
uint256 tokenAmount = balances[tokenAddress][user];
if (0 == tokenAmount) {
continue;
}
IERC20(tokenAddress).safeApprove(exchange, tokenAmount);
balances[tokenAddress][user] = 0;
IExchangeUpgradability(exchange).importTokens(tokenAddress, tokenAmount, user);
}
}
/**
* @dev Helper function to migrate user's Ethers. Should be called only from the new exchange contract.
*/
function importEthers(address user)
external
payable
{
require(
false != migrationAllowed,
"MIGRATION_DISALLOWED"
);
require(
user != address(0x0),
"INVALID_USER"
);
require(
msg.value > 0,
"INVALID_AMOUNT"
);
require(
IExchangeUpgradability(msg.sender).VERSION() < VERSION,
"INVALID_VERSION"
);
balances[address(0x0)][user] = balances[address(0x0)][user].add(msg.value); // todo: constants
}
/**
* @dev Helper function to migrate user's Tokens. Should be called only from the new exchange contract.
*/
function importTokens(
address token,
uint256 amount,
address user
)
external
{
require(
false != migrationAllowed,
"MIGRATION_DISALLOWED"
);
require(
token != address(0x0),
"INVALID_TOKEN"
);
require(
user != address(0x0),
"INVALID_USER"
);
require(
amount > 0,
"INVALID_AMOUNT"
);
require(
IExchangeUpgradability(msg.sender).VERSION() < VERSION,
"INVALID_VERSION"
);
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
balances[token][user] = balances[token][user].add(amount);
}
}
contract ExchangeOffering is ExchangeStorage, LibCrowdsale {
address constant internal BURN_ADDRESS = address(0x000000000000000000000000000000000000dEaD);
address constant internal ETH_ADDRESS = address(0x0);
using SafeERC20 for IERC20;
using SafeMath for uint256;
mapping(address => Crowdsale) public crowdsales;
mapping(address => mapping(address => uint256)) public contributions;
event TokenPurchase(
address indexed token,
address indexed user,
uint256 tokenAmount,
uint256 weiAmount
);
event TokenBurned(
address indexed token,
uint256 tokenAmount
);
function registerCrowdsale(
Crowdsale memory crowdsale,
address token
)
public
onlyOwner
{
require(
CrowdsaleStatus.VALID == getCrowdsaleStatus(crowdsale),
"INVALID_CROWDSALE"
);
require(
crowdsales[token].wallet == address(0),
"CROWDSALE_ALREADY_EXISTS"
);
uint256 tokenForSale = crowdsale.hardCap.mul(crowdsale.tokenRatio);
IERC20(token).safeTransferFrom(crowdsale.wallet, address(this), tokenForSale);
crowdsales[token] = crowdsale;
}
function buyTokens(address token)
public
payable
{
require(msg.value != 0, "INVALID_MSG_VALUE");
uint256 weiAmount = msg.value;
address user = msg.sender;
Crowdsale memory crowdsale = crowdsales[token];
require(
ContributionStatus.VALID == validContribution(weiAmount, crowdsale, user, token),
"INVALID_CONTRIBUTION"
);
uint256 purchasedTokens = weiAmount.mul(crowdsale.tokenRatio);
crowdsale.leftAmount = crowdsale.leftAmount.sub(purchasedTokens);
crowdsale.weiRaised = crowdsale.weiRaised.add(weiAmount);
balances[ETH_ADDRESS][crowdsale.wallet] = balances[ETH_ADDRESS][crowdsale.wallet].add(weiAmount);
balances[token][user] = balances[token][user].add(purchasedTokens);
contributions[token][user] = contributions[token][user].add(weiAmount);
crowdsales[token] = crowdsale;
emit TokenPurchase(token, user, purchasedTokens, weiAmount);
}
function burnTokensWhenFinished(address token) public
{
require(
isFinished(crowdsales[token].endBlock),
"CROWDSALE_NOT_FINISHED_YET"
);
uint256 leftAmount = crowdsales[token].leftAmount;
crowdsales[token].leftAmount = 0;
IERC20(token).safeTransfer(BURN_ADDRESS, leftAmount);
emit TokenBurned(token, leftAmount);
}
function validContribution(
uint256 weiAmount,
Crowdsale memory crowdsale,
address user,
address token
)
public
view
returns(ContributionStatus)
{
if (!isOpened(crowdsale.startBlock, crowdsale.endBlock)) {
return ContributionStatus.CROWDSALE_NOT_OPEN;
}
if(weiAmount < crowdsale.minContribution) {
return ContributionStatus.MIN_CONTRIBUTION;
}
if (contributions[token][user].add(weiAmount) > crowdsale.maxContribution) {
return ContributionStatus.MAX_CONTRIBUTION;
}
if (crowdsale.hardCap < crowdsale.weiRaised.add(weiAmount)) {
return ContributionStatus.HARDCAP_REACHED;
}
return ContributionStatus.VALID;
}
}
contract ExchangeSwap is Exchange, ExchangeMovements {
/**
* @dev Swaps ETH/TOKEN, TOKEN/ETH or TOKEN/TOKEN using off-chain signed messages.
* The flow of the function is Deposit -> Trade -> Withdraw to allow users to directly
* take liquidity without the need of deposit and withdraw.
*/
function swapFill(
Order[] memory orders,
bytes[] memory signatures,
uint256 givenAmount,
address givenToken,
address receivedToken,
address referral
)
public
payable
{
address taker = msg.sender;
uint256 balanceGivenBefore = balances[givenToken][taker];
uint256 balanceReceivedBefore = balances[receivedToken][taker];
deposit(givenToken, givenAmount, taker, referral);
for (uint256 index = 0; index < orders.length; index++) {
require(orders[index].makerBuyToken == givenToken, "GIVEN_TOKEN");
require(orders[index].makerSellToken == receivedToken, "RECEIVED_TOKEN");
_trade(orders[index], signatures[index]);
}
uint256 balanceGivenAfter = balances[givenToken][taker];
uint256 balanceReceivedAfter = balances[receivedToken][taker];
uint256 balanceGivenDelta = balanceGivenAfter.sub(balanceGivenBefore);
uint256 balanceReceivedDelta = balanceReceivedAfter.sub(balanceReceivedBefore);
if(balanceGivenDelta > 0) {
withdraw(givenToken, balanceGivenDelta);
}
if(balanceReceivedDelta > 0) {
withdraw(receivedToken, balanceReceivedDelta);
}
}
}
contract WeiDex is
Exchange,
ExchangeKyberProxy,
ExchangeBatchTrade,
ExchangeMovements,
ExchangeUpgradability,
ExchangeOffering,
ExchangeSwap
{
function () external payable { }
}
{
"compilationTarget": {
"WeiDex.sol": "WeiDex"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"newTakerFeeRate","type":"uint256"}],"name":"setTakerFeeRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newExchange","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"partialAmount","type":"uint256"},{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"order","type":"tuple"}],"name":"getOrderInfo","outputs":[{"components":[{"name":"filledAmount","type":"uint256"},{"name":"hash","type":"bytes32"},{"name":"status","type":"uint8"}],"name":"orderInfo","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"givenTokens","type":"address[]"},{"name":"receivedTokens","type":"address[]"},{"name":"givenAmounts","type":"uint256[]"}],"name":"getExpectedRateBatch","outputs":[{"name":"","type":"uint256[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"contributions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"order","type":"tuple"},{"name":"signature","type":"bytes"}],"name":"cancelSingleOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newMakerFeeRate","type":"uint256"}],"name":"setMakerFeeRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"orders","type":"tuple[]"},{"name":"signatures","type":"bytes[]"}],"name":"cancelMultipleOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"orders","type":"tuple[]"},{"name":"signatures","type":"bytes[]"}],"name":"takeAllPossible","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getReferral","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"order","type":"tuple"}],"name":"getHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"newFeeAccount","type":"address"}],"name":"setFeeAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"takerFeeRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"givenAmount","type":"uint256"},{"name":"givenToken","type":"address"},{"name":"receivedToken","type":"address"},{"name":"hash","type":"bytes32"}],"name":"kyberTrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"orders","type":"tuple[]"},{"name":"signatures","type":"bytes[]"},{"name":"givenAmount","type":"uint256"},{"name":"givenToken","type":"address"},{"name":"receivedToken","type":"address"},{"name":"referral","type":"address"}],"name":"swapFill","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"feeAccount","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address[]"}],"name":"getBalances","outputs":[{"name":"balanceArray","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"order","type":"tuple"},{"name":"signature","type":"bytes"}],"name":"trade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"makerFeeRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderHash","type":"bytes32[]"}],"name":"getFills","outputs":[{"name":"filledArray","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"burnTokensWhenFinished","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"user","type":"address"}],"name":"importTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderHash","type":"bytes32[]"}],"name":"getCancels","outputs":[{"name":"cancelledArray","type":"bool[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"exchange","type":"address"}],"name":"setNewExchangeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"orderHash","type":"bytes32"}],"name":"getFill","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"order","type":"tuple"}],"name":"getPrefixedHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"}],"name":"importEthers","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"allowOrRestrictMigrations","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"orderHash","type":"bytes32"}],"name":"getCancel","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokens","type":"address[]"}],"name":"migrateFunds","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"to","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"makerSellAmount","type":"uint256"},{"name":"makerBuyAmount","type":"uint256"},{"name":"takerSellAmount","type":"uint256"},{"name":"salt","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"taker","type":"address"},{"name":"maker","type":"address"},{"name":"makerSellToken","type":"address"},{"name":"makerBuyToken","type":"address"}],"name":"orders","type":"tuple[]"},{"name":"signatures","type":"bytes[]"}],"name":"takeAllOrRevert","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"weiAmount","type":"uint256"},{"components":[{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"hardCap","type":"uint256"},{"name":"leftAmount","type":"uint256"},{"name":"tokenRatio","type":"uint256"},{"name":"minContribution","type":"uint256"},{"name":"maxContribution","type":"uint256"},{"name":"weiRaised","type":"uint256"},{"name":"wallet","type":"address"}],"name":"crowdsale","type":"tuple"},{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"validContribution","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"referral","type":"address"}],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"token","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"crowdsales","outputs":[{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"hardCap","type":"uint256"},{"name":"leftAmount","type":"uint256"},{"name":"tokenRatio","type":"uint256"},{"name":"minContribution","type":"uint256"},{"name":"maxContribution","type":"uint256"},{"name":"weiRaised","type":"uint256"},{"name":"wallet","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"buyTokens","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"migrationAllowed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"givenAmount","type":"uint256"},{"name":"givenToken","type":"address"},{"name":"receivedToken","type":"address"},{"name":"hash","type":"bytes32"}],"name":"kyberSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"VERSION","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"hardCap","type":"uint256"},{"name":"leftAmount","type":"uint256"},{"name":"tokenRatio","type":"uint256"},{"name":"minContribution","type":"uint256"},{"name":"maxContribution","type":"uint256"},{"name":"weiRaised","type":"uint256"},{"name":"wallet","type":"address"}],"name":"crowdsale","type":"tuple"},{"name":"token","type":"address"}],"name":"registerCrowdsale","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"components":[{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"hardCap","type":"uint256"},{"name":"leftAmount","type":"uint256"},{"name":"tokenRatio","type":"uint256"},{"name":"minContribution","type":"uint256"},{"name":"maxContribution","type":"uint256"},{"name":"weiRaised","type":"uint256"},{"name":"wallet","type":"address"}],"name":"crowdsale","type":"tuple"}],"name":"getCrowdsaleStatus","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"weiAmount","type":"uint256"}],"name":"TokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"}],"name":"TokenBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"newExchange","type":"address"}],"name":"FundsMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"referral","type":"address"},{"indexed":false,"name":"beneficiary","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"balance","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"balance","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"beneficiary","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"userBalance","type":"uint256"},{"indexed":false,"name":"beneficiaryBalance","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"makerAddress","type":"address"},{"indexed":true,"name":"takerAddress","type":"address"},{"indexed":true,"name":"orderHash","type":"bytes32"},{"indexed":false,"name":"makerFilledAsset","type":"address"},{"indexed":false,"name":"takerFilledAsset","type":"address"},{"indexed":false,"name":"makerFilledAmount","type":"uint256"},{"indexed":false,"name":"takerFilledAmount","type":"uint256"},{"indexed":false,"name":"takerFeePaid","type":"uint256"},{"indexed":false,"name":"makerFeeReceived","type":"uint256"},{"indexed":false,"name":"referralFeeReceived","type":"uint256"}],"name":"Trade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"makerBuyToken","type":"address"},{"indexed":false,"name":"makerSellToken","type":"address"},{"indexed":true,"name":"maker","type":"address"},{"indexed":true,"name":"orderHash","type":"bytes32"}],"name":"Cancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]