ℹ️OrdersRepo

Listing Orders Repo

API

Source Code:

OrdersRepo
// SPDX-License-Identifier: UNLICENSED

/* *
 * Copyright (c) 2021-2023 LI LI @ JINGTIAN & GONGCHENG.
 *
 * This WORK is licensed under ComBoox SoftWare License 1.0, a copy of which 
 * can be obtained at:
 *         [https://github.com/paul-lee-attorney/comboox]
 *
 * THIS WORK IS PROVIDED ON AN "AS IS" BASIS, WITHOUT 
 * WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 * TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. IN NO 
 * EVENT SHALL ANY CONTRIBUTOR BE LIABLE TO YOU FOR ANY DAMAGES.
 *
 * YOU ARE PROHIBITED FROM DEPLOYING THE SMART CONTRACTS OF THIS WORK, IN WHOLE 
 * OR IN PART, FOR WHATEVER PURPOSE, ON ANY BLOCKCHAIN NETWORK THAT HAS ONE OR 
 * MORE NODES THAT ARE OUT OF YOUR CONTROL.
 * */

pragma solidity ^0.8.8;

import "./GoldChain.sol";
import "./EnumerableSet.sol";

library OrdersRepo {
    using EnumerableSet for EnumerableSet.UintSet;
    using GoldChain for GoldChain.Chain;
    using GoldChain for GoldChain.Node;

    enum StateOfInvestor {
        Pending,
        Approved,
        Revoked
    }

    struct Investor {
        uint40 userNo;
        uint40 groupRep;
        uint48 regDate;
        uint40 verifier;
        uint48 approveDate;
        uint32 data;
        uint8 state;
        bytes32 idHash;
    }

    struct Deal {
        uint16 classOfShare;
        uint32 seqOfShare;
        uint40 buyer;
        uint40 groupRep;
        uint64 paid;
        uint32 price;
        uint16 votingWeight;
    }

    struct Repo {
        // class => Chain
        mapping(uint256 => GoldChain.Chain) ordersOfClass;
        EnumerableSet.UintSet classesList;
        mapping(uint256 => Investor) investors;
        uint[] investorsList;
        // ---- tempArry ----
        GoldChain.Node[] expired;
        Deal[] deals;
    }

    //################
    //##  Modifier  ##
    //################

    modifier investorExist(
        Repo storage repo,
        uint acct
    ) {
        require(isInvestor(repo, acct),
            "OR.investorExist: not");
        _;
    }

    modifier classExist(
        Repo storage repo,
        uint classOfShare
    ) {
        require (isClass(repo, classOfShare),
            "OR.classExist: not");
        _;
    }

    //#################
    //##  Write I/O  ##
    //#################

    // ==== Codify & Parse ====

    function parseSn(bytes32 sn) public pure returns(
        Deal memory deal
    ) {
        uint _sn = uint(sn);

        deal.classOfShare = uint16(_sn >> 240);
        deal.seqOfShare = uint32(_sn >> 208);
        deal.buyer = uint40(_sn >> 168);
        deal.groupRep = uint40(_sn >> 128);
        deal.paid = uint64(_sn >> 64);
        deal.price = uint32(_sn >> 32);
        deal.votingWeight = uint16(_sn >> 16);
    }

    function codifyDeal(
        Deal memory deal
    ) public pure returns(bytes32 sn) {
        bytes memory _sn = 
            abi.encodePacked(
                deal.classOfShare,
                deal.seqOfShare,
                deal.buyer,
                deal.groupRep,
                deal.paid,
                deal.price,
                deal.votingWeight
            );

        assembly {
            sn := mload(add(_sn, 0x20))
        }                        
    }

    // ==== Investor ====

    function regInvestor(
        Repo storage repo,
        uint userNo,
        uint groupRep,
        bytes32 idHash
    ) public {
        require(idHash != bytes32(0), 
            "OR.regInvestor: zero idHash");
        
        uint40 user = uint40(userNo);

        require(user > 0,
            "OR.regInvestor: zero userNo");

        Investor storage investor = repo.investors[user];
        
        investor.userNo = user;
        investor.groupRep = uint40(groupRep);
        investor.idHash = idHash;

        if (!isInvestor(repo, userNo)) {
            repo.investorsList.push(user);
            investor.regDate = uint48(block.timestamp);
        } else {
            if (investor.state == uint8(StateOfInvestor.Approved))
                _decreaseQtyOfInvestors(repo);
            investor.state = uint8(StateOfInvestor.Pending);
        }
    }

    function approveInvestor(
        Repo storage repo,
        uint acct,
        uint verifier
    ) public investorExist(repo, acct) {

        Investor storage investor = repo.investors[acct];

        require(investor.state != uint8(StateOfInvestor.Approved),
            "OR,apprInv: wrong state");

        investor.verifier = uint40(verifier);
        investor.approveDate = uint48(block.timestamp);
        investor.state = uint8(StateOfInvestor.Approved);

        _increaseQtyOfInvestors(repo);
    }

    function revokeInvestor(
        Repo storage repo,
        uint acct,
        uint verifier
    ) public {

        Investor storage investor = repo.investors[acct];

        require(investor.state == uint8(StateOfInvestor.Approved),
            "OR,revokeInvestor: wrong state");

        investor.verifier = uint40(verifier);
        investor.approveDate = uint48(block.timestamp);
        investor.state = uint8(StateOfInvestor.Revoked);

        _decreaseQtyOfInvestors(repo);
    }

    

    // ==== Order ====

    function placeSellOrder(
        Repo storage repo,
        uint classOfShare,
        uint seqOfShare,
        uint votingWeight,
        uint paid,
        uint price,
        uint execHours,
        bool sortFromHead
    ) public returns (bytes32 sn) {

        repo.classesList.add(classOfShare);

        GoldChain.Chain storage chain = 
            repo.ordersOfClass[classOfShare];

        sn = chain.createNode(
            seqOfShare,
            votingWeight,
            paid,
            price,
            execHours,
            sortFromHead
        );
    }

    function withdrawSellOrder(
        Repo storage repo,
        uint classOfShare,
        uint seqOfOrder
    ) public classExist(repo, classOfShare) 
        returns (GoldChain.Node memory) 
    {
        return repo.ordersOfClass[classOfShare].offChain(seqOfOrder);
    }

    function placeBuyOrder(
        Repo storage repo,
        uint acct,
        uint classOfShare,
        uint paid,
        uint price
    ) public classExist(repo, classOfShare) returns (
        Deal[] memory deals,
        Deal memory call,
        GoldChain.Node[] memory expired
    ) {

        Investor memory investor = 
            getInvestor(repo, acct);

        require (investor.state == uint8(StateOfInvestor.Approved),
            "OR.placeBuyOrder: wrong stateOfInvestor");

        call.classOfShare = uint16(classOfShare);
        call.paid = uint64(paid);
        call.price = uint32(price);
        call.buyer = investor.userNo;
        call.groupRep = investor.groupRep;         

        _checkOffers(repo, call);
        
        deals = repo.deals;
        delete repo.deals;

        expired = repo.expired;
        delete repo.expired;
    }

    function _checkOffers(
        Repo storage repo,
        Deal memory call
    ) private {

        GoldChain.Chain storage chain = 
            repo.ordersOfClass[call.classOfShare];

        uint32 seqOfOffer = chain.head();

        while(seqOfOffer > 0 && call.paid > 0) {

            GoldChain.Node memory offer = chain.nodes[seqOfOffer];

            if (offer.expireDate <= block.timestamp) {

                repo.expired.push(
                    chain.offChain(seqOfOffer)
                );
                seqOfOffer = offer.next;
                
                continue;
            }
            
            if (offer.price <= call.price) {

                bool paidAsPut = offer.paid <= call.paid;

                Deal memory deal = Deal({
                    classOfShare: call.classOfShare,
                    seqOfShare: offer.seqOfShare,
                    buyer: call.buyer,
                    groupRep: call.groupRep,
                    paid: paidAsPut ? offer.paid : call.paid,
                    price: offer.price,
                    votingWeight: offer.votingWeight
                });

                repo.deals.push(deal);

                if (paidAsPut) {
                    chain.offChain(seqOfOffer);
                    seqOfOffer = offer.next;
                } else {
                    chain.nodes[seqOfOffer].paid -= deal.paid;
                }

                call.paid -= deal.paid;
            } else break;
        }
    }

    function _increaseQtyOfInvestors(
        Repo storage repo
    ) private {
        repo.investors[0].verifier++;
    }

    function _decreaseQtyOfInvestors(
        Repo storage repo
    ) private {
        repo.investors[0].verifier--;
    }


    //################
    //##  Read I/O  ##
    //################

    // ==== Investor ====

    function isInvestor(
        Repo storage repo,
        uint acct
    ) public view returns(bool) {
        return repo.investors[acct].regDate > 0;
    }

    function getInvestor(
        Repo storage repo,
        uint acct
    ) public view investorExist(repo, acct) returns(Investor memory) {
        return repo.investors[acct];
    }

    function getQtyOfInvestors(
        Repo storage repo
    ) public view returns(uint) {
        return repo.investors[0].verifier;
    }

    function investorList(
        Repo storage repo
    ) public view returns(uint[] memory) {
        return repo.investorsList;
    }

    function investorInfoList(
        Repo storage repo
    ) public view returns(Investor[] memory list) {
        uint[] memory seqList = repo.investorsList;
        uint len = seqList.length;

        list = new Investor[](len);

        while (len > 0) {
            list[len - 1] = repo.investors[seqList[len - 1]];
            len--;
        }

        return list;
    }

    // ==== Class ====

    function isClass(
        Repo storage repo,
        uint classOfShare
    ) public view returns (bool) {
        return repo.classesList.contains(classOfShare);
    }

    function getClassesList(
        Repo storage repo    
    ) public view returns (uint[] memory) {
        return repo.classesList.values();
    }

    // ==== TempArrays ====

    function getExpired(
        Repo storage repo
    ) public view returns (GoldChain.Node[] memory) {
        return repo.expired;
    }

    function getDeals(
        Repo storage repo
    ) public view returns(Deal[] memory) {
        return repo.deals;
    }

}