GNTTokenTrader And GNTTokenTraderFactory

BokkyPooBah edited this page Mar 7, 2017 · 75 revisions
Clone this wiki locally

The GNTTokenTrader contract is the original SellOnly TokenTraderFactory deployed on the Ethereum network to support the Golem Network Token (GNT) that is partially ERC20 compliant.

A Market Maker uses the GNTTokenTraderFactory to create a GNTTokenTrader contract that specifies the price the Market Maker is willing to sell GNTs. The Market Maker then loads the contracts with GNTs.

A Market Taker simply sends ethers to the GNTTokenTrader contracts to receive the GNTs at the specified price. The Market Maker can later withdraw the ethers from the GNTTokenTrader contracts.

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.
  4. When convenient, please replace the GNTTokenTrader and GNTTokenTraderFactory contracts with the improved TokenSeller and TokenSellerFactory. The differences between the GNTTokenTrader and GNTTokenTraderFactory compared to the TokenSeller and TokenSellerFactory can be found in the table at the Dec 19 2016 History entry.


Table of contents



GNTTokenTrader And GNTTokenTraderFactory 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 40,874 0.000817480 $0.0083 0x78e4ca4a...
Maker Create Sale Contract 761,317 0.031213997 $0.3200 0x6feb16a4...
Maker (De)Activate Contract No sample yet
Maker Add Tokens To Contract 51,720 0.001034400 $0.0100 0x54bd74fa...
Maker Withdraw Ethers From Contract 30,516 0.000610320 $0.0062 0xf70bbfed...
Maker 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 Taker Operations

Buying Tokens From GNTTokenTrader Contracts

