0

DeFi on Bitcoin Part 3: Uniswap

 2 years ago
source link: https://coingeek.com/defi-on-bitcoin-part-3-uniswap/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Home » Tech » DeFi on Bitcoin Part 3: Uniswap

DeFi on Bitcoin Part 3: Uniswap

Tech

15 February 2022

This post was first published on Medium. Read DeFi on Bitcoin Part 1: Fungible tokens and token swap and DeFi on Bitcoin Part 2: NFT and Marketplace.

We illustrate how to build a Uniswap-like exchange directly on Bitcoin.

Uniswap

Uniswap liquidity poolsUniswap

Uniswap is a so-called “decentralized exchange” (DEX). It allows individuals, called liquidity providers, to pool tokens/bitcoins together into smart contracts and provide liquidity to the exchange.

Overview

We implement Uniswap V1 that only swaps between bitcoins and a token directly. We use a stateful contract to represent the pool, as indicated by the @state decorator. It contains two tokens: one for the token we are swapping (Line 7) and the other is a governance token (Line 11), call liquidity pool (LP) token. The pool stores bitcoins directly in the amount part of the UTXO (in unit of satoshis) and tokens under its public key (Line 3).

contract Uniswap { // pool's public key PubKey poolPubkey;

// the main token @state ERC20 token;

// the liquidity pool governance token @state ERC20 lpToken;

... }

Complete code can be found here.

Add Liquidity

Anyone can add liquidity to the pool by calling function addLiquidity. There are two cases (checked at Line 9):

  1. add liquidity for the first time: arbitrary amount of bitcoins and tokens can be deposited.
  2. add more liquidity later on: the ratio of bitcoins and tokens deposited must match the existing ratio in the pool (Line 22).

// add bitcoin and token to liquidity pool public function addLiquidity(PubKey sender, Sig senderSig, int tokenAmount, int senderBalance, int senderKeyIndex, int oldTokenBalance, int lpSenderBalance, int lpSenderKeyIndex, int newBitcoinBalance, SigHashPreimage txPreimage) { require(checkSig(senderSig, sender));

int oldBitcoinBalance = SigHash.value(txPreimage); // mint new lp tokens for the liquidity provider if (oldBitcoinBalance == 0) { // initialize pool // initially, just mint new lp tokens per the amount of new bitcoins deposited int lpMint = newBitcoinBalance; require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex)); } else { // add more liquidity

int bitcoinAmount = newBitcoinBalance - oldBitcoinBalance; // deposit ratio must be the same with current pool ration // i.e., oldBitcoinBalance / oldTokenBalance == bitcoinAmount / tokenAmount require(oldBitcoinBalance * tokenAmount == bitcoinAmount * oldTokenBalance);

// mint new lp tokens, proportinal to the amount of new bitcoins deposited int lpMint = this.lpToken.totalSupply() * bitcoinAmount / oldBitcoinBalance; require(this.lpToken.mint(sender, lpSenderBalance, lpMint, lpSenderKeyIndex)); }

// transfer tokens to the pool require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex)); require(this.propagateState(newBitcoinBalance, txPreimage)); }

After bitcoins are deposited, new LP tokens are minted to the provider proportionally at Line 26. Tokens are transferred to the pool’s account at Line 30.

For example, if the pool has 10 bitcoins and 100 LP tokens and Alice deposits another 5 bitcoins into it, 50 new LP tokens will be minted to her.

Remove Liquidity

Liquidity providers call function removeLiquidity to withdraw their portion of the reserves, for both bitcoins and tokens.

// remove bitcoin and token from liquidity pool public function removeLiquidity(PubKey sender, int lpAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance, int lpSenderBalance, int lpSenderKeyIndex, SigHashPreimage txPreimage) { require(checkSig(senderSig, sender));

int oldBitcoinBalance = SigHash.value(txPreimage); // withdraw amount int bitcoinAmount = oldBitcoinBalance * lpAmount / this.lpToken.totalSupply(); int tokenAmount = oldTokenBalance * lpAmount / this.lpToken.totalSupply();

// burn the lp tokens require(this.lpToken.burn(sender, lpSenderBalance, lpAmount, lpSenderKeyIndex));

// transfer tokens from pool to the sender require(this.token.transferFrom(this.poolPubkey, sender, tokenAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex)); // transfer bitcoins to the sender int newBitcoinBalance = oldBitcoinBalance - bitcoinAmount; require(this.propagateState(newBitcoinBalance, txPreimage)); }

