Trustless Golem Network Token (GNT) Selling Contract

Some individuals missed out on purchasing the Golem Network Token (GNT) in the Golem Crowdfunding Funded To 820,000 ETH Cap, as the sale was over within ~ half an hour. The tokens were sold a 1 ether (ETH) = 1,000 GNTs, or 0.001 ETH per 1 GNT.

User /u/cintix on reddit.com/r/ethereum posted Trustless GNT Selling Contract stating:

I saw there was interest in a contract that sells GNT, so I made one! It sells 100,000 GNT at 2x crowdsale price. You just send ETH to it and it sends you GNT back! Be sure to check through the code and test with a small amount first!

See also Trustless GNT Selling Contract on reddit.com/r/ethtrader for many more contracts.

The code in this Trustless GNT Selling contract is based on user /u/JonnyLatte‘s code originally posted in Live testing ETH <-> Token, Atomic swap market to trade Augur REP tokens.


Table of contents


cryptoderivatives.market

Check out the first version of https://cryptoderivatives.market/, a decentralised GNT/ETH exchange contracts market.


Find GNT TokenTrader Contracts Script

I’ve just created a script to list the TokenTrader contracts – see:


Find GNT TokenTrader Contracts In Ethereum Wallet/Mist

If you want to find the TokenTrader contracts that have been created by the TokenTraderFactory contract, add the TokenTraderFactor to your Watch Contracts. To do this, click on the Contracts tab and click Watch Token. Enter the GNTTokenTraderFactory in the Contract Name field, 0xc4af56cd5254aef959d4bce2f75874007808b701 in the Contract Address field, and the following Application Binary Interace (ABI) data in the JSON Interface field and click OK:

[{"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"}]

Screen-Shot-2016-11-14-at-23.32.32

Now click on the GNTTOKENTRADERFACTORY contract in the Contracts tab. Scroll down and click the checkbox for Watch contract events. You will find the list of created TokenTrader contracts – the highlighted address is the address used in the next “Watch GNT TokenTrader Contract In Ethereum Wallet/Mist” section.
Screen-Shot-2016-11-14-at-23.44.06


Watch GNT TokenTrader Contract In Ethereum Wallet/Mist

If you want to confirm the contract parameters for a TokenTrader contract in Mist, find the TokenTrader contract address your want to watch, e.g., #28 0x406a65de7a2e94ef19597a79296e269fada85a71, from the list generated in the “Find GNT TokenTrader Contracts” section above:

28 TokenTraderAddress: 0x406a65de7a2e94ef19597a79296e269fada85a71
  Deployed                   : #2624456 at Mon, 14 Nov 2016 08:39:06 UTC txHash 0x0eca65c8457f60599d55dfe8554e9232b824eba714d941d318660d7e691629af
  Owner                      : 0x45689bac94edae26625521f1361748119f05fc30
  Asset address              : 0xa74476443119a942de498590fe1f2454d7d4ac0d GNT
  Sells tokens               : true
  Sell price                 : 190
  Units                      : 100000
  Ethers per 1,000 tokens    : 1.9
  Current token balance      : 195684.2105263157895
Current ether balance : 9.952798346

Use the following ABI:

[{"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,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sells","type":"bool"}],"name":"ActivatedEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdateEvent","type":"event"}]

Watch GNT Token In Ethereum Wallet/Mist

If you have not already added a Watch Token for the Golem Network Token in Ethereum Wallet / Mist, click on the Contracts tab and click on Watch Token. Enter the GNT TokenTrader address 0xa74476443119A942dE498590Fe1f2454d7D4aC0d into the Token Contract Address field as shown below, then click OK:
Screen-Shot-2016-11-14-at-23.13.19

In Ethereum Wallet / Mist, click on the Contracts tab and click on Watch Contract. I have entered the Contract Name of “GNTTokenTrader28”, the appropriate Contract Address and add the ABI above in the JSON Interface field, then click OK.
Screen-Shot-2016-11-14-at-22.56.15

In Ethereum Wallet / Mist, click on the Contracts tab and click on your newly created Watch Contract. You will see the number of GNTs that the contract is selling:
Screen-Shot-2016-11-14-at-22.59.53

Scrolling down the page, you will see the Read From Contract fields on the left side. You can find out the Sell Price (190 in contract #28), Units (100,000 in contract #28). The price per unit of GNT is 190 / 100,000 = 0.0019 ETH. The price per 1,000 GNTs is 1.9 ETH for this contract #28.
Screen-Shot-2016-11-14-at-23.06.49

If you want to, you can execute the Buy function on the right side of the screen – test with a small amount first.


Trustless GNT Token Selling Contract

Following is the verified contact source for the GNT TokenTraderFactory contract at 0xc4af56cd5254aef959d4bce2f75874007808b701. This factory contract is used to create TokenTrader contracts as deployed to 0x399156ee3339f4b29a53e307b98cce09fda3bac7 for the 2x contract, and 0x4104e4b12e73bc99dd4f20a39525d07aa395c0d4 for the 3x contract:

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
    }
}

Following are the instructions by /u/JonnyLatte posted at Trustless GNT Selling Contract for using the contract above:

Someone has asked me how to use my contract to sell GNT. I’m going to put the reply here so that I am not repeating myself:

I gave some simple instructions for using my platform to make your own trades here

It should be fine to sell GNT however I have not tested it and it wont be able to buy GNT only sell because GNT is missing the approve() and transferFrom() functions. Since these functions are not used for selling or adding and withdrawing funds from a trade contract it should not be a problem. I have not tested this though so if you do decide to use it you should test with a small amount first.

