🏛️RegCenter

Registration Center

Name

RegCenter

Dependent Contract

IRegCenter, ERC20, PriceConsumer, UsersRepo, DocsRepo, IGeneralKeeper, IAccessControl, IERC20, IPriceConsumer

API

Source Code:

RegCenter
// 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 "./IRegCenter.sol";
import "./ERC20/ERC20.sol";
import "./Oracles/PriceConsumer2.sol";

contract RegCenter is IRegCenter, ERC20("ComBooxPoints", "CBP"), PriceConsumer2 {
    using DocsRepo for DocsRepo.Repo;
    using DocsRepo for DocsRepo.Head;
    using UsersRepo for UsersRepo.Repo;
    using UsersRepo for uint256;
    
    UsersRepo.Repo private _users;
    DocsRepo.Repo private _docs;
    mapping(address => uint256) private _coffers;
    
    constructor() {
        _users.regUser(msg.sender);
    }

    // ########################
    // ##    Opts Setting    ##
    // ########################

    function setPlatformRule(bytes32 snOfRule) external {
        _users.setPlatformRule(snOfRule, msg.sender);
        emit SetPlatformRule(snOfRule);
    }

    function setPriceFeed(uint seq, address feed_ ) external {
        require(msg.sender == _users.getBookeeper(), "RC: not bookeeper");
        _setPriceFeed(seq, feed_);
        emit SetPriceFeed(seq, feed_);
    }

    // ==== Power transfer ====

    function transferOwnership(address newOwner) external {
        _users.transferOwnership(newOwner, msg.sender);
        emit TransferOwnership(newOwner);
    }

    function handoverCenterKey(address newKeeper) external {
        _users.handoverCenterKey(newKeeper, msg.sender);
        emit TurnOverCenterKey(newKeeper);
    }

    // ##################
    // ##  Mint & Lock ##
    // ##################

    function mint(uint256 to, uint amt) external {

        require(msg.sender == _users.getOwner(), 
            "RC.mintPoints: not owner");

        require(to > 0, "RC.mintPoints: zero to");
        
        _mint(_users.users[to].primeKey.pubKey, amt);
    }

    function burn(uint amt) external {
        require(msg.sender == _users.getOwner(), 
            "RC.burnPoints: not owner");

        _burn(msg.sender, amt);
    }

    function mintAndLockPoints(
        uint to, 
        uint amtOfGLee, 
        uint expireDate, 
        bytes32 hashLock
    ) external {   
        LockersRepo.Head memory head = 
            _users.mintAndLockPoints(
                to, 
                amtOfGLee, 
                expireDate, 
                hashLock, 
                msg.sender
            );

        _mint(address(this), amtOfGLee * 10 ** 9);
        _coffers[msg.sender] += amtOfGLee * 10 ** 9;

        emit LockPoints( LockersRepo.codifyHead(head), hashLock);
    }

    function lockPoints(
        uint to, 
        uint amtOfGLee, 
        uint expireDate, 
        bytes32 hashLock
    ) external {

        LockersRepo.Head memory head = 
            _users.lockPoints(
                to, 
                amtOfGLee, 
                expireDate, 
                hashLock, 
                msg.sender
            );
        _lockPointsInCoffer(msg.sender, amtOfGLee * 10 ** 9);
        emit LockPoints(LockersRepo.codifyHead(head), hashLock);
    }

    function _lockPointsInCoffer(address caller, uint value) private {
        _transfer(caller, address(this), value);
        _coffers[caller] += value;
    }

    function lockConsideration(
        uint to, 
        uint amtOfGLee, 
        uint expireDate, 
        address counterLocker, 
        bytes calldata payload, 
        bytes32 hashLock
    ) external {

        LockersRepo.Head memory head =
            _users.lockConsideration(
                to, 
                amtOfGLee, 
                expireDate, 
                counterLocker, 
                payload, 
                hashLock, 
                msg.sender
            );
            
        _lockPointsInCoffer(msg.sender, amtOfGLee * 10 ** 9);
        emit LockConsideration(LockersRepo.codifyHead(head), counterLocker, payload, hashLock);
    }

    function pickupPoints(bytes32 hashLock, string memory hashKey) external
    {
        LockersRepo.Head memory head = 
            _users.pickupPoints(hashLock, hashKey, msg.sender);

        if (head.value > 0) {
            emit PickupPoints(LockersRepo.codifyHead(head));

            _pickupPointsFromCoffer(
                _users.users[head.from].primeKey.pubKey, 
                _users.users[head.to].primeKey.pubKey, 
                head.value * 10 ** 9
            );
        }
    }

    function _pickupPointsFromCoffer(address from, address to, uint amt) private {
        require(_coffers[from] >= amt, 
            "RC.pickupPointsFromCoffer: insufficient balance");
        _coffers[from] -= amt;
        _transfer(address(this), to, amt);
    }

    function withdrawPoints(bytes32 hashLock) external
    {
        LockersRepo.Head memory head = 
            _users.withdrawDeposit(hashLock, msg.sender);

        if (head.value > 0) {
            _withdrawPoints(
                _users.users[head.from].primeKey.pubKey, 
                head.value * 10 ** 9
            );
            emit WithdrawPoints(LockersRepo.codifyHead(head));
        }
    }

    function _withdrawPoints(address from, uint amt) private {
        require(_coffers[from] >= amt, 
            "RC.withdrawPoints: insufficient balance");
        _coffers[from] -= amt;
        _transfer(address(this), from, amt);
    }

    function getDepositAmt(address from) external view returns(uint) {
        return _coffers[from];
    }

    function getLocker(bytes32 hashLock) external
        view returns (LockersRepo.Locker memory locker)
    {
        locker = _users.getLocker(hashLock);
    }

    function getLocksList() external 
        view returns (bytes32[] memory)
    {
        return _users.getLocksList();
    }

    // ################
    // ##    Users   ##
    // ################

    function regUser() external {
        UsersRepo.User memory user = _users.regUser(msg.sender);
        if (user.primeKey.gift > 0) {
            _mint(user.primeKey.pubKey, uint(user.primeKey.gift) * 10 ** 9);
        }
    }

    function setBackupKey(address bKey) external {
        _users.setBackupKey(bKey, msg.sender);
    }

    function upgradeBackupToPrime() external {
        _users.upgradeBackupToPrime(msg.sender);
    }

    function setRoyaltyRule(bytes32 snOfRoyalty) external {
        _users.setRoyaltyRule(snOfRoyalty, msg.sender);
    }

    // ###############
    // ##    Docs   ##
    // ###############

    function setTemplate(uint typeOfDoc, address body, uint author) external {
        require(msg.sender == getBookeeper(), 
            "RC.setTemplate: not bookeeper");
        
        DocsRepo.Head memory head = 
            _docs.setTemplate(typeOfDoc, body, author, _users.getUserNo(msg.sender));

        emit SetTemplate(head.typeOfDoc, head.version, body);
    }

    function transferIPR(uint typeOfDoc, uint version, uint transferee) external {
        _docs.transferIPR(typeOfDoc, version, transferee, _users.getUserNo(msg.sender));

        emit TransferIPR(typeOfDoc, version, transferee);
    }

    function createDoc(
        bytes32 snOfDoc,
        address primeKeyOfOwner
    ) public returns(DocsRepo.Doc memory doc)
    {
        doc = _docs.createDoc(snOfDoc, _users.getUserNo(primeKeyOfOwner));
        emit CreateDoc(doc.head.codifyHead(), doc.body);
    }

    // #########################
    // ## Comp Deploy Scripts ##
    // #########################

    function createComp(address dk) external 
    {
        address primeKeyOfOwner = msg.sender;
        address rc = address(this);
        
        address gk = _createDocAtLatestVersion(20, primeKeyOfOwner);
        IAccessControl(gk).init(primeKeyOfOwner, rc, rc, gk);
        IGeneralKeeper(gk).createCorpSeal();

        address[11] memory keepers = 
            _deployKeepers(primeKeyOfOwner, dk, rc, gk);

        _deployBooks(keepers, primeKeyOfOwner, rc, gk);
    
        IAccessControl(gk).setDirectKeeper(dk);
    }

    function _deployKeepers(
        address primeKeyOfOwner, 
        address dk,
        address rc,
        address gk
    ) private returns (address[11] memory keepers) {
        keepers[0] = dk;
        uint i = 1;
        while (i < 11) {
            keepers[i] = _createDocAtLatestVersion(i, primeKeyOfOwner);
            IAccessControl(keepers[i]).init(primeKeyOfOwner, gk, rc, gk);
            IGeneralKeeper(gk).regKeeper(i, keepers[i]);
            i++;
        }
    }

    function _deployBooks(
        address[11] memory keepers,
        address primeKeyOfOwner, 
        address rc,
        address gk
    ) private {
        address[10] memory books;
        uint8[10] memory types = [11, 12, 13, 14, 13, 15, 16, 17, 18, 19];
        uint8[10] memory seqOfDK = [1, 2, 3, 0, 5, 6, 7, 8, 0, 10];

        uint i;
        while (i < 10) {
            books[i] = _createDocAtLatestVersion(types[i], primeKeyOfOwner);
            IAccessControl(books[i]).init(primeKeyOfOwner, keepers[seqOfDK[i]], rc, gk);
            IGeneralKeeper(gk).regBook(i+1, books[i]);
            i++;
        }
    }

    function _createDocAtLatestVersion(uint256 typeOfDoc, address primeKeyOfOwner) internal
        returns(address body)
    {
        uint256 latest = _docs.counterOfVersions(typeOfDoc);
        bytes32 snOfDoc = bytes32((typeOfDoc << 224) + uint224(latest << 192));
        body = createDoc(snOfDoc, primeKeyOfOwner).body;
    }

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

    // ==== options ====

    function getOwner() public view returns (address) {
        return _users.getOwner();
    }

    function getBookeeper() public view returns (address) {
        return _users.getBookeeper();
    }

    function getPlatformRule() external view returns(UsersRepo.Rule memory) {
        return _users.getPlatformRule();
    }

    // ==== Users ====

    function isKey(address key) external view returns (bool) {
        return _users.isKey(key);
    }

    function counterOfUsers() external view returns(uint40) {
        return _users.counterOfUsers();
    }

    function getUser() external view returns (UsersRepo.User memory)
    {
        return _users.getUser(msg.sender);
    }

    function getRoyaltyRule(uint author)external view returns (UsersRepo.Key memory) {
        return _users.getRoyaltyRule(author);
    }

    function getUserNo(address targetAddr, uint fee, uint author) external returns (uint40) {

        uint40 target = _users.getUserNo(targetAddr);

        if (msg.sender != targetAddr && author > 0) {

            require(_docs.docExist(msg.sender), 
                "RC.getUserNo: msgSender not registered ");
            
            UsersRepo.Key memory rr = _users.getRoyaltyRule(author);
            address authorAddr = _users.users[author].primeKey.pubKey; 

            _chargeFee(targetAddr, fee, authorAddr, rr);

        }

        return target;
    }

    function _chargeFee(
        address targetAddr, 
        uint fee, 
        address authorAddr,
        UsersRepo.Key memory rr    
    ) private {

        UsersRepo.User storage t = _users.users[_users.getUserNo(targetAddr)];
        address ownerAddr = _users.getOwner();

        UsersRepo.Rule memory pr = _users.getPlatformRule();
        
        uint floorPrice = uint(pr.floor) * 10 ** 9;

        require(fee >= floorPrice, "RC.chargeFee: lower than floor");

        uint offAmt = uint(t.primeKey.coupon) * uint(rr.discount) * fee / 10000 + uint(rr.coupon) * 10 ** 9;
        
        fee = (offAmt < (fee - floorPrice))
            ? (fee - offAmt)
            : floorPrice;

        uint giftAmt = uint(rr.gift) * 10 ** 9;

        if (ownerAddr == authorAddr || pr.rate == 2000) {
            if (fee > giftAmt)
                _transfer(t.primeKey.pubKey, authorAddr, fee - giftAmt);
        } else {
            _transfer(t.primeKey.pubKey, ownerAddr, fee * (2000 - pr.rate) / 10000);
            
            uint balaceAmt = fee * (8000 + pr.rate) / 10000;
            if ( balaceAmt > giftAmt)
                _transfer(t.primeKey.pubKey, authorAddr, balaceAmt - giftAmt);
        }

        t.primeKey.coupon++;
    }

    function getMyUserNo() external view returns(uint40) {
        return _users.getUserNo(msg.sender);
    }

    // ==== Docs ====

    function counterOfTypes() external view returns(uint32) {
        return _docs.counterOfTypes();
    }

    function counterOfVersions(uint256 typeOfDoc) external view returns(uint32) {
        return _docs.counterOfVersions(uint32(typeOfDoc));
    }

    function counterOfDocs(uint256 typeOfDoc, uint256 version) external view returns(uint64) {
        return _docs.counterOfDocs(uint32(typeOfDoc), uint32(version));
    }

    function docExist(address body) public view returns(bool) {
        return _docs.docExist(body);
    }

    function getAuthor(uint typeOfDoc, uint version) external view returns(uint40) {
        return _docs.getAuthor(typeOfDoc, version);
    }

    function getAuthorByBody(address body) external view returns(uint40) {
        return _docs.getAuthorByBody(body);
    }

    function getHeadByBody(address body) public view returns (DocsRepo.Head memory ) {
        return _docs.getHeadByBody(body);
    }

    function getDoc(bytes32 snOfDoc) external view returns(DocsRepo.Doc memory doc) {
        doc = _docs.getDoc(snOfDoc);
    }

    function getDocByUserNo(uint acct) external view returns (DocsRepo.Doc memory doc) {
        if (_users.counterOfUsers() >= acct) { 
            doc.body = _users.users[acct].primeKey.pubKey;
            if (_docs.docExist(doc.body)) doc.head = _docs.heads[doc.body];
            else doc.body = address(0);
        }
    }

    function verifyDoc(bytes32 snOfDoc) external view returns(bool flag) {
        flag = _docs.verifyDoc(snOfDoc);
    }

    function getVersionsList(uint256 typeOfDoc) external view returns(DocsRepo.Doc[] memory) {
        return _docs.getVersionsList(uint32(typeOfDoc));
    }

    function getDocsList(bytes32 snOfDoc) external view returns(DocsRepo.Doc[] memory) {
        return _docs.getDocsList(snOfDoc);
    } 

}
IRegCenter
// 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 "../comps/IGeneralKeeper.sol";
import "../comps/common/access/IAccessControl.sol";

