分享一个简单的DEX项目代码及文档

Dex.top项目源码及文档分享

// DEx.top - Instant Trading on Chain
//
// Author: DEx.top Teampragma 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 addressuint64  scaleFactor;  // <original token amount> = <scaleFactor> x <DEx amountE8> / 1e8uint    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 tradinguint64 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 SELLuint8  ioc;     // 0 means a regular order; 1 means an immediate-or-cancel (IOC) orderuint64 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 codeuint16 cashCode;           // cash token codeuint64 stockDealAmountE8;uint64 cashDealAmountE8;}struct ExeStatus {uint64 logicTimeSec;       // logic timestamp for checking order expirationuint64 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> / 1e8uint64 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 changedmapping (uint16 => TokenInfo) public tokens;  // mapping of token code to token information//----------------- Other states: ----------------------------------------------------------------uint8 public marketStatus;        // market status: 0 - Active; 1 - Suspended; 2 - Closeduint16 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 operationExeStatus public exeStatus;       // status of operation executionmapping (address => TraderInfo) public traders;     // mapping of trade address to trader informationmapping (uint176 => TokenAccount) public accounts;  // mapping of trader token key to its account informationmapping (uint224 => Order) public orders;           // mapping of order key to order informationmapping (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 confirmationevent 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 initiationevent 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 forevermarketStatus = 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 settraders[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 countuint64 pendingAmountE8 = uint64(msg.value / (ETH_SCALE_FACTOR / 10**8));  // msg.value is in Weiif (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 ETHif (msg.data.length != 4 + 32 + 32 + 32) revert();  // length condition of param countTokenInfo 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 overflowuint 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 countuint176 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 ETHif (msg.data.length != 4 + 32 + 32) revert();  // length condition of param countTokenInfo memory tokenInfo = tokens[tokenCode];if (tokenInfo.scaleFactor == 0) revert();  // unsupported tokenuint176 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) {  // ETHtoAddr.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 indexuint64 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();// MatchOrdersif (opcode == 0xDE03) {uint8 v1 = uint8(bits);bits >>= 8;            // bits is now the key of the maker orderOrder memory makerOrder;if (v1 == 0) {         // order already in storageif (i + 1 >= body.length) revert();  // at least 1 body element leftmakerOrder = orders[uint224(bits)];i += 1;} else {if (orders[uint224(bits)].pairId != 0) revert();  // order must not be already in storageif (i + 4 >= body.length) revert();  // at least 4 body elements leftmakerOrder = 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 storagetakerOrder = orders[takerOrderKey];i += 1;} else {if (orders[takerOrderKey].pairId != 0) revert();  // order must not be already in storageif (i + 3 >= body.length) revert();  // at least 3 body elements lefttakerOrder = 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 loopsetExeStatus(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 overflowpendingWithdrawE8 += 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 overflowaccounts[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 tradingdeal.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 overflowaccounts[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 keyif (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 ordermakerOrder.amountE8 = 0;} else {makerOrder.amountE8 -= deal.stockDealAmountE8;}if (takerOrder.ioc == 1) {  // IOC ordertakerOrder.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 traderbytes32 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

DEx Smart Contract Spec

Bits format in this doc: The rightmost bit is the LSB, the leftmost bit is the MSB. For example, a byte <1001>(4) <0011>(4) has value 147.

Market States

State that can not be changed:

  • admin: address

States that can be changed directly by an external function:

  • marketStatus: uint8

    • 0: Active
    • 1: Suspended
      • A suspended market temporarily stops accepting deposits and executing operations. Withdrawing is Ok.
    • 2: Closed
      • A closed status cannot be changed even by the admin. Only withdrawing, setWithdrawAddr, transferFee and operation ConfirmDeposit and InitiateWithdraw are allowed in a closed market.
  • tokens: map[tokenCode(16)] -> TokenInfo

    • tokenCode is a uint16
    • TokenInfo is a struct
    struct TokenInfo {bytes[4] symbol // e.g. "ETH ", "ADX " (there is a trailing whitespace 0x20).Address tokenAddr // the address of the ERC20 token contract.uint64 scaleFactor // <original amount> = <Dex amountE8> x scaleFactor / 1e8uint minDeposit // minimum deposit (original token amount) allowed per token
    }
    
    • Note
  1. Once a token added, its info cannot be changed.
    2. The token code of ETH is 0 and the scale factor is 1e18.
    3. Token code [0, 99] are reserved for cash tokens. [100, 999] are reserved for tokens on Ethereum block chain.
    4. For an ERC20 token, the scaleFactor of the token must be set as
    scaleFactor = 10 ** decimal. For example, demical=3 => scaleFactor=1000, decimal=0 => scaleFactor=1.

