From 42d285a6904d5fbe808d5e856befad0b2b454e9a Mon Sep 17 00:00:00 2001 From: freshair Date: Sun, 2 Jan 2022 14:23:09 +0800 Subject: [PATCH] init aircash code --- AppealStorage.sol | 457 ++++++++++++++++++++++++ OrderStorage.sol | 573 ++++++++++++++++++++++++++++++ RecordInterface.sol | 50 +++ RecordStorage.sol | 839 ++++++++++++++++++++++++++++++++++++++++++++ RestStorage.sol | 578 ++++++++++++++++++++++++++++++ UserStorage.sol | 291 +++++++++++++++ 6 files changed, 2788 insertions(+) create mode 100644 AppealStorage.sol create mode 100644 OrderStorage.sol create mode 100644 RecordInterface.sol create mode 100644 RecordStorage.sol create mode 100644 RestStorage.sol create mode 100644 UserStorage.sol diff --git a/AppealStorage.sol b/AppealStorage.sol new file mode 100644 index 0000000..cf6f8b8 --- /dev/null +++ b/AppealStorage.sol @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./RecordInterface.sol"; +import "./UserStorage.sol"; + +contract AppealStorage { + OrderInterface private _oSt; + RecordInterface private _rSt; + UserInterface private _uSt; + address recAddr; + + struct Appeal { + address user; + uint256 appealNo; + uint256 orderNo; + address witness; + address buyer; + address seller; + uint256 mortgage; + uint256 status; + uint256 appealTime; + uint256 witTakeTime; + uint256 obTakeTime; + AppealDetail detail; + } + + struct AppealDetail { + address finalAppealAddr; + uint256 updateTime; + string witnessReason; + uint256 witnessAppealStatus; + string observerReason; + uint256 witnessHandleTime; + uint256 observerHandleTime; + address observerAddr; + uint256 witnessHandleReward; + uint256 observerHandleReward; + uint256 witnessHandleCredit; + uint256 observerHandleCredit; + uint256 witReward; + uint256 witSub; + uint256 witCreditR; + uint256 witCreditS; + } + + mapping(uint256 => Appeal) public appeals; + mapping(uint256 => uint256) public appealIndex; + + Appeal[] public appealList; + + constructor( + address _r, + address _o, + address _u + ) { + _rSt = RecordInterface(_r); + _oSt = OrderInterface(_o); + _uSt = UserInterface(_u); + recAddr = _r; + } + + modifier onlyWit(uint256 _o) { + Appeal memory _al = appeals[_o]; + require(_al.witness == msg.sender, "1"); + require(_al.buyer != msg.sender && _al.seller != msg.sender, "2"); + _; + } + + modifier onlyOb(uint256 _o) { + Appeal memory _al = appeals[_o]; + require(_al.detail.observerAddr == msg.sender, "1"); + require(_al.buyer != msg.sender && _al.seller != msg.sender, "2"); + _; + } + + modifier onlyBOS(uint256 _o) { + OrderStorage.Order memory _or = _oSt.searchOrder(_o); + require( + _or.orderDetail.sellerAddr == msg.sender || + _or.orderDetail.buyerAddr == msg.sender, + "1" + ); + _; + } + + function _insert(uint256 _o, uint256 _count) internal { + OrderStorage.Order memory _or = _oSt.searchOrder(_o); + + require(appeals[_o].appealNo == uint256(0), "4"); + + AppealDetail memory _detail = AppealDetail({ + finalAppealAddr: address(0), + updateTime: uint256(0), + witnessReason: "", + observerReason: "", + witnessAppealStatus: 0, + witnessHandleTime: uint256(0), + observerHandleTime: uint256(0), + observerAddr: address(0), + witnessHandleReward: 0, + observerHandleReward: 0, + witnessHandleCredit: 0, + observerHandleCredit: 0, + witReward: 0, + witSub: 0, + witCreditR: 0, + witCreditS: 0 + }); + + uint256 _appealNo = block.timestamp; + + Appeal memory _appeal = Appeal({ + user: msg.sender, + appealNo: _appealNo, + orderNo: _o, + witness: address(0), + buyer: _or.orderDetail.buyerAddr, + seller: _or.orderDetail.sellerAddr, + mortgage: _count, + status: 1, + appealTime: block.timestamp, + witTakeTime: 0, + obTakeTime: 0, + detail: _detail + }); + + appeals[_o] = _appeal; + + appealList.push(_appeal); + appealIndex[_o] = appealList.length - 1; + + chanT(_or.orderDetail.sellerAddr, _or.orderDetail.buyerAddr, 1, 0); + } + + function chanT( + address _seller, + address _buyer, + uint256 _t, + uint256 _r + ) internal { + uint256 _tc = _rSt.getTradeCredit(); + uint256 _rs = _rSt.getSubTCredit(); + + UserStorage.User memory _user = _uSt.searchUser(_seller); + UserStorage.TradeStats memory _tr = _user.tradeStats; + + uint256 _c = _user.credit; + if (_t == 1) { + _tr.tradeTotal = _tr.tradeTotal > 0 ? (_tr.tradeTotal - 1) : 0; + + _c = (_c >= _tc) ? (_c - _tc) : 0; + } else if (_t == 2) { + _tr.tradeTotal += 1; + + if (_r == 1) { + _c += _tc; + } else if (_r == 2) { + _c = (_c >= _rs) ? (_c - _rs) : 0; + } + } + + _uSt.updateTradeStats(_seller, _tr, _c); + + UserStorage.User memory _user2 = _uSt.searchUser(_buyer); + UserStorage.TradeStats memory _tr2 = _user2.tradeStats; + uint256 _c2 = _user2.credit; + if (_t == 1) { + _tr2.tradeTotal = _tr2.tradeTotal > 0 ? (_tr2.tradeTotal - 1) : 0; + + _c2 = (_c2 >= _tc) ? (_c2 - _tc) : 0; + } else if (_t == 2) { + _tr2.tradeTotal += 1; + + if (_r == 1) { + _c2 = (_c2 >= _rs) ? (_c2 - _rs) : 0; + } else if (_r == 2) { + _c2 += _tc; + } + } + + _uSt.updateTradeStats(_buyer, _tr2, _c2); + } + + function applyAppeal(uint256 _o) external onlyBOS(_o) { + uint256 _fee = _rSt.getAppealFee(); + _insert(_o, _fee); + + TokenTransfer _tokenTransfer = _rSt.setERC20Address("AIR"); + _tokenTransfer.transferFrom(msg.sender, recAddr, _fee); + } + + function takeWit(uint256 _o) external { + Appeal memory _al = appeals[_o]; + + require(_al.buyer != msg.sender && _al.seller != msg.sender, "1"); + + require(_al.witness == address(0), "2"); + require(_al.status == 1, "3"); + + bool _f = witOrOb(1); + require(_f, "4"); + + _al.witness = msg.sender; + _al.witTakeTime = block.timestamp; + + appeals[_o] = _al; + appealList[appealIndex[_o]] = _al; + } + + function takeOb(uint256 _o) external { + Appeal memory _al = appeals[_o]; + + require(_al.buyer != msg.sender && _al.seller != msg.sender, "1"); + + require(_al.status == 4 || _al.status == 5, "2"); + require(_al.detail.observerAddr == address(0), "3"); + + bool _f = witOrOb(2); + require(_f, "4"); + + _al.detail.observerAddr = msg.sender; + _al.obTakeTime = block.timestamp; + + appeals[_o] = _al; + appealList[appealIndex[_o]] = _al; + } + + function changeHandler(uint256 _o, uint256 _type) external { + Appeal memory _al = appeals[_o]; + + if (_type == 1) { + require(_al.status == 1, "2"); + require(_al.witness != address(0), "3"); + require(block.timestamp - _al.witTakeTime > 24 hours, "4"); + + _al.witness = address(0); + _al.witTakeTime = 0; + } else if (_type == 2) { + require(_al.status == 4 || _al.status == 5, "5"); + require(_al.detail.observerAddr != address(0), "6"); + require(block.timestamp - _al.obTakeTime > 24 hours, "7"); + + _al.detail.observerAddr = address(0); + _al.obTakeTime = 0; + } + + appeals[_o] = _al; + appealList[appealIndex[_o]] = _al; + } + + function witOrOb(uint256 _f) internal view returns (bool) { + UserStorage.User memory _user = _uSt.searchUser(msg.sender); + if (_user.userFlag == _f) { + return true; + } + return false; + } + + function applyFinal(uint256 _o) external onlyBOS(_o) { + Appeal memory _al = appeals[_o]; + + require(_al.status == 2 || _al.status == 3, "1"); + + require( + block.timestamp - _al.detail.witnessHandleTime <= 24 hours, + "2" + ); + + chanT(_al.seller, _al.buyer, 1, 0); + + uint256 _fee = _rSt.getAppealFeeFinal(); + + TokenTransfer _tokenTransfer = _rSt.setERC20Address("AIR"); + _tokenTransfer.transferFrom(msg.sender, recAddr, _fee); + + if (_al.status == 2) { + _al.status = 4; + } else if (_al.status == 3) { + _al.status = 5; + } + _al.detail.finalAppealAddr = msg.sender; + _al.detail.updateTime = block.timestamp; + appeals[_o] = _al; + appealList[appealIndex[_o]] = _al; + } + + function witnessOpt( + uint256 _o, + string memory _r, + uint256 _s + ) external onlyWit(_o) { + require(_s == 2 || _s == 3, "1"); + Appeal memory _al = appeals[_o]; + + require(_al.status == 1, "2"); + uint256 _fee = _rSt.getAppealFee(); + uint256 _rcedit = _rSt.getWitnessHandleCredit(); + + _al.status = _s; + _al.detail.witnessAppealStatus = _s; + _al.detail.witnessReason = _r; + _al.detail.witnessHandleTime = block.timestamp; + _al.detail.witnessHandleReward = _fee; + _al.detail.witnessHandleCredit = _rcedit; + _al.detail.witReward = _fee; + _al.detail.witCreditR = _rcedit; + + _al.detail.updateTime = block.timestamp; + appeals[_o] = _al; + appealList[appealIndex[_o]] = _al; + + if (_s == 2) { + if (_al.user == _al.buyer) { + _rSt.subAvaAppeal(_al.seller, _al.buyer, _al, _fee, 1, 0); + chanT(_al.seller, _al.buyer, 2, 2); + } else if (_al.user == _al.seller) { + _rSt.subAvaAppeal(_al.buyer, _al.seller, _al, _fee, 1, 0); + + chanT(_al.seller, _al.buyer, 2, 1); + } + } + + if (_s == 3) { + if (_al.user == _al.buyer) { + _rSt.subAvaAppeal(_al.buyer, _al.seller, _al, _fee, 1, 1); + chanT(_al.seller, _al.buyer, 2, 1); + } else if (_al.user == _al.seller) { + _rSt.subAvaAppeal(_al.seller, _al.buyer, _al, _fee, 1, 1); + chanT(_al.seller, _al.buyer, 2, 2); + } + } + } + + function observerOpt( + uint256 _o, + string memory _r, + uint256 _s + ) external onlyOb(_o) { + require(_s == 6 || _s == 7, "1"); + Appeal memory _appeal = appeals[_o]; + + require(_appeal.status == 4 || _appeal.status == 5, "2"); + uint256 _fee = _rSt.getAppealFeeFinal(); + uint256 _rcedit = _rSt.getObserverHandleCredit(); + + _appeal.status = _s; + _appeal.detail.observerReason = _r; + _appeal.detail.observerHandleTime = block.timestamp; + _appeal.detail.observerHandleReward = _fee; + _appeal.detail.observerHandleCredit = _rcedit; + + uint256 _subWC = _rSt.getSubWitCredit(); + uint256 _subWF = _rSt.getSubWitFee(); + + if (_s == 6) { + if (_appeal.user == _appeal.buyer) { + _rSt.subAvaAppeal( + _appeal.seller, + _appeal.buyer, + _appeal, + _fee, + 2, + 0 + ); + + chanT(_appeal.seller, _appeal.buyer, 2, 1); + _rSt.subFrozenTotal(_o, _appeal.buyer); + } else if (_appeal.user == _appeal.seller) { + _rSt.subAvaAppeal( + _appeal.buyer, + _appeal.seller, + _appeal, + _fee, + 2, + 0 + ); + + chanT(_appeal.seller, _appeal.buyer, 2, 2); + _rSt.subFrozenTotal(_o, _appeal.seller); + } + if (_appeal.detail.witnessAppealStatus == 3) { + _appeal.detail.witSub = _subWF; + _appeal.detail.witCreditS = _subWC; + + if (_appeal.detail.witnessHandleCredit >= _subWC) { + _appeal.detail.witnessHandleCredit = SafeMath.sub( + _appeal.detail.witnessHandleCredit, + _subWC + ); + } else { + _appeal.detail.witnessHandleCredit = 0; + } + _rSt.subWitnessAvailable(_appeal.witness); + } + } + + if (_s == 7) { + if (_appeal.user == _appeal.buyer) { + _rSt.subAvaAppeal( + _appeal.buyer, + _appeal.seller, + _appeal, + _fee, + 2, + 1 + ); + chanT(_appeal.seller, _appeal.buyer, 2, 1); + _rSt.subFrozenTotal(_o, _appeal.seller); + } else if (_appeal.user == _appeal.seller) { + _rSt.subAvaAppeal( + _appeal.seller, + _appeal.buyer, + _appeal, + _fee, + 2, + 1 + ); + chanT(_appeal.seller, _appeal.buyer, 2, 2); + _rSt.subFrozenTotal(_o, _appeal.buyer); + } + if (_appeal.detail.witnessAppealStatus == 2) { + _appeal.detail.witSub = _subWF; + _appeal.detail.witCreditS = _subWC; + + if (_appeal.detail.witnessHandleCredit >= _subWC) { + _appeal.detail.witnessHandleCredit = SafeMath.sub( + _appeal.detail.witnessHandleCredit, + _subWC + ); + } else { + _appeal.detail.witnessHandleCredit = 0; + } + _rSt.subWitnessAvailable(_appeal.witness); + } + } + + _appeal.detail.updateTime = block.timestamp; + appeals[_o] = _appeal; + appealList[appealIndex[_o]] = _appeal; + } + + function searchAppeal(uint256 _o) + external + view + returns (Appeal memory appeal) + { + return appeals[_o]; + } + + function searchAppealList() external view returns (Appeal[] memory) { + return appealList; + } +} diff --git a/OrderStorage.sol b/OrderStorage.sol new file mode 100644 index 0000000..cd23efb --- /dev/null +++ b/OrderStorage.sol @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./RecordInterface.sol"; +import "./RestStorage.sol"; +import "./UserStorage.sol"; +import "./RecordStorage.sol"; +import "./AppealStorage.sol"; + +contract OrderStorage is Ownable { + RestStorage private _restStorage; + RecordInterface private _recordStorage; + UserInterface private _userStorage; + AppealInterface private _appealS; + address recordAddress; + + struct Order { + address userAddr; + uint256 orderNo; + uint256 restNo; + uint256 coinCount; + uint256 orderAmount; + uint256 payType; + string currencyType; + uint256 orderType; + uint256 orderStatus; + OrderDetail orderDetail; + } + struct OrderDetail { + address buyerAddr; + address sellerAddr; + string coinType; + uint256 price; + uint256 tradeTime; + uint256 updateTime; + string tradeHash; + uint256 tradeFee; + } + + mapping(uint256 => Order) private orders; + mapping(uint256 => uint256) private orderIndex; + + Order[] private orderList; + + mapping(address => mapping(uint256 => uint256)) orderFrozenTotal; + + uint256 cancelOrderTime = 30; + + function setCancelOrderTime(uint256 _count) public onlyOwner { + cancelOrderTime = _count; + } + + function getCancelOrderTime() public view returns (uint256) { + return cancelOrderTime; + } + + event OrderAdd( + uint256 _orderNo, + uint256 _restNo, + uint256 _coinCount, + uint256 _tradeFee, + uint256 _orderAmount, + uint256 _payType, + uint256 _orderType, + address _buyerAddr, + address _sellerAddr + ); + event OrderUpdateHash(uint256 _orderNo, string _tradeHash); + event OrderPaidMoney(uint256 _orderNo); + event OrderConfirmCollect(uint256 _orderNo); + event OrderCancel(uint256 _orderNo); + event OrderUpdateStatus(uint256 _orderNo, uint256 _orderStatus); + + constructor( + address _recordAddr, + address _restAddr, + address _userAddr + ) { + _recordStorage = RecordInterface(_recordAddr); + _restStorage = RestStorage(_restAddr); + _userStorage = UserInterface(_userAddr); + recordAddress = _recordAddr; + } + + address _appealCAddr; + + modifier onlyAuthFromAddr() { + require(_appealCAddr != address(0), "Invalid address call order"); + _; + } + + function authFromContract(address _fromAppeal) external { + require(_appealCAddr == address(0), "appeal address has Auth"); + _appealCAddr = _fromAppeal; + _appealS = AppealInterface(_appealCAddr); + } + + modifier onlyBuyer(uint256 _orderNo) { + require(_orderNo != uint256(0), "orderNo null is not allowed"); + require( + orders[_orderNo].orderDetail.buyerAddr == msg.sender, + "Only buyer can call" + ); + _; + } + + modifier onlySeller(uint256 _orderNo) { + require(_orderNo != uint256(0), "orderNo null is not allowed"); + require( + orders[_orderNo].orderDetail.sellerAddr == msg.sender, + "Only seller can call" + ); + _; + } + + modifier onlyBuyerOrSeller(uint256 _orderNo) { + require(_orderNo != uint256(0), "orderNo null is not allowed"); + require( + orders[_orderNo].orderDetail.sellerAddr == msg.sender || + orders[_orderNo].orderDetail.buyerAddr == msg.sender, + "Only buyer or seller can call" + ); + _; + } + + function _checkParam( + uint256 _restNo, + uint256 _coinCount, + uint256 _orderAmount, + uint256 _payType + ) internal pure { + require( + _restNo != uint256(0), + "OrderStorage: restNo null is not allowed" + ); + require(_coinCount > 0, "OrderStorage: coinCount null is not allowed"); + require( + _orderAmount > 0, + "OrderStorage: orderAmount null is not allowed" + ); + require( + _payType != uint256(0), + "OrderStorage: payType null is not allowed" + ); + } + + function _insert( + uint256 _restNo, + uint256 _coinCount, + uint256 _tradeFee, + uint256 _orderAmount, + uint256 _payType, + uint256 _orderType, + address _buyerAddr, + address _sellerAddr + ) internal returns (uint256 restNo) { + _checkParam(_restNo, _coinCount, _orderAmount, _payType); + + RestStorage.Rest memory _rest = _restStorage.searchRest(_restNo); + require(_rest.userAddr != address(0), "rest not exist"); + OrderDetail memory _orderDetail = OrderDetail({ + buyerAddr: _buyerAddr, + sellerAddr: _sellerAddr, + coinType: _rest.coinType, + price: _rest.price, + tradeTime: block.timestamp, + updateTime: 0, + tradeHash: "", + tradeFee: _tradeFee + }); + + uint256 _orderNo = block.timestamp; + Order memory order = Order({ + userAddr: msg.sender, + orderNo: _orderNo, + restNo: _restNo, + coinCount: _coinCount, + orderAmount: _orderAmount, + payType: _payType, + currencyType: _rest.currencyType, + orderType: _orderType, + orderStatus: 1, + orderDetail: _orderDetail + }); + + orders[_orderNo] = order; + + orderList.push(order); + orderIndex[_orderNo] = orderList.length - 1; + + if (_orderType == 2) { + orderFrozenTotal[msg.sender][_orderNo] = _coinCount; + } else if (_orderType == 1) { + orderFrozenTotal[_rest.userAddr][_orderNo] = _coinCount; + } + + emit OrderAdd( + _orderNo, + _restNo, + _coinCount, + _tradeFee, + _orderAmount, + _payType, + _orderType, + _buyerAddr, + _sellerAddr + ); + + return _orderNo; + } + + function addBuyOrder( + uint256 _restNo, + uint256 _coinCount, + uint256 _orderAmount, + uint256 _payType + ) external { + RestStorage.Rest memory _rest = _restStorage.searchRest(_restNo); + require(_rest.userAddr != msg.sender, "rest not exist"); + require(_rest.restType == 2, "sell rest not exist"); + require(_coinCount > 0 && _orderAmount > 0, "coin count error"); + require(_rest.restStatus == 1, "rest status error"); + UserStorage.User memory _currentUser = _userStorage.searchUser( + msg.sender + ); + + require( + _currentUser.userFlag != 1 && _currentUser.userFlag != 2, + "invalid user" + ); + + uint256 _restFrozen = _restStorage.getRestFrozenTotal( + _rest.userAddr, + _restNo + ); + require(_restFrozen >= _coinCount, "coin not enough"); + + uint256 _amo = SafeMath.mul(_rest.price, _coinCount); + require( + _amo >= _rest.restDetail.limitAmountFrom && + _amo <= _rest.restDetail.limitAmountTo, + "amount error" + ); + require( + _currentUser.credit >= _rest.restDetail.limitMinCredit, + "credit error" + ); + require( + _currentUser.morgageStats.mortgage >= + _rest.restDetail.limitMinMortgage, + "mortgage error" + ); + + _restStorage.updateRestFinishCount(_restNo, _coinCount); + _insert( + _restNo, + _coinCount, + 0, + _orderAmount, + _payType, + 1, + msg.sender, + _rest.userAddr + ); + } + + function addSellOrder( + uint256 _restNo, + uint256 _coinCount, + uint256 _tradeFee, + uint256 _orderAmount, + uint256 _payType + ) external { + RestStorage.Rest memory _rest = _restStorage.searchRest(_restNo); + require(_rest.userAddr != msg.sender, "rest not exist"); + require(_rest.restType == 1, "buy rest not exist"); + require(_coinCount > 0 && _tradeFee >= 0, "coin count error"); + require(_orderAmount > 0, "orderAmount error"); + require(_rest.restStatus == 1, "rest status error"); + + uint256 _amo = SafeMath.mul(_rest.price, _coinCount); + require( + _amo >= _rest.restDetail.limitAmountFrom && + _amo <= _rest.restDetail.limitAmountTo, + "amount error" + ); + + UserStorage.User memory _currentUser = _userStorage.searchUser( + msg.sender + ); + + require( + _currentUser.userFlag != 1 && _currentUser.userFlag != 2, + "invalid user" + ); + require( + _currentUser.credit >= _rest.restDetail.limitMinCredit, + "credit error" + ); + require( + _currentUser.morgageStats.mortgage >= + _rest.restDetail.limitMinMortgage, + "mortgage error" + ); + + uint256 _needSub = SafeMath.add(_coinCount, _tradeFee); + + _restStorage.updateRestFinishCount(_restNo, _coinCount); + _insert( + _restNo, + _coinCount, + _tradeFee, + _orderAmount, + _payType, + 2, + _rest.userAddr, + msg.sender + ); + + TokenTransfer _tokenTransfer = _recordStorage.setERC20Address( + _rest.coinType + ); + _tokenTransfer.transferFrom(msg.sender, recordAddress, _needSub); + _recordStorage.addRecord( + msg.sender, + "", + _rest.coinType, + _coinCount, + 2, + 1, + 2 + ); + } + + function setPaidMoney(uint256 _orderNo) + external + onlyBuyer(_orderNo) + returns (bool) + { + _updateOrderStatus(_orderNo, 2); + emit OrderPaidMoney(_orderNo); + return true; + } + + function confirmCollect(uint256 _orderNo) external onlySeller(_orderNo) { + require( + _orderNo != uint256(0), + "OrderStorage:orderNo null is not allowed" + ); + Order memory _order = orders[_orderNo]; + require(_order.orderStatus == 2, "OrderStorage:Invalid order status"); + require( + _order.orderDetail.buyerAddr != address(0), + "OrderStorage:Invalid buyer address" + ); + require( + orderFrozenTotal[msg.sender][_orderNo] >= _order.coinCount, + "OrderStorage:coin not enough" + ); + + _updateOrderStatus(_orderNo, 3); + + orderFrozenTotal[msg.sender][_orderNo] = 0; + + uint256 _rc = _recordStorage.getTradeCredit(); + UserStorage.User memory _user = _userStorage.searchUser(msg.sender); + uint256 _credit = _user.credit + _rc; + UserStorage.TradeStats memory _tradeStats = _user.tradeStats; + _tradeStats.tradeTotal += 1; + _userStorage.updateTradeStats(msg.sender, _tradeStats, _credit); + + UserStorage.User memory _user2 = _userStorage.searchUser( + _order.orderDetail.buyerAddr + ); + uint256 _credit2 = _user2.credit + _rc; + UserStorage.TradeStats memory _tradeStats2 = _user2.tradeStats; + _tradeStats2.tradeTotal += 1; + _userStorage.updateTradeStats( + _order.orderDetail.buyerAddr, + _tradeStats2, + _credit2 + ); + + _recordStorage.subFrozenTotal(_orderNo, _order.orderDetail.buyerAddr); + + emit OrderConfirmCollect(_orderNo); + } + + function cancelOrder(uint256 _orderNo) + external + onlyBuyerOrSeller(_orderNo) + returns (bool) + { + Order memory _order = orders[_orderNo]; + require( + _order.orderNo != uint256(0), + "OrderStorage: current Order not exist" + ); + + require(_order.orderStatus == 1, "Can't cancel order"); + + require( + _order.orderDetail.tradeTime + cancelOrderTime * 1 minutes < + block.timestamp, + "30 minutes limit" + ); + RestStorage.Rest memory _rest = _restStorage.searchRest(_order.restNo); + + if (_rest.restStatus == 4 || _rest.restStatus == 5) { + orderFrozenTotal[_order.orderDetail.sellerAddr][_orderNo] = 0; + + _recordStorage.addAvailableTotal( + _order.orderDetail.sellerAddr, + _order.orderDetail.coinType, + _order.coinCount + ); + } else { + if (_order.orderType == 2) { + orderFrozenTotal[_order.orderDetail.sellerAddr][_orderNo] = 0; + + _recordStorage.addAvailableTotal( + _order.orderDetail.sellerAddr, + _order.orderDetail.coinType, + _order.coinCount + ); + } + + _restStorage.addRestRemainCount(_order.restNo, _order.coinCount); + } + _updateOrderStatus(_orderNo, 4); + emit OrderCancel(_orderNo); + return true; + } + + function takeCoin(uint256 _o) external onlyBuyerOrSeller(_o) { + AppealStorage.Appeal memory _appeal = _appealS.searchAppeal(_o); + require( + block.timestamp - _appeal.detail.witnessHandleTime > 24 hours, + "time error" + ); + + address _win; + + if (_appeal.user == _appeal.buyer) { + if (_appeal.status == 2) { + _win = _appeal.buyer; + } else if (_appeal.status == 3) { + _win = _appeal.seller; + } + } else { + if (_appeal.status == 2) { + _win = _appeal.seller; + } else if (_appeal.status == 3) { + _win = _appeal.buyer; + } + } + require(_win == msg.sender, "opt error"); + + _updateOrderStatus(_o, 5); + orderFrozenTotal[_appeal.seller][_o] = 0; + _recordStorage.subFrozenTotal(_o, msg.sender); + } + + function _updateOrderStatus(uint256 _orderNo, uint256 _orderStatus) + internal + onlyBuyerOrSeller(_orderNo) + { + Order memory order = orders[_orderNo]; + require( + order.orderNo != uint256(0), + "OrderStorage: current Order not exist" + ); + require(_orderStatus >= 1 && _orderStatus <= 5, "Invalid order status"); + + if (_orderStatus == 2 && order.orderStatus != 1) { + revert("Invalid order status 2"); + } + if (_orderStatus == 3 && order.orderStatus != 2) { + revert("Invalid order status 3"); + } + if (_orderStatus == 4 && order.orderStatus != 1) { + revert("Invalid order status 4"); + } + if ( + _orderStatus == 5 && + order.orderStatus != 1 && + order.orderStatus != 2 + ) { + revert("Invalid order status 5"); + } + + if (_orderStatus == 2) { + require( + order.orderDetail.buyerAddr == msg.sender, + "only buyer call" + ); + } + if (_orderStatus == 3) { + require( + order.orderDetail.sellerAddr == msg.sender, + "only seller call" + ); + } + order.orderStatus = _orderStatus; + + order.orderDetail.updateTime = block.timestamp; + orders[_orderNo] = order; + orderList[orderIndex[_orderNo]] = order; + emit OrderUpdateStatus(_orderNo, _orderStatus); + } + + function _search(uint256 _orderNo) + internal + view + returns (Order memory order) + { + require( + _orderNo != uint256(0), + "OrderStorage: orderNo null is not allowed" + ); + require( + orders[_orderNo].orderNo != uint256(0), + "OrderStorage: current Order not exist" + ); + + Order memory o = orders[_orderNo]; + return o; + } + + function searchOrder(uint256 _orderNo) + external + view + returns (Order memory order) + { + return _search(_orderNo); + } + + function searchOrderList() external view returns (Order[] memory) { + return orderList; + } + + function searchMyOrderList() external view returns (Order[] memory) { + Order[] memory resultList = new Order[](orderList.length); + for (uint256 i = 0; i < orderList.length; i++) { + Order memory _order = orderList[i]; + if ( + _order.orderDetail.buyerAddr == msg.sender || + _order.orderDetail.sellerAddr == msg.sender + ) { + resultList[i] = _order; + } + } + return resultList; + } + + function searchListByRest(uint256 _restNo) + external + view + returns (Order[] memory) + { + Order[] memory resultList = new Order[](orderList.length); + for (uint256 i = 0; i < orderList.length; i++) { + Order memory _order = orderList[i]; + if (_order.restNo == _restNo) { + resultList[i] = _order; + } + } + return resultList; + } +} diff --git a/RecordInterface.sol b/RecordInterface.sol new file mode 100644 index 0000000..479b578 --- /dev/null +++ b/RecordInterface.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; +import "./RestStorage.sol"; +import './OrderStorage.sol'; +import './UserStorage.sol'; +import './RecordStorage.sol'; +import './AppealStorage.sol'; + +interface RecordInterface { + function getErcBalance(string memory _coinType, address _addr) external returns(uint); + function getAvailableTotal(address _addr, string memory _coinType) external returns(uint); + function getFrozenTotal(address _addr, string memory _coinType) external returns(uint); + function addAvailableTotal(address _addr, string memory _coinType, uint remainHoldCoin) external; + function subAvaAppeal(address _from, address _to, AppealStorage.Appeal memory _al, uint _amt, uint _type, uint _self) external; + function subWitnessAvailable(address _addr) external; + function setERC20Address(string memory _coinType) external returns(TokenTransfer); + function subFrozenTotal(uint _orderNo, address _addr) external; + function addRecord(address _addr, string memory _tradeHash, string memory _coinType, uint _hostCount, uint _hostStatus, uint _hostType, uint _hostDirection) external; + function getAppealFee() external view returns(uint); + function getAppealFeeFinal() external view returns(uint); + function getWitnessHandleReward() external view returns(uint); + function getObserverHandleReward() external view returns(uint); + function getWitnessHandleCredit() external view returns(uint); + function getObserverHandleCredit() external view returns(uint); + function getSubWitCredit() external view returns(uint); + function getOpenTrade() external view returns(bool); + function getTradeCredit() external view returns(uint); + function getSubTCredit() external view returns(uint); + function getSubWitFee() external view returns(uint); +} +interface RestInterface { + function searchRest(uint _restNo) external returns(RestStorage.Rest memory rest); + function getRestFrozenTotal(address _addr, uint _restNo) external returns(uint); + function updateRestFinishCount(uint _restNo, uint _coinCount) external returns(uint); + function addRestRemainCount(uint _restNo, uint _remainCount) external returns(uint); +} +interface OrderInterface { + function searchOrder(uint _orderNo) external returns(OrderStorage.Order memory order); +} +interface UserInterface { + function searchUser(address _addr) external view returns(UserStorage.User memory user); + function searchWitnessList(uint _userFlag) external returns(UserStorage.User[] memory userList); + function updateTradeStats(address _addr, UserStorage.TradeStats memory _tradeStats, uint _credit) external; + function updateMorgageStats(address _addr, UserStorage.MorgageStats memory _morgageStats) external; + function updateUserRole(address _addr, uint _userFlag) external; +} +interface AppealInterface { + function searchAppeal(uint _o) external view returns(AppealStorage.Appeal memory appeal); +} diff --git a/RecordStorage.sol b/RecordStorage.sol new file mode 100644 index 0000000..47b67ab --- /dev/null +++ b/RecordStorage.sol @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./RecordInterface.sol"; +import "./UserStorage.sol"; +import "./OrderStorage.sol"; + +interface TokenTransfer { + function transfer(address recipient, uint256 amount) external; + + function balanceOf(address account) external view returns (uint256); + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external; + + function allowance(address _owner, address _spender) + external + view + returns (uint256 remaining); +} + +contract RecordStorage is Ownable { + mapping(string => address) coinTypeMaping; + string[] coinTypeList = ["USDT", "USDC", "BUSD"]; + + uint256 merchantNeedCount = 10000000000 * (10**18); + uint256 witnessNeedCount = 100000000000 * (10**18); + uint256 congressNeedCount = 1000000000000 * (10**18); + string[] appealFeeCoinTypeList = ["AIR"]; + uint256 appealFee = 1000000 * (10**18); + uint256 appealFeeFinal = 10000000 * (10**18); + uint256 canWithdrawToTime = 28; + uint256 subWitFee = 2000000 * (10**18); + uint256 subWitCredit = 10; + uint256 witnessHandleReward = 1000000 * (10**18); + uint256 observerHandleReward = 10000000 * (10**18); + uint256 witnessHandleCredit = 1; + uint256 observerHandleCredit = 1; + bool openTrade = false; + uint256 tradeCredit = 1; + uint256 subTCredit = 10; + + mapping(address => uint256) witnessFlag; + mapping(address => uint256) congressFlag; + + function setWitnessFlag(address _addr, uint256 _flag) public onlyOwner { + witnessFlag[_addr] = _flag; + if (_flag == 1) { + uint256 _amt = availableTotal[_addr]["AIR"]; + require(_amt >= witnessNeedCount, "coin not enough"); + _userStorage.updateUserRole(_addr, 1); + } else { + _userStorage.updateUserRole(_addr, 0); + } + } + + function getWitnessFlag(address _addr) public view returns (uint256) { + return witnessFlag[_addr]; + } + + function setCongressFlag(address _addr, uint256 _flag) public onlyOwner { + congressFlag[_addr] = _flag; + if (_flag == 1) { + uint256 _amt = availableTotal[_addr]["AIR"]; + require(_amt >= congressNeedCount, "coin not enough"); + _userStorage.updateUserRole(_addr, 2); + } else { + _userStorage.updateUserRole(_addr, 0); + } + } + + function getCongressFlag(address _addr) public view returns (uint256) { + return congressFlag[_addr]; + } + + function setCoinType(string[] memory _coinTypeList) public onlyOwner { + coinTypeList = _coinTypeList; + } + + function getCoinType() public view returns (string[] memory) { + return coinTypeList; + } + + function setCoinTypeMapping(string memory _coinType, address _coinTypeAddr) + public + onlyOwner + { + coinTypeMaping[_coinType] = _coinTypeAddr; + } + + function getCoinTypeMapping(string memory _coinType) + public + view + returns (address) + { + return coinTypeMaping[_coinType]; + } + + function setMerchantNeedCount(uint256 _count) public onlyOwner { + merchantNeedCount = _count * (10**18); + } + + function getMerchantNeedCount() public view returns (uint256) { + return merchantNeedCount; + } + + function setWitnessNeedCount(uint256 _count) public onlyOwner { + witnessNeedCount = _count * (10**18); + } + + function getWitnessNeedCount() public view returns (uint256) { + return witnessNeedCount; + } + + function setCongressNeedCount(uint256 _count) public onlyOwner { + congressNeedCount = _count * (10**18); + } + + function getCongressNeedCount() public view returns (uint256) { + return congressNeedCount; + } + + function setAppealFee(uint256 _count) public onlyOwner { + appealFee = _count * (10**18); + } + + function getAppealFee() public view returns (uint256) { + return appealFee; + } + + function setAppealFeeFinal(uint256 _count) public onlyOwner { + appealFeeFinal = _count * (10**18); + } + + function getAppealFeeFinal() public view returns (uint256) { + return appealFeeFinal; + } + + function setCanWithdrawToTime(uint256 _days) public onlyOwner { + canWithdrawToTime = _days; + } + + function getCanWithdrawToTime() public view returns (uint256) { + return canWithdrawToTime; + } + + function setAppealFeeCoinTypeList(string[] memory _list) public onlyOwner { + appealFeeCoinTypeList = _list; + } + + function getAppealFeeCoinTypeList() public view returns (string[] memory) { + return appealFeeCoinTypeList; + } + + function setSubWitFee(uint256 _c) public onlyOwner { + subWitFee = _c * (10**18); + } + + function getSubWitFee() public view returns (uint256) { + return subWitFee; + } + + function setSubWitCredit(uint256 _c) public onlyOwner { + subWitCredit = _c; + } + + function getSubWitCredit() public view returns (uint256) { + return subWitCredit; + } + + function setWitnessHandleReward(uint256 _c) public onlyOwner { + witnessHandleReward = _c * (10**18); + } + + function getWitnessHandleReward() public view returns (uint256) { + return witnessHandleReward; + } + + function setObserverHandleReward(uint256 _c) public onlyOwner { + observerHandleReward = _c * (10**18); + } + + function getObserverHandleReward() public view returns (uint256) { + return observerHandleReward; + } + + function setWitnessHandleCredit(uint256 _c) public onlyOwner { + witnessHandleCredit = _c; + } + + function getWitnessHandleCredit() public view returns (uint256) { + return witnessHandleCredit; + } + + function setObserverHandleCredit(uint256 _c) public onlyOwner { + observerHandleCredit = _c; + } + + function getObserverHandleCredit() public view returns (uint256) { + return observerHandleCredit; + } + + function setOpenTrade(bool _c) public onlyOwner { + openTrade = _c; + } + + function getOpenTrade() public view returns (bool) { + return openTrade; + } + + function setTradeCredit(uint256 _c) public onlyOwner { + tradeCredit = _c; + } + + function getTradeCredit() public view returns (uint256) { + return tradeCredit; + } + + function setSubTCredit(uint256 _c) public onlyOwner { + subTCredit = _c; + } + + function getSubTCredit() public view returns (uint256) { + return subTCredit; + } + + function punishPerson( + address _from, + address _to, + uint256 _count + ) public onlyOwner { + require(_from != address(0), "Invalid from"); + require(_to != address(0), "Invalid to"); + UserStorage.User memory _user = _userStorage.searchUser(_from); + require( + _user.userFlag == 1 || _user.userFlag == 2, + "can't punish this person" + ); + + require( + availableTotal[_from]["AIR"] >= _count, + "user balance not enough" + ); + availableTotal[_from]["AIR"] = SafeMath.sub( + availableTotal[_from]["AIR"], + _count + ); + availableTotal[_to]["AIR"] = SafeMath.add( + availableTotal[_to]["AIR"], + _count + ); + } + + UserInterface private _userStorage; + OrderInterface private _orderStorage; + struct Record { + uint256 recordNo; + address userAddr; + string tradeHash; + string coinType; + uint256 hostCount; + uint256 hostStatus; + uint256 hostType; + uint256 hostDirection; + uint256 hostTime; + uint256 updateTime; + } + + mapping(uint256 => Record) public records; + mapping(uint256 => uint256) public recordIndex; + + Record[] public recordList; + uint256 subFromDraing = 0; + + mapping(address => mapping(string => uint256)) public availableTotal; + + mapping(address => mapping(string => uint256)) public frozenTotal; + + mapping(address => mapping(string => uint256)) public unfrozenTotal; + + mapping(address => uint256) lastWithdrawTime; + + mapping(address => mapping(uint256 => uint256)) lastWithdrawAmount; + + mapping(address => mapping(string => uint256)) public withdrawingTotal; + + mapping(address => mapping(uint256 => uint256)) orderSubFrozenList; + + constructor( + address _usdtAddress, + address _usdcAddress, + address _busdAddress, + address _airAddress + ) { + coinTypeMaping["USDT"] = _usdtAddress; + coinTypeMaping["USDC"] = _usdcAddress; + coinTypeMaping["BUSD"] = _busdAddress; + coinTypeMaping["AIR"] = _airAddress; + } + + function setERC20Address(string memory _coinType) + public + view + returns (TokenTransfer) + { + require(bytes(_coinType).length != 0, "Invalid coin type"); + address _remoteAddr = coinTypeMaping[_coinType]; + + require(_remoteAddr != address(0), "Invalid coin type"); + + TokenTransfer _tokenTransfer = TokenTransfer(_remoteAddr); + return _tokenTransfer; + } + + event RecordAdd( + uint256 _recordNo, + address _addr, + string _tradeHash, + string _coinType, + uint256 _hostCount, + uint256 _hostStatus, + uint256 _hostType, + uint256 _hostDirection + ); + event RecordApplyUnfrozen(address _addr, uint256 _amt); + event UnfrozenTotalTransfer( + address _addr, + string _coinType, + uint256 _lastAmount + ); + event RecordUpdate( + address _addr, + uint256 _recordNo, + string _hash, + uint256 _hostStatus + ); + + address _userAddr; + address _restCAddr; + address _orderCAddr; + address _appealCAddr; + + modifier onlyAuthFromAddr() { + require(_userAddr != address(0), "Invalid address call user"); + require(_restCAddr != address(0), "Invalid address call rest"); + require(_orderCAddr != address(0), "Invalid address call order"); + require(_appealCAddr != address(0), "Invalid address call appeal"); + _; + } + + function authFromContract( + address _fromUser, + address _fromRest, + address _fromOrder, + address _fromAppeal + ) external { + require(_userAddr == address(0), "rest address has Auth"); + require(_restCAddr == address(0), "rest address has Auth"); + require(_orderCAddr == address(0), "order address has Auth"); + require(_appealCAddr == address(0), "appeal address has Auth"); + _userAddr = _fromUser; + _restCAddr = _fromRest; + _orderCAddr = _fromOrder; + _appealCAddr = _fromAppeal; + _userStorage = UserInterface(_userAddr); + _orderStorage = OrderInterface(_orderCAddr); + } + + function _insert( + address _addr, + string memory _tradeHash, + string memory _coinType, + uint256 _hostCount, + uint256 _hostStatus, + uint256 _hostType, + uint256 _hostDirection + ) internal returns (uint256 recordNo) { + require(_addr != address(0), "address null is not allowed"); + require(bytes(_coinType).length != 0, "coinType null is not allowed"); + require(_hostCount != uint256(0), "hostCount null is not allowed"); + require(_hostType != uint256(0), "hostType null is not allowed"); + require( + _hostDirection != uint256(0), + "hostDirection null is not allowed" + ); + + uint256 _recordNo = block.timestamp; + Record memory _record = Record({ + recordNo: _recordNo, + userAddr: _addr, + tradeHash: _tradeHash, + coinType: _coinType, + hostCount: _hostCount, + hostStatus: _hostStatus, + hostType: _hostType, + hostDirection: _hostDirection, + hostTime: block.timestamp, + updateTime: 0 + }); + + records[_recordNo] = _record; + + recordList.push(_record); + recordIndex[_recordNo] = recordList.length - 1; + emit RecordAdd( + _recordNo, + _addr, + _tradeHash, + _coinType, + _hostCount, + _hostStatus, + _hostType, + _hostDirection + ); + return _recordNo; + } + + function tokenEscrow(string memory _coinType, uint256 _amt) external { + require(_amt > 0, "Invalid transfer amount"); + require( + availableTotal[msg.sender][_coinType] + _amt > + availableTotal[msg.sender][_coinType], + "Invalid transfer amount" + ); + + availableTotal[msg.sender][_coinType] = SafeMath.add( + availableTotal[msg.sender][_coinType], + _amt + ); + + uint256 _hostType = 1; + if ( + keccak256(abi.encodePacked(_coinType)) == + keccak256(abi.encodePacked("AIR")) + ) { + _hostType = 3; + UserStorage.User memory _user = _userStorage.searchUser(msg.sender); + UserStorage.MorgageStats memory _morgageStats = _user.morgageStats; + _morgageStats.mortgage = SafeMath.add(_morgageStats.mortgage, _amt); + _userStorage.updateMorgageStats(msg.sender, _morgageStats); + + if ( + _user.userFlag == 0 && + _morgageStats.mortgage >= merchantNeedCount + ) { + _userStorage.updateUserRole(msg.sender, 3); + } + } + _insert(msg.sender, "", _coinType, _amt, 2, _hostType, 1); + + TokenTransfer _tokenTransfer = setERC20Address(_coinType); + _tokenTransfer.transferFrom(msg.sender, address(this), _amt); + } + + function addRecord( + address _addr, + string memory _tradeHash, + string memory _coinType, + uint256 _hostCount, + uint256 _hostStatus, + uint256 _hostType, + uint256 _hostDirection + ) public onlyAuthFromAddr { + require( + msg.sender == _restCAddr || msg.sender == _orderCAddr, + "RedocrdStorage:Invalid from contract address" + ); + + frozenTotal[_addr][_coinType] = SafeMath.add( + frozenTotal[_addr][_coinType], + _hostCount + ); + _insert( + _addr, + _tradeHash, + _coinType, + _hostCount, + _hostStatus, + _hostType, + _hostDirection + ); + } + + function addAvailableTotal( + address _addr, + string memory _coinType, + uint256 _amt + ) public onlyAuthFromAddr { + require( + msg.sender == _restCAddr || msg.sender == _orderCAddr, + "Invalid address" + ); + require(_amt > 0, "Invalid transfer amount"); + uint256 _aBalance = getErcBalance(_coinType, address(this)); + require(_aBalance >= _amt, "balance not enough"); + require(frozenTotal[_addr][_coinType] >= _amt, "Invalid amount"); + require( + SafeMath.sub(frozenTotal[_addr][_coinType], _amt) <= + frozenTotal[_addr][_coinType], + "Invalid amount" + ); + frozenTotal[_addr][_coinType] = SafeMath.sub( + frozenTotal[_addr][_coinType], + _amt + ); + + TokenTransfer _tokenTransfer = setERC20Address(_coinType); + _tokenTransfer.transfer(_addr, _amt); + } + + function getAvailableTotal(address _addr, string memory _coinType) + public + view + returns (uint256) + { + return availableTotal[_addr][_coinType]; + } + + function subFrozenTotal(uint256 _orderNo, address _addr) + public + onlyAuthFromAddr + { + require( + msg.sender == _orderCAddr || msg.sender == _appealCAddr, + "Invalid from contract address" + ); + OrderStorage.Order memory _order = _orderStorage.searchOrder(_orderNo); + require(_order.orderNo != uint256(0), "order not exist"); + address _seller = _order.orderDetail.sellerAddr; + string memory _coinType = _order.orderDetail.coinType; + + uint256 _subAmount = orderSubFrozenList[_seller][_orderNo]; + require(_subAmount == 0, "order not exist"); + + uint256 _frozen = frozenTotal[_seller][_coinType]; + uint256 _orderCount = _order.coinCount; + require(_frozen >= _orderCount, "Invalid amount"); + require( + SafeMath.sub(_frozen, _orderCount) <= _frozen, + "Invalid amount 2" + ); + + frozenTotal[_seller][_coinType] = SafeMath.sub(_frozen, _orderCount); + orderSubFrozenList[_seller][_orderNo] = _orderCount; + + TokenTransfer _tokenTransfer = setERC20Address(_coinType); + _tokenTransfer.transfer(_addr, _orderCount); + } + + function subAvaAppeal( + address _from, + address _to, + AppealStorage.Appeal memory _al, + uint256 _amt, + uint256 _t, + uint256 _self + ) public onlyAuthFromAddr { + require( + msg.sender == _appealCAddr, + "RedocrdStorage:Invalid from contract address" + ); + + uint256 _available = getAvailableTotal(_from, "AIR"); + uint256 _need = 0; + address _opt = _t == 1 ? _al.witness : _al.detail.observerAddr; + if (_available >= _amt) { + _need = _amt; + } else { + _need = _available; + } + + if ( + (_t == 1 && _self == 0) || + (_t == 2 && _al.detail.finalAppealAddr != _from) + ) { + availableTotal[_from]["AIR"] = SafeMath.sub( + availableTotal[_from]["AIR"], + _need + ); + availableTotal[_to]["AIR"] = SafeMath.add( + availableTotal[_to]["AIR"], + _need + ); + } + + availableTotal[_opt]["AIR"] = SafeMath.add( + availableTotal[_opt]["AIR"], + _amt + ); + chanRole(_from); + + UserStorage.User memory _user = _userStorage.searchUser(_opt); + if (_t == 1) { + _user.credit = _user.credit + witnessHandleCredit; + } else if (_t == 2) { + _user.credit = _user.credit + observerHandleCredit; + } + UserStorage.TradeStats memory _tradeStats = _user.tradeStats; + _userStorage.updateTradeStats(_opt, _tradeStats, _user.credit); + } + + function subWitnessAvailable(address _addr) public onlyAuthFromAddr { + require( + msg.sender == _appealCAddr, + "RedocrdStorage:Invalid from contract address" + ); + require(_addr != address(0), "witness address is null"); + uint256 _availableTotal = availableTotal[_addr]["AIR"]; + uint256 _need = 0; + if (_availableTotal >= subWitFee) { + _need = subWitFee; + availableTotal[_addr]["AIR"] = SafeMath.sub(_availableTotal, _need); + } else { + availableTotal[_addr]["AIR"] = 0; + + uint256 _draing = withdrawingTotal[_addr]["AIR"]; + if (SafeMath.add(_availableTotal, _draing) >= subWitFee) { + _need = subWitFee; + subFromDraing = subWitFee - _availableTotal - _draing; + withdrawingTotal[_addr]["AIR"] = SafeMath.sub( + withdrawingTotal[_addr]["AIR"], + subFromDraing + ); + } else { + _need = SafeMath.add( + withdrawingTotal[_addr]["AIR"], + availableTotal[_addr]["AIR"] + ); + withdrawingTotal[_addr]["AIR"] = 0; + } + } + chanRole(_addr); + + UserStorage.User memory _user = _userStorage.searchUser(_addr); + _user.credit = _user.credit >= subWitCredit + ? (_user.credit - subWitCredit) + : 0; + UserStorage.TradeStats memory _tradeStats = _user.tradeStats; + _userStorage.updateTradeStats(_addr, _tradeStats, _user.credit); + + TokenTransfer _tokenTransfer = setERC20Address("AIR"); + _tokenTransfer.transfer(owner(), _need); + } + + function getFrozenTotal(address _addr, string memory _coinType) + public + view + returns (uint256) + { + return frozenTotal[_addr][_coinType]; + } + + function applyUnfrozen(uint256 _amt) public returns (uint256) { + string memory _coinType = "AIR"; + require(_amt > 0, "Invalid transfer amount"); + require( + availableTotal[msg.sender][_coinType] >= _amt, + "Invalid amount" + ); + require( + SafeMath.sub(availableTotal[msg.sender][_coinType], _amt) < + availableTotal[msg.sender][_coinType], + "Invalid amount 2" + ); + + lastWithdrawTime[msg.sender] = block.timestamp; + lastWithdrawAmount[msg.sender][lastWithdrawTime[msg.sender]] = _amt; + availableTotal[msg.sender][_coinType] = SafeMath.sub( + availableTotal[msg.sender][_coinType], + _amt + ); + withdrawingTotal[msg.sender][_coinType] = SafeMath.add( + withdrawingTotal[msg.sender][_coinType], + _amt + ); + + chanRole(msg.sender); + + _insert(msg.sender, "", _coinType, _amt, 3, 3, 2); + + emit RecordApplyUnfrozen(msg.sender, _amt); + + return getAvailableTotal(msg.sender, _coinType); + } + + function chanRole(address _addr) internal { + uint256 _avail = availableTotal[_addr]["AIR"]; + + UserStorage.User memory _user = _userStorage.searchUser(_addr); + UserStorage.MorgageStats memory _morgageStats = _user.morgageStats; + _morgageStats.mortgage = _avail; + + _userStorage.updateMorgageStats(_addr, _morgageStats); + + if ( + _user.userFlag == 2 && + _avail < congressNeedCount && + _avail >= merchantNeedCount + ) { + _userStorage.updateUserRole(_addr, 3); + } + + if ( + _user.userFlag == 1 && + _avail < witnessNeedCount && + _avail >= merchantNeedCount + ) { + _userStorage.updateUserRole(_addr, 3); + } + + if (_avail < merchantNeedCount) { + _userStorage.updateUserRole(_addr, 0); + } + } + + function applyWithdraw(uint256 _recordNo) public { + Record memory _record = records[_recordNo]; + + require(_record.recordNo != uint256(0), "record not exist"); + require(_record.userAddr == msg.sender, "record user not exist"); + + require(_record.hostStatus == 3, "status error"); + + require( + withdrawingTotal[msg.sender]["AIR"] >= _record.hostCount, + "balance not enough" + ); + + require( + block.timestamp >= (_record.hostTime + canWithdrawToTime * 1 days), + "can't withdraw" + ); + + withdrawingTotal[msg.sender]["AIR"] = SafeMath.sub( + withdrawingTotal[msg.sender]["AIR"], + _record.hostCount + ); + unfrozenTotal[msg.sender]["AIR"] = SafeMath.add( + unfrozenTotal[msg.sender]["AIR"], + _record.hostCount + ); + + _record.hostCount = SafeMath.sub(_record.hostCount, subFromDraing); + _record.hostStatus = 4; + _record.updateTime = block.timestamp; + records[_recordNo] = _record; + recordList[recordIndex[_recordNo]] = _record; + emit RecordUpdate(msg.sender, _recordNo, _record.tradeHash, 4); + + TokenTransfer _tokenTransfer = setERC20Address("AIR"); + _tokenTransfer.transfer(msg.sender, _record.hostCount); + } + + function unfrozenTotalSearch(address _addr, string memory _coinType) + public + view + returns (uint256) + { + require(_addr != address(0), "user address is null"); + + return unfrozenTotal[_addr][_coinType]; + } + + function getUnfrozenTotal(address _addr, string memory _coinType) + external + view + returns (uint256) + { + return unfrozenTotal[_addr][_coinType]; + } + + function getWithdrawingTotal(address _addr, string memory _coinType) + public + view + returns (uint256) + { + return withdrawingTotal[_addr][_coinType]; + } + + function getErcBalance(string memory _coinType, address _addr) + public + view + returns (uint256) + { + TokenTransfer _tokenTransfer = setERC20Address(_coinType); + return _tokenTransfer.balanceOf(_addr); + } + + function _updateInfo( + address _addr, + uint256 _recordNo, + string memory _hash, + uint256 _hostStatus + ) internal returns (bool) { + Record memory _record = records[_recordNo]; + require(_record.userAddr == _addr, "record not exist"); + require(_hostStatus == 1 || _hostStatus == 2, "invalid hostStatus"); + + if (_hostStatus != uint256(0)) { + _record.hostStatus = _hostStatus; + } + if (bytes(_hash).length != 0) { + _record.tradeHash = _hash; + } + + _record.updateTime = block.timestamp; + records[_recordNo] = _record; + recordList[recordIndex[_recordNo]] = _record; + emit RecordUpdate(_addr, _recordNo, _hash, _hostStatus); + return true; + } + + function updateInfo( + address _addr, + uint256 _recordNo, + string memory _hash, + uint256 _hostStatus + ) external returns (bool) { + return _updateInfo(_addr, _recordNo, _hash, _hostStatus); + } + + function searchRecord(uint256 _recordNo) + external + view + returns (Record memory record) + { + return records[_recordNo]; + } + + function searchRecordList() external view returns (Record[] memory) { + return recordList; + } +} diff --git a/RestStorage.sol b/RestStorage.sol new file mode 100644 index 0000000..85788c1 --- /dev/null +++ b/RestStorage.sol @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "./RecordInterface.sol"; +import "./UserStorage.sol"; +import "./RecordStorage.sol"; + +contract RestStorage { + RecordInterface private _recordStorage; + UserInterface private _userStorage; + address recordAddress; + + struct Rest { + address userAddr; + uint256 restNo; + uint256 restType; + string coinType; + string currencyType; + uint256 restCount; + uint256 price; + uint256[] payType; + uint256 restStatus; + RestDetail restDetail; + } + struct RestDetail { + uint256 finishCount; + uint256 remainderCount; + uint256 limitAmountFrom; + uint256 limitAmountTo; + uint256 limitMinCredit; + uint256 limitMinMortgage; + string restRemark; + uint256 restTime; + uint256 updateTime; + uint256 restFee; + string restHash; + } + + mapping(uint256 => Rest) private rests; + mapping(uint256 => uint256) private restIndex; + + Rest[] private restList; + + mapping(address => mapping(uint256 => uint256)) restFrozenTotal; + + event RestAdd( + uint256 _restNo, + uint256 _restType, + string _coinType, + string _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] _payType, + RestDetail _restDetail + ); + event RestUpdate( + uint256 _restNo, + string _coinType, + string _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] _payType, + RestDetail _restDetail + ); + event RestUpdateHash(uint256 _restNo, string _restHash); + + constructor(address _recordAddr, address _userContractAddr) { + _recordStorage = RecordInterface(_recordAddr); + _userStorage = UserInterface(_userContractAddr); + recordAddress = _recordAddr; + } + + address _orderCAddr; + + modifier onlyAuthFromAddr() { + require(_orderCAddr != address(0), "Invalid address call rest"); + _; + } + + function authFromContract(address _fromOrder) external { + require(_orderCAddr == address(0), "order address has Auth"); + _orderCAddr = _fromOrder; + } + + modifier onlyRestOwner(uint256 _restNo) { + require( + rests[_restNo].userAddr == msg.sender, + "rest address not exist" + ); + _; + } + + function _checkParam( + uint256 _restType, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] memory _payType + ) internal pure { + require( + _restType != uint256(0), + "RestStorage: restType null is not allowed" + ); + require( + bytes(_coinType).length != 0, + "RestStorage: coinType null is not allowed" + ); + require( + bytes(_currencyType).length != 0, + "RestStorage: currencyType null is not allowed" + ); + require( + _restCount != uint256(0), + "RestStorage: restCount null is not allowed" + ); + require(_price != uint256(0), "RestStorage: price null is not allowed"); + require( + _payType.length != 0, + "RestStorage: payType null is not allowed" + ); + } + + function _insert( + uint256 _restType, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) internal returns (uint256) { + _checkParam( + _restType, + _coinType, + _currencyType, + _restCount, + _price, + _payType + ); + + uint256 _restNo = block.timestamp; + _restDetail.finishCount = 0; + _restDetail.remainderCount = _restCount; + _restDetail.restTime = block.timestamp; + _restDetail.updateTime = 0; + + if ( + _restDetail.limitAmountTo > SafeMath.mul(_restCount, _price) || + _restDetail.limitAmountTo == 0 + ) { + _restDetail.limitAmountTo = SafeMath.mul(_restCount, _price); + } + Rest memory r = Rest({ + userAddr: msg.sender, + restNo: _restNo, + restType: _restType, + coinType: _coinType, + currencyType: _currencyType, + restCount: _restCount, + price: _price, + payType: _payType, + restStatus: 1, + restDetail: _restDetail + }); + rests[_restNo] = r; + + restList.push(r); + restIndex[_restNo] = restList.length - 1; + + emit RestAdd( + _restNo, + _restType, + _coinType, + _currencyType, + _restCount, + _price, + _payType, + _restDetail + ); + return _restNo; + } + + function _updateInfo( + uint256 _restNo, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) internal { + require(_restNo != uint256(0), "Invalid restNo"); + Rest memory r = rests[_restNo]; + r.restStatus = 1; + if (bytes(_coinType).length != 0) { + r.coinType = _coinType; + } + if (bytes(_currencyType).length != 0) { + r.currencyType = _currencyType; + } + + if (_restCount != uint256(0)) { + r.restCount = _restCount; + } + if (_price != uint256(0)) { + r.price = _price; + } + if (_payType.length != 0) { + r.payType = _payType; + } + + if (_restDetail.limitAmountFrom != uint256(0)) { + r.restDetail.limitAmountFrom = _restDetail.limitAmountFrom; + } + if (_restDetail.limitAmountTo != uint256(0)) { + if (_restDetail.limitAmountTo > SafeMath.mul(_restCount, _price)) { + _restDetail.limitAmountTo = SafeMath.mul(_restCount, _price); + } + r.restDetail.limitAmountTo = _restDetail.limitAmountTo; + } + if (_restDetail.limitMinCredit != uint256(0)) { + r.restDetail.limitMinCredit = _restDetail.limitMinCredit; + } + if (_restDetail.limitMinMortgage != uint256(0)) { + r.restDetail.limitMinMortgage = _restDetail.limitMinMortgage; + } + if (bytes(_restDetail.restRemark).length != 0) { + r.restDetail.restRemark = _restDetail.restRemark; + } + + if (_restDetail.restFee != uint256(0)) { + r.restDetail.restFee = _restDetail.restFee; + } + if (bytes(_restDetail.restHash).length != 0) { + r.restDetail.restHash = _restDetail.restHash; + } + + r.restDetail.updateTime = block.timestamp; + rests[_restNo] = r; + restList[restIndex[_restNo]] = r; + emit RestUpdate( + _restNo, + _coinType, + _currencyType, + _restCount, + _price, + _payType, + _restDetail + ); + } + + function addBuyRest( + uint256 _restType, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) external { + require(_restType == 1, "must buy rest"); + + UserStorage.User memory _user = _userStorage.searchUser(msg.sender); + + bool _openTrade = _recordStorage.getOpenTrade(); + require(_openTrade || _user.userFlag == 3, "invalid user"); + + _insert( + _restType, + _coinType, + _currencyType, + _restCount, + _price, + _payType, + _restDetail + ); + } + + function _addSell( + uint256 _restType, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _restFee, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) internal { + require(_restType == 2, "must sell rest"); + require(_restCount > 0, "restCount error"); + require(_restFee >= 0, "restFee error"); + + UserStorage.User memory _user = _userStorage.searchUser(msg.sender); + bool _openTrade = _recordStorage.getOpenTrade(); + require(_openTrade || _user.userFlag == 3, "invalid user"); + + uint256 _newRestNo = _insert( + _restType, + _coinType, + _currencyType, + _restCount, + _price, + _payType, + _restDetail + ); + + restFrozenTotal[msg.sender][_newRestNo] = _restCount; + + _recordStorage.addRecord( + msg.sender, + "", + _coinType, + _restCount, + 2, + 1, + 2 + ); + uint256 _needSub = SafeMath.add(_restCount, _restFee); + TokenTransfer _tokenTransfer = _recordStorage.setERC20Address( + _coinType + ); + _tokenTransfer.transferFrom(msg.sender, recordAddress, _needSub); + } + + function addSellRest( + uint256 _restType, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _restFee, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) external { + _addSell( + _restType, + _coinType, + _currencyType, + _restCount, + _restFee, + _price, + _payType, + _restDetail + ); + } + + function getRestFrozenTotal(address _addr, uint256 _restNo) + public + view + returns (uint256) + { + return restFrozenTotal[_addr][_restNo]; + } + + function cancelBuyRest(uint256 _restNo) external onlyRestOwner(_restNo) { + require(rests[_restNo].restStatus == 1, "can't change this rest"); + require(rests[_restNo].restType == 1, "Invalid rest type"); + require( + rests[_restNo].restDetail.finishCount < rests[_restNo].restCount, + "this rest has finished" + ); + + Rest memory r = rests[_restNo]; + r.restStatus = 4; + r.restDetail.updateTime = block.timestamp; + rests[_restNo] = r; + restList[restIndex[_restNo]] = r; + } + + function _cancelSell(uint256 _restNo) internal onlyRestOwner(_restNo) { + require(rests[_restNo].restStatus == 1, "can't cancel this rest"); + require(rests[_restNo].restType == 2, "Invalid rest type"); + require( + rests[_restNo].restDetail.finishCount < rests[_restNo].restCount, + "this rest has finished" + ); + require(restFrozenTotal[msg.sender][_restNo] > 0, "rest has finished"); + uint256 _frozenTotal = _recordStorage.getFrozenTotal( + msg.sender, + rests[_restNo].coinType + ); + require( + _frozenTotal >= restFrozenTotal[msg.sender][_restNo], + "can't cancel this rest" + ); + + uint256 remainHoldCoin = restFrozenTotal[msg.sender][_restNo]; + + Rest memory r = rests[_restNo]; + r.restStatus = 4; + + if (remainHoldCoin < rests[_restNo].restCount) { + r.restStatus = 5; + } + r.restDetail.remainderCount = 0; + r.restDetail.updateTime = block.timestamp; + rests[_restNo] = r; + restList[restIndex[_restNo]] = r; + + restFrozenTotal[msg.sender][_restNo] = 0; + + _recordStorage.addAvailableTotal( + msg.sender, + rests[_restNo].coinType, + remainHoldCoin + ); + } + + function cancelSellRest(uint256 _restNo) external { + _cancelSell(_restNo); + } + + function startOrStop(uint256 _restNo, uint256 _restStatus) + external + onlyRestOwner(_restNo) + { + require(_restStatus == 1 || _restStatus == 3, "Invalid rest status"); + + Rest memory r = rests[_restNo]; + require( + r.restStatus == 1 || r.restStatus == 3, + "Invalid rest status,opt error" + ); + r.restStatus = _restStatus; + r.restDetail.updateTime = block.timestamp; + rests[_restNo] = r; + restList[restIndex[_restNo]] = r; + } + + function updateInfo( + uint256 _restNo, + string memory _coinType, + string memory _currencyType, + uint256 _restCount, + uint256 _restFee, + uint256 _price, + uint256[] memory _payType, + RestDetail memory _restDetail + ) external onlyRestOwner(_restNo) { + require(_restNo != uint256(0), "Invalid restNo"); + Rest memory _rest = rests[_restNo]; + require(_rest.restNo != uint256(0), "rest not exist"); + + if (_rest.restType == 2) { + _cancelSell(_restNo); + _addSell( + 2, + _coinType, + _currencyType, + _restCount, + _restFee, + _price, + _payType, + _restDetail + ); + } else { + _updateInfo( + _restNo, + _coinType, + _currencyType, + _restCount, + _price, + _payType, + _restDetail + ); + } + } + + function updateRestFinishCount(uint256 _restNo, uint256 _finishCount) + external + onlyAuthFromAddr + { + require( + msg.sender == _orderCAddr, + "RestStorage:Invalid from contract address" + ); + Rest memory _rest = rests[_restNo]; + + require( + _rest.restDetail.remainderCount >= _finishCount, + "RestStorage:finish count error" + ); + + if (_rest.restType == 2) { + restFrozenTotal[_rest.userAddr][_restNo] = SafeMath.sub( + restFrozenTotal[_rest.userAddr][_restNo], + _finishCount + ); + } + + _rest.restDetail.finishCount = SafeMath.add( + _rest.restDetail.finishCount, + _finishCount + ); + _rest.restDetail.remainderCount = SafeMath.sub( + _rest.restDetail.remainderCount, + _finishCount + ); + _rest.restDetail.limitAmountTo = SafeMath.mul( + _rest.price, + _rest.restDetail.remainderCount + ); + if (_rest.restDetail.remainderCount == 0) { + _rest.restStatus = 2; + } + + _rest.restDetail.updateTime = block.timestamp; + rests[_restNo] = _rest; + restList[restIndex[_restNo]] = _rest; + } + + function addRestRemainCount(uint256 _restNo, uint256 _remainCount) + public + onlyAuthFromAddr + { + require( + msg.sender == _orderCAddr, + "RestStorage:Invalid from contract address" + ); + Rest memory _rest = rests[_restNo]; + require( + _remainCount > 0 && _rest.restDetail.finishCount >= _remainCount, + "RestStorage:finish count error" + ); + + if (_rest.restType == 2) { + restFrozenTotal[_rest.userAddr][_restNo] = SafeMath.add( + restFrozenTotal[_rest.userAddr][_restNo], + _remainCount + ); + } + + _rest.restDetail.finishCount = SafeMath.sub( + _rest.restDetail.finishCount, + _remainCount + ); + _rest.restDetail.remainderCount = SafeMath.add( + _rest.restDetail.remainderCount, + _remainCount + ); + _rest.restStatus = 1; + + _rest.restDetail.updateTime = block.timestamp; + rests[_restNo] = _rest; + restList[restIndex[_restNo]] = _rest; + } + + function searchRest(uint256 _restNo) + external + view + returns (Rest memory rest) + { + require(_restNo != uint256(0), "restNo null is not allowed"); + Rest memory r = rests[_restNo]; + return r; + } + + function searchRestList() + external + view + returns (Rest[] memory, UserStorage.User[] memory) + { + UserStorage.User[] memory userList = new UserStorage.User[]( + restList.length + ); + for (uint256 i = 0; i < restList.length; i++) { + Rest memory _rest = restList[i]; + UserStorage.User memory _user = _userStorage.searchUser( + _rest.userAddr + ); + userList[i] = _user; + } + return (restList, userList); + } +} diff --git a/UserStorage.sol b/UserStorage.sol new file mode 100644 index 0000000..1a17c9a --- /dev/null +++ b/UserStorage.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +pragma abicoder v2; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract UserStorage is Ownable { + struct User { + address userAddr; + string avatar; + string email; + uint256 isOnline; + uint256 userFlag; + uint256 credit; + uint256 regTime; + TradeStats tradeStats; + MorgageStats morgageStats; + } + struct TradeStats { + uint256 tradeTotal; + uint256 restTotal; + } + struct MorgageStats { + uint256 mortgage; + uint256 freezeMortgage; + uint256 relieveMortgage; + uint256 inviteUserCount; + uint256 inviteUserReward; + uint256 applyRelieveTime; + uint256 handleRelieveTime; + } + mapping(address => User) public users; + mapping(address => uint256) public userIndex; + + User[] public userList; + + event addUser(address _userAddr); + event updateUser(string _avatar, string _email, uint256 _isOnline); + + address _restCAddr; + address _orderCAddr; + address _recordCAddr; + address _appealCAddr; + + modifier onlyAuthFromAddr() { + require(_restCAddr != address(0), "Invalid address call rest"); + require(_orderCAddr != address(0), "Invalid address call order"); + require(_recordCAddr != address(0), "Invalid address call record"); + require(_appealCAddr != address(0), "Invalid address call appeal"); + _; + } + + function authFromContract( + address _fromRest, + address _fromOrder, + address _fromRecord, + address _fromAppeal + ) external { + require(_restCAddr == address(0), "rest address has Auth"); + require(_orderCAddr == address(0), "order address has Auth"); + require(_recordCAddr == address(0), "record address has Auth"); + require(_appealCAddr == address(0), "appeal address has Auth"); + _restCAddr = _fromRest; + _orderCAddr = _fromOrder; + _recordCAddr = _fromRecord; + _appealCAddr = _fromAppeal; + } + + modifier onlyMemberOf() { + require(users[msg.sender].userAddr != address(0), "has no permission"); + _; + } + + function _insert(address _addr) internal { + require(_addr != address(0), "UserStorage: addr null is not allowed"); + require( + users[_addr].userAddr == address(0), + "UserStorage: current User exist" + ); + + TradeStats memory tradeStats = TradeStats({ + tradeTotal: 0, + restTotal: 0 + }); + MorgageStats memory morgageStats = MorgageStats({ + mortgage: 0, + freezeMortgage: 0, + relieveMortgage: 0, + inviteUserCount: 0, + inviteUserReward: 0, + applyRelieveTime: 0, + handleRelieveTime: 0 + }); + + User memory u = User({ + userAddr: _addr, + avatar: "", + email: "", + isOnline: 1, + userFlag: 0, + credit: 0, + regTime: block.timestamp, + tradeStats: tradeStats, + morgageStats: morgageStats + }); + users[_addr] = u; + + userList.push(u); + userIndex[_addr] = userList.length - 1; + emit addUser(_addr); + } + + function _updateInfo( + address _addr, + string memory _avatar, + string memory _email, + uint256 _isOnline + ) internal { + require(_addr != address(0), "UserStorage: _addr null is not allowed"); + require( + users[_addr].userAddr != address(0), + "UserStorage: current User not exist" + ); + + User memory u = users[_addr]; + if (bytes(_avatar).length != 0) { + u.avatar = _avatar; + } + if (bytes(_email).length != 0) { + u.email = _email; + } + + if (_isOnline != uint256(0)) { + u.isOnline = _isOnline; + } + + users[_addr] = u; + userList[userIndex[_addr]] = u; + } + + function _updateTradeStats( + address _addr, + TradeStats memory _tradeStats, + uint256 _credit + ) internal { + require(_addr != address(0), "UserStorage: _addr null is not allowed"); + require( + users[_addr].userAddr != address(0), + "UserStorage: current User not exist" + ); + + User memory u = users[_addr]; + + u.credit = _credit; + + u.tradeStats.tradeTotal = _tradeStats.tradeTotal; + + u.tradeStats.restTotal = _tradeStats.restTotal; + + users[_addr] = u; + userList[userIndex[_addr]] = u; + } + + function _updateMorgageStats( + address _addr, + MorgageStats memory _morgageStats + ) internal { + require(_addr != address(0), "UserStorage: _addr null is not allowed"); + require( + users[_addr].userAddr != address(0), + "UserStorage: current User not exist" + ); + + User memory u = users[_addr]; + + u.morgageStats.mortgage = _morgageStats.mortgage; + u.morgageStats.freezeMortgage = _morgageStats.freezeMortgage; + u.morgageStats.relieveMortgage = _morgageStats.relieveMortgage; + u.morgageStats.inviteUserCount = _morgageStats.inviteUserCount; + u.morgageStats.inviteUserReward = _morgageStats.inviteUserReward; + u.morgageStats.applyRelieveTime = _morgageStats.applyRelieveTime; + u.morgageStats.handleRelieveTime = _morgageStats.handleRelieveTime; + + users[_addr] = u; + userList[userIndex[_addr]] = u; + } + + function _search(address _addr) internal view returns (User memory user) { + require(_addr != address(0), "UserStorage: _addr null is not allowed"); + require( + users[_addr].userAddr != address(0), + "UserStorage: current User not exist" + ); + + User memory a = users[_addr]; + return a; + } + + function register() external { + require(!isMemberOf()); + _insert(msg.sender); + } + + function isMemberOf() public view returns (bool) { + return (users[msg.sender].userAddr != address(0)); + } + + function updateInfo( + string memory _avatar, + string memory _email, + uint256 _isOnline + ) external onlyMemberOf { + _updateInfo(msg.sender, _avatar, _email, _isOnline); + emit updateUser(_avatar, _email, _isOnline); + } + + function updateTradeStats( + address _addr, + TradeStats memory _tradeStats, + uint256 _credit + ) public onlyAuthFromAddr { + require( + msg.sender == _restCAddr || + msg.sender == _orderCAddr || + msg.sender == _appealCAddr || + msg.sender == _recordCAddr, + "UserStorage:Invalid from contract address" + ); + _updateTradeStats(_addr, _tradeStats, _credit); + } + + function updateMorgageStats( + address _addr, + MorgageStats memory _morgageStats + ) public onlyAuthFromAddr { + require( + msg.sender == _recordCAddr, + "UserStorage:Invalid from contract address" + ); + _updateMorgageStats(_addr, _morgageStats); + } + + function updateUserRole(address _addr, uint256 _userFlag) + public + onlyAuthFromAddr + { + require( + msg.sender == _recordCAddr, + "UserStorage:Invalid from contract address" + ); + require(_addr != address(0), "UserStorage: _addr null is not allowed"); + require( + users[_addr].userAddr != address(0), + "UserStorage: current User not exist" + ); + require(_userFlag >= 0, "UserStorage: Invalid userFlag 1"); + require(_userFlag <= 3, "UserStorage: Invalid userFlag 3"); + + User memory u = users[_addr]; + u.userFlag = _userFlag; + users[_addr] = u; + userList[userIndex[_addr]] = u; + } + + function searchUser(address _addr) + external + view + returns (User memory user) + { + return _search(_addr); + } + + function searchUserList() external view returns (User[] memory) { + return userList; + } + + function searchWitnessList(uint256 _userFlag) + external + view + returns (User[] memory) + { + User[] memory _resultList = new User[](userList.length); + for (uint256 i = 0; i < userList.length; i++) { + User memory _u = userList[i]; + if (_u.userFlag == _userFlag) { + _resultList[i] = _u; + } + } + return _resultList; + } +}