import "../lib/UsersRepo.sol";
import "../lib/DocsRepo.sol";

import "./ERC20/IERC20.sol";
import "./Oracles/IPriceConsumer2.sol";

interface IRegCenter is IERC20, IPriceConsumer2{

    enum TypeOfDoc{
        ZeroPoint,
        ROCKeeper,      // 1
        RODKeeper,      // 2
        BMMKeeper,      // 3
        ROMKeeper,      // 4
        GMMKeeper,      // 5
        ROAKeeper,      // 6
        ROOKeeper,      // 7
        ROPKeeper,      // 8
        SHAKeeper,      // 9
        LOOKeeper,      // 10
        ROC,            // 11
        ROD,            // 12
        MeetingMinutes, // 13
        ROM,            // 14
        ROA,            // 15
        ROO,            // 16
        ROP,            // 17
        ROS,            // 18
        LOO,            // 19
        GeneralKeeper,  // 20
        IA,             // 21
        SHA,            // 22 
        AntiDilution,   // 23
        LockUp,         // 24
        Alongs,         // 25
        Options         // 26
    }

    // ##################
    // ##    Event     ##
    // ##################

    // ==== Options ====

    event SetPlatformRule(bytes32 indexed snOfRule);

    event SetPriceFeed(uint indexed seq, address indexed priceFeed);