States that can only be changed via executing the operation sequence:

  • makerFeeRateE4: uint16

  • takerFeeRateE4: uint16

  • lastDepositIndex: uint64

    • Initial value is 0 (i.e. the first deposit has index 1)
  • exeStatus: struct ExeStatus

    struct ExeStatus {uint64 logicTimeSecuint64 lastOperationIndex
    }
    
    • logicTimeSec: uint64

      • This is the time (seconds since the epoch) for checking order expiration.
      • Expires can directly check against this value for validation purpose.
    • lastOperationIndex: uint64
      • Updated upon operation sequence completion.
      • Initial value is 0 (i.e. the first operation has index 1)

Account States (Trader Related States)

  • traders: map[traderAddr] -> TraderInfo(256)

    • traderAddr is an address
    • TraderInfo is a struct
    struct TraderInfo {address withdrawAddruint8   feeRebatePercent  // between 0~100
    }
    
  • accounts: map[tokenAccountKey(176)] -> TokenAccount(256)

    • tokenAccountKey is a uint176

      • <tokenCode>(16) <traderAddr>(160)
    • TokenAccount is a struct
    struct TokenAccount {uint64 balanceE8  // available amount for tradinguint64 pendingWithdrawE8
    }
    
    • Note

      1. The total fee collected in a token is kept as the pendingWithdrawE8 of that token of traderAddr 0, i.e. accounts[tokenCode << 160].pendingWithdrawE8
      2. We use address 0 but not a real address to avoid handling the case where the fee address is one of the trader address. Besides, we do not have to set the fee address in advance.
  • orders: map[orderKey(224)] -> Order

    • orderKey is a uint224

      • <orderNonce>(64) <traderAddr>(160)
    • Order is a struct
    struct Order {uint32 pairId  // <cashId>(16) <stockId>(16)uint8  actionuint8  iocuint64 priceE8uint64 amountE8uint64 expireTimeSec
    }
    
    • Note

      1. action: 0 means BUY, 1 means SELL.
      2. ioc: 0 means not IOC (Immediate-Or-Cancel), 1 means IOC.
        If an order has been hard cancelled, update its amountE8 to 0.
  • deposits: map[depositIndex(64)] -> Deposit

    • depositIndex is a uint64
    • Deposit is a struct
    struct Deposit {address traderAddruint16  tokenCodeuint64  pendingAmountE8  // the amount to be confirmed for trading
    }
    

Operation Sequence

An operation sequence is represented as an array of uint256 with the following format:

<OpSeq>: <header>(256) {[operation],[operation],...}

Format of header:

<header>: <newLogicTimeSec>(64) <beginIndex>(64)

External function:

function exeSequence(uint header, uint[] body)

Operations

