Introducing Blockdrop Network, an automated market maker for exchanging ERC20 tokens

Blockdrop Network Whitepaper

Introduction

Blockdrop is an automated token exchange built on Ethereum. It is designed around ease-of-use, gas efficiency, decentralization, and zero rent extraction. The exchange will be useful for traders and will function particularly well as a component of other smart contracts which require guaranteed on-chain liquidity.

Most exchanges maintain an order book and facilitate matches between buyers and sellers. Blockdrop smart contracts hold liquidity reserves of various tokens, and trades are executed directly against these reserves. Prices are set automatically using the constant product (x*y=k) market maker mechanism, which keeps overall reserves in relative equilibrium. Reserves are provided by incentivised liquidity providers who supply the system with tokens in exchange for a share of transaction fees.

An important feature of Blockdrop is the utilisation of a factory/registry contract that deploys a separate exchange contract for each ERC20 token. These exchange contracts each hold a reserve of ETH and their associated ERC20. This allows trades between the two based on relative supply. Exchange contracts are linked through the registry, allowing for direct ERC20 to ERC20 trades between any tokens using ETH as an intermediary.

This document outlines the core mechanics and technical details for Blockdrop. Some code is simplified for readability. Safety features such as overflow checks and purchase minimums are ommited. The full source code is publicly available on Gitlab.

Vyper: https://gitlab.com/blockdropnetwork/contracts (production version)

Website: https://blockdrop.network/

Gas Comparison

Blockdrop is extremely gas efficient due to its minimalistic design. For ETH to ERC20 trades, it uses almost 10x less gas than Bancor (leading market maker). It can perform ERC20 to ERC20 trades more efficiently than 0x protocol and does not require a secondary token for fees. It also has significant gas reductions when compared to established on-chain order book exchanges, such as EtherDelta and IDEX.

The cost of a direct ERC20 token transfer is 36,000 gas, which is only ~20% less than an ETH to ERC20 trade on Blockdrop. 🔥🔥🔥

Exchange Creation

blockdrop_factory.v.py is a smart contract that serves as both a factory and registry for Blockdrop exchanges. The public function launchExchange() allows any Ethereum user to add support for an ERC20 that does not already have an associated exchange:

exchange_template: public(address)
token_to_exchange: address[address]
exchange_to_token: address[address]

@public
def __init__(template_contract: address):
self.exchange_template = template
@public
def launchExchange(token: address) -> address:
assert self.token_to_exchange[token] == ZERO_ADDRESS
new_exchange: address = create_with_code_of(self.exchange_template)
self.token_to_exchange[token] = new_exchange
self.exchange_to_token[new_exchange] = token
return new_exchange

When a new exchange is launched, mappings between the token and exchange address are created. Both the token and exchange are now part of the registry and can be found using the functions getExchange() and exchange_to_token_lookup():

@public
@constant
def getExchange(token_addr: address) -> address:
return self.token_to_exchange[token_addr]
@public
@constant
def getToken(exchange_addr: address) -> address:
return self.exchange_to_token[exchange_addr]

It is important to note that the factory does not perform any checks on a token when launching an exchange contract (other than enforcing the one-exchange-per-token limit). It is recommended that users only interact with exchanges that are associated with verified and known token addresses such as OmiseGo or Status.

ETH to ERC20 Trades

Each Blockdrop exchange contract (blockdrop_exchange.v.py) is associated with a single ERC20 token and stores a liquidity pool of both ETH and that token. The exchange rate between ETH and an ERC20 is based on the relative sizes of their liquidity pools within the contract. This is done by maintaining the relationship eth_pool * token_pool = invariant. This invariant is held constant during trades and only changes when liquidity is added or removed from the market.

A simplified version of ethToTokenSwap(), the function for converting ETH to ERC20 tokens, is shown below:

eth_pool: uint256         
token_pool: uint256
token: address(ERC20)
@public
@payable
def ethToTokenSwap():
fee: uint256 = msg.value / 500
invariant: uint256 = self.eth_pool * self.token_pool
new_eth_pool: uint256 = self.eth_pool + msg.value
new_token_pool: uint256 = invariant / (new_eth_pool - fee)
tokens_out: uint256 = self.token_pool - new_token_pool
self.eth_pool = new_eth_pool
self.token_pool = new_token_pool
self.token.transfer(msg.sender, tokens_out)

Note: In order to save gas, eth_pool and token_pool are no longer stored variables. They are respectively found using self.balance and through an external call to the ERC20 contract

