Transaction Hash:
Block:
6690217 at Nov-12-2018 10:41:29 AM +UTC
Transaction Fee:
0.000850355 ETH
$1.94
Gas Used:
77,305 Gas / 11 Gwei
Emitted Events:
85 |
PolicyPalNetworkToken.Transfer( from=[Sender] 0xc64dee63dde48588f3061c5724e19a30a471df46, to=[Receiver] Dex2, value=9150000000000000000000 )
|
86 |
Dex2.DepositEvent( trader=0xF056D004...F360945ea, tokenCode=107, symbol=PAL, amountE8=915000000000, depositIndex=13921 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x7600977E...E11566341 | (DEx.top) | ||||
0xb2930B35...e543a0347
Miner
| (MiningPoolHub: Old Address) | 17,021.485286969231088876 Eth | 17,021.486137324231088876 Eth | 0.000850355 | |
0xC64deE63...0a471Df46 |
0.4351671457 Eth
Nonce: 9870
|
0.4343167907 Eth
Nonce: 9871
| 0.000850355 | ||
0xfeDAE564...215F942EE |
Execution Trace
Dex2.depositToken( traderAddr=0xF056D00411cc826E6fCa7D2D280A8AEF360945ea, tokenCode=107, originalAmount=9150000000000000000000 )
-
PolicyPalNetworkToken.transferFrom( _from=0xC64deE63DdE48588F3061c5724E19a30a471Df46, _to=0x7600977Eb9eFFA627D6BD0DA2E5be35E11566341, _value=9150000000000000000000 ) => ( True )
depositToken[Dex2 (ln:171)]
revert[Dex2 (ln:172)]
revert[Dex2 (ln:173)]
revert[Dex2 (ln:174)]
revert[Dex2 (ln:175)]
revert[Dex2 (ln:178)]
revert[Dex2 (ln:179)]
transferFrom[Dex2 (ln:182)]
revert[Dex2 (ln:182)]
revert[Dex2 (ln:184)]
revert[Dex2 (ln:186)]
setDeposits[Dex2 (ln:189)]
DepositEvent[Dex2 (ln:190)]
File 1 of 2: Dex2
File 2 of 2: PolicyPalNetworkToken
// DEx.top - Instant Trading on Chain // // Author: DEx.top Team pragma solidity 0.4.21; pragma experimental "v0.5.0"; interface Token { function transfer(address to, uint256 value) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); } contract Dex2 { //------------------------------ Struct Definitions: --------------------------------------------- struct TokenInfo { string symbol; // e.g., "ETH", "ADX" address tokenAddr; // ERC20 token address uint64 scaleFactor; // <original token amount> = <scaleFactor> x <DEx amountE8> / 1e8 uint minDeposit; // mininum deposit (original token amount) allowed for this token } struct TraderInfo { address withdrawAddr; uint8 feeRebatePercent; // range: [0, 100] } struct TokenAccount { uint64 balanceE8; // available amount for trading uint64 pendingWithdrawE8; // the amount to be transferred out from this contract to the trader } struct Order { uint32 pairId; // <cashId>(16) <stockId>(16) uint8 action; // 0 means BUY; 1 means SELL uint8 ioc; // 0 means a regular order; 1 means an immediate-or-cancel (IOC) order uint64 priceE8; uint64 amountE8; uint64 expireTimeSec; } struct Deposit { address traderAddr; uint16 tokenCode; uint64 pendingAmountE8; // amount to be confirmed for trading purpose } struct DealInfo { uint16 stockCode; // stock token code uint16 cashCode; // cash token code uint64 stockDealAmountE8; uint64 cashDealAmountE8; } struct ExeStatus { uint64 logicTimeSec; // logic timestamp for checking order expiration uint64 lastOperationIndex; // index of the last executed operation } //----------------- Constants: ------------------------------------------------------------------- uint constant MAX_UINT256 = 2**256 - 1; uint16 constant MAX_FEE_RATE_E4 = 60; // upper limit of fee rate is 0.6% (60 / 1e4) // <original ETH amount in Wei> = <DEx amountE8> * <ETH_SCALE_FACTOR> / 1e8 uint64 constant ETH_SCALE_FACTOR = 10**18; uint8 constant ACTIVE = 0; uint8 constant CLOSED = 2; bytes32 constant HASHTYPES = keccak256('string title', 'address market_address', 'uint64 nonce', 'uint64 expire_time_sec', 'uint64 amount_e8', 'uint64 price_e8', 'uint8 immediate_or_cancel', 'uint8 action', 'uint16 cash_token_code', 'uint16 stock_token_code'); //----------------- States that cannot be changed once set: -------------------------------------- address public admin; // admin address, and it cannot be changed mapping (uint16 => TokenInfo) public tokens; // mapping of token code to token information //----------------- Other states: ---------------------------------------------------------------- uint8 public marketStatus; // market status: 0 - Active; 1 - Suspended; 2 - Closed uint16 public makerFeeRateE4; // maker fee rate (* 10**4) uint16 public takerFeeRateE4; // taker fee rate (* 10**4) uint16 public withdrawFeeRateE4; // withdraw fee rate (* 10**4) uint64 public lastDepositIndex; // index of the last deposit operation ExeStatus public exeStatus; // status of operation execution mapping (address => TraderInfo) public traders; // mapping of trade address to trader information mapping (uint176 => TokenAccount) public accounts; // mapping of trader token key to its account information mapping (uint224 => Order) public orders; // mapping of order key to order information mapping (uint64 => Deposit) public deposits; // mapping of deposit index to deposit information //------------------------------ Dex2 Events: ---------------------------------------------------- event DeployMarketEvent(); event ChangeMarketStatusEvent(uint8 status); event SetTokenInfoEvent(uint16 tokenCode, string symbol, address tokenAddr, uint64 scaleFactor, uint minDeposit); event SetWithdrawAddrEvent(address trader, address withdrawAddr); event DepositEvent(address trader, uint16 tokenCode, string symbol, uint64 amountE8, uint64 depositIndex); event WithdrawEvent(address trader, uint16 tokenCode, string symbol, uint64 amountE8, uint64 lastOpIndex); event TransferFeeEvent(uint16 tokenCode, uint64 amountE8, address toAddr); // `balanceE8` is the total balance after this deposit confirmation event ConfirmDepositEvent(address trader, uint16 tokenCode, uint64 balanceE8); // `amountE8` is the post-fee initiated withdraw amount // `pendingWithdrawE8` is the total pending withdraw amount after this withdraw initiation event InitiateWithdrawEvent(address trader, uint16 tokenCode, uint64 amountE8, uint64 pendingWithdrawE8); event MatchOrdersEvent(address trader1, uint64 nonce1, address trader2, uint64 nonce2); event HardCancelOrderEvent(address trader, uint64 nonce); event SetFeeRatesEvent(uint16 makerFeeRateE4, uint16 takerFeeRateE4, uint16 withdrawFeeRateE4); event SetFeeRebatePercentEvent(address trader, uint8 feeRebatePercent); //------------------------------ Contract Initialization: ---------------------------------------- function Dex2(address admin_) public { admin = admin_; setTokenInfo(0 /*tokenCode*/, "ETH", 0 /*tokenAddr*/, ETH_SCALE_FACTOR, 0 /*minDeposit*/); emit DeployMarketEvent(); } //------------------------------ External Functions: --------------------------------------------- function() external { revert(); } // Change the market status of DEX. function changeMarketStatus(uint8 status_) external { if (msg.sender != admin) revert(); if (marketStatus == CLOSED) revert(); // closed is forever marketStatus = status_; emit ChangeMarketStatusEvent(status_); } // Each trader can specify a withdraw address (but cannot change it later). Once a trader's // withdraw address is set, following withdrawals of this trader will go to the withdraw address // instead of the trader's address. function setWithdrawAddr(address withdrawAddr) external { if (withdrawAddr == 0) revert(); if (traders[msg.sender].withdrawAddr != 0) revert(); // cannot change withdrawAddr once set traders[msg.sender].withdrawAddr = withdrawAddr; emit SetWithdrawAddrEvent(msg.sender, withdrawAddr); } // Deposit ETH from msg.sender for the given trader. function depositEth(address traderAddr) external payable { if (marketStatus != ACTIVE) revert(); if (traderAddr == 0) revert(); if (msg.value < tokens[0].minDeposit) revert(); if (msg.data.length != 4 + 32) revert(); // length condition of param count uint64 pendingAmountE8 = uint64(msg.value / (ETH_SCALE_FACTOR / 10**8)); // msg.value is in Wei if (pendingAmountE8 == 0) revert(); uint64 depositIndex = ++lastDepositIndex; setDeposits(depositIndex, traderAddr, 0, pendingAmountE8); emit DepositEvent(traderAddr, 0, "ETH", pendingAmountE8, depositIndex); } // Deposit token (other than ETH) from msg.sender for a specified trader. // // After the deposit has been confirmed enough times on the blockchain, it will be added to the // trader's token account for trading. function depositToken(address traderAddr, uint16 tokenCode, uint originalAmount) external { if (marketStatus != ACTIVE) revert(); if (traderAddr == 0) revert(); if (tokenCode == 0) revert(); // this function does not handle ETH if (msg.data.length != 4 + 32 + 32 + 32) revert(); // length condition of param count TokenInfo memory tokenInfo = tokens[tokenCode]; if (originalAmount < tokenInfo.minDeposit) revert(); if (tokenInfo.scaleFactor == 0) revert(); // unsupported token // Need to make approval by calling Token(address).approve() in advance for ERC-20 Tokens. if (!Token(tokenInfo.tokenAddr).transferFrom(msg.sender, this, originalAmount)) revert(); if (originalAmount > MAX_UINT256 / 10**8) revert(); // avoid overflow uint amountE8 = originalAmount * 10**8 / uint(tokenInfo.scaleFactor); if (amountE8 >= 2**64 || amountE8 == 0) revert(); uint64 depositIndex = ++lastDepositIndex; setDeposits(depositIndex, traderAddr, tokenCode, uint64(amountE8)); emit DepositEvent(traderAddr, tokenCode, tokens[tokenCode].symbol, uint64(amountE8), depositIndex); } // Withdraw ETH from the contract. function withdrawEth(address traderAddr) external { if (traderAddr == 0) revert(); if (msg.data.length != 4 + 32) revert(); // length condition of param count uint176 accountKey = uint176(traderAddr); uint amountE8 = accounts[accountKey].pendingWithdrawE8; if (amountE8 == 0) return; // Write back to storage before making the transfer. accounts[accountKey].pendingWithdrawE8 = 0; uint truncatedWei = amountE8 * (ETH_SCALE_FACTOR / 10**8); address withdrawAddr = traders[traderAddr].withdrawAddr; if (withdrawAddr == 0) withdrawAddr = traderAddr; withdrawAddr.transfer(truncatedWei); emit WithdrawEvent(traderAddr, 0, "ETH", uint64(amountE8), exeStatus.lastOperationIndex); } // Withdraw token (other than ETH) from the contract. function withdrawToken(address traderAddr, uint16 tokenCode) external { if (traderAddr == 0) revert(); if (tokenCode == 0) revert(); // this function does not handle ETH if (msg.data.length != 4 + 32 + 32) revert(); // length condition of param count TokenInfo memory tokenInfo = tokens[tokenCode]; if (tokenInfo.scaleFactor == 0) revert(); // unsupported token uint176 accountKey = uint176(tokenCode) << 160 | uint176(traderAddr); uint amountE8 = accounts[accountKey].pendingWithdrawE8; if (amountE8 == 0) return; // Write back to storage before making the transfer. accounts[accountKey].pendingWithdrawE8 = 0; uint truncatedAmount = amountE8 * uint(tokenInfo.scaleFactor) / 10**8; address withdrawAddr = traders[traderAddr].withdrawAddr; if (withdrawAddr == 0) withdrawAddr = traderAddr; if (!Token(tokenInfo.tokenAddr).transfer(withdrawAddr, truncatedAmount)) revert(); emit WithdrawEvent(traderAddr, tokenCode, tokens[tokenCode].symbol, uint64(amountE8), exeStatus.lastOperationIndex); } // Transfer the collected fee out of the contract. function transferFee(uint16 tokenCode, uint64 amountE8, address toAddr) external { if (msg.sender != admin) revert(); if (toAddr == 0) revert(); if (msg.data.length != 4 + 32 + 32 + 32) revert(); TokenAccount memory feeAccount = accounts[uint176(tokenCode) << 160]; uint64 withdrawE8 = feeAccount.pendingWithdrawE8; if (amountE8 < withdrawE8) { withdrawE8 = amountE8; } feeAccount.pendingWithdrawE8 -= withdrawE8; accounts[uint176(tokenCode) << 160] = feeAccount; TokenInfo memory tokenInfo = tokens[tokenCode]; uint originalAmount = uint(withdrawE8) * uint(tokenInfo.scaleFactor) / 10**8; if (tokenCode == 0) { // ETH toAddr.transfer(originalAmount); } else { if (!Token(tokenInfo.tokenAddr).transfer(toAddr, originalAmount)) revert(); } emit TransferFeeEvent(tokenCode, withdrawE8, toAddr); } // Replay the trading sequence from the off-chain ledger exactly onto the on-chain ledger. function exeSequence(uint header, uint[] body) external { if (msg.sender != admin) revert(); uint64 nextOperationIndex = uint64(header); if (nextOperationIndex != exeStatus.lastOperationIndex + 1) revert(); // check sequence index uint64 newLogicTimeSec = uint64(header >> 64); if (newLogicTimeSec < exeStatus.logicTimeSec) revert(); for (uint i = 0; i < body.length; nextOperationIndex++) { uint bits = body[i]; uint opcode = bits & 0xFFFF; bits >>= 16; if ((opcode >> 8) != 0xDE) revert(); // check the magic number // ConfirmDeposit: <depositIndex>(64) if (opcode == 0xDE01) { confirmDeposit(uint64(bits)); i += 1; continue; } // InitiateWithdraw: <amountE8>(64) <tokenCode>(16) <traderAddr>(160) if (opcode == 0xDE02) { initiateWithdraw(uint176(bits), uint64(bits >> 176)); i += 1; continue; } //-------- The rest operation types are allowed only when the market is active --------- if (marketStatus != ACTIVE) revert(); // MatchOrders if (opcode == 0xDE03) { uint8 v1 = uint8(bits); bits >>= 8; // bits is now the key of the maker order Order memory makerOrder; if (v1 == 0) { // order already in storage if (i + 1 >= body.length) revert(); // at least 1 body element left makerOrder = orders[uint224(bits)]; i += 1; } else { if (orders[uint224(bits)].pairId != 0) revert(); // order must not be already in storage if (i + 4 >= body.length) revert(); // at least 4 body elements left makerOrder = parseNewOrder(uint224(bits) /*makerOrderKey*/, v1, body, i); i += 4; } uint8 v2 = uint8(body[i]); uint224 takerOrderKey = uint224(body[i] >> 8); Order memory takerOrder; if (v2 == 0) { // order already in storage takerOrder = orders[takerOrderKey]; i += 1; } else { if (orders[takerOrderKey].pairId != 0) revert(); // order must not be already in storage if (i + 3 >= body.length) revert(); // at least 3 body elements left takerOrder = parseNewOrder(takerOrderKey, v2, body, i); i += 4; } matchOrder(uint224(bits) /*makerOrderKey*/, makerOrder, takerOrderKey, takerOrder); continue; } // HardCancelOrder: <nonce>(64) <traderAddr>(160) if (opcode == 0xDE04) { hardCancelOrder(uint224(bits) /*orderKey*/); i += 1; continue; } // SetFeeRates: <withdrawFeeRateE4>(16) <takerFeeRateE4>(16) <makerFeeRateE4>(16) if (opcode == 0xDE05) { setFeeRates(uint16(bits), uint16(bits >> 16), uint16(bits >> 32)); i += 1; continue; } // SetFeeRebatePercent: <rebatePercent>(8) <traderAddr>(160) if (opcode == 0xDE06) { setFeeRebatePercent(address(bits) /*traderAddr*/, uint8(bits >> 160) /*rebatePercent*/); i += 1; continue; } } // for loop setExeStatus(newLogicTimeSec, nextOperationIndex - 1); } // function exeSequence //------------------------------ Public Functions: ----------------------------------------------- // Set information of a token. function setTokenInfo(uint16 tokenCode, string symbol, address tokenAddr, uint64 scaleFactor, uint minDeposit) public { if (msg.sender != admin) revert(); if (marketStatus != ACTIVE) revert(); if (scaleFactor == 0) revert(); TokenInfo memory info = tokens[tokenCode]; if (info.scaleFactor != 0) { // this token already exists // For an existing token only the minDeposit field can be updated. tokens[tokenCode].minDeposit = minDeposit; emit SetTokenInfoEvent(tokenCode, info.symbol, info.tokenAddr, info.scaleFactor, minDeposit); return; } tokens[tokenCode].symbol = symbol; tokens[tokenCode].tokenAddr = tokenAddr; tokens[tokenCode].scaleFactor = scaleFactor; tokens[tokenCode].minDeposit = minDeposit; emit SetTokenInfoEvent(tokenCode, symbol, tokenAddr, scaleFactor, minDeposit); } //------------------------------ Private Functions: ---------------------------------------------- function setDeposits(uint64 depositIndex, address traderAddr, uint16 tokenCode, uint64 amountE8) private { deposits[depositIndex].traderAddr = traderAddr; deposits[depositIndex].tokenCode = tokenCode; deposits[depositIndex].pendingAmountE8 = amountE8; } function setExeStatus(uint64 logicTimeSec, uint64 lastOperationIndex) private { exeStatus.logicTimeSec = logicTimeSec; exeStatus.lastOperationIndex = lastOperationIndex; } function confirmDeposit(uint64 depositIndex) private { Deposit memory deposit = deposits[depositIndex]; uint176 accountKey = (uint176(deposit.tokenCode) << 160) | uint176(deposit.traderAddr); TokenAccount memory account = accounts[accountKey]; // Check that pending amount is non-zero and no overflow would happen. if (account.balanceE8 + deposit.pendingAmountE8 <= account.balanceE8) revert(); account.balanceE8 += deposit.pendingAmountE8; deposits[depositIndex].pendingAmountE8 = 0; accounts[accountKey].balanceE8 += deposit.pendingAmountE8; emit ConfirmDepositEvent(deposit.traderAddr, deposit.tokenCode, account.balanceE8); } function initiateWithdraw(uint176 tokenAccountKey, uint64 amountE8) private { uint64 balanceE8 = accounts[tokenAccountKey].balanceE8; uint64 pendingWithdrawE8 = accounts[tokenAccountKey].pendingWithdrawE8; if (balanceE8 < amountE8 || amountE8 == 0) revert(); balanceE8 -= amountE8; uint64 feeE8 = calcFeeE8(amountE8, withdrawFeeRateE4, address(tokenAccountKey)); amountE8 -= feeE8; if (pendingWithdrawE8 + amountE8 < amountE8) revert(); // check overflow pendingWithdrawE8 += amountE8; accounts[tokenAccountKey].balanceE8 = balanceE8; accounts[tokenAccountKey].pendingWithdrawE8 = pendingWithdrawE8; // Note that the fee account has a dummy trader address of 0. if (accounts[tokenAccountKey & (0xffff << 160)].pendingWithdrawE8 + feeE8 >= feeE8) { // no overflow accounts[tokenAccountKey & (0xffff << 160)].pendingWithdrawE8 += feeE8; } emit InitiateWithdrawEvent(address(tokenAccountKey), uint16(tokenAccountKey >> 160) /*tokenCode*/, amountE8, pendingWithdrawE8); } function getDealInfo(uint32 pairId, uint64 priceE8, uint64 amount1E8, uint64 amount2E8) private pure returns (DealInfo deal) { deal.stockCode = uint16(pairId); deal.cashCode = uint16(pairId >> 16); if (deal.stockCode == deal.cashCode) revert(); // we disallow homogeneous trading deal.stockDealAmountE8 = amount1E8 < amount2E8 ? amount1E8 : amount2E8; uint cashDealAmountE8 = uint(priceE8) * uint(deal.stockDealAmountE8) / 10**8; if (cashDealAmountE8 >= 2**64) revert(); deal.cashDealAmountE8 = uint64(cashDealAmountE8); } function calcFeeE8(uint64 amountE8, uint feeRateE4, address traderAddr) private view returns (uint64) { uint feeE8 = uint(amountE8) * feeRateE4 / 10000; feeE8 -= feeE8 * uint(traders[traderAddr].feeRebatePercent) / 100; return uint64(feeE8); } function settleAccounts(DealInfo deal, address traderAddr, uint feeRateE4, bool isBuyer) private { uint16 giveTokenCode = isBuyer ? deal.cashCode : deal.stockCode; uint16 getTokenCode = isBuyer ? deal.stockCode : deal.cashCode; uint64 giveAmountE8 = isBuyer ? deal.cashDealAmountE8 : deal.stockDealAmountE8; uint64 getAmountE8 = isBuyer ? deal.stockDealAmountE8 : deal.cashDealAmountE8; uint176 giveAccountKey = uint176(giveTokenCode) << 160 | uint176(traderAddr); uint176 getAccountKey = uint176(getTokenCode) << 160 | uint176(traderAddr); uint64 feeE8 = calcFeeE8(getAmountE8, feeRateE4, traderAddr); getAmountE8 -= feeE8; // Check overflow. if (accounts[giveAccountKey].balanceE8 < giveAmountE8) revert(); if (accounts[getAccountKey].balanceE8 + getAmountE8 < getAmountE8) revert(); // Write storage. accounts[giveAccountKey].balanceE8 -= giveAmountE8; accounts[getAccountKey].balanceE8 += getAmountE8; if (accounts[uint176(getTokenCode) << 160].pendingWithdrawE8 + feeE8 >= feeE8) { // no overflow accounts[uint176(getTokenCode) << 160].pendingWithdrawE8 += feeE8; } } function setOrders(uint224 orderKey, uint32 pairId, uint8 action, uint8 ioc, uint64 priceE8, uint64 amountE8, uint64 expireTimeSec) private { orders[orderKey].pairId = pairId; orders[orderKey].action = action; orders[orderKey].ioc = ioc; orders[orderKey].priceE8 = priceE8; orders[orderKey].amountE8 = amountE8; orders[orderKey].expireTimeSec = expireTimeSec; } function matchOrder(uint224 makerOrderKey, Order makerOrder, uint224 takerOrderKey, Order takerOrder) private { // Check trading conditions. if (marketStatus != ACTIVE) revert(); if (makerOrderKey == takerOrderKey) revert(); // the two orders must not have the same key if (makerOrder.pairId != takerOrder.pairId) revert(); if (makerOrder.action == takerOrder.action) revert(); if (makerOrder.priceE8 == 0 || takerOrder.priceE8 == 0) revert(); if (makerOrder.action == 0 && makerOrder.priceE8 < takerOrder.priceE8) revert(); if (takerOrder.action == 0 && takerOrder.priceE8 < makerOrder.priceE8) revert(); if (makerOrder.amountE8 == 0 || takerOrder.amountE8 == 0) revert(); if (makerOrder.expireTimeSec <= exeStatus.logicTimeSec) revert(); if (takerOrder.expireTimeSec <= exeStatus.logicTimeSec) revert(); DealInfo memory deal = getDealInfo( makerOrder.pairId, makerOrder.priceE8, makerOrder.amountE8, takerOrder.amountE8); // Update accounts. settleAccounts(deal, address(makerOrderKey), makerFeeRateE4, (makerOrder.action == 0)); settleAccounts(deal, address(takerOrderKey), takerFeeRateE4, (takerOrder.action == 0)); // Update orders. if (makerOrder.ioc == 1) { // IOC order makerOrder.amountE8 = 0; } else { makerOrder.amountE8 -= deal.stockDealAmountE8; } if (takerOrder.ioc == 1) { // IOC order takerOrder.amountE8 = 0; } else { takerOrder.amountE8 -= deal.stockDealAmountE8; } // Write orders back to storage. setOrders(makerOrderKey, makerOrder.pairId, makerOrder.action, makerOrder.ioc, makerOrder.priceE8, makerOrder.amountE8, makerOrder.expireTimeSec); setOrders(takerOrderKey, takerOrder.pairId, takerOrder.action, takerOrder.ioc, takerOrder.priceE8, takerOrder.amountE8, takerOrder.expireTimeSec); emit MatchOrdersEvent(address(makerOrderKey), uint64(makerOrderKey >> 160) /*nonce*/, address(takerOrderKey), uint64(takerOrderKey >> 160) /*nonce*/); } function hardCancelOrder(uint224 orderKey) private { orders[orderKey].pairId = 0xFFFFFFFF; orders[orderKey].amountE8 = 0; emit HardCancelOrderEvent(address(orderKey) /*traderAddr*/, uint64(orderKey >> 160) /*nonce*/); } function setFeeRates(uint16 makerE4, uint16 takerE4, uint16 withdrawE4) private { if (makerE4 > MAX_FEE_RATE_E4) revert(); if (takerE4 > MAX_FEE_RATE_E4) revert(); if (withdrawE4 > MAX_FEE_RATE_E4) revert(); makerFeeRateE4 = makerE4; takerFeeRateE4 = takerE4; withdrawFeeRateE4 = withdrawE4; emit SetFeeRatesEvent(makerE4, takerE4, withdrawE4); } function setFeeRebatePercent(address traderAddr, uint8 feeRebatePercent) private { if (feeRebatePercent > 100) revert(); traders[traderAddr].feeRebatePercent = feeRebatePercent; emit SetFeeRebatePercentEvent(traderAddr, feeRebatePercent); } function parseNewOrder(uint224 orderKey, uint8 v, uint[] body, uint i) private view returns (Order) { // bits: <expireTimeSec>(64) <amountE8>(64) <priceE8>(64) <ioc>(8) <action>(8) <pairId>(32) uint240 bits = uint240(body[i + 1]); uint64 nonce = uint64(orderKey >> 160); address traderAddr = address(orderKey); if (traderAddr == 0) revert(); // check zero addr early since `ecrecover` returns 0 on error // verify the signature of the trader bytes32 hash1 = keccak256("\x19Ethereum Signed Message:\n70DEx2 Order: ", address(this), nonce, bits); if (traderAddr != ecrecover(hash1, v, bytes32(body[i + 2]), bytes32(body[i + 3]))) { bytes32 hashValues = keccak256("DEx2 Order", address(this), nonce, bits); bytes32 hash2 = keccak256(HASHTYPES, hashValues); if (traderAddr != ecrecover(hash2, v, bytes32(body[i + 2]), bytes32(body[i + 3]))) revert(); } Order memory order; order.pairId = uint32(bits); bits >>= 32; order.action = uint8(bits); bits >>= 8; order.ioc = uint8(bits); bits >>= 8; order.priceE8 = uint64(bits); bits >>= 64; order.amountE8 = uint64(bits); bits >>= 64; order.expireTimeSec = uint64(bits); return order; } } // contract
File 2 of 2: PolicyPalNetworkToken
pragma solidity ^0.4.18; // File: contracts/zeppelin/math/SafeMath.sol /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ 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; } /** * @dev Integer division of two numbers, truncating the quotient. */ 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; } /** * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } // File: contracts/CrowdsaleAuthorizer.sol /** * @title CrowdsaleAuthorizer * @dev Crowd Sale Authorizer */ contract CrowdsaleAuthorizer { mapping(address => uint256) public participated; mapping(address => bool) public whitelistAddresses; address public admin; uint256 public saleStartTime; uint256 public saleEndTime; uint256 public increaseMaxContribTime; uint256 public minContribution; uint256 public maxContribution; using SafeMath for uint256; /** * @dev Modifier for only admin */ modifier onlyAdmin() { require(msg.sender == admin); _; } /** * @dev Modifier for valid address */ modifier validAddress(address _addr) { require(_addr != address(0x0)); require(_addr != address(this)); _; } /** * @dev Contract Constructor * @param _saleStartTime - The Start Time of the Token Sale * @param _saleEndTime - The End Time of the Token Sale * @param _increaseMaxContribTime - Time to increase Max Contribution of the Token Sale * @param _minContribution - Minimum ETH contribution per contributor * @param _maxContribution - Maximum ETH contribution per contributor */ function CrowdsaleAuthorizer( address _admin, uint256 _saleStartTime, uint256 _saleEndTime, uint256 _increaseMaxContribTime, uint256 _minContribution, uint256 _maxContribution ) validAddress(_admin) public { require(_saleStartTime > now); require(_saleEndTime > now); require(_increaseMaxContribTime > now); require(_saleStartTime < _saleEndTime); require(_increaseMaxContribTime > _saleStartTime); require(_maxContribution > 0); require(_minContribution < _maxContribution); admin = _admin; saleStartTime = _saleStartTime; saleEndTime = _saleEndTime; increaseMaxContribTime = _increaseMaxContribTime; minContribution = _minContribution; maxContribution = _maxContribution; } event UpdateWhitelist(address _user, bool _allow, uint _time); /** * @dev Update Whitelist Address * @param _user - Whitelist address * @param _allow - eligibility */ function updateWhitelist(address _user, bool _allow) public onlyAdmin { whitelistAddresses[_user] = _allow; UpdateWhitelist(_user, _allow, now); } /** * @dev Batch Update Whitelist Address * @param _users - Array of Whitelist addresses * @param _allows - Array of eligibilities */ function updateWhitelists(address[] _users, bool[] _allows) external onlyAdmin { require(_users.length == _allows.length); for (uint i = 0 ; i < _users.length ; i++) { address _user = _users[i]; bool _allow = _allows[i]; whitelistAddresses[_user] = _allow; UpdateWhitelist(_user, _allow, now); } } /** * @dev Get Eligible Amount * @param _contributor - Contributor address * @param _amount - Intended contribution amount */ function eligibleAmount(address _contributor, uint256 _amount) public view returns(uint256) { // If sales has not started or sale ended, there's no allocation if (!saleStarted() || saleEnded()) { return 0; } // Amount lesser than minimum contribution will be rejected if (_amount < minContribution) { return 0; } uint256 userMaxContribution = maxContribution; // If sale has past 24hrs, increase max cap if (now >= increaseMaxContribTime) { userMaxContribution = maxContribution.mul(10); } // Calculate remaining contribution for the contributor uint256 remainingCap = userMaxContribution.sub(participated[_contributor]); // Return either the amount contributed or cap whichever is lower return (remainingCap > _amount) ? _amount : remainingCap; } /** * @dev Get if sale has started */ function saleStarted() public view returns(bool) { return now >= saleStartTime; } /** * @dev Get if sale has ended */ function saleEnded() public view returns(bool) { return now > saleEndTime; } /** * @dev Check for eligible amount and modify participation map * @param _contributor - Contributor address * @param _amount - Intended contribution amount */ function eligibleAmountCheck(address _contributor, uint256 _amount) internal returns(uint256) { // Check if contributor is whitelisted if (!whitelistAddresses[_contributor]) { return 0; } uint256 result = eligibleAmount(_contributor, _amount); participated[_contributor] = participated[_contributor].add(result); return result; } } // File: contracts/zeppelin/ownership/Ownable.sol /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ 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; } } // File: contracts/zeppelin/token/ERC20Basic.sol /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/179 */ contract ERC20Basic { function totalSupply() public view returns (uint256); 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); } // File: contracts/zeppelin/token/BasicToken.sol /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is ERC20Basic { using SafeMath for uint256; mapping(address => uint256) balances; uint256 totalSupply_; /** * @dev total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @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]; } } // File: contracts/zeppelin/token/BurnableToken.sol /** * @title Burnable Token * @dev Token that can be irreversibly burned (destroyed). */ contract BurnableToken is BasicToken { 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 <= 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); } } // File: contracts/zeppelin/token/ERC20.sol /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ 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); } // File: contracts/zeppelin/token/StandardToken.sol /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * @dev https://github.com/ethereum/EIPs/issues/20 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ 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]; } /** * @dev Increase the amount of tokens that an owner allowed to a 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 * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ 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; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * * approve should be called when allowed[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param _spender The address which will spend the funds. * @param _subtractedValue The amount of tokens to decrease the allowance by. */ 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; } } // File: contracts/PolicyPalNetworkToken.sol /** * @title PolicyPalNetwork Token * @dev A standard ownable token */ contract PolicyPalNetworkToken is StandardToken, BurnableToken, Ownable { /** * @dev Token Contract Constants */ string public constant name = "PolicyPal Network Token"; string public constant symbol = "PAL"; uint8 public constant decimals = 18; /** * @dev Token Contract Public Variables */ address public tokenSaleContract; bool public isTokenTransferable = false; /** * @dev Token Contract Modifier * * Check if a transfer is allowed * Transfers are restricted to token creator & owner(admin) during token sale duration * Transfers after token sale is limited by `isTokenTransferable` toggle * */ modifier onlyWhenTransferAllowed() { require(isTokenTransferable || msg.sender == owner || msg.sender == tokenSaleContract); _; } /** * @dev Token Contract Modifier * @param _to - Address to check if valid * * Check if an address is valid * A valid address is as follows, * 1. Not zero address * 2. Not token address * */ modifier isValidDestination(address _to) { require(_to != address(0x0)); require(_to != address(this)); _; } /** * @dev Enable Transfers (Only Owner) */ function toggleTransferable(bool _toggle) external onlyOwner { isTokenTransferable = _toggle; } /** * @dev Token Contract Constructor * @param _adminAddr - Address of the Admin */ function PolicyPalNetworkToken( uint _tokenTotalAmount, address _adminAddr ) public isValidDestination(_adminAddr) { require(_tokenTotalAmount > 0); totalSupply_ = _tokenTotalAmount; // Mint all token balances[msg.sender] = _tokenTotalAmount; Transfer(address(0x0), msg.sender, _tokenTotalAmount); // Assign token sale contract to creator tokenSaleContract = msg.sender; // Transfer contract ownership to admin transferOwnership(_adminAddr); } /** * @dev Token Contract transfer * @param _to - Address to transfer to * @param _value - Value to transfer * @return bool - Result of transfer * "Overloaded" Function of ERC20Basic's transfer * */ function transfer(address _to, uint256 _value) public onlyWhenTransferAllowed isValidDestination(_to) returns (bool) { return super.transfer(_to, _value); } /** * @dev Token Contract transferFrom * @param _from - Address to transfer from * @param _to - Address to transfer to * @param _value - Value to transfer * @return bool - Result of transferFrom * * "Overloaded" Function of ERC20's transferFrom * Added with modifiers, * 1. onlyWhenTransferAllowed * 2. isValidDestination * */ function transferFrom(address _from, address _to, uint256 _value) public onlyWhenTransferAllowed isValidDestination(_to) returns (bool) { return super.transferFrom(_from, _to, _value); } /** * @dev Token Contract burn * @param _value - Value to burn * "Overloaded" Function of BurnableToken's burn */ function burn(uint256 _value) public { super.burn(_value); Transfer(msg.sender, address(0x0), _value); } /** * @dev Token Contract Emergency Drain * @param _token - Token to drain * @param _amount - Amount to drain */ function emergencyERC20Drain(ERC20 _token, uint256 _amount) public onlyOwner { _token.transfer(owner, _amount); } } // File: contracts/PolicyPalNetworkCrowdsale.sol /** * @title PPN Crowdsale * @dev Crowd Sale Contract */ contract PolicyPalNetworkCrowdsale is CrowdsaleAuthorizer { /** * @dev Token Crowd Sale Contract Public Variables */ address public multiSigWallet; PolicyPalNetworkToken public token; uint256 public raisedWei; bool public haltSale; uint public rate; /** * @dev Modifier for valid sale */ modifier validSale() { require(!haltSale); require(saleStarted()); require(!saleEnded()); _; } /** * @dev Buy Event */ event Buy(address _buyer, uint256 _tokens, uint256 _payedWei); /** * @dev Token Crowd Sale Contract Constructor * @param _admin - Address of the Admin * @param _multiSigWallet - Address of Multisig wallet * @param _totalTokenSupply - Total Token Supply * @param _premintedTokenSupply - Total preminted token supply * @param _saleStartTime - The Start Time of the Token Sale * @param _saleEndTime - The End Time of the Token Sale * @param _increaseMaxContribTime - Time to increase max contribution * @param _rate - Rate of ETH to PAL * @param _minContribution - Minimum ETH contribution per contributor * @param _maxContribution - Maximum ETH contribution per contributor */ function PolicyPalNetworkCrowdsale( address _admin, address _multiSigWallet, uint256 _totalTokenSupply, uint256 _premintedTokenSupply, uint256 _presaleTokenSupply, uint256 _saleStartTime, uint256 _saleEndTime, uint256 _increaseMaxContribTime, uint _rate, uint256 _minContribution, uint256 _maxContribution ) CrowdsaleAuthorizer( _admin, _saleStartTime, _saleEndTime, _increaseMaxContribTime, _minContribution, _maxContribution ) validAddress(_multiSigWallet) public { require(_totalTokenSupply > 0); require(_premintedTokenSupply > 0); require(_presaleTokenSupply > 0); require(_rate > 0); require(_premintedTokenSupply < _totalTokenSupply); require(_presaleTokenSupply < _totalTokenSupply); multiSigWallet = _multiSigWallet; rate = _rate; token = new PolicyPalNetworkToken( _totalTokenSupply, _admin ); // transfer preminted tokens to company wallet token.transfer(multiSigWallet, _premintedTokenSupply); // transfer presale tokens to admin token.transfer(_admin, _presaleTokenSupply); } /** * @dev Token Crowd Sale Contract Halter * @param _halt - Flag to halt sale */ function setHaltSale(bool _halt) onlyAdmin public { haltSale = _halt; } /** * @dev Token Crowd Sale payable */ function() public payable { buy(msg.sender); } /** * @dev Token Crowd Sale Buy * @param _recipient - Address of the recipient */ function buy(address _recipient) public payable validSale validAddress(_recipient) returns(uint256) { // Get the contributor's eligible amount uint256 weiContributionAllowed = eligibleAmountCheck(_recipient, msg.value); require(weiContributionAllowed > 0); // Get tokens remaining for sale uint256 tokensRemaining = token.balanceOf(address(this)); require(tokensRemaining > 0); // Get tokens that the contributor will receive uint256 receivedTokens = weiContributionAllowed.mul(rate); // Check remaining tokens // If lesser, update tokens to be transfer and contribution allowed if (receivedTokens > tokensRemaining) { receivedTokens = tokensRemaining; weiContributionAllowed = tokensRemaining.div(rate); } // Transfer tokens to contributor assert(token.transfer(_recipient, receivedTokens)); // Send ETH payment to MultiSig Wallet sendETHToMultiSig(weiContributionAllowed); raisedWei = raisedWei.add(weiContributionAllowed); // Check weiContributionAllowed is larger than value sent // If larger, transfer the excess back to the contributor if (msg.value > weiContributionAllowed) { msg.sender.transfer(msg.value.sub(weiContributionAllowed)); } // Broadcast event Buy(_recipient, receivedTokens, weiContributionAllowed); return weiContributionAllowed; } /** * @dev Token Crowd Sale Emergency Drain * In case something went wrong and ETH is stuck in contract * @param _anyToken - Token to drain */ function emergencyDrain(ERC20 _anyToken) public onlyAdmin returns(bool) { if (this.balance > 0) { sendETHToMultiSig(this.balance); } if (_anyToken != address(0x0)) { assert(_anyToken.transfer(multiSigWallet, _anyToken.balanceOf(this))); } return true; } /** * @dev Token Crowd Sale * Transfer ETH to MultiSig Wallet * @param _value - Value of ETH to send */ function sendETHToMultiSig(uint256 _value) internal { multiSigWallet.transfer(_value); } }