Each operation can be one of the following. As a convention, the lowest 16 bits of the first uint256 of an operation is always the opcode.

  • ConfirmDeposit

    • move a pending deposit to balance
    • opcode: 0xDE 0x01
    • body length (in uint256): 1
    • <depositIndex>(64) <opcode>(16)
  • InitiateWithdraw

    • move fund from balance to pending withdraw

    • opcode: 0xDE 0x02

    • length (in uint256): 1

    • <amountE8>(64) <tokenCode>(16) <traderAddr>(160) <opcode>(16)

    • Note

      • No signature required. The admin keeps the right to clear a trader account by sending the funds of the trader back to the trader’s account.
  • MatchOrders

    • execute a pair of matching orders.

    • opcode: 0xDE 0x03

    • body length (in uint256): 2 ~ 8

    • <nonce1>(64) <traderAddr1>(160) <v1>(8) <opcode>(16)

      • The following 3 uint256s exists if and only if v1 is NOT 0. (v1 = 0 means that this order is already in the storage.)
      <expireTimeSec1>(64) <amount1E8>(64) <price1E8>(64) <ioc1>(8) <action1>(8) <pairId1>(32)
      <r1>(256)
      <s1>(256)
      
    • <nonce2>(64) <traderAddr2>(160) <v2>(8)

      • The following 3 uint256s exists if and only if v2 is NOT 0. (v2 = 0 means that this order is already in the storage.)
      <expireTimeSec2>(64) <amount2E8>(64) <price2E8>(64) <ioc2>(8) <action2>(8) <pairId2>(32)
      <r2>(256)
      <s2>(256)
      
    • Note

      1. The first order is the maker, the second order is the taker.
      2. When v1/v2 is not zero, it is either 27 or 28.
      3. Format of <pairId>: <cashId>(16) <stockId>(16)
      4. stockId is the lower u16, cashId is the higher u16
      5. AmountE8 is referring to stock amount
    Signing Scheme 1 (Friendly to API usage)
    • The bytes to be hashed (using keccak256) for signing are the concatenation of the following (uints are in big-endian order):

      • Prefix “\x19Ethereum Signed Message:\n70”.
      • String "DEx2 Order: " (Note the trailing whitespace)
      • The market address.
        • This is for replay attack protection when we deploy a new market.
      • <nonce>(64)
      • <expireTimeSec>(64) <amountE8>(64) <priceE8>(64) <ioc>(8) <action>(8) <pairId>(32)
    Signing Scheme 2 (Friendly to UI supporting eth_signTypedData)
    • The fields to be sent to eth_signTypedData:

      string title = "DEx2 Order"
      address market_address = <market address>
      uint64 nonce
      uint64 expire_time_sec
      uint64 amount_e8
      uint64 price_e8
      uint8 immediate_or_cancel
      uint8 action
      uint16 cash_token_symbol = `<high 16 bits of pairId>`
      uint16 stock_token_symbol = `<low 16 bits of pairId>`
      
    • The bytes to be hashed (using keccak256) for signing are the concatenation of the following:

      keccak256(<TYPES_NAMES_HASH>, <DATA_HASH>)

      • <TYPES_NAMES_HASH> is u256 0x8382001f45a579e41d75c5535cff758120963de17b4af5e92d5191b9a5f69f1f

        • It is the result of:

          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_symbol", "uint16 stock_token_symbol")
          
      • <DATA_HASH> is the keccak256 result of the following:

        "DEx2 Order"
        <market address>(160)
        <nonce>(64)
        <expireTimeSec>(64) <amountE8>(64) <priceE8>(64) <ioc>(8) <action>(8) <pairId>(32)
        
  • HardCancelOrder

    • mark an order as cancelled in the storage, no matter whether it is currently in the storage or not.

    • opcode: 0xDE 0x04

    • body length (in uint256): 1

    • <nonce>(64) <traderAddr>(160) <opcode>(16)

    • Note

      • No signature required. The admin keeps the right to cancel any order.
  • SetFeeRates

    • set the fee rates.
    • opcode: 0xDE 0x05
    • body length (in uint256): 1
    • <withdrawFeeRateE4>(16) <takerFeeRateE4>(16) <makerFeeRateE4>(16) <opcode>(16)
  • SetFeeRebatePercent

    • set the rebate fee percentage.
    • opcode: 0xDE 0x06
    • body length (in uint256): 1
    • <feeRebatePercent>(8) <traderAddr>(160) <opcode>(16)

Free External Methods

  • Free external methods can be called by either the admin or the traders, at anytime, in any order.

Deposit

  • depositEth(address traderAddr) external payable

  • depositToken(address traderAddr, uint16 tokenCode, uint originalAmount) external

  • Note

    • confirmDeposit (called by exeSequence) comes after depositEth/depositToken

Withdraw

  • withdrawEth(address traderAddr) external

  • withdrawToken(address traderAddr, uint16 tokenCode) external

  • Note

    • initiateWithdraw (called by exeSequence) comes before withdrawEth/withdrawToken

