Skip to content

TokenSeller And TokenSellerFactory

BokkyPooBah edited this page Mar 29, 2017 · 14 revisions

Page status: To be updated with market making instructions.

The TokenSeller contract is created by a user on the Ethereum blockchain. This contract allows them to sell a digital asset for a specified price in ethers.

The user creating the smart contract to list the buy and/or sell trade is called the market Maker.

The user that executes a trade against a smart contract is called the market Taker.

Table of contents



TokenSeller And TokenSellerFactory Contract Information



Market Maker Operations

Create TokenSeller Contract

(De)Activate TokenSeller Contract

Adding Tokens To TokenSeller Contract

Withdrawing Ethers From The TokenSeller Contract

Withdrawing Tokens From The TokenSeller Contract

Withdrawing Other Tokens From The TokenSeller Contract



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

  • Under CONTRACT ADDRESS:

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

    [{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"ownerWithdrawERC20Token","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"tradeContract","type":"address"}],"name":"verify","outputs":[{"name":"valid","type":"bool"},{"name":"owner","type":"address"},{"name":"asset","type":"address"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"sellsTokens","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"asset","type":"address"},{"name":"sellPrice","type":"uint256"},{"name":"units","type":"uint256"},{"name":"sellsTokens","type":"bool"}],"name":"createSaleContract","outputs":[{"name":"seller","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"payable":false,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"ownerAddress","type":"address"},{"indexed":true,"name":"tokenSellerAddress","type":"address"},{"indexed":true,"name":"asset","type":"address"},{"indexed":false,"name":"sellPrice","type":"uint256"},{"indexed":false,"name":"units","type":"uint256"},{"indexed":false,"name":"sellsTokens","type":"bool"}],"name":"TradeListing","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tokenAddress","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"OwnerWithdrewERC20Token","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"}]

  • Click OK



How To Watch A TokenSeller 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 TokenSeller {further identifying information}

  • Under CONTRACT ADDRESS, enter the contract address as listed in https://cryptoderivatives.market/tokenTraderContracts . These contracts will have a Factory Name of TokenSellerFactory.

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

    [{"constant":false,"inputs":[{"name":"ethers","type":"uint256"}],"name":"makerWithdrawEther","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":"toTokenSeller","type":"address"},{"name":"tokens","type":"uint256"}],"name":"makerTransferAsset","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":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"makerWithdrawERC20Token","outputs":[{"name":"ok","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"takerBuyAsset","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"tokens","type":"uint256"}],"name":"makerWithdrawAsset","outputs":[{"name":"ok","type":"bool"}],"payable":false,"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"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sells","type":"bool"}],"name":"ActivatedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerWithdrewAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"toTokenSeller","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerTransferredAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokenAddress","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"MakerWithdrewERC20Token","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ethers","type":"uint256"}],"name":"MakerWithdrewEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethersSent","type":"uint256"},{"indexed":false,"name":"ethersReturned","type":"uint256"},{"indexed":false,"name":"tokensBought","type":"uint256"}],"name":"TakerBoughtAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"}]



Maker - How to list your contract



Taker - How to execute a trade against a listed contract



TokenSellerFactory has the following methods:

createSaleContract(...)

createSaleContract(address _asset, uint256 _sellPrice, uint256 _units, bool _sellsTokens) returns (address)

verify(...)

verify(address saleContract) constant returns (bool valid, address asset, uint256 sellPrice, uint256 units, bool sellsTokens)



Source Code

Following is the source code for TokenSellerFactory contract (which includes TokenSeller) on:

pragma solidity ^0.4.4;

// ------------------------------------------------------------------------
// TokenSellerFactory
//
// Decentralised trustless ERC20-partially-compliant token to ETH exchange
// contract on the Ethereum blockchain.
//
// This caters for the Golem Network Token which does not implement the
// ERC20 transferFrom(...), approve(...) and allowance(...) methods
//
// History:
//   Jan 25 2017 - BPB Added makerTransferAsset(...)
//   Feb 05 2017 - BPB Bug fix in the change calculation for the Unicorn
//                     token with natural number 1
//
// Enjoy. (c) JonnyLatte, Cintix & BokkyPooBah 2017. The MIT licence.
// ------------------------------------------------------------------------