To purchase tokens from a GNTTokenTrader contract, Find the GNTTokenTrader contract with a price suitable to you and with a GNT balance. Send ethers from your Ethereum address using your favourite wallet (e.g. Ethereum Wallet / Mist, Parity or https://www.myetherwallet.com/). DO NOT send ethers from your exchange (e.g. Poloniex or Kraken) account.

Send a small amount of ethers first and Check Your GNT Balance. Confirm the number of tokens you receive is correct. You should also see your executed trade in https://cryptoderivatives.market/gnt_trades.html:

If you send more ethers than there are tokens owned by the contract at the exchange rate, you will receive a refund of the excess ethers you paid back to your address.


The Price Calculations

When the Market Taker sends ethers to the GNTTokenTrader contract, the function () payable in the code below is called. This function then calls function buy() payable.

    //user buys token with ETH
    function buy() payable {
        if(sellsTokens || msg.sender == owner) 
        {
            uint order   = msg.value / sellPrice; 
            uint can_sell = ERC20(asset).balanceOf(address(this)) / units;

            if(order > can_sell)
            {
                uint256 change = msg.value - (can_sell * sellPrice);
                order = can_sell;
                if(!msg.sender.send(change)) throw;
            }

            if(order > 0) {
                if(!ERC20(asset).transfer(msg.sender,order * units)) throw;
            }
            UpdateEvent();
        }
        else if(!msg.sender.send(msg.value)) throw;  // return user funds if the contract is not selling
    }

    // sending ETH to contract sells GNT to user
    function () payable {
        buy();
    }

The code above has the effect of:

  1. Transferring numberOfEthersSentByTaker / sellPrice x units tokens to the Taker's account
  2. Transferring numberOfEthersSentByTaker to the Maker's contract
  3. If there are insufficient tokens in the Maker's contract, the number of tokens transferred to the Taker's account will be limited by this amount. The excess amount of ethers over the amount required to purchase the limited number of tokens in the contract will be sent back to the Taker's account

So, if sellPrice is 194 and units is 100,000, the effective price is 0.00194 ETH per GNT. If if sellPrice is 1939 and units is 1,000,000, the effective price is 0.001939 ETH per GNT.



Market Maker Operations

The operations documented below are for use with Ethereum Wallet / Mist.

Create GNTTokenTrader Contract

Here are some sample contracts. Enter the contract address into the GNTTokenTraderFactory verify field and click Query to view the contract data:

Address asset sellPrice units ETH per GNT
0x45ff5e9d... 0xa7447644... 193 100000 0.00193000
0x32ed5652... 0xa7447644... 2109 999977 0.00210905
0x66712798... 0xa7447644... 190 90000 0.00211111
(sample) 0xa7447644... 2345 1000000 0.00234500

Note that asset must always be the GNT contract address which is 0xa74476443119a942de498590fe1f2454d7d4ac0d.

For a contract to be listed on https://cryptoderivatives.market/, the following conditions need to be met:

  • sellsTokens = true
  • units >= 1
  • units <= 100,000,000
  • (sellPrice / units) >= 0.0000001
  • (sellPrice / units) <= 1,000,000
  • tokenBalance >= 0.00000001

(De)Activate GNTTokenTrader Contract

Watch the GNTTokenTrader contract. View the contract and under the WRITE TO CONTRACT section:

  • Under Select function, select Activate

  • Under sells tokens, tick the Yes checkbox to activate the contract or untick the checkbox to deactivate the contract

  • Click on Execute, then confirm your transaction.


Adding Tokens To GNTTokenTrader Contract

To add tokens to the GNTTokenTrader contract, Transfer Your GNT Tokens to the GNTTokenTrader contract address.


Withdrawing Ethers From The GNTTokenTrader Contract

Watch the GNTTokenTrader contract. View the contract and under the WRITE TO CONTRACT section:

  • Under Select function, select Withdraw

  • Under value, enter the number of ethers to 18 decimal places. e.g., 1230000000000000000 is 1230000000000000000 / 1e18 = 1.23 ETH

  • Under Execute from, select your account that is the owner of this GNTTokenTrader contract

  • Click on Execute, then confirm your transaction.


Withdrawing Tokens From The GNTTokenTrader Contract

Watch the GNTTokenTrader contract. View the contract and under the WRITE TO CONTRACT section:

  • Under Select function, select Withdraw Asset

  • Under value, enter the number of GNTs to 18 decimal places. e.g., 1230000000000000000 is 1230000000000000000 / 1e18 = 1.23 GNT

  • Under Execute from, select your account that is the owner of this GNTTokenTrader contract

  • Click on Execute, then confirm your transaction.


Withdrawing Other Tokens From The GNTTokenTrader Contract

This function is to allow the owner to withdraw tokens OTHER than GNTs that have been accidentally sent to the GNTTokenTrader contract.

Watch the GNTTokenTrader contract. View the contract and under the WRITE TO CONTRACT section:

  • Under Select function, select Withdraw Token

  • Under token - address, enter the address of the token contract. e.g., the address for Augur REP is 0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5

  • Under value, enter the number of tokens. Note that you will have to take into account the number of decimal places the tokens are denominated in. For Augur REP this is 18 decimal places. e.g., 2340000000000000000 is 2340000000000000000 / 1e18 = 2.34 REP

  • Under Execute from, select your account that is the owner of this GNTTokenTrader contract

  • Click on Execute, then confirm your transaction.



How To Watch The GNTTokenTraderFactory 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 GNTTokenTraderFactory

  • Under CONTRACT ADDRESS:

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

    [{"constant":false,"inputs":[{"name":"_asset","type":"address"},{"name":"_sellPrice","type":"uint256"},{"name":"_units","type":"uint256"},{"name":"_sellsTokens","type":"bool"}],"name":"createTradeContract","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"tradeContract","type":"address"}],"name":"verify","outputs":[{"name":"valid","type":"bool"},{"name":"asset","type":"address"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"sellsTokens","type":"bool"}],"payable":false,"type":"function"},{"payable":false,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bookid","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"addr","type":"address"}],"name":"TradeListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"bookid","type":"bytes32"},{"indexed":false,"name":"asset","type":"address"},{"indexed":false,"name":"units","type":"uint256"}],"name":"NewBook","type":"event"}]

  • Click OK



How To Find GNTTokenTrader Contracts

Finding GNTTokenTrader Contracts On CryptoDerivatives.Market

Navigate to https://cryptoderivatives.market/ and the contract addresses are on the left column of the top grid:


Finding GNTTokenTrader Contracts In Ethereum Wallet

View the GNTTokenTraderFactory contract in Ethereum Wallet (see above). Scroll to the bottom of the page. You may have to click on Watch contract events to trigger the loading of the events. In the first event on the page, the GNTTokenTrader address is listed in the field addr, and in this case is 0x1a22bb6827dbb7df60a6de726519c2a509271695.


Finding GNTTokenTrader Contracts On EtherScan.io

Navigate to https://etherscan.io/address/0xc4af56cd5254aef959d4bce2f75874007808b701#internaltx.

Click on the Contract Creation field in the To column to view the GNTTokenTrader contract details as shown below:


Finding GNTTokenTrader Contracts On Geth Script

