A TokenTrader quoting Augur's REP against ether at a fixed spread, in lots of 100,000 token units.
Historical Significance
An order book kept by a contract rather than an exchange, at a time when trading a token meant finding a counterparty or trusting a custodian. Each trader holds one asset at fixed prices with an explicit spread, which is a market maker without a matching engine: the liquidity is whatever the owner deposited and the price does not move with it.
Context
Deployed in late 2016 and early 2017, after ERC-20 had settled the transfer interface but before automated market makers with a pricing curve existed. Trading a token then meant a centralised exchange listing it or a contract like this one quoting a fixed price.
Key Facts
Description
A trading contract from the TokenTrader set: an automated market maker in a single ERC-20 asset, funded by its owner and priced in wei per lot rather than per token. The asset address, the lot size and the prices are fixed in the constructor and no function changes them.
A buyer sends ether and receives whole lots of the token, with any remainder refunded. The contract never sells more lots than it holds. The owner may deposit ether, withdraw ether, withdraw the traded asset, and withdraw any other token that arrives by mistake.
The switches that turn buying and selling on and off are exposed through activate(), which carries no owner check, so any account can stop or restart the contract's trading.
This one trades Reputation, the Augur token at 0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5. Its lot is 100,000 token units; it buys a lot for 65,000 wei and sells one for 65,325 wei, a spread of half a percent, and it was created with both sides switched on.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
DAO Fork Era
The controversial fork to recover funds from The DAO hack.
Bytecode Overview
Verified Source Available
This contract has verified source code on Etherscan.
Show source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
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
)
{
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 {
}
// allow owner to remove trade token
function withdrawAsset(uint256 _value) onlyOwner returns (bool ok)
{
return ERC20(asset).transfer(owner,_value);
}
// 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);
}
// allow owner to remove ETH
function withdraw(uint256 _value) onlyOwner returns (bool ok)
{
if(this.balance >= _value) {
return owner.send(_value);
}
}
//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 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 ) public verify;
mapping( bytes32 => bool ) pairExits;
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
}
}External Links
Related contracts
TokenTrader
Same deployerA TokenTrader quoting Augur's REP against ether, buying a lot at 32,013 wei and selling at 32,500.
0xe7f810...5259f9January 3, 2017ShapeShift Chain-Split Forwarder
Same eraEarliest ShapeShift on-chain contract (Jul 24, 2016). Simple ETH forwarder with an active flag — routes deposits to target when active and msg.value > 0.
0xa2d5c5...d549deJuly 24, 2016Balance Router
Same eraDAO whitehat balance-conditional router: if EF/whitehat address holds >1M ETH, route to addr1; otherwise route to addr2.
0x6e9ccd...aee851July 25, 2016ShapeShift Chain-Split Receiver
Same eraShapeShift ETH/ETC routing contract (Jul 26, 2016). Forwards ETH to target only when on-chain forked() oracle result matches stored boolean — routing deposits to the correct wallet on each chain post-DAO-fork.
0x3e7756...2e7a25July 26, 2016ShapeShift Chain-Split Receiver
Same eraShapeShift ETH/ETC routing contract (Jul 26, 2016). Twin instance of the ShapeShiftReceiver — identical bytecode, configured for the opposite chain fork state.
0x89afcc...a51456July 26, 2016CanaryV7
Same eraA liveness canary for the Ethereum Alarm Clock, deployed 28 July 2016.
0x6c3abc...4aa180July 28, 2016