When ETH is sent to the function, eth_pool increases and token_pool decreases. The relationship eth_pool * token_pool = invariant causes a price shift, which raises the cost of tokens and lowers the cost of ETH for future trades. Fees are added directly to the liqudity pools. This functions as a distribution to liquidity providers. This is described in greater detail below.

Exchanging tokens for ETH is done with the function tokenToEthSwap():

@public
def tokenToEthSwap(tokens_in: uint256):
fee: uint256 = tokens_in / 500
invariant: uint256 = self.eth_pool * self.token_pool
new_token_pool: uint256 = self.token_pool + tokens_in
new_eth_pool: uint256 = self.invariant / (new_token_pool - fee)
eth_out: uint256 = self.eth_pool - new_eth_pool
self.eth_pool = new_eth_pool
self.token_pool = new_token_pool
self.token.transferFrom(msg.sender, self, tokens_out)
send(msg.sender, eth_out)

This increases token_pool and decreases eth_pool, shifting the price in the opposite direction. An example ETH to OMG purchase is shown below.


Example — ETH to OMG trade

10 ETH and 500 OMG (ERC20) are deposited into a smart contract by liquidity providers. An invariant is automatically set such that ETH * OMG = invariant.

ETH_pool = 10
OMG_pool = 500
invariant = 10 * 500 = 5000

When a buyer sends ETH to the contract a 0.2% fee is taken out for the liquidity providers. Next, the invariant is divided by the new amount of ETH in the liquidity pool to determine how the size of the OMG pool. The remaining OMG is sent to the buyer.

Buyer sends: 1 ETH
Fee = 1 ETH / 500 = 0.002 ETH
ETH_pool = 10 + 1–0.002 = 10.998
OMG_pool = 5000/10.998 = 454.6
Buyer receieves: 500–454.6 = 45.4 OMG

The fee is now added back into the liquidity pool, which acts as a payout to liquidity providers that is collected when liquidity is divested from the market. Since the fee is added after the price calculation, the invariant has increased slightly. In fact, what the invariant really represents ETH * OMG at the end of the previous trade.

ETH_pool = 10.998 + 0.002 = 11
OMG_pool = 454.6
new invariant = 11 * 454.6 = 5,000.6

In this case the buyer received a rate of 44.5 OMG/ETH. However the price has shifted. If another buyer makes a trade in the same direction, they will get a slightly worse rate of OMG/ETH. However, if a buyer makes a trade in the opposite direction they will get a slightly better ETH/OMG rate.

1 ETH in
44.5 OMG out
Rate = 44.5 OMG/ETH

The larger a purchase relative to the size of the liquidity pool, the more the price will shift. In an active market, aribitrage will ensure that the price will not shift too far from that of other exchanges.

ERC20 to ERC20 Trades

Blockdrop exchange contracts are capable of trading between each other, allowing direct ERC20 to ERC20 trades. It is possible to convert from OMG to ETH on one exchange and then from ETH to KNC on another, all within a single atomic transaction.

To convert from OMG to KNC (for example), a buyer can call the function tokens_to_tokens_swap() on the OMG exchange contract:

contract Factory():
def token_to_exchange_lookup(token_addr: address) -> address: pass
contract Exchange():
def ethToTokenTransfer(recipent: address, min_tokens: uint256, timeout: uint256) -> bool: pass

@public
def tokenToTokenSwap(token_addr: address, tokens_sold: uint256):
exchange: address = Factory(self.factory_address).token_to_exchange_lookup(token_addr)
fee: uint256 = tokens_sold / 500
invariant: uint256 = self.eth_pool * self.token_pool
new_token_pool: uint256 = self.token_pool + tokens_sold
new_eth_pool: uint256 = invariant / (new_token_pool - fee)
eth_out: uint256 = self.eth_pool - new_eth_pool
self.eth_pool = new_eth_pool
self.token_pool = new_token_pool
Exchange(exchange).ethToTokenTransfer(msg.sender, value=eth_out)

where token_addr is the address of KNC token and tokens_sold is the amount of OMG being sold. This function first checks the registry to retreive the address of the associated exchange, if one exists. Next, similar to tokenToEthSwap(), the exchange converts the input OMG to ETH. However instead of returning the purchased ETH to the buyer, the function instead calls the payable function ethToTokenTransfer() on the KNC exchange:

@public
@payable
def eth_to_tokens_payment(recipent: address):
fee: uint256 = msg.value / 500
invariant: uint256 = self.eth_pool * self.token_pool
new_eth_pool: uint256 = self.eth_pool + msg.value
new_token_pool: uint256 = invariant / (new_eth_pool - fee)
tokens_out: uint256 = self.token_pool - new_token_pool
self.eth_pool = new_eth_pool
self.token_pool = new_token_pool
self.invariant = new_eth_pool * new_token_pool
self.token_address.transfer(recipent, tokens_out)