搭建去中心化交易所——分享一个简单的DEX项目代码及文档相关推荐

  1. 快速搭建去中心化视频分享平台peertube

    ## 简介 peertube 是一款开源的去中心化视频分享平台,你可以用peertube创建属于自己的实例,相较于传统的视频平台,peertube最大的优点就是采用webtorrent技术,网站管理员 ...

  2. 大话 Cosmos:除了去中心化交易所,扩容与复杂数据的跨链调用也是 Cosmos 的应用场景...

    从区块链技术发展至今,2019 年也许是人们最关心跨链技术的一年. 新的一年,公链从大举扩张进入缓冲期,可扩展性成为公链更深切的主题,在跨链技术出现之前,区块链中各公链是一座座孤岛,无法相互沟通,不仅 ...

  3. 烤星 DeFi 课堂 | 去中心化交易所适合小白用吗?

    王也 打开 DeFi 世界之门,从小白到 DeFi 大神的进阶必修课. 由 Conflux × Odaily星球日报联合出品,星球日报资深记者@王也担任主笔. 烤星 · DeFi 课堂 专注 DeFi ...

  4. 继FCoin后的下一轮浪潮 —— 去中心化交易所的逆风翻盘

    本文由微信公众号DappVision原创首发,转载请联系授权 互联网时代的腥风血雨是在烧钱补贴中结束的,从2011年的千团大战,到17年的ofo与摩拜之争,基本都是这个套路. 而在区块链的时代,FCo ...

  5. 交易所要变天?去中心化交易所崛起,蚕食用户,抢夺流量

    文 | 棘轮 林格 2020年,在DeFi浪潮的推动下,去中心化交易所大红大紫. 从4月开始,它的市场份额飞速增长,并不断蚕食中心化交易所的市场.比如去中心化交易所中的佼佼者Uniswap,就在交易量 ...

  6. 谈谈我对去中心化交易所鲸交所的感受

    在2018年3月1日,币乎上线的第一天,赵公子斗志昂扬的在币乎写下来自己的第一次真情告白:和咕噜共同见证.然而一直到2019年4月1日,我才明白,那次告白是和咕噜见证的不只是币乎,还有他的鲸交所. 当 ...

  7. Bancor 2.0:ET去中心化交易所的极致体验

    伴随着数字资产的浪潮兴起,Token迎来了"百家争鸣"的时代,群雄并起之际,数字资产交易所更是如火如荼,然而随着区块链生态的繁荣数字资产对交易所的要求更高,传统的交易所无法满足币民 ...

  8. 去中心化交易所研究报告

    本文分析了数字货币交易所行业的痛点.现状,去中心化交易所的发展模式.投资逻辑和优缺点,最后对去中心化交易所的未来进行了展望. 鲸准 2018/04/13 09:56字体:宋 中心化交易所因为其易用性. ...

  9. Cybex在日本:去中心化交易所5大核心功能解决安全问题

    点击上方 "蓝色字" 可关注我们! 记者:铅笔盒 True Global Ventures主办的Disruption in Financial Services Event于201 ...

最新文章

  1. Maven 开 发 规 范
  2. string 基本用法
  3. 如何查看linux版本
  4. C++中标准模板库std::pair的实现
  5. Luogu P1967 NOIP2013 货车运输
  6. poj1015 Jury Compromise
  7. [云炬创业基础笔记] 第四章测试9
  8. PHP封装对象名字的思路
  9. Xilinx发布实时视频编码服务器
  10. 八大排序算法的Python实现
  11. Replace Error Code with Exception(以异常取代错误码)
  12. java构造方法赋值内存图_java 面向对象(九):类的结构:构造器(一)简介;属性赋值顺序;JavaBean的概念...
  13. jquery 停止事件冒泡方法
  14. 织梦php gbk转换utf8,dedecms 5.1 utf-8版本英文怎么修改
  15. java 8中排序_如何在JAVA 8中一起使用分组和排序
  16. bzoj2525 1426
  17. 关于色域与BT.2020相关学习心得笔记
  18. 为什么是“深度”学习而不是宽度?
  19. 如何使用 Zend Expressive 建立 NASA 图片库?
  20. 重磅!python获取同步输出的桌面网易云音乐歌词(内存偏移获取)

热门文章

  1. 电脑CPU型号规格清单及参数
  2. java directdraw_Java中使用DirectDraw
  3. ElasticSearch的优缺点
  4. ActiveMQ的消息格式MapMessage
  5. 经验分享 | SEN+Mk趋势分析(matlab代码分享)
  6. 多线程详解---(多案例实战)
  7. linux 待办事项_Linux桌面的4个待办事项列表管理器
  8. 沧海竞舟热身赛I-题解
  9. 抽象类与接口的区别及应用
  10. oracle中图片是黑色的,黑色高清壁纸