    event TransferOwnership(address indexed newOwner);

    event TurnOverCenterKey(address indexed newKeeper);

    // ==== Points ====

    event MintPoints(uint256 indexed to, uint256 indexed amt);

    event TransferPoints(uint256 indexed from, uint256 indexed to, uint256 indexed amt);

    event LockPoints(bytes32 indexed headSn, bytes32 indexed hashLock);

    event LockConsideration(bytes32 indexed headSn, address indexed counterLocker, bytes payload, bytes32 indexed hashLock);

    event PickupPoints(bytes32 indexed headSn);

    event PickupConsideration(bytes32 indexed headSn);

    event WithdrawPoints(bytes32 indexed headSn);

    // ==== Docs ====
    
    event SetTemplate(uint256 indexed typeOfDoc, uint256 indexed version, address indexed body);

    event TransferIPR(uint indexed typeOfDoc, uint indexed version, uint indexed transferee);

    event CreateDoc(bytes32 indexed snOfDoc, address indexed body);

    // ##################
    // ##    Write     ##
    // ##################

    // ==== Opts Setting ====

    function setPlatformRule(bytes32 snOfRule) external;
    
    function setPriceFeed(uint seq, address feed_ ) external;

    // ==== Power transfer ====

    function transferOwnership(address newOwner) external;