Liquidity is withdrawn according to the potion of LP tokens a provider has (Line 9 & 10). After the withdrawal, the LP tokens are burned at Line 13. Line 16 transfers tokens from the pool to the provider. Line 18 & 20 does the same for bitcoins.

Note another output in the same transaction is needed to return the bitcoins to the provider, besides the contract output.

Bitcoin -> Token

A user calls functions swapBitcoinToToken to exchange bitcoins to tokens. After bitcoins are received by the pool (calculated at Line 7), Line 10 calculates the amount of tokens to be returned and Line 13 returns them to the user.

// swap bitcoins for tokens public function swapBitcoinToToken(PubKey sender, int tokenAmount, Sig senderSig, int oldTokenBalance, int senderKeyIndex, int senderBalance, int newBitcoinBalance, SigHashPreimage txPreimage) { require(checkSig(senderSig, sender));

int oldBitcoinBalance = SigHash.value(txPreimage); int bitcoinAmount = newBitcoinBalance - oldBitcoinBalance;

// calculate tokens in return int tokensAmount = this.getAmount(bitcoinAmount, oldBitcoinBalance, oldTokenBalance);

// transfer tokens from pool to the sender require(this.token.transferFrom(this.poolPubkey, sender, tokensAmount, oldTokenBalance, senderKeyIndex, senderBalance, senderKeyIndex));

require(this.propagateState(newBitcoinBalance, txPreimage)); }

Token -> Bitcoin

A user calls functions swapTokenToBitcoin to exchange tokens to bitcoins. Line 9 calculates the amount of bitcoins to be returned. Line 13 transfers tokens to the pool.

// swap tokens for bitcoins public function swapTokenToBitcoin(PubKey sender, int tokenAmount, Sig senderSig, int senderBalance, int senderKeyIndex, int oldTokenBalance, int lpSenderBalance, SigHashPreimage txPreimage) { require(checkSig(senderSig, sender));

int oldBitcoinBalance = SigHash.value(txPreimage);

// calculate bitcoins in return int bitcoinsAmount = this.getAmount(tokenAmount, oldTokenBalance, oldBitcoinBalance); int newBitcoinBalance = oldBitcoinBalance - bitcoinsAmount;

// transfer tokens to the pool require(this.token.transferFrom(sender, this.poolPubkey, tokenAmount, senderBalance, senderKeyIndex, oldTokenBalance, senderKeyIndex)); require(this.propagateState(newBitcoinBalance, txPreimage)); }

Similar to removeLiquidity, another output is needed to return the bitcoins to the user.

Discussion

We have demonstrated how to implement a basic Uniswap-like exchange on Bitcoin. There are many ways to extend it to be more practical.

  • price formula: we use the following code to determine price, based only on bitcoin and token reserves. It is called a constant sum formula and the pool can be drained. To avoid draining, a more sophisticated formula like constant product formula (x * y = k) can be used, as in Uniswap.

// use reserve ratio as price function getAmount(int input, int inputReserve, int outputReserve) : int { return outputReserve * input / inputReserve; }

Caption: getAmount() uses a constant sum price formula

  • liquidity mining: we can charge a fee for each swap and use the fees to reward liquidity providers.
  • allows users to directly swap one token to another.

TokenSwap has actually implemented all of the above and many more.

Acknowledgements:

This work is inspired by this article of Chen Cheng.

Watch: CoinGeek New York panel, Tokenized Assets, Stablecoins and Custody with BSV

New to Bitcoin? Check out CoinGeek’s Bitcoin for Beginners section, the ultimate resource guide to learn more about Bitcoin—as originally envisioned by Satoshi Nakamoto—and blockchain.

latest news

Tech

27 minutes ago

BSV Academy introduces new course on digital signatures for Bitcoin developers

The course is divided into four stages, discussing the concept of digital signatures, pre-requisites to understanding ECDSA, technical aspects of ECDSA, and how it forms part of the Bitcoin protocol.

Tech

15 February 2022

Kazakhstan wants to raise tax on block reward miners by 500%

The proposal by the Minister of Finance will also see miners pay a monthly tax on their mining rigs, whether they use them to mine or even if they’re not in operation.

Tech

14 February 2022

The future economics of block reward mining

If producing a block consumes the same amount of energy across all chains, then the most energy-efficient chain is the one that can package the most data into a single block.

Tech

13 February 2022

DeFi on Bitcoin Part 2: NFT and marketplace

In the second part of this series on why it's more advantageous to run DeFi on Bitcoin, Xiaohui Liu illustrated how to build non-fungible tokens (NFT) and sell them directly on Bitcoin.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK