---
# System prepended metadata

title: NFT SWAPER

---

# NFT SWAPER

[TOC]

## 典範
[NFT Trader](https://www.nfttrader.io/)
[Sudo Swap](https://sudoswap.xyz/#/)


## 商務模式 
- 每筆交易收取的交易手續費(從雙方收取固定的金額或是地板的%，可以設定最高收取上限值)
- 如果交易有補上差價(補ETH或USDT etc.)，收取交易手續費與價差的%費用
- 發行平台的NFT，NFT持有者可以免手續費交易


## 執行內容
- 白皮書 - Norman
- 使用者操作流程 - Andy
- 智能合約撰寫 - CT Joe
- 前端WEB JS CSS開發 -Lu Andy
- 智能合約與WEB串接 -Joe
- 前端UX/UI設計(OPTION)


## Session note: 

- 白名單交換，尚有待解決問題
- NFT交換
    - 補差價交易
    - 跨Standard trade 
    - 跨Collection trade
    - NFT一對多trade
    - 顯示某錢包所有NFT, Opensea 
- 需不需要平台幣？ 原因
- 平台公信力問題
- 合約安全性
- 交易流程：
  線上媒合交易？ 上不上架交易？
    - scenario 1:
    - scenario 2: 
- 手續費

## Spec

- 功能需求
    1. 可以進行NFT交換（雙方交易者確認好交易的需求，由任一方發起交易請求，將會產生一個唯一連結傳給他方並進行交易）
    2. NFT交易可以補價差
    3. 可以查看所有的交易紀錄
    4. 個人資料頁(OPTION)
    5. 可以連結METAMASK顯示所有的NFT(OPTION)
- 前端元件架構
- 非功能需求
    - store設計
    - 優化
    - accessibility
    - 擴展性考量


## 6/5會議討論
1. 雙方交易的GAS支付確認，GAS負擔原則
2. 連結錢包截圖當有NFT時的介面呈現 (https://nftx.io/rewards/dashboard)
3. 介面設計先不用太複雜，因為功能不多簡單明瞭即可
4. 先以參考sudoswap功能為主(參考以下功能流程)
![](https://i.imgur.com/4w8qq8M.png)

- HOME PAGE
![](https://i.imgur.com/lluriZA.png)
- CREATE SWAP
![](https://i.imgur.com/XoxppUq.png)
- YOURS NFTS
![](https://i.imgur.com/MKvrprl.png)

## 流程截圖

### NFTX
![](https://i.imgur.com/IvYcbLy.jpg)

### Sudo Swap
![](https://i.imgur.com/ghrM71R.jpg)

### NFTrader
![](https://i.imgur.com/cg8qVcD.jpg)


### 初始功能定案 2022/06/11
1. 創建交易紀錄(NFT SWAP)
2. 首頁：列出所有的交易紀錄
3. 你的NFT：顯示錢包的所有的NFT收集
4. 交換的交易紀錄(兩個TAB以下)：
    1.交易中的紀錄
    2.交易完成的紀錄




---

## NFTSwap Contract
 ```jsx=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract NFTSwap is Initializable, ERC721Upgradeable, PausableUpgradeable, OwnableUpgradeable {

    // Transaction State 
    enum TransactionState {
        PENDING, // 0 uint256()
        COMPLETED //1 uint256()
    }
    
    // Transaction struct 
    struct Transaction {
        uint transactionId;
        address initiator;
        address receiver;
        address contractAddress;
        uint256 haveTokenId;
        uint256 wantTokenId;
        uint state;
        bool designated; // option
        uint amount; // option
        uint256 expiredDate; // option
    }

    // mapping from address => transaction 
    mapping(address => uint []) public ownerTransactions; 

    // transactions list
    Transaction[] public transactions;

    // modifier isAddress(address ethereumAddress) {
    //     string memory regex = "^0x[0-9a-f]{40}$";
    //     require(!ethereumAddress.matches(regex),"invalid address");
    //     _;
    // }

    // Check address is not zero
    modifier isZeroAddress(address _ethereumAddress) {
        require(_ethereumAddress != address(0), "Can't input zero address");
        _;
    }

    // Check transaction exist
    modifier transactionExists(uint _transactionId) {
        require(_transactionId < transactions.length, "Transaction does not exist");
        _;
    }

    // Check transaction state is completed
    modifier transactionIsCompleted(uint _transactionId) {
        require(!(uint(transactions[_transactionId].state) == 1), "transaction already completed");
        _;
    }

    // Check is transaction receiver
    modifier isTransactionReceiver(uint _transactionId) {
        require(transactions[_transactionId].receiver == msg.sender,"You're not the transaction receiver");
        _;
    }

    // Check swap address is not equal
    modifier notSameAddress(address _initiator, address _reciver) {
        require(_initiator != _reciver ,"Initiator address can't equal reciver address (Not same address in the transaction)");
         _;
    }

    // Check swap token id is correct
    modifier isCorrectTokenIdToSwap(uint _transactionId, uint _wantTokenId) {
        require(transactions[_transactionId].wantTokenId == _wantTokenId,"The Swap NFT token is incorrect");
        _;
    }

    // Check token of owner
    modifier isTokenOwner(address _contractAddress, uint256 _tokenId) {

        // call another contract ownerOf
        require(ERC721(_contractAddress).ownerOf(_tokenId) == msg.sender, "You're not token owner");
        _;
    }

    // Check token of owner
    modifier isTokenApproved(address _contractAddress, uint256 _tokenId) {

          // call another contract getApproved
        require(ERC721(_contractAddress).getApproved(_tokenId) != address(0), "Please approve token first");
        _;
    }

    // function test(address _contractAddress, uint256 _tokenId) public view returns (bool) {
    //     return ERC721(_contractAddress).getApproved(_tokenId) != address(0);
    // }
    
    constructor() {
        _disableInitializers();
    }
    
    function createTransaction(
        address _receiver,
        address _contractAddress,
        uint256 _haveTokenId,
        uint256 _wantTokenId,
        bool _designated,
        uint _amount,
        uint256 _expiredDate
    ) public
        isTokenOwner(_contractAddress,_haveTokenId)
        isTokenApproved(_contractAddress,_haveTokenId)
    {

        require(_receiver != msg.sender,"Initiator address can't equal reciver address (Not same address in the transaction)");
        require(ERC721(_contractAddress).ownerOf(_haveTokenId) != ERC721(_contractAddress).ownerOf(_wantTokenId),"Can't not swap nft to same owner");

        // get transaction current total count
        uint index = transactions.length;
        
        // push transaction data to transactions
        transactions.push(
            Transaction({
                transactionId:index,
                initiator:msg.sender,
                receiver:_receiver,
                contractAddress:_contractAddress,
                haveTokenId:_haveTokenId,
                wantTokenId:_wantTokenId,
                state:uint(TransactionState.PENDING),
                designated:_designated,
                amount:_amount,
                expiredDate:_expiredDate
            })
        );

        // push transaction data to ownerTransactions for initiator 
        ownerTransactions[msg.sender].push(index);

        // push transaction data to ownerTransactions for receiver
        ownerTransactions[_receiver].push(index);
    }

    function confirmTransaction(
        address _contractAddress,
        uint _transactionId,
        uint _wantTokenId
    ) public
        transactionExists(_transactionId)
        transactionIsCompleted(_transactionId)
        isTransactionReceiver(_transactionId)
        isCorrectTokenIdToSwap(_transactionId,_wantTokenId)
        isTokenOwner(_contractAddress,_wantTokenId)
        isTokenApproved(_contractAddress,_wantTokenId)
    {
        // get transaction data
        Transaction storage transaction = transactions[_transactionId];

        // update transaction state to completed
        transaction.state = uint(TransactionState.COMPLETED);

        //excute NFT swap
        _excuteSwap(transaction.transactionId);
    }
    
    function _excuteSwap(uint _transactionId) private {
        
        // get transaction data
        Transaction storage transaction = transactions[_transactionId];
        
        // initiator transfer to receiver haveTokenId
        ERC721(transaction.contractAddress).transferFrom(
            transaction.initiator,
            transaction.receiver,
            transaction.haveTokenId           
        );

        // receiver transfer to initiator wantTokenId
        ERC721(transaction.contractAddress).transferFrom(
            transaction.receiver,
            transaction.initiator,
            transaction.wantTokenId      
        );
    }
    
    function getOwnerTransactions(address ownerAddress) public view returns (uint[] memory) {
        return ownerTransactions[ownerAddress];
    }

    function getTransactionCount() public view returns (uint) {
        return transactions.length;
    }
    
    function getTransaction(uint _transactionId) 
        public 
        view 
            transactionExists(_transactionId)
        returns(
            uint transactionId,
            address initiator,
            address receiver,
            address contractAddress,
            uint256 haveTokenId,
            uint256 wantTokenId,
            uint256 state,
            bool designated,
            uint amount,
            uint256 expiredDate
        )
    {

        Transaction storage transaction = transactions[_transactionId];

        return (
            transaction.transactionId,
            transaction.initiator,
            transaction.receiver,
            transaction.contractAddress,
            transaction.haveTokenId,
            transaction.wantTokenId,
            transaction.state,
            transaction.designated,
            transaction.amount,
            transaction.expiredDate
        );
    }

    function initialize() initializer public {
        __ERC721_init("NFTSWAP", "NFTS");
        __Pausable_init();
        __Ownable_init();
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        whenNotPaused
        override
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }
}    
```
## Write function
- createTransaction
- confirmTransaction
- 
## Read function
- getOwnerTransactions
- getTransactionCount
- getTransaction

## initialize?

## _beforeTokenTransfer
- revert ?
- check pause?

## 6/14 Meeting minutes

### Contract
- add revoke->**CT**
- approval debug->**CT**
- transaction record need jason.array for FE calling->**Andy**
- create NFT projects with enough metadata->**Joe**
### FE 
- optimization ->**Lu**
### Whitepaper
- Flow modification->**Norman**

## 6/15 Feedback
- 寫test contract
- 確認NFT和ETH是否還在,在confirm交換前


## 6/16(四) 19:00 專題會議 - 進度確認與討論事項
1. 透過前端Approve交換的ERC721 TOKEN
2. 交易明細與清單取得
	2.1 取得所有交易編號-(PS:透過promise all方式去跑所有交易明細欄位)
	2.2 取得持有者發起或被指定的交換者交易編號
	2.3 透過ID取得交易明細欄
3. 部署NFTSWAP合約，並進行前段串接(PS:基本的『SWAP』功能以及要可以『REVOKE』交易)
4. NFTSWAP進階功能
	4.1 補ETH
	4.2 加入交易到期日
	4.3 可以指定或不指定交換者
5. *進行交換判斷事項*：
	5.1 雙方都APPROVE了，最後進行CONFIRM進行交換時，需要再次判斷雙邊交易者是否還持有TOKEN以及足夠的ETH
6. 白皮書的內容太單薄要補強（CT提出可以以ROADMAP形式呈現,這部分需要由）

* 以上為今日會議確認與討論內容，希望今天的會議有初步可以進行流程的DEMO，甚至看能否直接部署到RINKEBY直接走一次測試流程。


 ```jsx=

// add transactions of owner
ownerTransactions[msg.sender].push(Id);
ownerTransactions[_receiver].push(Id);




// transactions of owner
    mapping(address => uint[]) public ownerTransactions;

   // Get transactions of owner.
    function getOwnerTransactions(address ownerAddress) public view returns (uint[] memory) {
        return ownerTransactions[ownerAddress];
    }

    // Get transaction count.
    function getTransaction(uint id) public view 
        returns (
            uint256 transactionId,
            address requestor,
            address receiver,
            uint256 myToken,
            uint256 wantToken,
            uint256 state
        ) 
    {

        Transaction storage transaction = transactions[id];

        return (
            transaction.transactionId,
            transaction.requestor,
            transaction.receiver,
            transaction.myToken,
            transaction.wantToken,
            transaction.state
        );
    }

    // Get transaction count.
    function getTransactionCount() public view returns (uint) {
        return transactions.length;
    }
```
## Whitepaper
- Introduction
- Team Background
- Website Tutorial
- Roadmap
  - 1st Stage: Platform, ERC721 token swap fuction
  - 2nd Stage: ERC721 trading with ETH
  - 3rd Stage: Launch platform NFT (utility:swap fee discount.airdrop Swaper$)
  - 4th Stahe: Launch platform token-Swaper$
    -20%:Team
    -40%:Treasury
    -10%:Airdrop
    -30%:IDO(Initial DEX Offering)
## 6/18 Pre-Demo FeedBack
- 可展示目前市場NFT交易需求 (Opensea交易量/場外交易需求)
- 平台Demo流程優化(預錄影片?)
- 有哪些優勢功能, 可讓手續費提高但仍就有需求
- 要怎麼找到對家讓需求浮現
- Demo 預錄影片