eth_to_tokens_payment() receives the ETH and buyer address, verifies that the call is made from an exchange in the registry, converts the ETH to KNC, and forwards the KNC to the original buyer. eth_to_tokens_payment() functions similar to eth_to_tokens_swap() but has the additional input parameter recipient: address. This is used to forward the funds to the original buyer instead of msg.sender which in this case will be the OMG exchange.

Investing and Divesting Liquidity

Initialization

The first liquidity provider to invest in an exchange must initialise it. This is done by depositing some amount of both ETH and the exchange token into the contract, which sets the initial exchange rate. The provider is rewarded with initial “shares” of the market (based on the wei value deposited). These shares are ERC20 tokens, which represent proportional ownership of a single Blockdrop exchange. Shares are highly divisible and can be burned at any time to return a proportional share of the markets liquidity to the owner.

eth_pool: public(uint256)                   
token_pool: public(uint256)
total_shares: public(uint256)
shares: uint256[address]

# Sets initial token pool, ETH pool, and share amount
@public
@payable
def initialize(tokens_invested: uint256):
assert self.total_shares == 0
eth_invested: uint256 = msg.value
self.eth_pool = eth_invested
self.token_pool = tokens_invested
initial_shares: uint256 = eth_invested / 100000
self.total_shares = initial_shares
self.shares[msg.sender] = initial_shares
self.token_address.transferFrom(msg.sender, self, tokens_invested)

Investing

Once an exchange is initialized, investors can mint new shares by investing in the liquidity pools for both ETH and the exchange token at the current price ratio. This is done using the invest() function:

@public
@payable
def invest(min_shares: uint256, timeout: uint256):
eth_invested: uint256 = msg.value
shares_minted: uint256 = (eth_invested * self.total_shares) / self.eth_pool
tokens_invested: uint256 = (shares_minted * self.token_pool) / self.total_shares)
self.shares[msg.sender] = self.shares[msg.sender] + shares_minted
self.total_shares = self.total_shares + shares_minted
self.eth_pool = self.eth_pool + eth_invested
self.token_pool = self.token_pool + tokens_invested
self.token_address.transferFrom(msg.sender, self, tokens_invested)

The number of shares minted is determined by the amount of ETH sent to the function. It can be calulcated using the equation:

sharesPurchased=totalShares∗ethSentethPool

The amount of ERC20 tokens required to purchase this amount of shares is calculated with the equation:

tokensRequired=tokenPool∗sharesPurchasedtotalShares

If an investor has enough tokens and has approved a token allowance for the exchange, then the shares will be minted. If not, the function will revert, and all funds will be returned.

Divesting

Investors can burn liquidity shares at any time to withdraw their proportional share of ETH and Tokens from the market.

ethDivested=ethPool∗sharesBurnedtotalShares

tokensDivested=tokenPool∗sharesBurnedtotalShares

ETH and Tokens are withdrawn at the current market ratio, not the ratio during their originial investment. Since fees taken during trades are added to the total liquidity pools, divested ETH and divested tokens includes a proportional share of all fees collected since the liquidity provider first invested.

Shares

Blockdrop exchange shares are ERC20 tokens, and include a full implementation of EIP-20. This allows liquidity providers to sell their shares or transfer them between accounts without removing liquidity from the market. Shares are specific to a single exchange. There is no single, unifying ERC20 token for this project.

Fee Structure

  • ETH to ERC20 trades
  • there is a 0.2% fee paid in ETH
  • ERC20 to ETH trades
  • there is a 0.2% fee paid in ERC20 tokens
  • ERC20 to ERC20 trades
  • there is a 0.2% fee paid in tokens for ERC20 to ETH conversion on input exchange
  • there is a 0.2% fee paid in ETH for ETH to ERC20 conversion on output exchange

When a buyer exchanges currencies, they must pay a 0.2% fee to the liquidity providers on the exchange. Since ERC20 to ERC20 purchases involve both an ERC20 to ETH swap and an ETH to ERC20 swap, the fee is paid on both exchanges.

ETH and token fees are immediately added to the eth_pool and token_pool respectively. This increases the value of all shares equally and acts as a payout to liquidity providers that can be collected by burning shares. Since fees are added to liquidity pools, the invariant increases at the end of every trade. Within a single transaction, the invariant represents eth_pool*token_pool at the end of the previous transaction.

