/* solium-disable security/no-block-members */ pragma solidity ^0.5.8; import "./License.sol"; /** * @title ArbitratorLicense * @dev Contract for management of an arbitrator license */ contract ArbitrationLicense is License { enum RequestStatus {NONE,AWAIT,ACCEPTED,REJECTED,CLOSED} struct Request{ address seller; address arbitrator; RequestStatus status; uint date; } struct ArbitratorLicenseDetails { uint id; bool acceptAny;// accept any seller } mapping(address => ArbitratorLicenseDetails) public arbitratorlicenseDetails; mapping(address => mapping(address => bool)) public permissions; mapping(address => mapping(address => bool)) public blacklist; mapping(bytes32 => Request) public requests; event ArbitratorRequested(bytes32 id, address indexed seller, address indexed arbitrator); event RequestAccepted(bytes32 id, address indexed arbitrator, address indexed seller); event RequestRejected(bytes32 id, address indexed arbitrator, address indexed seller); event RequestCanceled(bytes32 id, address indexed arbitrator, address indexed seller); event BlacklistSeller(address indexed arbitrator, address indexed seller); event UnBlacklistSeller(address indexed arbitrator, address indexed seller); /** * @param _tokenAddress Address of token used to pay for licenses (SNT) * @param _price Amount of token needed to buy a license * @param _burnAddress Burn address where the price of the license is sent */ constructor(address _tokenAddress, uint256 _price, address _burnAddress) License(_tokenAddress, _price, _burnAddress) public {} /** * @notice Buy an arbitrator license */ function buy() external returns(uint) { return _buy(msg.sender, false); } /** * @notice Buy an arbitrator license and set if the arbitrator accepts any seller * @param _acceptAny When set to true, all sellers are accepted by the arbitrator */ function buy(bool _acceptAny) external returns(uint) { return _buy(msg.sender, _acceptAny); } /** * @notice Buy an arbitrator license and set if the arbitrator accepts any seller. Sets the arbitrator as the address in params instead of the sender * @param _sender Address of the arbitrator * @param _acceptAny When set to true, all sellers are accepted by the arbitrator */ function _buy(address _sender, bool _acceptAny) internal returns (uint id) { id = _buyFrom(_sender); arbitratorlicenseDetails[_sender].id = id; arbitratorlicenseDetails[_sender].acceptAny = _acceptAny; } /** * @notice Change acceptAny parameter for arbitrator * @param _acceptAny indicates does arbitrator allow to accept any seller/choose sellers */ function changeAcceptAny(bool _acceptAny) public { require(isLicenseOwner(msg.sender), "Message sender should have a valid arbitrator license"); require(arbitratorlicenseDetails[msg.sender].acceptAny != _acceptAny, "Message sender should pass parameter different from the current one"); arbitratorlicenseDetails[msg.sender].acceptAny = _acceptAny; } /** * @notice Allows arbitrator to accept a seller * @param _arbitrator address of a licensed arbitrator */ function requestArbitrator(address _arbitrator) public { require(isLicenseOwner(_arbitrator), "Arbitrator should have a valid license"); require(!arbitratorlicenseDetails[_arbitrator].acceptAny, "Arbitrator already accepts all cases"); bytes32 _id = keccak256(abi.encodePacked(_arbitrator, msg.sender)); RequestStatus _status = requests[_id].status; require(_status != RequestStatus.AWAIT && _status != RequestStatus.ACCEPTED, "Invalid request status"); if(_status == RequestStatus.REJECTED || _status == RequestStatus.CLOSED){ require(requests[_id].date + 3 days < block.timestamp, "Must wait 3 days before requesting the arbitrator again"); } requests[_id] = Request({ seller: msg.sender, arbitrator: _arbitrator, status: RequestStatus.AWAIT, date: block.timestamp }); emit ArbitratorRequested(_id, msg.sender, _arbitrator); } /** * @dev Get Request Id * @param _arbitrator Arbitrator address * @param _account Seller account * @return Request Id */ function getId(address _arbitrator, address _account) external pure returns(bytes32){ return keccak256(abi.encodePacked(_arbitrator,_account)); } /** * @notice Allows arbitrator to accept a seller's request * @param _id request id */ function acceptRequest(bytes32 _id) public { require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license"); require(requests[_id].status == RequestStatus.AWAIT, "This request is not pending"); require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator already accepts all cases"); require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator"); requests[_id].status = RequestStatus.ACCEPTED; address _seller = requests[_id].seller; permissions[msg.sender][_seller] = true; emit RequestAccepted(_id, msg.sender, requests[_id].seller); } /** * @notice Allows arbitrator to reject a request * @param _id request id */ function rejectRequest(bytes32 _id) public { require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license"); require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED, "Invalid request status"); require(!arbitratorlicenseDetails[msg.sender].acceptAny, "Arbitrator accepts all cases"); require(requests[_id].arbitrator == msg.sender, "Invalid arbitrator"); requests[_id].status = RequestStatus.REJECTED; requests[_id].date = block.timestamp; address _seller = requests[_id].seller; permissions[msg.sender][_seller] = false; emit RequestRejected(_id, msg.sender, requests[_id].seller); } /** * @notice Allows seller to cancel a request * @param _id request id */ function cancelRequest(bytes32 _id) public { require(requests[_id].seller == msg.sender, "This request id does not belong to the message sender"); require(requests[_id].status == RequestStatus.AWAIT || requests[_id].status == RequestStatus.ACCEPTED, "Invalid request status"); address arbitrator = requests[_id].arbitrator; requests[_id].status = RequestStatus.CLOSED; requests[_id].date = block.timestamp; address _arbitrator = requests[_id].arbitrator; permissions[_arbitrator][msg.sender] = false; emit RequestCanceled(_id, arbitrator, requests[_id].seller); } /** * @notice Allows arbitrator to blacklist a seller * @param _seller Seller address */ function blacklistSeller(address _seller) public { require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license"); blacklist[msg.sender][_seller] = true; emit BlacklistSeller(msg.sender, _seller); } /** * @notice Allows arbitrator to remove a seller from the blacklist * @param _seller Seller address */ function unBlacklistSeller(address _seller) public { require(isLicenseOwner(msg.sender), "Arbitrator should have a valid license"); blacklist[msg.sender][_seller] = false; emit UnBlacklistSeller(msg.sender, _seller); } /** * @notice Checks if Arbitrator permits to use his/her services * @param _seller sellers's address * @param _arbitrator arbitrator's address */ function isAllowed(address _seller, address _arbitrator) public view returns(bool) { return (arbitratorlicenseDetails[_arbitrator].acceptAny && !blacklist[_arbitrator][_seller]) || permissions[_arbitrator][_seller]; } /** * @notice Support for "approveAndCall". Callable only by `token()`. * @param _from Who approved. * @param _amount Amount being approved, need to be equal `price()`. * @param _token Token being approved, need to be equal `token()`. * @param _data Abi encoded data with selector of `buy(and)`. */ function receiveApproval(address _from, uint256 _amount, address _token, bytes memory _data) public { require(_amount == price, "Wrong value"); require(_token == address(token), "Wrong token"); require(_token == address(msg.sender), "Wrong call"); require(_data.length == 4, "Wrong data length"); require(_abiDecodeBuy(_data) == bytes4(0xa6f2ae3a), "Wrong method selector"); //bytes4(keccak256("buy()")) _buy(_from, false); } }