NOTE Dec 14 2016: I’ve found a bug in the original REP TokenTraderFactory that could potentially lead to a loss in $$$. I’m working with the original author to fix this. Do not use this contract anymore. A new contract with the bug fixed will be deployed.
The GNT SellOnly TokenTraderFactory does not contain the same bug.
The Trustless Golem Network Token (GNT) Selling Contract allows GNT sellers to list their sale contracts on https://cryptoderivatives.market/. As of Tue Nov 22 2016 22:37:16 UTC, 15,782.71674413 ethers (ETH) has been paid to purchase 10,035,267.26729285 GNTs through these sale contracts with an average rate of 0.00157273 ETH for 1 GNT. This is equivalent to ~ BTC 208, USD 156,468 or AUD 211,976. Source https://cryptoderivatives.market/gnt_trades.html.
There has been some interest in creating a buy/sell market using the original Augur REP TokenTraderFactory contract to trade The Golem Network Token Wrapped (GNTW) Contract (Work In Progress). Let’s look at this REP TokenTraderFactory contract in this post.
Table of contents
- 1. How The Buy/Sell Contract Works
- 2. How To List A Buy/Sell Contract
- 3. How To Deactivate/Activate Your Listing
- 4. How To Execute A Buy On A Buy/Sell Contract
- 5. How To Execute A Sell On A Buy/Sell Contract
- 6. Watch The TokenTraderFactory In Ethereum Wallet / Mist
- 7. Watch The TokenTrader Contract In Ethereum Wallet / Mist
- 8. The Original Trustless REP Token Trading Contract Source Code
- 9. Application Binary Interface (ABI)
1. How The Buy/Sell Contract Works
2. How To List A Buy/Sell Contract
3. How To Deactivate/Activate Your Listing
4. How To Execute A Buy On A Buy/Sell Contract
5. How To Execute A Sell On A Buy/Sell Contract
6. Watch The TokenTraderFactory In Ethereum Wallet / Mist
7. Watch The TokenTrader Contract In Ethereum Wallet / Mist
8. The Original Trustless REP Token Trading Contract Source Code
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
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 } } |
9. Application Binary Interface (ABI)
9.1 TokenTraderFactory ABI
[{"constant":false,"inputs":[{"name":"_asset","type":"address"},{"name":"_buyPrice","type":"uint256"},{"name":"_sellPrice","type":"uint256"},{"name":"_units","type":"uint256"},{"name":"_sellsTokens","type":"bool"},{"name":"_buysTokens","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":"buyPrice","type":"uint256"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"sellsTokens","type":"bool"},{"name":"buysTokens","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"}]
Here is the corresponding event and method listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
contract TokenTraderFactory { event TradeListing(bytes32 bookid, address owner, address addr); event NewBook(bytes32 bookid, address asset, uint256 units); function verify(address tradeContract) constant returns ( bool valid, address asset, uint256 buyPrice, uint256 sellPrice, uint256 units, bool sellsTokens, bool buysTokens); function createTradeContract(address _asset, uint256 _buyPrice, uint256 _sellPrice, uint256 _units, bool _sellsTokens, bool _buysTokens) returns (address); function (); } |
9.2 TokenTrader 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":true,"inputs":[],"name":"buyPrice","outputs":[{"name":"","type":"uint256"}],"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":"_buysTokens","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":"deposit","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"sell","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":"_sellsTokens","type":"bool"},{"name":"_buysTokens","type":"bool"}],"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sells","type":"bool"},{"indexed":false,"name":"buys","type":"bool"}],"name":"ActivatedEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdateEvent","type":"event"}]
Here is the corresponding property, event and method listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
contract TokenTrader is owned { address public asset; uint256 public buyPrice; uint256 public sellPrice; uint256 public units; bool public sellsTokens; bool public buysTokens; event ActivatedEvent(bool sells, bool buys); event UpdateEvent(); function TokenTrader(address _asset, uint256 _buyPrice, uint256 _sellPrice, uint256 _units, bool _sellsTokens, bool _buysTokens); function activate(bool _sellsTokens, bool _buysTokens) onlyOwner; function deposit() payable onlyOwner; function withdrawAsset(uint256 _value) onlyOwner returns (bool ok); function withdrawToken(address _token, uint256 _value) onlyOwner returns (bool ok); function withdraw(uint256 _value) onlyOwner returns (bool ok); function buy() payable; function sell(uint256 amount); function (); } |