From this 0.2% fee 60% fee is paid to liquidity provider and 40% fee is paid to Blockdrop Token Holders (BDP).

Swaps vs Payments

The functions eth_to_token_swap(), token_to_eth_swap() , and token_to_token_swap()are used for transactions where the buyer wants to receive purchased tokens in the same account they used for selling. The additional functions eth_to_token_payment(), token_to_eth_payment(), and token_to_token_payment() have been included, which lets the buyer pass in a recipient address. This functions similar to ShapeShift, enabling anything-to-anything payments on the Ethereum blockchain.

Roadmap

Completed

  • ETH to ERC20 exchanges
  • Decentralized liquidity providers with shared fee payouts
  • Exchange registry and factory
  • ERC20 to ERC20 exchanges (tunneling between contracts)
  • Basic front-running resistance (minimum purchases)
  • Anything-to-Anything payments (ShapeShift functionality)
  • Python unit tests
  • Testnet launch
  • Basic frontend
  • Vyper re-write
  • ERC20 liquidity shares

June 2018

  • Fine-tuning smart contracts
  • Begin security audit
  • Improve frontend
  • Documentation

July 2018

  • Finished frontend with mobile UI
  • DAPP integration example project
  • Begin integrations
  • Conclude Audit

August 2018

  • Recruit Liquidity Providers
  • ENS Support
  • Mainet Launch*

*Mainet launch should be done after the conclusion of the Vyper language audit. This could potentially delay the launch.

Post-Launch Goals

  • DAPP integration
    - Ocean Protocol, MakerDAO, Balance and a few other companies have expressed interest in integrating.
    - Use Blockdrop for continuous token buybacks as a method for burning ownership tokens to distribute fees.
  • Improved UX
    - Mobile App
    - Token-to-Token payments with QR codes
    - Mobile wallet integration
    - Remove reliance on MetaMask
  • Additional front running resistance
  • Limit orders
    - Second smart contract containing an orderbook of limit orders that can be executed on Blockdrop exchanges
    - Anyone can execute a limit order on someone elses behalf
    - A small bounty is paid to the person who executes the transaction

Post-Launch Goals

Expand ENS support

Converting ETH to OMG from any wallet by sending ETH to omg.Blockdrop.eth will be possible trustlessly using fallback functions.

Converting OMG to ETH by sending OMG to omg.Blockdrop.eth, and converting OMG to KNC by sending OMG to knc.Blockdrop.eth could be done using a trusted system of off-chain event watchers.

DAPP integration

Blockdrop can be built directly into other DAPPs. An interesting mechanism I designed of can turn any ERC20 token into an ownership/dividends token that evenly distributes fees collected in ETH. A full writeup is coming, along with an example implementation.

Blockdrop Token (BDP)

Blockdrop TOKENS (BDP) is an ERC-20 token and an integral part of Blockdrop Liquidity Network.

Blockdrop is an automated market maker for exchange and BDP is the glue that connects different participants in the Network ecosystem, including both liquidity providers and the different entities that leverage on the liquidity network.

HOW Blockdrop TOKEN (BDP) IS USEFUL?

LIQUIDITY PROVIDER

To operate and collect fees, Liquidity Providers are required to purchase any token they want to add in the liquidity pool with BDP in Blockdrop dapp otherwise liquidity portal will not unlock. Every liquidity provider will earn 60% fee on user transactions.

DIVIDENDS TO HOLDERS

Blockdrop will reward BDP token holders with 40% fee. Every 40% of trading/transactions made in Blockdrop is completely given to holders as dividend. Of course user MUST be a token holder and the MORE token user hold, the higher % dividend user receive. Users are being given ETHEREUM as dividend. (40% is split between all token holders).

EARLY HOLDER REWARD

Blockdrop Token (BDP) is launching on exchanges from 24 July.

Blockdrop is not conducting any ICO and BDP will be tradeable from 24 JULY on Exchanges. Until then we are offering BDP at very cheap rates as special Early holder reward to few early holders , if you are early holder you will earn 50% transaction fee as dividend instead of regular 40% as bonus in Ether, also more BDP you have more fee in Eth(Ethereum) you will earn and Early holder reward is best opportunity to get more BDP at cheap rates.

BDP is rare token with low supply of only 50000 and Early holders can get BDP at most cheapest rates possible.

Blockdrop Token (BDP) Total Supply=50000 Only

Rate=1000 BDP/ETH

Raising Goal= Only 5 ETH

Supply available to Early Holders=5000 BDP

Early holder reward will be support and reward to early holders in return for supporting Blockdrop Network project in early stages.

Become Early Holder