    function handoverCenterKey(address newKeeper) external;

    // ==== Mint/Sell Points ====

    function mint(uint256 to, uint amt) external;

    function burn(uint amt) external;

    function mintAndLockPoints(uint to, uint amt, uint expireDate, bytes32 hashLock) external;

    // ==== Points Trade ====

    function lockPoints(uint to, uint amt, uint expireDate, bytes32 hashLock) external;

    function lockConsideration(uint to, uint amt, uint expireDate, address counterLocker, bytes memory payload, bytes32 hashLock) external;

    function pickupPoints(bytes32 hashLock, string memory hashKey) external;

    function withdrawPoints(bytes32 hashLock) external;

    function getDepositAmt(address from) external view returns(uint);

    function getLocker(bytes32 hashLock) external view 
        returns (LockersRepo.Locker memory locker);

    function getLocksList() external view 
        returns (bytes32[] memory);

    // ==== User ====

    function regUser() external;

    function setBackupKey(address bKey) external;

    function upgradeBackupToPrime() external;

    function setRoyaltyRule(bytes32 snOfRoyalty) external;

    // ==== Doc ====

    function setTemplate(uint typeOfDoc, address body, uint author) external;

    function createDoc(bytes32 snOfDoc, address primeKeyOfOwner) external 
        returns(DocsRepo.Doc memory doc);