// https://github.com/ethereum/EIPs/issues/20
contract ERC20Partial {
    function totalSupply() constant returns (uint totalSupply);
    function balanceOf(address _owner) constant returns (uint balance);
    function transfer(address _to, uint _value) returns (bool success);
    // function transferFrom(address _from, address _to, uint _value) returns (bool success);
    // function approve(address _spender, uint _value) returns (bool success);
    // function allowance(address _owner, address _spender) constant returns (uint remaining);
    event Transfer(address indexed _from, address indexed _to, uint _value);
    // event Approval(address indexed _owner, address indexed _spender, uint _value);
}

contract Owned {
    address public owner;
    event OwnershipTransferred(address indexed _from, address indexed _to);

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

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

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

// contract can sell tokens for ETH
// prices are in amount of wei per batch of token units

contract TokenSeller 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 MakerWithdrewAsset(uint256 tokens);
    event MakerTransferredAsset(address toTokenSeller, uint256 tokens);
    event MakerWithdrewERC20Token(address tokenAddress, uint256 tokens);
    event MakerWithdrewEther(uint256 ethers);
    event TakerBoughtAsset(address indexed buyer, uint256 ethersSent,
        uint256 ethersReturned, uint256 tokensBought);

    // Constructor - only to be called by the TokenSellerFactory contract
    function TokenSeller (
        address _asset,
        uint256 _sellPrice,
        uint256 _units,
        bool    _sellsTokens
    ) {
        asset       = _asset;
        sellPrice   = _sellPrice;
        units       = _units;
        sellsTokens = _sellsTokens;
        ActivatedEvent(sellsTokens);
    }

    // Maker can activate or deactivate this contract's
    // selling status
    //
    // The ActivatedEvent() event is logged with the following
    // parameter:
    //   sellsTokens  this contract can sell asset tokens
    function activate (
        bool _sellsTokens
    ) onlyOwner {
        sellsTokens = _sellsTokens;
        ActivatedEvent(sellsTokens);
    }

    // Maker can withdraw asset tokens from this contract, with the
    // following parameter:
    //   tokens  is the number of asset tokens to be withdrawn
    //
    // The MakerWithdrewAsset() event is logged with the following
    // parameter:
    //   tokens  is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawAsset() in the old version
    function makerWithdrawAsset(uint256 tokens) onlyOwner returns (bool ok) {
        MakerWithdrewAsset(tokens);
        return ERC20Partial(asset).transfer(owner, tokens);
    }

    // Maker can transfer asset tokens from this contract to another
    // TokenSeller contract, with the following parameter:
    //   toTokenSeller  Another TokenSeller contract owned by the
    //                  same owner
    //   tokens         is the number of asset tokens to be moved
    //
    // The MakerTransferredAsset() event is logged with the following
    // parameters:
    //   toTokenSeller  The other TokenSeller contract owned by
    //                  the same owner
    //   tokens         is the number of tokens transferred
    //
    // The asset Transfer() event is logged from this contract to
    // the other contract
    //
    function makerTransferAsset(
        TokenSeller toTokenSeller,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        if (owner != toTokenSeller.owner() || asset != toTokenSeller.asset()) {
            throw;
        }
        MakerTransferredAsset(toTokenSeller, tokens);
        return ERC20Partial(asset).transfer(toTokenSeller, tokens);
    }

    // Maker can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The MakerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawToken() in the old version
    function makerWithdrawERC20Token(
        address tokenAddress,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        MakerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20Partial(tokenAddress).transfer(owner, tokens);
    }

    // Maker withdraws ethers from this contract
    //
    // The MakerWithdrewEther() event is logged with the following parameter
    //   ethers  is the number of ethers withdrawn by the maker
    //
    // This method was called withdraw() in the old version
    function makerWithdrawEther(uint256 ethers) onlyOwner returns (bool ok) {
        if (this.balance >= ethers) {
            MakerWithdrewEther(ethers);
            return owner.send(ethers);
        }
    }

    // Taker buys asset tokens by sending ethers
    //
    // The TakerBoughtAsset() event is logged with the following parameters
    //   buyer           is the buyer's address
    //   ethersSent      is the number of ethers sent by the buyer
    //   ethersReturned  is the number of ethers sent back to the buyer as
    //                   change
    //   tokensBought    is the number of asset tokens sent to the buyer
    //
    // This method was called buy() in the old version
    function takerBuyAsset() payable {
        if (sellsTokens || msg.sender == owner) {
            // Note that sellPrice has already been validated as > 0
            uint order    = msg.value / sellPrice;
            // Note that units has already been validated as > 0
            uint can_sell = ERC20Partial(asset).balanceOf(address(this)) / units;
            uint256 change = 0;
            if (msg.value > (can_sell * sellPrice)) {
                change  = msg.value - (can_sell * sellPrice);
                order = can_sell;
            }
            if (change > 0) {
                if (!msg.sender.send(change)) throw;
            }
            if (order > 0) {
                if (!ERC20Partial(asset).transfer(msg.sender, order * units)) throw;
            }
            TakerBoughtAsset(msg.sender, msg.value, change, order * units);
        }
        // Return user funds if the contract is not selling
        else if (!msg.sender.send(msg.value)) throw;
    }

    // Taker buys tokens by sending ethers
    function () payable {
        takerBuyAsset();
    }
}

