TokenTrader And TokenTraderFactory

BokkyPooBah edited this page Mar 29, 2017 · 47 revisions

Page status: Complete, except that I'll have to replace some of the images that label a SWT TokenTrader contract with the name REP.

The TokenTrader contract enables users to trade fully ERC20 compliant tokens on the Ethereum blockchain.

A Market Maker calls the TokenTraderFactory createTradeContract(...) function to deploy a new TokenTrader contract that the Maker owns. The Market Maker specifies the rate at which tokens will be bought and sold by the contract. The Market Maker then loads up the new TokenTrader contract with ERC20 compliant tokens and/or ethers.

If the TokenTrader contract has a positive token balance, a Market Taker can send ethers to the TokenTrader contract and the contract will send the number of tokens bought to the Taker's account.

If the TokenTrader contract has a positive ether balance, a Taker can approve(...) the transfer of their tokens to the TokenTrader contract, and then call the TokenTrader contract's takerSellAsset(...) function that will transfer the number of tokens sold to the TokenTrader contract and send ethers to the Taker's ccount.

Notes

  1. Always test the use of these contracts with small amounts of ethers and/or tokens before committing larger amounts.
  2. You are responsible for your own trades. We accept no liability for errors or ommissions. Methods are provided on this page for you to independently verify the correctness of these contracts, and always test first with small amounts.
  3. The TokenTraderFactory And TokenSellerFactory Bug Bounty is now active. Please consider checking the contracts for bugs.
  4. Do NOT use the TokenTrader contracts for Golem Network Tokens (GNTs) as the GNT contract does not implement the approve(...) and transferFrom(...) functions. There is a check in the TokenTraderFactory createTraderContract(...) function to prevent users accidentally listing GNTs with the TokenTrader contracts anyway.


Table of contents



TokenTrader And TokenTraderFactory Contract Information



Fees

The approximate Ethereum transaction fees to use these contracts follow, assuming a gas price of 0.00000002 Ether and USD ~ cost @ Jan 19 2017. These fees do not vary with the amount of ethers or tokens traded or transferred.

Role Action Gas Cost ETH Cost USD Sample Transaction
Taker Buy Token 75,927 0.001518540 $0.03 0x2611e521...
Taker Approve Token Transfer 45,182 0.001852462 $0.04 0xa4447afb...
Taker Sell Token 58,035 0.002379435 $0.05 0x2808bcca...
Maker Create Trade Contract 950,720 0.019014400 $0.38 0x684a5f24...
Maker (De)Activate Contract No sample yet
Maker Deposit Tokens To Contract 84,317 0.00168634 $0.03 0x73c5e8a1...
Maker Deposit Ethers To Contract 22,979 0.000942139 $0.02 0xba12caf2...
Maker Transfer Tokens To Another Contract 63,741 0.002613381 $0.05 0x1415544e...
Maker TODO Withdraw Ethers From Contract 30,516 0.000610320 $0.0062 0xf70bbfed...
Maker TODO Withdraw Tokens From Contract 39,504 0.000790080 $0.0081 0x3bf83f5e...
Maker Withdraw Other Tokens From Contract No sample yet

Compare these fees with other exchanges, and consider send some of your huge savings on exchange fees to 0x000001f568875f378bf6d170b790967fe429c81a to help keep this site running and improving.



Market Maker Operations

Buy price, Sell price, Units And Decimals

When creating your TokenTrader contract using the TokenTraderFactory createTradeContract(address asset, uint256 buyPrice, uint256 sellPrice, uint256 units, bool buysTokens, bool sellsTokens) (displayed as Create Trade Contract in Ethereum Wallet / Mist), following are the rules to determine the buyPrice, sellPrice and units parameters.

You will need to know the decimals setting of the Token contract. See Supported ERC20 Tokens for a listing of tokens and their associated decimal places.