There is a findGNTTokenTrader script in https://github.com/bokkypoobah/FindGNTTokenTrader .

Here is some sample output from https://github.com/bokkypoobah/FindGNTTokenTrader/blob/master/findGNTTokenTraderOutput_20161116_005815UTC.csv:

\#,TokenTraderAddress,SellsTokens,EtherPer1000Tokens,TokenBalance
26,0x4b17f65fc6450cbcced9e893dd84123a5fc13362,TRUE,1.45,110624.7596
27,0x1e00980cb7b109d290fcf6c4e579debb8c836c39,TRUE,1.45,164700.3448
24,0x1a22bb6827dbb7df60a6de726519c2a509271695,TRUE,1.5,499660.2167
6,0xc0081f0e16cbceec6df8e63986212a52ee493540,TRUE,1.88,102821.2909
23,0x406a65de7a2e94ef19597a79296e269fada85a71,TRUE,1.9,136166.8742
15,0x5eb860c816789bb52300a0675300701eca203cf8,TRUE,1.95,158854.1026
0,0x399156ee3339f4b29a53e307b98cce09fda3bac7,TRUE,2,5430.015
10,0xa7f8da2594eaf1e8573a5974be6028f64157cd7a,TRUE,2,713516
22,0xf31057bc0ff5e3e3b32652b29c0451624392567c,TRUE,2,100050
25,0xffd41efe3d3c073f4c7dbf2cec7c19111fc7523e,TRUE,2,4250
17,0x6e3f7ad4d9accb12452744fb2785ecac31ea8026,TRUE,2.05,50000
18,0x4b34da26bfb35d3089c455cb483a5fff8695d447,TRUE,2.11,200000
11,0x42be3b481f25b0148cd1a144d8ca22cda5b677e7,TRUE,2.33,9.00E-12
2,0x4104e4b12e73bc99dd4f20a39525d07aa395c0d4,TRUE,3,1794.833333
19,0x593fd4a14f64282891a1369442462361a1908b01,TRUE,3.75,900


How To Verify GNTTokenTrader Contracts

Verifying GNTTokenTrader Contracts In Ethereum Wallet

View the GNTTokenTraderFactory contract in Ethereum Wallet (see above). You may have to click on SHOW CONTRACT INFO. In the READ FROM CONTRACT section, enter the address of the GNTTokenTrader contract you want to verify. I have used 0x1a22bb6827dbb7df60a6de726519c2a509271695 in the following screen. The status of the contract at the address entered will be displayed.


Verifying GNTTokenTrader Contracts On EtherScan.io

Navigate to https://etherscan.io/ and enter the GNTTokenTrader contract into the search box. In this example I have used the address 0xaf4ace80410fe1274a2244a3714d387c11b020a8. You can view the ether balance in the ETH Balance field. You can view the token balance in the View Token Balances dropdown. Only the Golem token balance is relevant to this GNTTokenTrader contract.

Click on the first address in the Contract Creator field - this should be the GNTTokenTraderFactory contract at address 0xc4af56cd5254aef959d4bce2f75874007808b701. Click on the Read Contract tab. In the verify field, enter the address of the GNTTokenTrader contract to be verified. In this example I have used the address 0xaf4ace80410fe1274a2244a3714d387c11b020a8 and the results show that this contract is a valid GNTTokenTrader contract with a selling price of 194 / 100000 = 0.00194 ETH per GNT.



How To Watch A GNTTokenTrader 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 {GNTTokenTrader identifying information}, e.g., GNTTT 1.94

  • Under CONTRACT ADDRESS, enter the contract address as you have found and verified in the section above.

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

    [{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"withdraw","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":"_value","type":"uint256"}],"name":"withdrawAsset","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"units","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_value","type":"uint256"}],"name":"withdrawToken","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"buy","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_sellsTokens","type":"bool"}],"name":"activate","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":"_sellPrice","type":"uint256"},{"name":"_units","type":"uint256"},{"name":"_sellsTokens","type":"bool"}],"type":"constructor","payable":true},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sells","type":"bool"}],"name":"ActivatedEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdateEvent","type":"event"}]

  • Click OK

If you now click on the newly created Watch Contract entry, you will see a screen like the following:

The Golem Network Token balance requires that you have created a Watch Token entry for the Golem Network Token as described in How To Watch The GNT Token In Ethereum Wallet / Mist.

Click on the SHOW CONTRACT INFO button. You can now see the functions the Market Maker and Market Taker are able to execute - the details are described in the next two sections.