// This contract deploys TokenSeller contracts and logs the event
contract TokenSellerFactory is Owned {

    event TradeListing(address indexed ownerAddress, address indexed tokenSellerAddress,
        address indexed asset, uint256 sellPrice, uint256 units, bool sellsTokens);
    event OwnerWithdrewERC20Token(address indexed tokenAddress, uint256 tokens);

    mapping(address => bool) _verify;

    // Anyone can call this method to verify the settings of a
    // TokenSeller contract. The parameters are:
    //   tradeContract  is the address of a TokenSeller contract
    //
    // Return values:
    //   valid        did this TokenTraderFactory create the TokenTrader contract?
    //   owner        is the owner of the TokenTrader contract
    //   asset        is the ERC20 asset address
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   sellsTokens  is the TokenTrader contract selling tokens?
    //
    function verify(address tradeContract) constant returns (
        bool    valid,
        address owner,
        address asset,
        uint256 sellPrice,
        uint256 units,
        bool    sellsTokens
    ) {
        valid = _verify[tradeContract];
        if (valid) {
            TokenSeller t = TokenSeller(tradeContract);
            owner         = t.owner();
            asset         = t.asset();
            sellPrice     = t.sellPrice();
            units         = t.units();
            sellsTokens   = t.sellsTokens();
        }
    }

    // Maker can call this method to create a new TokenSeller contract
    // with the maker being the owner of this new contract
    //
    // Parameters:
    //   asset        is the ERC20 asset address
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   sellsTokens  is the TokenSeller contract selling tokens?
    //
    // For example, listing a TokenSeller contract on the GNT Golem Network Token
    // where the contract will sell GNT tokens at a rate of 170/100000 = 0.0017 ETH
    // per GNT token:
    //   asset        0xa74476443119a942de498590fe1f2454d7d4ac0d
    //   sellPrice    170
    //   units        100000
    //   sellsTokens  true
    //
    // The TradeListing() event is logged with the following parameters
    //   ownerAddress        is the Maker's address
    //   tokenSellerAddress  is the address of the newly created TokenSeller contract
    //   asset               is the ERC20 asset address
    //   sellPrice           is the sell price in ethers per `units` of asset tokens
    //   unit                is the number of units of asset tokens
    //   sellsTokens         is the TokenSeller contract selling tokens?
    //
    // This method was called createTradeContract() in the old version
    //
    function createSaleContract(
        address asset,
        uint256 sellPrice,
        uint256 units,
        bool    sellsTokens
    ) returns (address seller) {
        // Cannot have invalid asset
        if (asset == 0x0) throw;
        // Cannot set zero or negative price
        if (sellPrice <= 0) throw;
        // Cannot sell zero or negative units
        if (units <= 0) throw;
        seller = new TokenSeller(
            asset,
            sellPrice,
            units,
            sellsTokens);
        // Record that this factory created the trader
        _verify[seller] = true;
        // Set the owner to whoever called the function
        TokenSeller(seller).transferOwnership(msg.sender);
        TradeListing(msg.sender, seller, asset, sellPrice, units, sellsTokens);
    }

    // Factory owner can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The OwnerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    function ownerWithdrawERC20Token(address tokenAddress, uint256 tokens) onlyOwner returns (bool ok) {
        OwnerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20Partial(tokenAddress).transfer(owner, tokens);
    }

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