In the following formulae and examples

  • buyRate and sellRate are the rates listed in https://cryptoderivatives.market/ and represent the amount of ethers to buy and sell a single token

  • buyPrice and sellPrice are the numbers enterd in the createTradeContract(...) function, and this is relative to the units

  • The examples will use the REP token which has 18 decimal places and the SNGLS token which has 0 decimal places

  • The formulae relating price to rate and token decimal places are:

      buyPrice = buyRate x units / 10^(tokenDecimals - etherDecimals)
    

    and

      sellPrice = sellRate x units / 10^(tokenDecimals - etherDecimals)
    
  • The number of decimal places for ethers, etherDecimals is 18, so:

      buyPrice = buyRate x units / 10^(tokenDecimals - 18)
    

    and

      sellPrice = sellRate x units / 10^(tokenDecimals - 18)
    
  • For the REP token with 18 decimal places:

      buyPrice = buyRate x units / 10^(18 - 18)
               = buyRate x units
    

    and

      sellPrice = sellRate x units / 10^(18 - 18)
                = sellRate x units
    

    In the following screen, REP is selling at a rate of 0.395 ETH per 1 REP token:

    And in the following screen, the same REP contract has the sellPrice = 39500 and units=100000:

    and

      39,500 = 0.395 x 100,000
    
  • For the SNGLS token with 0 decimal places

      buyPrice = buyRate x units / 10^(0 - 18)
               = buyRate x units / 10^-18
               = buyRate x units x 10^18
    

    and

      sellPrice = sellRate x units / 10^(0 - 18)
                = sellRate x units / 10^-18
                = sellRate x units x 10^18
    

    In the following screen, SNGLS is selling at a rate of 0.00109872 ETH per 1 SNGLS token:

    And in the following screen, the same SNGLS contract has the sellPrice = 1,098,720,000,000,000 and units=1:

    and

      1,098,720,000,000,000 = 0.00109872 x 1 x 10^18
    
  • How do you choose the units? See the existing contracts on https://cryptoderivatives.market/tokenTraderContracts , determine how many decimal places your rate requires and perform the calculation on paper. Test out your new TokenTrader contract by transferring a small amount of tokens and/or ethers to it. Use another account to buy and/or sell to the contract and check the trade exchanges the ethers and tokens correctly.