However since OP has gone to the trouble of stripping out the missing GNT functionality and is successfully selling I may as well show how to use this contract rather than my own.

First up let me describe a trade contract and then how to create one:

A trade contract holds funds and has an owner that can withdraw those funds to their account. To deposit tokens into it you just send them to its address. To withdraw them you call its withdrawToken function giving it the address of the token and the amount in that tokens smallest units.

The trade contract can also sell one token called the asset at a fixed rate that is determined when the contract is created. For ease of use you can also withdraw that token using withdrawAsset which does not require you to specify the address of the token because that was already given when the contract was created. I have pasted a copy of the trade contract’s ABI which the owners of a trade contract can use to follow instances for withdrawing funds.

You do not deploy a trade contract directly, instead you use a factory contract which creates an instance for you given the setup. You do that by following the factory address with the factory ABI and calling the function createTradeContract.

The parameters of createTradeContract for OP’s sell only factory are:

address _asset, 
uint256 _sellPrice, 
uint256 _units,
bool    _sellsTokens
the asset is the address of the token being sold. In the
 case of GNT thats 0xa74476443119A942dE498590Fe1f2454d7D4aC0d

_sellPrice is the number of WEI the contract needs to be
 sent to send some tokens in return

_units is how many of the smallest units of the token that
 are sent in return for sending _sellPrice amounts of WEI

_sellsTokens should be set to true if you want the
 contract to immediately accept ETH and sell the
 token or false if you want to start with selling
 deactivated for the purpose of testing (the owner
 account will be able to sell to it but no one
 else until activate(true) is called)

For example OP made the contract at 0x4104e4b12e73bc99dd4f20a39525d07aa395c0d4 selling GNT at 3x the crowd sale price. The parameters they used are:

address _asset = 0xa74476443119a942de498590fe1f2454d7d4ac0d (GNT)
uint256 _sellPrice = 300
uint256 _units = 100000
bool    _sellsTokens = true

I would suggest sticking with their choice of units and adjusting _sellPrice to fit whatever price level you want: 100 for the same price as the crowd sale 200 for double etc.

If you successfully create a trade contract the factory will fire off an event showing the address and owner. You can also see them at https://etherscan.io/address/0xc4af56cd5254aef959d4bce2f75874007808b701#internaltx and then follow the one you think is yours double checking you are the owner.


The Original Trustless REP Token Trading Contract

Following is the verified contact source for the original Augur REP TokenTraderFactory contract at 0x3398080b81a1cff1429af347ce2b17fc28de3937 by /u/JonnyLatte. This factory contract then creates the TokenTrader contract as deployed to 0x399156ee3339f4b29a53e307b98cce09fda3bac7. See The Original Augur REP TokenTraderFactory Contract for further information on this contract:

pragma solidity ^0.4.0;

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

contract ERC20Constant {
    function totalSupply() constant returns (uint supply);
    function balanceOf( address who ) constant returns (uint value);
    function allowance(address owner, address spender) constant returns (uint _allowance);
}
contract ERC20Stateful {
    function transfer( address to, uint value) returns (bool ok);
    function transferFrom( address from, address to, uint value) returns (bool ok);
    function approve(address spender, uint value) returns (bool ok);
}
contract ERC20Events {
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval( address indexed owner, address indexed spender, 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 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;   // contact 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 sellsTokens;    // is contract selling
    bool public buysTokens;     // is contract buying

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

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

          ActivatedEvent(sellsTokens,buysTokens);
    }

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

          ActivatedEvent(sellsTokens,buysTokens);
    }

    // allows owner to deposit ETH
    // deposit tokens by sending them directly to contract
    // buyers must not send tokens to the contract, use: sell(...)
    function deposit() payable onlyOwner {
        UpdateEvent();
    }

    // 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
    }

    // user sells token for ETH
    // user must set allowance for this contract before calling
    function sell(uint256 amount) {
        if (buysTokens || msg.sender == owner) {
            uint256 can_buy = this.balance / buyPrice;  // token lots contract can buy
            uint256 order = amount / units;             // token lots available

            if(order > can_buy) order = can_buy;        // adjust order for funds

            if (order > 0)
            { 
                // extract user tokens
                if(!ERC20(asset).transferFrom(msg.sender, address(this), amount)) throw;

                // pay user
                if(!msg.sender.send(order * buyPrice)) throw;
            }
            UpdateEvent();
        }
    }

    // sending ETH to contract sells ETH 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 buyPrice, 
        uint256 sellPrice, 
        uint256 units,
        bool    sellsTokens,
        bool    buysTokens       
        ) {
            
            valid = _verify[tradeContract];
            
            if(valid) {
                TokenTrader t = TokenTrader(tradeContract);
                
                asset = t.asset();
                buyPrice =t.buyPrice();
                sellPrice = t.sellPrice();
                units = t.units();
                sellsTokens = t.sellsTokens();
                buysTokens = t.buysTokens();
            }
        
    }

    function createTradeContract(       
        address _asset, 
        uint256 _buyPrice, 
        uint256 _sellPrice, 
        uint256 _units,
        bool    _sellsTokens,
        bool    _buysTokens
        ) returns (address) 
    {
        if(_buyPrice > _sellPrice) throw; // must make profit on spread
        if(_units == 0) throw;              // can't sell zero units

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

        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
    }
}
This entry was posted in Blog and tagged , , , , , . Bookmark the permalink.