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:
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:
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:
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 ();
}