Create TokenTrader Contract

  • Refer to Buy price, Sell price, Units And Decimals to decide the rate that you want your contract to buy and sell tokens at

  • Watch the TokenTraderFactory contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Create Trade Contract

  • Under asset - address, enter the token address. See Supported ERC20 Tokens for a list of tokens and their addresses. Note that the Golem Network Token (GNT) contract is not supported by this TokenTraderFactory contract

  • Under Buy price, enter the buying price per unit

  • Under Sell price, enter the sell price per unit

  • Under Units, enter the units

  • Under Buys tokens, tick the Yes checkbox if you want this buying function activated immediately

  • Under Sells tokens, tick the Yes checkbox if you want this selling function activated immediately

  • Click on Execute, then confirm your transaction

  • Point your web browser to https://etherscan.io and enter your account (Account 20 in the image above) in the search box on the top right of the EtherScan page. Search for your newly created transaction and click TxHash field link for this transaction to view your new contract creation transaction

  • Select the Internal Transactions tab

  • Your newly created TokenTrader contract is listed under the To column in the image above. Click on this link, then select on the Contract Source tab

  • Use the Contract Address and the Contract ABI in the screen above to Watch Your TokenTrader Contract In Ethereum Wallet / Mist

  • View this contract in Ethereum Wallet / Mist to see the range of available functions to interact with your contract

  • Test out your new TokenTrader contract by [transferring a small amount of tokens(#adding-tokens-to-tokentrader-contract) and/or ethers to it. Use another account to buy and/or sell to the contract and check the trade exchanges the ethers and tokens correctly.


(De)Activate TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Activate

  • Under _buys tokens, tick the Yes checkbox if you want the buying function activated

  • Under _sells tokens, tick the Yes checkbox if you want the selling function activated

  • Click on Execute, then confirm your transaction


Adding Ethers To TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Deposit Ether

  • Under Send ETHER, enter the number of ethers you want to deposit into your TokenTrader contract

  • Click on Execute, then confirm your transaction


Adding Tokens To TokenTrader Contract

  • Watch the Token contract in Ethereum Wallet / Mist. Refer to Supported ERC20 Tokens for the list of tokens and select the link for the token you want to trade

  • Transfer the tokens to your TokenTrader contract. Note that the units are in the token's natural units


Withdrawing Ethers From The TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Withdraw Ether

  • Under Ethers, enter the number of ethers you want to withdraw from your TokenTrader contract. Note that the number is in natural units. 1 ether is specified as 1,000,000,000,000,000,000 or 10^18

  • Click on Execute, then confirm your transaction


Withdrawing Tokens From The TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Withdraw Asset

  • Under Tokens, enter the number of tokens you want to withdraw from your TokenTrader contract. Note that the number is in natural units and this number depends on the token contract

  • Click on Execute, then confirm your transaction


Transferring Ethers To Another TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Transfer Ether

  • Under To token trader, enter the address of the other TokenTrader contract (with the same owner and asset)

  • Under Ethers, enter the number of ethers you want to transfer from your TokenTrader contract. Note that the number is in natural units. 1 ether is specified as 1,000,000,000,000,000,000 or 10^18

  • Click on Execute, then confirm your transaction


Transferring Tokens To Another TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Transfer Asset

  • Under To token trader, enter the address of the other TokenTrader contract (with the same owner and asset)

  • Under Tokens, enter the number of tokens you want to transfer from your TokenTrader contract. Note that the number is in natural units and this number depends on the token contract

  • Click on Execute, then confirm your transaction


Withdrawing Other Tokens From The TokenTrader Contract

  • Watch the TokenTrader contract

  • Click on SHOW CONTRACT INFO to display the READ FROM CONTRACT and WRITE TO CONTRACT headings

  • Under Select function, select Maker Withdraw ERC20 Token

  • Under Token address, enter the address of the other Token contract (which is not the asset managed by your TokenTrader contract

  • Under Tokens, enter the number of tokens you want to withdraw from your TokenTrader contract. Note that the number is in natural units and this number depends on the token contract

  • Click on Execute, then confirm your transaction



How To Watch The TokenTraderFactory Contract In Ethereum Wallet / Mist

In Ethereum Wallet / Mist, select the CONTRACTS tab and click WATCH CONTRACT to open the Watch contract window. Then:

  • Under CONTRACT NAME, enter TokenTraderFactory

  • Under CONTRACT ADDRESS:

    • For Mainnet, enter 0xa9f801f160fe6a866dd3404599350abbcaa95274
    • For Testnet, enter 0x66bdad9baac0deed299bcd6331e02bc92865db8d
  • Copy the Application Binary Interface below and paste it into the JSON INTERFACE text box

    [{"constant":false,"inputs":[{"name":"asset","type":"address"},{"name":"buyPrice","type":"uint256"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"buysTokens","type":"bool"},{"name":"sellsTokens","type":"bool"}],"name":"createTradeContract","outputs":[{"name":"trader","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"ownerWithdrawERC20Token","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"tradeContract","type":"address"}],"name":"verify","outputs":[{"name":"valid","type":"bool"},{"name":"owner","type":"address"},{"name":"asset","type":"address"},{"name":"buyPrice","type":"uint256"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"buysTokens","type":"bool"},{"name":"sellsTokens","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ownerAddress","type":"address"},{"indexed":true,"name":"tokenTraderAddress","type":"address"},{"indexed":true,"name":"asset","type":"address"},{"indexed":false,"name":"buyPrice","type":"uint256"},{"indexed":false,"name":"sellPrice","type":"uint256"},{"indexed":false,"name":"units","type":"uint256"},{"indexed":false,"name":"buysTokens","type":"bool"},{"indexed":false,"name":"sellsTokens","type":"bool"}],"name":"TradeListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tokenAddress","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"OwnerWithdrewERC20Token","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"}]

  • Click OK



How To Watch A TokenTrader Contract In Ethereum Wallet / Mist

In Ethereum Wallet / Mist, select the CONTRACTS tab and click WATCH CONTRACT to open the Watch contract window. Then:

  • Under CONTRACT NAME, enter TokenTrader {further identifying information}

  • Under CONTRACT ADDRESS, enter the contract address as listed in https://cryptoderivatives.market/tokenTraderContracts . These contracts will have a Factory Name of TokenTraderFactory.

  • Copy the Application Binary Interface below and paste it into the JSON INTERFACE text box

    [{"constant":false,"inputs":[{"name":"ethers","type":"uint256"}],"name":"makerWithdrawEther","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"asset","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"sellPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"sellsTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"toTokenTrader","type":"address"},{"name":"tokens","type":"uint256"}],"name":"makerTransferAsset","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"buyPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"makerDepositEther","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"units","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_buysTokens","type":"bool"},{"name":"_sellsTokens","type":"bool"}],"name":"activate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"buysTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"toTokenTrader","type":"address"},{"name":"ethers","type":"uint256"}],"name":"makerTransferEther","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"makerWithdrawERC20Token","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"takerBuyAsset","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"tokens","type":"uint256"}],"name":"makerWithdrawAsset","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"amountOfTokensToSell","type":"uint256"}],"name":"takerSellAsset","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_asset","type":"address"},{"name":"_buyPrice","type":"uint256"},{"name":"_sellPrice","type":"uint256"},{"name":"_units","type":"uint256"},{"name":"_buysTokens","type":"bool"},{"name":"_sellsTokens","type":"bool"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"buys","type":"bool"},{"indexed":false,"name":"sells","type":"bool"}],"name":"ActivatedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"MakerDepositedEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerWithdrewAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"toTokenTrader","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerTransferredAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokenAddress","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerWithdrewERC20Token","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ethers","type":"uint256"}],"name":"MakerWithdrewEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"toTokenTrader","type":"address"},{"indexed":false,"name":"ethers","type":"uint256"}],"name":"MakerTransferredEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethersSent","type":"uint256"},{"indexed":false,"name":"ethersReturned","type":"uint256"},{"indexed":false,"name":"tokensBought","type":"uint256"}],"name":"TakerBoughtAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"seller","type":"address"},{"indexed":false,"name":"amountOfTokensToSell","type":"uint256"},{"indexed":false,"name":"tokensSold","type":"uint256"},{"indexed":false,"name":"etherValueOfTokensSold","type":"uint256"}],"name":"TakerSoldAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"}]



Source Code

Following is the source code for the TokenTraderFactory contract (which includes TokenTrader) on:

pragma solidity ^0.4.4;

// ------------------------------------------------------------------------
// TokenTraderFactory
//
// Decentralised trustless ERC20-compliant token to ETH exchange contract
// on the Ethereum blockchain.
//
// Note that this TokenTrader cannot be used with the Golem Network Token
// directly as the token does not implement the ERC20
// transferFrom(...), approve(...) and allowance(...) methods
//
// History:
//   Jan 25 2017 - BPB Added makerTransferAsset(...) and
//                     makerTransferEther(...)
//   Feb 05 2017 - BPB Bug fix in the change calculation for the Unicorn
//                     token with natural number 1
//   Feb 08 2017 - BPB/JL Renamed etherValueOfTokensToSell to
//                     amountOfTokensToSell in takerSellAsset(...) to
//                     better describe the parameter
//                     Added check in createTradeContract(...) to prevent
//                     GNTs from being used with this contract. The asset
//                     token will need to have an allowance(...) function.
//
// Enjoy. (c) JonnyLatte & BokkyPooBah 2017. The MIT licence.
// ------------------------------------------------------------------------

// https://github.com/ethereum/EIPs/issues/20
contract ERC20 {
    function totalSupply() constant returns (uint totalSupply);
    function balanceOf(address _owner) constant returns (uint balance);
    function transfer(address _to, uint _value) returns (bool success);
    function transferFrom(address _from, address _to, uint _value) returns (bool success);
    function approve(address _spender, uint _value) returns (bool success);
    function allowance(address _owner, address _spender) constant returns (uint remaining);
    event Transfer(address indexed _from, address indexed _to, uint _value);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}

contract Owned {
    address public owner;
    event OwnershipTransferred(address indexed _from, address indexed _to);

    function Owned() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _;
    }

    modifier onlyOwnerOrTokenTraderWithSameOwner {
        if (msg.sender != owner && TokenTrader(msg.sender).owner() != owner) throw;
        _;
    }

    function transferOwnership(address newOwner) onlyOwner {
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

// contract can buy or sell tokens for ETH
// prices are in amount of wei per batch of token units

contract TokenTrader is Owned {

    address public asset;       // address of token
    uint256 public buyPrice;    // contract buys lots of token at this price
    uint256 public sellPrice;   // contract sells lots at this price
    uint256 public units;       // lot size (token-wei)

    bool public buysTokens;     // is contract buying
    bool public sellsTokens;    // is contract selling

    event ActivatedEvent(bool buys, bool sells);
    event MakerDepositedEther(uint256 amount);
    event MakerWithdrewAsset(uint256 tokens);
    event MakerTransferredAsset(address toTokenTrader, uint256 tokens);
    event MakerWithdrewERC20Token(address tokenAddress, uint256 tokens);
    event MakerWithdrewEther(uint256 ethers);
    event MakerTransferredEther(address toTokenTrader, uint256 ethers);
    event TakerBoughtAsset(address indexed buyer, uint256 ethersSent,
        uint256 ethersReturned, uint256 tokensBought);
    event TakerSoldAsset(address indexed seller, uint256 amountOfTokensToSell,
        uint256 tokensSold, uint256 etherValueOfTokensSold);

    // Constructor - only to be called by the TokenTraderFactory contract
    function TokenTrader (
        address _asset,
        uint256 _buyPrice,
        uint256 _sellPrice,
        uint256 _units,
        bool    _buysTokens,
        bool    _sellsTokens
    ) {
        asset       = _asset;
        buyPrice    = _buyPrice;
        sellPrice   = _sellPrice;
        units       = _units;
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can activate or deactivate this contract's buying and
    // selling status
    //
    // The ActivatedEvent() event is logged with the following
    // parameter:
    //   buysTokens   this contract can buy asset tokens
    //   sellsTokens  this contract can sell asset tokens
    //
    function activate (
        bool _buysTokens,
        bool _sellsTokens
    ) onlyOwner {
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can deposit ethers to this contract so this contract
    // can buy asset tokens.
    //
    // Maker deposits asset tokens to this contract by calling the
    // asset's transfer() method with the following parameters
    //   _to     is the address of THIS contract
    //   _value  is the number of asset tokens to be transferred
    //
    // Taker MUST NOT send tokens directly to this contract. Takers
    // MUST use the takerSellAsset() method to sell asset tokens
    // to this contract
    //
    // Maker can also transfer ethers from one TokenTrader contract
    // to another TokenTrader contract, both owned by the Maker
    //
    // The MakerDepositedEther() event is logged with the following
    // parameter:
    //   ethers  is the number of ethers deposited by the maker
    //
    // This method was called deposit() in the old version
    //
    function makerDepositEther() payable onlyOwnerOrTokenTraderWithSameOwner {
        MakerDepositedEther(msg.value);
    }

    // Maker can withdraw asset tokens from this contract, with the
    // following parameter:
    //   tokens  is the number of asset tokens to be withdrawn
    //
    // The MakerWithdrewAsset() event is logged with the following
    // parameter:
    //   tokens  is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawAsset() in the old version
    //
    function makerWithdrawAsset(uint256 tokens) onlyOwner returns (bool ok) {
        MakerWithdrewAsset(tokens);
        return ERC20(asset).transfer(owner, tokens);
    }

    // Maker can transfer asset tokens from this contract to another
    // TokenTrader contract, with the following parameter:
    //   toTokenTrader  Another TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   tokens         is the number of asset tokens to be moved
    //
    // The MakerTransferredAsset() event is logged with the following
    // parameters:
    //   toTokenTrader  The other TokenTrader contract owned by
    //                  the same owner and with the same asset
    //   tokens         is the number of tokens transferred
    //
    // The asset Transfer() event is also logged from this contract
    // to the other contract
    //
    function makerTransferAsset(
        TokenTrader toTokenTrader,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        if (owner != toTokenTrader.owner() || asset != toTokenTrader.asset()) {
            throw;
        }
        MakerTransferredAsset(toTokenTrader, tokens);
        return ERC20(asset).transfer(toTokenTrader, tokens);
    }

    // Maker can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The MakerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawToken() in the old version
    //
    function makerWithdrawERC20Token(
        address tokenAddress,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        MakerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

    // Maker can withdraw ethers from this contract
    //
    // The MakerWithdrewEther() event is logged with the following parameter
    //   ethers  is the number of ethers withdrawn by the maker
    //
    // This method was called withdraw() in the old version
    //
    function makerWithdrawEther(uint256 ethers) onlyOwner returns (bool ok) {
        if (this.balance >= ethers) {
            MakerWithdrewEther(ethers);
            return owner.send(ethers);
        }
    }

    // Maker can transfer ethers from this contract to another TokenTrader
    // contract, with the following parameters:
    //   toTokenTrader  Another TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   ethers         is the number of ethers to be moved
    //
    // The MakerTransferredEther() event is logged with the following parameter
    //   toTokenTrader  The other TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   ethers         is the number of ethers transferred
    //
    // The MakerDepositedEther() event is logged on the other
    // contract with the following parameter:
    //   ethers  is the number of ethers deposited by the maker
    //
    function makerTransferEther(
        TokenTrader toTokenTrader,
        uint256 ethers
    ) onlyOwner returns (bool ok) {
        if (owner != toTokenTrader.owner() || asset != toTokenTrader.asset()) {
            throw;
        }
        if (this.balance >= ethers) {
            MakerTransferredEther(toTokenTrader, ethers);
            toTokenTrader.makerDepositEther.value(ethers)();
        }
    }

    // Taker buys asset tokens by sending ethers
    //
    // The TakerBoughtAsset() event is logged with the following parameters
    //   buyer           is the buyer's address
    //   ethersSent      is the number of ethers sent by the buyer
    //   ethersReturned  is the number of ethers sent back to the buyer as
    //                   change
    //   tokensBought    is the number of asset tokens sent to the buyer
    //
    // This method was called buy() in the old version
    //
    function takerBuyAsset() payable {
        if (sellsTokens || msg.sender == owner) {
            // Note that sellPrice has already been validated as > 0
            uint order    = msg.value / sellPrice;
            // Note that units has already been validated as > 0
            uint can_sell = ERC20(asset).balanceOf(address(this)) / units;
            uint256 change = 0;
            if (msg.value > (can_sell * sellPrice)) {
                change  = msg.value - (can_sell * sellPrice);
                order = can_sell;
            }
            if (change > 0) {
                if (!msg.sender.send(change)) throw;
            }
            if (order > 0) {
                if (!ERC20(asset).transfer(msg.sender, order * units)) throw;
            }
            TakerBoughtAsset(msg.sender, msg.value, change, order * units);
        }
        // Return user funds if the contract is not selling
        else if (!msg.sender.send(msg.value)) throw;
    }

    // Taker sells asset tokens for ethers by:
    // 1. Calling the asset's approve() method with the following parameters
    //    _spender            is the address of this contract
    //    _value              is the number of tokens to be sold
    // 2. Calling this takerSellAsset() method with the following parameter
    //    etherValueOfTokens  is the ether value of the asset tokens to be sold
    //                        by the taker
    //
    // The TakerSoldAsset() event is logged with the following parameters
    //   seller                  is the seller's address
    //   amountOfTokensToSell    is the amount of the asset tokens being
    //                           sold by the taker
    //   tokensSold              is the number of the asset tokens sold
    //   etherValueOfTokensSold  is the ether value of the asset tokens sold
    //
    // This method was called sell() in the old version
    //
    function takerSellAsset(uint256 amountOfTokensToSell) {
        if (buysTokens || msg.sender == owner) {
            // Maximum number of token the contract can buy
            // Note that buyPrice has already been validated as > 0
            uint256 can_buy = this.balance / buyPrice;
            // Token lots available
            // Note that units has already been validated as > 0
            uint256 order = amountOfTokensToSell / units;
            // Adjust order for funds available
            if (order > can_buy) order = can_buy;
            if (order > 0) {
                // Extract user tokens
                if (!ERC20(asset).transferFrom(msg.sender, address(this), order * units)) throw;
                // Pay user
                if (!msg.sender.send(order * buyPrice)) throw;
            }
            TakerSoldAsset(msg.sender, amountOfTokensToSell, order * units, order * buyPrice);
        }
    }

    // Taker buys tokens by sending ethers
    function () payable {
        takerBuyAsset();
    }
}

// This contract deploys TokenTrader contracts and logs the event
contract TokenTraderFactory is Owned {

    event TradeListing(address indexed ownerAddress, address indexed tokenTraderAddress,
        address indexed asset, uint256 buyPrice, uint256 sellPrice, uint256 units,
        bool buysTokens, bool sellsTokens);
    event OwnerWithdrewERC20Token(address indexed tokenAddress, uint256 tokens);

    mapping(address => bool) _verify;

    // Anyone can call this method to verify the settings of a
    // TokenTrader contract. The parameters are:
    //   tradeContract  is the address of a TokenTrader contract
    //
    // Return values:
    //   valid        did this TokenTraderFactory create the TokenTrader contract?
    //   owner        is the owner of the TokenTrader contract
    //   asset        is the ERC20 asset address
    //   buyPrice     is the buy price in ethers per `units` of asset tokens
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   buysTokens   is the TokenTrader contract buying tokens?
    //   sellsTokens  is the TokenTrader contract selling tokens?
    //
    function verify(address tradeContract) constant returns (
        bool    valid,
        address owner,
        address asset,
        uint256 buyPrice,
        uint256 sellPrice,
        uint256 units,
        bool    buysTokens,
        bool    sellsTokens
    ) {
        valid = _verify[tradeContract];
        if (valid) {
            TokenTrader t = TokenTrader(tradeContract);
            owner         = t.owner();
            asset         = t.asset();
            buyPrice      = t.buyPrice();
            sellPrice     = t.sellPrice();
            units         = t.units();
            buysTokens    = t.buysTokens();
            sellsTokens   = t.sellsTokens();
        }
    }

    // Maker can call this method to create a new TokenTrader contract
    // with the maker being the owner of this new contract
    //
    // Parameters:
    //   asset        is the ERC20 asset address
    //   buyPrice     is the buy price in ethers per `units` of asset tokens
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   buysTokens   is the TokenTrader contract buying tokens?
    //   sellsTokens  is the TokenTrader contract selling tokens?
    //
    // For example, listing a TokenTrader contract on the REP Augur token where
    // the contract will buy REP tokens at a rate of 39000/100000 = 0.39 ETH
    // per REP token and sell REP tokens at a rate of 41000/100000 = 0.41 ETH
    // per REP token:
    //   asset        0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5
    //   buyPrice     39000
    //   sellPrice    41000
    //   units        100000
    //   buysTokens   true
    //   sellsTokens  true
    //
    // The TradeListing() event is logged with the following parameters
    //   ownerAddress        is the Maker's address
    //   tokenTraderAddress  is the address of the newly created TokenTrader contract
    //   asset               is the ERC20 asset address
    //   buyPrice            is the buy price in ethers per `units` of asset tokens
    //   sellPrice           is the sell price in ethers per `units` of asset tokens
    //   unit                is the number of units of asset tokens
    //   buysTokens          is the TokenTrader contract buying tokens?
    //   sellsTokens         is the TokenTrader contract selling tokens?
    //
    function createTradeContract(
        address asset,
        uint256 buyPrice,
        uint256 sellPrice,
        uint256 units,
        bool    buysTokens,
        bool    sellsTokens
    ) returns (address trader) {
        // Cannot have invalid asset
        if (asset == 0x0) throw;
        // Check for ERC20 allowance function
        // This will throw an error if the allowance function
        // is undefined to prevent GNTs from being used
        // with this factory
        uint256 allowance = ERC20(asset).allowance(msg.sender, this);
        // Cannot set zero or negative price
        if (buyPrice <= 0 || sellPrice <= 0) throw;
        // Must make profit on spread
        if (buyPrice >= sellPrice) throw;
        // Cannot buy or sell zero or negative units
        if (units <= 0) throw;

        trader = new TokenTrader(
            asset,
            buyPrice,
            sellPrice,
            units,
            buysTokens,
            sellsTokens);
        // Record that this factory created the trader
        _verify[trader] = true;
        // Set the owner to whoever called the function
        TokenTrader(trader).transferOwnership(msg.sender);
        TradeListing(msg.sender, trader, asset, buyPrice, sellPrice, units, buysTokens, sellsTokens);
    }

    // Factory owner can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The OwnerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    function ownerWithdrawERC20Token(address tokenAddress, uint256 tokens) onlyOwner returns (bool ok) {
        OwnerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

    // Prevents accidental sending of ether to the factory
    function () {
        throw;
    }
}
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.