    // ==== Comp ====

    function createComp(address dk) external;

    // #################
    // ##   Read      ##
    // #################

    // ==== Options ====

    function getOwner() external view returns (address);

    function getBookeeper() external view returns (address);

    function getPlatformRule() external returns(UsersRepo.Rule memory);

    // ==== Users ====

    function isKey(address key) external view returns (bool);

    function counterOfUsers() external view returns(uint40);

    function getUser() external view returns (UsersRepo.User memory);

    function getRoyaltyRule(uint author)external view returns (UsersRepo.Key memory);

    function getUserNo(address targetAddr, uint fee, uint author) external returns (uint40);

    function getMyUserNo() external returns (uint40);

    // ==== Docs ====

    function counterOfTypes() external view returns(uint32);

    function counterOfVersions(uint256 typeOfDoc) external view returns(uint32 seq);

    function counterOfDocs(uint256 typeOfDoc, uint256 version) external view returns(uint64 seq);

    function docExist(address body) external view returns(bool);

    function getAuthor(uint typeOfDoc, uint version) external view returns(uint40);

    function getAuthorByBody(address body) external view returns(uint40);

    function getHeadByBody(address body) external view returns (DocsRepo.Head memory );
    
    function getDoc(bytes32 snOfDoc) external view returns(DocsRepo.Doc memory doc);

    function getDocByUserNo(uint acct) external view returns (DocsRepo.Doc memory doc);

    function verifyDoc(bytes32 snOfDoc) external view returns(bool flag);

    function getVersionsList(uint256 typeOfDoc) external view returns(DocsRepo.Doc[] memory);

    function getDocsList(bytes32 snOfDoc) external view returns(DocsRepo.Doc[] memory);
}
ERC20
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./IERC20Metadata.sol";
import "./Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}
IERC20
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 indexed value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
IERC20Metadata
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
Context
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
PriceConsumer
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@chainlink/contracts/src/v0.8/Denominations.sol";

import "./IPriceConsumer2.sol";

contract PriceConsumer2 is IPriceConsumer2{

    mapping(address => AggregatorV3Interface) private _priceFeeds;
    
    address[20] private _currencies = [
        Denominations.USD, Denominations.GBP, Denominations.EUR, Denominations.JPY, Denominations.KRW, Denominations.CNY,
        Denominations.AUD, Denominations.CAD, Denominations.CHF, Denominations.ARS, Denominations.PHP, Denominations.NZD,
        Denominations.SGD, Denominations.NGN, Denominations.ZAR, Denominations.RUB, Denominations.INR, Denominations.BRL,
        Denominations.ETH, Denominations.BTC
    ];

    /**
     * Network: Arbitrum One
     * ETH/USD (Base_0): 0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612
     * GBP/USD (quote_1): 0x9C4424Fd84C6661F97D8d6b3fc3C1aAc2BeDd137
     * EUR/USD (quote_2): 0xA14d53bC1F1c0F31B4aA3BD109344E5009051a84
     * JPY/USD (quote_3): 0x3dD6e51CB9caE717d5a8778CF79A04029f9cFDF8
     * KRW/USD (quote_4): 0x85bb02E0Ae286600d1c68Bb6Ce22Cc998d411916
     * CNY/USD (quote_5): 0xcC3370Bde6AFE51e1205a5038947b9836371eCCb
     * AUD/USD (quote_6): 0x9854e9a850e7C354c1de177eA953a6b1fba8Fc22
     * CAD/USD (quote_7): 0xf6DA27749484843c4F02f5Ad1378ceE723dD61d4
     * CHF/USD (quote_8): 0xe32AccC8c4eC03F6E75bd3621BfC9Fbb234E1FC3
     * ARS/USD (quote_9): 0x0000000000000000000000000000000000000000
     * PHP/USD (quote_10): 0xfF82AAF635645fD0bcc7b619C3F28004cDb58574
     * NZD/USD (quote_11): 0x0000000000000000000000000000000000000000
     * SGD/USD (quote_12): 0xF0d38324d1F86a176aC727A4b0c43c9F9d9c5EB1
     * NGN/USD (quote_13): 0x0000000000000000000000000000000000000000
     * ZAR/USD (quote_14): 0x0000000000000000000000000000000000000000
     * RUB/USD (quote_15): 0x0000000000000000000000000000000000000000
     * INR/USD (quote_16): 0x0000000000000000000000000000000000000000
     * BRL/USD (quote_17): 0x04b7384473A2aDF1903E3a98aCAc5D62ba8C2702
     */

    constructor(){
        _priceFeeds[_currencies[0]] = AggregatorV3Interface(
            0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612
        );
        _priceFeeds[_currencies[1]] = AggregatorV3Interface(
            0x9C4424Fd84C6661F97D8d6b3fc3C1aAc2BeDd137
        );
        _priceFeeds[_currencies[2]] = AggregatorV3Interface(
            0xA14d53bC1F1c0F31B4aA3BD109344E5009051a84
        );
        _priceFeeds[_currencies[3]] = AggregatorV3Interface(
            0x3dD6e51CB9caE717d5a8778CF79A04029f9cFDF8
        );
        _priceFeeds[_currencies[4]] = AggregatorV3Interface(
            0x85bb02E0Ae286600d1c68Bb6Ce22Cc998d411916
        );
        _priceFeeds[_currencies[5]] = AggregatorV3Interface(
            0xcC3370Bde6AFE51e1205a5038947b9836371eCCb
        );
        _priceFeeds[_currencies[6]] = AggregatorV3Interface(
            0x9854e9a850e7C354c1de177eA953a6b1fba8Fc22
        );
        _priceFeeds[_currencies[7]] = AggregatorV3Interface(
            0xf6DA27749484843c4F02f5Ad1378ceE723dD61d4
        );
        _priceFeeds[_currencies[8]] = AggregatorV3Interface(
            0xe32AccC8c4eC03F6E75bd3621BfC9Fbb234E1FC3
        );
        _priceFeeds[_currencies[10]] = AggregatorV3Interface(
            0xfF82AAF635645fD0bcc7b619C3F28004cDb58574
        );
        _priceFeeds[_currencies[12]] = AggregatorV3Interface(
            0xF0d38324d1F86a176aC727A4b0c43c9F9d9c5EB1
        );
        _priceFeeds[_currencies[17]] = AggregatorV3Interface(
            0x04b7384473A2aDF1903E3a98aCAc5D62ba8C2702
        );
    }

    function _setPriceFeed(uint seq, address _feed) internal {
        _priceFeeds[_currencies[seq]] = AggregatorV3Interface(_feed);
    }

    function getPriceFeed(uint seq) external view returns (address) {
        return address(_priceFeeds[_currencies[seq]]);
    }

    function decimals(address quote) public view returns (uint8) {
        return _priceFeeds[quote].decimals();
    }

    function getCentPriceInWei(uint seq) public view returns (uint) {

        (int base, uint8 dec) = _getLatestPrice(0);

        if (seq == 0) {
            
            return 10 ** uint(16 + dec) / uint(base);

        } else if (seq <= 17){

            (int quote, uint8 quoteDec) = _getLatestPrice(seq);
            quote = _scalePrice(quote, quoteDec, dec);

            return 10 ** uint(16 + dec) * uint(quote) / uint(base);

        } else revert("seqOfCurrency overflow");
    }

    function _getLatestPrice(uint seq) private view 
        returns(int price, uint8 dec) 
    {
        require(address(_priceFeeds[_currencies[seq]]) > address(0),
            "No Available PriceFeed");

        (
            /*uint80 roundID*/,
            price,
            /*uint startedAt*/,
            /*uint timeStamp*/,
            /*uint80 answeredInRound*/
        ) = _priceFeeds[_currencies[seq]].latestRoundData();

        dec = _priceFeeds[_currencies[seq]].decimals();        
    }

    function _scalePrice(
        int256 _price,
        uint8 _priceDecimals,
        uint8 _decimals
    ) private pure returns (int256) {
        if (_priceDecimals < _decimals) {
            return _price * int256(10 ** uint(_decimals - _priceDecimals));
        } else if (_priceDecimals > _decimals) {
            return _price / int256(10 ** uint(_priceDecimals - _decimals));
        }
        return _price;
    }

}
IPriceConsumer
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;

interface IPriceConsumer2 {

    /**
     * Network: Arbitrum One
     * ETH/USD (Base_0): 0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612
     * GBP/USD (quote_1): 0x9C4424Fd84C6661F97D8d6b3fc3C1aAc2BeDd137
     * EUR/USD (quote_2): 0xA14d53bC1F1c0F31B4aA3BD109344E5009051a84
     * JPY/USD (quote_3): 0x3dD6e51CB9caE717d5a8778CF79A04029f9cFDF8
     * KRW/USD (quote_4): 0x85bb02E0Ae286600d1c68Bb6Ce22Cc998d411916
     * CNY/USD (quote_5): 0xcC3370Bde6AFE51e1205a5038947b9836371eCCb
     * AUD/USD (quote_6): 0x9854e9a850e7C354c1de177eA953a6b1fba8Fc22
     * CAD/USD (quote_7): 0xf6DA27749484843c4F02f5Ad1378ceE723dD61d4
     * CHF/USD (quote_8): 0xe32AccC8c4eC03F6E75bd3621BfC9Fbb234E1FC3
     * ARS/USD (quote_9): 0x0000000000000000000000000000000000000000
     * PHP/USD (quote_10): 0xfF82AAF635645fD0bcc7b619C3F28004cDb58574
     * NZD/USD (quote_11): 0x0000000000000000000000000000000000000000
     * SGD/USD (quote_12): 0xF0d38324d1F86a176aC727A4b0c43c9F9d9c5EB1
     * NGN/USD (quote_13): 0x0000000000000000000000000000000000000000
     * ZAR/USD (quote_14): 0x0000000000000000000000000000000000000000
     * RUB/USD (quote_15): 0x0000000000000000000000000000000000000000
     * INR/USD (quote_16): 0x0000000000000000000000000000000000000000
     * BRL/USD (quote_17): 0x04b7384473A2aDF1903E3a98aCAc5D62ba8C2702
     */

    function getPriceFeed(uint seq) external view returns (address);

    function decimals(address quote) external view returns (uint8);

    function getCentPriceInWei(uint seq) external view returns (uint);

}