---
# System prepended metadata

title: Krypto 第二組第三週討論資料

---

# Krypto 第二組第三週討論資料
![](https://i.imgur.com/ckMIvvN.png)
# 如何製作交易所

1. 管理資金、訂單
2. 撮合引擎


# 自動做市商 AMM 資金池
代表 uniswap、curve
[AMM進化史](https://news.cnyes.com/news/id/4943990)

## 最簡易
上次想最簡易的是交易所儲備很多種幣如usdt ETH，用預言機取得幣價，然後讓用戶直接用這個價格賣給交易所，
```solidity=
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.18 <0.9.0;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "../W1/erc20.sol";

contract Pool {
    uint256 public ethTotal;
    uint256 public tokenTotal;
    address public creator;
    ERC20 public token = ERC20(0xae6B0f75b55fa4c90b2768e3157b7000241A41c5);
    AggregatorV3Interface internal ethPriceFeed = AggregatorV3Interface(0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e);

    constructor() payable {
        ethTotal = msg.value; // Store the amount of ETH sent while deploying the contract
        tokenTotal = 1_000_000 * 10 ** token.decimals(); // Total supply of 1,000,000 tokens with decimals
        creator = msg.sender;
    }

    function getLatestEthPrice() public view returns (uint256) {
        (, int256 answer,,,) = ethPriceFeed.latestRoundData();
        return uint256(answer);
    }

    function buyEthWithTokens(uint256 tokensToSpend) public {
        require(token.balanceOf(msg.sender) >= tokensToSpend, "Not enough tokens to spend");

        uint256 ethPrice = getLatestEthPrice();
        uint256 ethToBuy = (tokensToSpend * 1e18) / ethPrice;
        require(address(this).balance >= ethToBuy, "Not enough ETH in the pool");

        ethTotal -= ethToBuy;
        tokenTotal += tokensToSpend;

        token.transferFrom(msg.sender, creator, tokensToSpend);
        payable(msg.sender).transfer(ethToBuy);
    }

    function sellEthForTokens() public payable {
        uint256 ethPrice = getLatestEthPrice();
        uint256 tokensToReturn = (msg.value * ethPrice) / 1e18;
        require(token.balanceOf(creator) >= tokensToReturn, "Not enough tokens in the pool");

        ethTotal += msg.value;
        tokenTotal -= tokensToReturn;

        token.transferFrom(creator, msg.sender, tokensToReturn);
    }

    function withdraw() public {
        require(msg.sender == creator, "Only the contract creator can call this function");
        payable(msg.sender).transfer(address(this).balance);
    }
}

```

## 基礎資金池
有一種恆乘積的方法，xy=k，x,y是兩種幣的數量，k是常數，在一開始創立資金池以及改變資金池深度會改變k=xy，這時x幣的價格是x=k/y。 


# 訂單簿
代表 dydx、幣安交易所、中心化交易所

## 鏈上訂單簿
[Gridex Protocol](https://github.com/GridexProtocol/core)
[Gridex Protocol: Integrating First Fully On-chain Order Book For a New Generation of DEX](https://news.taiwannet.com.tw/c8/87529/Gridex-Protocol-Integrating-First-Fully-On-chain-Order-Book-For-a-New-Generation-of-DEX.html)
代表 [D5 excange](https://trade.d5.xyz/)
## sample code 參附件一

```solidity=
    struct Order //訂單
        address trader;
        uint256 amount;
        uint256 price;
        uint256 timestamp;
    }
```
MinHeap存賣單
MaxHeap存買單
![](https://i.imgur.com/kbi47Yq.png)

怎麼掛買單買東西的手續費這麼高????
[orderbook](https://github.com/penguin72487/KryptoCampSolidity/blob/main/W1/orderbook.sol)
[erc20](https://github.com/penguin72487/KryptoCampSolidity/blob/main/W1/erc20.sol)
[heap](https://github.com/penguin72487/KryptoCampSolidity/blob/main/W1/heap.sol)

收到訂單先看沒不能成交，成交就不用存起來，還沒成交再存起來
## 鏈下訂單簿
dYdX V4 將採用鏈外訂單簿機制，改為在每個驗證節點的內存中運行


# 產品亮點

## K線圖 
去中化交易所幾乎都沒有提供K線圖，所以我們也可以提供可能小時級別的價格連線。
現成去中心化交易所K線圖小工具
[dexGuru](https://dex.guru/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2-eth/history)
[astro](https://app.astrotools.io/)
## 多幣gas fee
有時候會出先一些尷尬的情況，我只有USDT，要去uniswap買ETH，但是要付ETH的手續費，但是我就是沒有ETH才要買ETH，結果你跟我說要ETH當手續費。
如果不小心把所有以太幣去質押，如果要拿回來還需要手續費，

遇到以上情況，可以從中心化交易所、朋友那邊轉一些ETH到錢包，很麻煩。但是如果使用者可以付多種幣當手續費，雖然手續費可能比較多，但是節省麻煩，還是有人願意，不然在去中心化世界一點交易都無法做。

## 多幣解決方案
我覺得有幾種方案可以解決這個問題，手續費有兩種，
1. 一種是數據上鏈的手續費由L1ETH或是L2 polygon、arbitrum決定
2. 為了維持交易所運轉要收的 
### 方案
1. 其中一個是先由交易所代付手續費，把ETH交給使用者的時候就已經扣掉交易手續費以及上鏈手續費。  
2. 讓用戶付多種的幣，然後在合約直接買成ETH付上鏈手續費，交易手續費直接扣在給用戶的錢。


# 附件一 還在測試階段

```solidity=
// SPDX-License-Identifier: MIT
//0xd9145CCE52D386f254917e481eB44e9943F39138
pragma solidity ^0.8.19;
import "./heap.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract OrderBook_tGD_ETH {   // sell tGD get ETH buy tGD pay ETH
    //enum OrderType { Buy, Sell }
    MinHeap private buyHeap;
    MaxHeap private sellHeap;

    IERC20 tGD = IERC20(0xd9145CCE52D386f254917e481eB44e9943F39138);
    constructor() {
        buyHeap = new MinHeap();
        sellHeap = new MaxHeap();
    }
    function getSellPrice() public view returns (uint256) {
        return sellHeap.top().price;
    }
    function getBuyPrice() public view returns (uint256) {
        return buyHeap.top().price;
    }
    function buyOrder(uint256 _amount, uint256 price) public payable{
        uint256 amount = _amount;
        require(msg.value >= price);

        if(buyHeap.top().price >= price) {

            buyHeap.push(MinHeap.Order(msg.sender, amount, price, block.timestamp));
        }
        if(sellHeap.top().price<=price )
        {
            //match
            if(sellHeap.top().price == price) {
                while(sellHeap.top().price == price) {
                    if(sellHeap.top().amount > amount) {
                        
                        address payable receiver = payable(sellHeap.top().trader);
                        receiver.transfer(amount);

                        sellHeap.top().amount -= amount;
                        amount = 0;
                        break;
                    } else {
                        address payable receiver = payable(sellHeap.top().trader);
                        receiver.transfer(sellHeap.top().amount);
                        amount -= sellHeap.top().amount;
                        sellHeap.pop();
                    }
                }
            }
            tGD.transferFrom(msg.sender, address(this), _amount-amount);
            if(amount > 0) {
                buyHeap.push(MinHeap.Order(msg.sender, amount, price, block.timestamp));
            }

        }
    }
    function buyOrder(uint256 _amount) public payable
    {
        uint256 amount = _amount;
        require(msg.value >= amount* buyHeap.top().price);
        while(amount > 0) {
            if(buyHeap.top().amount > amount) {
                address payable receiver = payable(sellHeap.top().trader);
                receiver.transfer(amount);
                buyHeap.top().amount -= amount;
                amount = 0;
                break;
            } else {
                address payable receiver = payable(sellHeap.top().trader);
                receiver.transfer(sellHeap.top().amount);
                amount -= buyHeap.top().amount;
                buyHeap.pop();
            }
        }
    }
    function sellOrder(uint256 _amount) public 
    {
        uint256 amount = _amount;
        require(tGD.balanceOf(msg.sender) >= _amount);
        while(amount > 0) {
            if(sellHeap.top().amount > amount) {
                tGD.transferFrom(msg.sender, buyHeap.top().trader, _amount-amount);
                sellHeap.top().amount -= amount;
                amount = 0;
                break;
            } else {
                tGD.transferFrom(msg.sender, buyHeap.top().trader, buyHeap.top().amount);
                amount -= sellHeap.top().amount;
                sellHeap.pop();
            }
        }
    }
    function sellOrder(uint256 _amount, uint256 price) public {
        uint256 amount = _amount;
        require(tGD.balanceOf(msg.sender) >= _amount);  

        if(sellHeap.top().price <= price) {
            tGD.transferFrom(msg.sender, address(this), _amount);
            sellHeap.push(MaxHeap.Order(msg.sender, amount, price, block.timestamp));
            return;
        }
        if(buyHeap.top().price>=price )
        {
            //match
            if(buyHeap.top().price == price) {
                while(buyHeap.top().price == price) {
                    if(buyHeap.top().amount > amount) {
                        tGD.transferFrom(msg.sender, buyHeap.top().trader, _amount-amount);
                        buyHeap.top().amount -= amount;
                        amount = 0;
                        break;
                    } else {
                        tGD.transferFrom(msg.sender, buyHeap.top().trader, buyHeap.top().amount);
                        amount -= buyHeap.top().amount;
                        buyHeap.pop();
                    }
                }
            }
            address payable receiver = payable(msg.sender);
            receiver.transfer(_amount-amount);
            if(amount > 0) {
                sellHeap.push(MaxHeap.Order(msg.sender, amount, price, block.timestamp));
            }

        }
    }
    
}


```
```solidity=
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

contract MinHeap {
    struct Order {
        address trader;
        uint256 amount;
        uint256 price;
        uint256 timestamp;
    }

    Order[] private heap;

    constructor() {
        heap.push(Order(address(0), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));
    }
    
    function push (Order memory o) public {
        heap.push(o);
        uint256 i = heap.length - 1;
        while (i > 0) {
            uint256 p = (i - 1) / 2;
            if (_conpare(i,p)) {
                _swap(p, i);
                i = p;
            } else {
                break;
            }
        }
    }

    function pop() public {
        require(heap.length > 0, "heap is empty");
        heap[0] = heap[heap.length - 1];
        delete heap[heap.length - 1];
        heap.pop();
        _heapify(0);
    }
    function top() public view returns (Order memory) {
        require(heap.length > 0, "heap is empty");
        return heap[0];
    }
    function _heapify(uint256 _i) private {
        uint256 l = 2 * _i + 1;
        uint256 r = 2 * _i + 2;
        uint256 smallest = _i;
        if (l < heap.length && _conpare(l ,smallest)) {
            smallest = l;
        }
        if (r < heap.length && _conpare(r ,smallest)) {
            smallest = r;
        }
        if (smallest != _i) {
            _swap(_i, smallest);
            _heapify(smallest);
        }
    } 
    function _conpare(uint256 i,uint256 j) private view returns (bool) {
        
        if(heap[i].price == heap[j].price) {
            if(heap[i].timestamp == heap[j].timestamp) {
                if(heap[i].amount == heap[j].amount) {
                    return heap[i].trader < heap[j].trader;
                }
                return heap[i].amount < heap[j].amount;
            }
            return heap[i].timestamp < heap[j].timestamp;
        }
        return heap[i].price < heap[j].price;
    }

    function _swap(uint256 a, uint256 b) private  {
        Order memory tmp = heap[a];
        heap[a] = heap[b];
        heap[b] = tmp;
    }
}

contract MaxHeap{
    struct Order {
        address trader;
        uint256 amount;
        uint256 price;
        uint256 timestamp;
    }
    Order[] private heap;
    constructor() {
        heap.push(Order(address(0), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff));
    }
    function push (Order memory o) public {
        heap.push(o);
        uint256 i = heap.length - 1;
        while (i > 0) {
            uint256 p = (i - 1) / 2;
            if (_conpare(i,p)) {
                _swap(p, i);
                i = p;
            } else {
                break;
            }
        }
    }
    function pop() public {
        require(heap.length > 0, "heap is empty");
        heap[0] = heap[heap.length - 1];
        delete heap[heap.length - 1];
        heap.pop();
        _heapify(0);
    }
    function top() public view returns (Order memory) {
        require(heap.length > 0, "heap is empty");
        return heap[0];
    }
    function _heapify(uint256 _i) private {
        uint256 l = 2 * _i + 1;
        uint256 r = 2 * _i + 2;
        uint256 largest = _i;
        if (l < heap.length && _conpare(l ,largest)) {
            largest = l;
        }
        if (r < heap.length && _conpare(r ,largest)) {
            largest = r;
        }
        if (largest != _i) {
            _swap(_i, largest);
            _heapify(largest);
        }
    }

    function _conpare(uint256 i,uint256 j) private view returns (bool) {
        
        if(heap[i].price == heap[j].price) {
            if(heap[i].timestamp == heap[j].timestamp) {
                if(heap[i].amount == heap[j].amount) {
                    return heap[i].trader < heap[j].trader;
                }
                return heap[i].amount < heap[j].amount;
            }
            return heap[i].timestamp < heap[j].timestamp;
        }
        return heap[i].price > heap[j].price;
    }
    function _swap(uint256 a, uint256 b) private  {
        Order memory tmp = heap[a];
        heap[a] = heap[b];
        heap[b] = tmp;
    }
    
}




```