Source Code

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

pragma solidity ^0.4.0;

//https://github.com/nexusdev/erc20/blob/master/contracts/erc20.sol

contract ERC20Constant {
    function balanceOf( address who ) constant returns (uint value);
}
contract ERC20Stateful {
    function transfer( address to, uint value) returns (bool ok);
}
contract ERC20Events {
    event Transfer(address indexed from, address indexed to, uint value);
}
contract ERC20 is ERC20Constant, ERC20Stateful, ERC20Events {}

contract owned {
    address public owner;

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

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

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

// contract can 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 sellPrice;   // contract sells lots of tokens at this price
    uint256 public units;       // lot size (token-wei)

    bool public sellsTokens;    // is contract selling

    event ActivatedEvent(bool sells);
    event UpdateEvent();

    function TokenTrader (
        address _asset, 
        uint256 _sellPrice, 
        uint256 _units,
        bool    _sellsTokens
        )
    {
          asset         = _asset; 
          sellPrice    = _sellPrice;
          units         = _units; 
          sellsTokens   = _sellsTokens;

          ActivatedEvent(sellsTokens);
    }

    // modify trading behavior
    function activate (
        bool    _sellsTokens
        ) onlyOwner
    {
          sellsTokens   = _sellsTokens;

          ActivatedEvent(sellsTokens);
    }

    // allow owner to remove trade token
    function withdrawAsset(uint256 _value) onlyOwner returns (bool ok)
    {
        return ERC20(asset).transfer(owner,_value);
        UpdateEvent();
    }

    // allow owner to remove arbitrary tokens
    // included just in case contract receives wrong token
    function withdrawToken(address _token, uint256 _value) onlyOwner returns (bool ok)
    {
        return ERC20(_token).transfer(owner,_value);
        UpdateEvent();
    }

    // allow owner to remove ETH
    function withdraw(uint256 _value) onlyOwner returns (bool ok)
    {
        if(this.balance >= _value) {
            return owner.send(_value);
        }
        UpdateEvent();
    }

    //user buys token with ETH
    function buy() payable {
        if(sellsTokens || msg.sender == owner) 
        {
            uint order   = msg.value / sellPrice; 
            uint can_sell = ERC20(asset).balanceOf(address(this)) / units;

            if(order > can_sell)
            {
                uint256 change = msg.value - (can_sell * sellPrice);
                order = can_sell;
                if(!msg.sender.send(change)) throw;
            }

            if(order > 0) {
                if(!ERC20(asset).transfer(msg.sender,order * units)) throw;
            }
            UpdateEvent();
        }
        else if(!msg.sender.send(msg.value)) throw;  // return user funds if the contract is not selling
    }

    // sending ETH to contract sells GNT to user
    function () payable {
        buy();
    }
}

// This contract deploys TokenTrader contracts and logs the event
// trade pairs are identified with sha3(asset,units)

contract TokenTraderFactory {

    event TradeListing(bytes32 bookid, address owner, address addr);
    event NewBook(bytes32 bookid, address asset, uint256 units);

    mapping( address => bool ) _verify;
    mapping( bytes32 => bool ) pairExits;
    
    function verify(address tradeContract)  constant returns (
        bool valid,
        address asset, 
        uint256 sellPrice, 
        uint256 units,
        bool    sellsTokens
        ) {
            
            valid = _verify[tradeContract];
            
            if(valid) {
                TokenTrader t = TokenTrader(tradeContract);
                
                asset = t.asset();
                sellPrice = t.sellPrice();
                units = t.units();
                sellsTokens = t.sellsTokens();
            }
        
    }

    function createTradeContract(       
        address _asset, 
        uint256 _sellPrice, 
        uint256 _units,
        bool    _sellsTokens
        ) returns (address) 
    {
        if(_units == 0) throw;              // can't sell zero units

        address trader = new TokenTrader (
                     _asset, 
                     _sellPrice, 
                     _units,
                     _sellsTokens);

        var bookid = sha3(_asset,_units);

        _verify[trader] = true; // record that this factory created the trader

        TokenTrader(trader).transferOwnership(msg.sender); // set the owner to whoever called the function

        if(pairExits[bookid] == false) {
            pairExits[bookid] = true;
            NewBook(bookid, _asset, _units);
        }

        TradeListing(bookid,msg.sender,trader);
    }

    function () {
        throw;     // Prevents accidental sending of ether to the factory
    }
}