/** * Source Code first verified at https://etherscan.io on Thursday, May 9, 2019 (UTC) */ pragma solidity ^0.4.24; interface IArbitrable { event MetaEvidence(uint indexed _metaEvidenceID, string _evidence); event Dispute(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _metaEvidenceID, uint _evidenceGroupID); event Evidence(Arbitrator indexed _arbitrator, uint indexed _evidenceGroupID, address indexed _party, string _evidence); event Ruling(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _ruling); function rule(uint _disputeID, uint _ruling) external; } contract Arbitrable is IArbitrable { Arbitrator public arbitrator; bytes public arbitratorExtraData; modifier onlyArbitrator {require(msg.sender == address(arbitrator), "Can only be called by the arbitrator."); _;} constructor(Arbitrator _arbitrator, bytes memory _arbitratorExtraData) public { arbitrator = _arbitrator; arbitratorExtraData = _arbitratorExtraData; } function rule(uint _disputeID, uint _ruling) public onlyArbitrator { emit Ruling(Arbitrator(msg.sender),_disputeID,_ruling); executeRuling(_disputeID,_ruling); } function executeRuling(uint _disputeID, uint _ruling) internal; } contract Arbitrator { enum DisputeStatus {Waiting, Appealable, Solved} modifier requireArbitrationFee(bytes memory _extraData) { require(msg.value >= arbitrationCost(_extraData), "Not enough ETH to cover arbitration costs."); _; } modifier requireAppealFee(uint _disputeID, bytes memory _extraData) { require(msg.value >= appealCost(_disputeID, _extraData), "Not enough ETH to cover appeal costs."); _; } event DisputeCreation(uint indexed _disputeID, Arbitrable indexed _arbitrable); event AppealPossible(uint indexed _disputeID, Arbitrable indexed _arbitrable); event AppealDecision(uint indexed _disputeID, Arbitrable indexed _arbitrable); function createDispute(uint _choices, bytes memory _extraData) public requireArbitrationFee(_extraData) payable returns(uint disputeID) {} function arbitrationCost(bytes memory _extraData) public view returns(uint fee); function appeal(uint _disputeID, bytes memory _extraData) public requireAppealFee(_disputeID,_extraData) payable { emit AppealDecision(_disputeID, Arbitrable(msg.sender)); } function appealCost(uint _disputeID, bytes memory _extraData) public view returns(uint fee); function appealPeriod(uint _disputeID) public view returns(uint start, uint end) {} function disputeStatus(uint _disputeID) public view returns(DisputeStatus status); function currentRuling(uint _disputeID) public view returns(uint ruling); } /** * @authors: [@clesaege, @n1c01a5, @epiqueras, @ferittuncer] * @reviewers: [@clesaege*, @unknownunknown1*] * @auditors: [] * @bounties: [] * @deployments: [] */ /** @title Centralized Arbitrator * @dev This is a centralized arbitrator deciding alone on the result of disputes. No appeals are possible. */ contract CentralizedArbitrator is Arbitrator { address public owner = msg.sender; uint arbitrationPrice; // Not public because arbitrationCost already acts as an accessor. uint constant NOT_PAYABLE_VALUE = (2**256-2)/2; // High value to be sure that the appeal is too expensive. struct DisputeStruct { Arbitrable arbitrated; uint choices; uint fee; uint ruling; DisputeStatus status; } modifier onlyOwner {require(msg.sender==owner, "Can only be called by the owner."); _;} DisputeStruct[] public disputes; /** @dev Constructor. Set the initial arbitration price. * @param _arbitrationPrice Amount to be paid for arbitration. */ constructor(uint _arbitrationPrice) public { arbitrationPrice = _arbitrationPrice; } /** @dev Set the arbitration price. Only callable by the owner. * @param _arbitrationPrice Amount to be paid for arbitration. */ function setArbitrationPrice(uint _arbitrationPrice) public onlyOwner { arbitrationPrice = _arbitrationPrice; } /** @dev Cost of arbitration. Accessor to arbitrationPrice. * @param _extraData Not used by this contract. * @return fee Amount to be paid. */ function arbitrationCost(bytes _extraData) public view returns(uint fee) { return arbitrationPrice; } /** @dev Cost of appeal. Since it is not possible, it's a high value which can never be paid. * @param _disputeID ID of the dispute to be appealed. Not used by this contract. * @param _extraData Not used by this contract. * @return fee Amount to be paid. */ function appealCost(uint _disputeID, bytes _extraData) public view returns(uint fee) { return NOT_PAYABLE_VALUE; } /** @dev Create a dispute. Must be called by the arbitrable contract. * Must be paid at least arbitrationCost(). * @param _choices Amount of choices the arbitrator can make in this dispute. When ruling ruling<=choices. * @param _extraData Can be used to give additional info on the dispute to be created. * @return disputeID ID of the dispute created. */ function createDispute(uint _choices, bytes _extraData) public payable returns(uint disputeID) { super.createDispute(_choices, _extraData); disputeID = disputes.push(DisputeStruct({ arbitrated: Arbitrable(msg.sender), choices: _choices, fee: msg.value, ruling: 0, status: DisputeStatus.Waiting })) - 1; // Create the dispute and return its number. emit DisputeCreation(disputeID, Arbitrable(msg.sender)); } /** @dev Give a ruling. UNTRUSTED. * @param _disputeID ID of the dispute to rule. * @param _ruling Ruling given by the arbitrator. Note that 0 means "Not able/wanting to make a decision". */ function _giveRuling(uint _disputeID, uint _ruling) internal { DisputeStruct storage dispute = disputes[_disputeID]; require(_ruling <= dispute.choices, "Invalid ruling."); require(dispute.status != DisputeStatus.Solved, "The dispute must not be solved already."); dispute.ruling = _ruling; dispute.status = DisputeStatus.Solved; msg.sender.send(dispute.fee); // Avoid blocking. dispute.arbitrated.rule(_disputeID,_ruling); } /** @dev Give a ruling. UNTRUSTED. * @param _disputeID ID of the dispute to rule. * @param _ruling Ruling given by the arbitrator. Note that 0 means "Not able/wanting to make a decision". */ function giveRuling(uint _disputeID, uint _ruling) public onlyOwner { return _giveRuling(_disputeID, _ruling); } /** @dev Return the status of a dispute. * @param _disputeID ID of the dispute to rule. * @return status The status of the dispute. */ function disputeStatus(uint _disputeID) public view returns(DisputeStatus status) { return disputes[_disputeID].status; } /** @dev Return the ruling of a dispute. * @param _disputeID ID of the dispute to rule. * @return ruling The ruling which would or has been given. */ function currentRuling(uint _disputeID) public view returns(uint ruling) { return disputes[_disputeID].ruling; } } /** * @title AppealableArbitrator * @author Enrique Piqueras - <[email protected]> * @dev A centralized arbitrator that can be appealed. */ contract AppealableArbitrator is CentralizedArbitrator, Arbitrable { /* Structs */ struct AppealDispute { uint rulingTime; Arbitrator arbitrator; uint appealDisputeID; } /* Storage */ uint public timeOut; mapping(uint => AppealDispute) public appealDisputes; mapping(uint => uint) public appealDisputeIDsToDisputeIDs; /* Constructor */ /** @dev Constructs the `AppealableArbitrator` contract. * @param _arbitrationPrice The amount to be paid for arbitration. * @param _arbitrator The back up arbitrator. * @param _arbitratorExtraData Not used by this contract. * @param _timeOut The time out for the appeal period. */ constructor( uint _arbitrationPrice, Arbitrator _arbitrator, bytes _arbitratorExtraData, uint _timeOut ) public CentralizedArbitrator(_arbitrationPrice) Arbitrable(_arbitrator, _arbitratorExtraData) { timeOut = _timeOut; } /* External */ /** @dev Changes the back up arbitrator. * @param _arbitrator The new back up arbitrator. */ function changeArbitrator(Arbitrator _arbitrator) external onlyOwner { arbitrator = _arbitrator; } /** @dev Changes the time out. * @param _timeOut The new time out. */ function changeTimeOut(uint _timeOut) external onlyOwner { timeOut = _timeOut; } /* External Views */ /** @dev Gets the specified dispute's latest appeal ID. * @param _disputeID The ID of the dispute. */ function getAppealDisputeID(uint _disputeID) external view returns(uint disputeID) { if (appealDisputes[_disputeID].arbitrator != Arbitrator(address(0))) disputeID = AppealableArbitrator(appealDisputes[_disputeID].arbitrator).getAppealDisputeID(appealDisputes[_disputeID].appealDisputeID); else disputeID = _disputeID; } /* Public */ /** @dev Appeals a ruling. * @param _disputeID The ID of the dispute. * @param _extraData Additional info about the appeal. */ function appeal(uint _disputeID, bytes _extraData) public payable requireAppealFee(_disputeID, _extraData) { super.appeal(_disputeID, _extraData); if (appealDisputes[_disputeID].arbitrator != Arbitrator(address(0))) appealDisputes[_disputeID].arbitrator.appeal.value(msg.value)(appealDisputes[_disputeID].appealDisputeID, _extraData); else { appealDisputes[_disputeID].arbitrator = arbitrator; appealDisputes[_disputeID].appealDisputeID = arbitrator.createDispute.value(msg.value)(disputes[_disputeID].choices, _extraData); appealDisputeIDsToDisputeIDs[appealDisputes[_disputeID].appealDisputeID] = _disputeID; } } /** @dev Gives a ruling. * @param _disputeID The ID of the dispute. * @param _ruling The ruling. */ function giveRuling(uint _disputeID, uint _ruling) public { require(disputes[_disputeID].status != DisputeStatus.Solved, "The specified dispute is already resolved."); if (appealDisputes[_disputeID].arbitrator != Arbitrator(address(0))) { require(Arbitrator(msg.sender) == appealDisputes[_disputeID].arbitrator, "Appealed disputes must be ruled by their back up arbitrator."); super._giveRuling(_disputeID, _ruling); } else { require(msg.sender == owner, "Not appealed disputes must be ruled by the owner."); if (disputes[_disputeID].status == DisputeStatus.Appealable) { if (now - appealDisputes[_disputeID].rulingTime > timeOut) super._giveRuling(_disputeID, disputes[_disputeID].ruling); else revert("Time out time has not passed yet."); } else { disputes[_disputeID].ruling = _ruling; disputes[_disputeID].status = DisputeStatus.Appealable; appealDisputes[_disputeID].rulingTime = now; emit AppealPossible(_disputeID, disputes[_disputeID].arbitrated); } } } /* Public Views */ /** @dev Gets the cost of appeal for the specified dispute. * @param _disputeID The ID of the dispute. * @param _extraData Additional info about the appeal. * @return The cost of the appeal. */ function appealCost(uint _disputeID, bytes _extraData) public view returns(uint cost) { if (appealDisputes[_disputeID].arbitrator != Arbitrator(address(0))) cost = appealDisputes[_disputeID].arbitrator.appealCost(appealDisputes[_disputeID].appealDisputeID, _extraData); else if (disputes[_disputeID].status == DisputeStatus.Appealable) cost = arbitrator.arbitrationCost(_extraData); else cost = NOT_PAYABLE_VALUE; } /** @dev Gets the status of the specified dispute. * @param _disputeID The ID of the dispute. * @return The status. */ function disputeStatus(uint _disputeID) public view returns(DisputeStatus status) { if (appealDisputes[_disputeID].arbitrator != Arbitrator(address(0))) status = appealDisputes[_disputeID].arbitrator.disputeStatus(appealDisputes[_disputeID].appealDisputeID); else status = disputes[_disputeID].status; } /* Internal */ /** @dev Executes the ruling of the specified dispute. * @param _disputeID The ID of the dispute. * @param _ruling The ruling. */ function executeRuling(uint _disputeID, uint _ruling) internal { require( appealDisputes[appealDisputeIDsToDisputeIDs[_disputeID]].arbitrator != Arbitrator(address(0)), "The dispute must have been appealed." ); giveRuling(appealDisputeIDsToDisputeIDs[_disputeID], _ruling); } } contract MultipleArbitrableTransaction is IArbitrable { // **************************** // // * Contract variables * // // **************************** // uint8 constant AMOUNT_OF_CHOICES = 2; uint8 constant SENDER_WINS = 1; uint8 constant RECEIVER_WINS = 2; enum Party {Sender, Receiver} enum Status {NoDispute, WaitingSender, WaitingReceiver, DisputeCreated, Resolved} struct Transaction { address sender; address receiver; uint amount; uint timeoutPayment; // Time in seconds after which the transaction can be automatically executed if not disputed. uint disputeId; // If dispute exists, the ID of the dispute. uint senderFee; // Total fees paid by the sender. uint receiverFee; // Total fees paid by the receiver. uint lastInteraction; // Last interaction for the dispute procedure. Status status; } Transaction[] public transactions; bytes public arbitratorExtraData; // Extra data to set up the arbitration. Arbitrator public arbitrator; // Address of the arbitrator contract. uint public feeTimeout; // Time in seconds a party can take to pay arbitration fees before being considered unresponding and lose the dispute. mapping (uint => uint) public disputeIDtoTransactionID; // One-to-one relationship between the dispute and the transaction. // **************************** // // * Events * // // **************************** // /** @dev To be emitted when meta-evidence is submitted. * @param _metaEvidenceID Unique identifier of meta-evidence. Should be the `transactionID`. * @param _evidence A link to the meta-evidence JSON that follows the ERC 1497 Evidence standard (https://github.com/ethereum/EIPs/issues/1497). */ event MetaEvidence(uint indexed _metaEvidenceID, string _evidence); /** @dev To be emitted when a party pays or reimburses the other. * @param _transactionID The index of the transaction. * @param _amount The amount paid. * @param _party The party that paid. */ event Payment(uint indexed _transactionID, uint _amount, address _party); /** @dev Indicate that a party has to pay a fee or would otherwise be considered as losing. * @param _transactionID The index of the transaction. * @param _party The party who has to pay. */ event HasToPayFee(uint indexed _transactionID, Party _party); /** @dev To be raised when evidence is submitted. Should point to the resource (evidences are not to be stored on chain due to gas considerations). * @param _arbitrator The arbitrator of the contract. * @param _evidenceGroupID Unique identifier of the evidence group the evidence belongs to. * @param _party The address of the party submitting the evidence. Note that 0 is kept for evidences not submitted by any party. * @param _evidence A link to an evidence JSON that follows the ERC 1497 Evidence standard (https://github.com/ethereum/EIPs/issues/1497). */ event Evidence(Arbitrator indexed _arbitrator, uint indexed _evidenceGroupID, address indexed _party, string _evidence); /** @dev To be emitted when a dispute is created to link the correct meta-evidence to the disputeID. * @param _arbitrator The arbitrator of the contract. * @param _disputeID ID of the dispute in the Arbitrator contract. * @param _metaEvidenceID Unique identifier of meta-evidence. Should be the transactionID. * @param _evidenceGroupID Unique identifier of the evidence group that is linked to this dispute. */ event Dispute(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _metaEvidenceID, uint _evidenceGroupID); /** @dev To be raised when a ruling is given. * @param _arbitrator The arbitrator giving the ruling. * @param _disputeID ID of the dispute in the Arbitrator contract. * @param _ruling The ruling which was given. */ event Ruling(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _ruling); // **************************** // // * Arbitrable functions * // // * Modifying the state * // // **************************** // /** @dev Constructor. * @param _arbitrator The arbitrator of the contract. * @param _arbitratorExtraData Extra data for the arbitrator. * @param _feeTimeout Arbitration fee timeout for the parties. */ constructor ( Arbitrator _arbitrator, bytes _arbitratorExtraData, uint _feeTimeout ) public { arbitrator = _arbitrator; arbitratorExtraData = _arbitratorExtraData; feeTimeout = _feeTimeout; } /** @dev Create a transaction. * @param _timeoutPayment Time after which a party can automatically execute the arbitrable transaction. * @param _receiver The recipient of the transaction. * @param _metaEvidence Link to the meta-evidence. * @return transactionID The index of the transaction. */ function createTransaction( uint _timeoutPayment, address _receiver, string _metaEvidence ) public payable returns (uint transactionID) { transactions.push(Transaction({ sender: msg.sender, receiver: _receiver, amount: msg.value, timeoutPayment: _timeoutPayment, disputeId: 0, senderFee: 0, receiverFee: 0, lastInteraction: now, status: Status.NoDispute })); emit MetaEvidence(transactions.length - 1, _metaEvidence); return transactions.length - 1; } /** @dev Pay receiver. To be called if the good or service is provided. * @param _transactionID The index of the transaction. * @param _amount Amount to pay in wei. */ function pay(uint _transactionID, uint _amount) public { Transaction storage transaction = transactions[_transactionID]; require(transaction.sender == msg.sender, "The caller must be the sender."); require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed."); require(_amount <= transaction.amount, "The amount paid has to be less than or equal to the transaction."); transaction.receiver.transfer(_amount); transaction.amount -= _amount; emit Payment(_transactionID, _amount, msg.sender); } /** @dev Reimburse sender. To be called if the good or service can't be fully provided. * @param _transactionID The index of the transaction. * @param _amountReimbursed Amount to reimburse in wei. */ function reimburse(uint _transactionID, uint _amountReimbursed) public { Transaction storage transaction = transactions[_transactionID]; require(transaction.receiver == msg.sender, "The caller must be the receiver."); require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed."); require(_amountReimbursed <= transaction.amount, "The amount reimbursed has to be less or equal than the transaction."); transaction.sender.transfer(_amountReimbursed); transaction.amount -= _amountReimbursed; emit Payment(_transactionID, _amountReimbursed, msg.sender); } /** @dev Transfer the transaction's amount to the receiver if the timeout has passed. * @param _transactionID The index of the transaction. */ function executeTransaction(uint _transactionID) public { Transaction storage transaction = transactions[_transactionID]; require(now - transaction.lastInteraction >= transaction.timeoutPayment, "The timeout has not passed yet."); require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed."); transaction.receiver.transfer(transaction.amount); transaction.amount = 0; transaction.status = Status.Resolved; } /** @dev Reimburse sender if receiver fails to pay the fee. * @param _transactionID The index of the transaction. */ function timeOutBySender(uint _transactionID) public { Transaction storage transaction = transactions[_transactionID]; require(transaction.status == Status.WaitingReceiver, "The transaction is not waiting on the receiver."); require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet."); executeRuling(_transactionID, SENDER_WINS); } /** @dev Pay receiver if sender fails to pay the fee. * @param _transactionID The index of the transaction. */ function timeOutByReceiver(uint _transactionID) public { Transaction storage transaction = transactions[_transactionID]; require(transaction.status == Status.WaitingSender, "The transaction is not waiting on the sender."); require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet."); executeRuling(_transactionID, RECEIVER_WINS); } /** @dev Pay the arbitration fee to raise a dispute. To be called by the sender. UNTRUSTED. * Note that the arbitrator can have createDispute throw, which will make this function throw and therefore lead to a party being timed-out. * This is not a vulnerability as the arbitrator can rule in favor of one party anyway. * @param _transactionID The index of the transaction. */ function payArbitrationFeeBySender(uint _transactionID) public payable { Transaction storage transaction = transactions[_transactionID]; uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData); require(transaction.status < Status.DisputeCreated, "Dispute has already been created or because the transaction has been executed."); require(msg.sender == transaction.sender, "The caller must be the sender."); transaction.senderFee += msg.value; // Require that the total pay at least the arbitration cost. require(transaction.senderFee >= arbitrationCost, "The sender fee must cover arbitration costs."); transaction.lastInteraction = now; // The receiver still has to pay. This can also happen if he has paid, but arbitrationCost has increased. if (transaction.receiverFee < arbitrationCost) { transaction.status = Status.WaitingReceiver; emit HasToPayFee(_transactionID, Party.Receiver); } else { // The receiver has also paid the fee. We create the dispute. raiseDispute(_transactionID, arbitrationCost); } } /** @dev Pay the arbitration fee to raise a dispute. To be called by the receiver. UNTRUSTED. * Note that this function mirrors payArbitrationFeeBySender. * @param _transactionID The index of the transaction. */ function payArbitrationFeeByReceiver(uint _transactionID) public payable { Transaction storage transaction = transactions[_transactionID]; uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData); require(transaction.status < Status.DisputeCreated, "Dispute has already been created or because the transaction has been executed."); require(msg.sender == transaction.receiver, "The caller must be the receiver."); transaction.receiverFee += msg.value; // Require that the total paid to be at least the arbitration cost. require(transaction.receiverFee >= arbitrationCost, "The receiver fee must cover arbitration costs."); transaction.lastInteraction = now; // The sender still has to pay. This can also happen if he has paid, but arbitrationCost has increased. if (transaction.senderFee < arbitrationCost) { transaction.status = Status.WaitingSender; emit HasToPayFee(_transactionID, Party.Sender); } else { // The sender has also paid the fee. We create the dispute. raiseDispute(_transactionID, arbitrationCost); } } /** @dev Create a dispute. UNTRUSTED. * @param _transactionID The index of the transaction. * @param _arbitrationCost Amount to pay the arbitrator. */ function raiseDispute(uint _transactionID, uint _arbitrationCost) internal { Transaction storage transaction = transactions[_transactionID]; transaction.status = Status.DisputeCreated; transaction.disputeId = arbitrator.createDispute.value(_arbitrationCost)(AMOUNT_OF_CHOICES, arbitratorExtraData); disputeIDtoTransactionID[transaction.disputeId] = _transactionID; emit Dispute(arbitrator, transaction.disputeId, _transactionID, _transactionID); // Refund sender if it overpaid. if (transaction.senderFee > _arbitrationCost) { uint extraFeeSender = transaction.senderFee - _arbitrationCost; transaction.senderFee = _arbitrationCost; transaction.sender.send(extraFeeSender); } // Refund receiver if it overpaid. if (transaction.receiverFee > _arbitrationCost) { uint extraFeeReceiver = transaction.receiverFee - _arbitrationCost; transaction.receiverFee = _arbitrationCost; transaction.receiver.send(extraFeeReceiver); } } /** @dev Submit a reference to evidence. EVENT. * @param _transactionID The index of the transaction. * @param _evidence A link to an evidence using its URI. */ function submitEvidence(uint _transactionID, string _evidence) public { Transaction storage transaction = transactions[_transactionID]; require( msg.sender == transaction.sender || msg.sender == transaction.receiver, "The caller must be the sender or the receiver." ); require( transaction.status < Status.Resolved, "Must not send evidence if the dispute is resolved." ); emit Evidence(arbitrator, _transactionID, msg.sender, _evidence); } /** @dev Appeal an appealable ruling. * Transfer the funds to the arbitrator. * Note that no checks are required as the checks are done by the arbitrator. * @param _transactionID The index of the transaction. */ function appeal(uint _transactionID) public payable { Transaction storage transaction = transactions[_transactionID]; arbitrator.appeal.value(msg.value)(transaction.disputeId, arbitratorExtraData); } /** @dev Give a ruling for a dispute. Must be called by the arbitrator. * The purpose of this function is to ensure that the address calling it has the right to rule on the contract. * @param _disputeID ID of the dispute in the Arbitrator contract. * @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision". */ function rule(uint _disputeID, uint _ruling) public { uint transactionID = disputeIDtoTransactionID[_disputeID]; Transaction storage transaction = transactions[transactionID]; require(msg.sender == address(arbitrator), "The caller must be the arbitrator."); require(transaction.status == Status.DisputeCreated, "The dispute has already been resolved."); emit Ruling(Arbitrator(msg.sender), _disputeID, _ruling); executeRuling(transactionID, _ruling); } /** @dev Execute a ruling of a dispute. It reimburses the fee to the winning party. * @param _transactionID The index of the transaction. * @param _ruling Ruling given by the arbitrator. 1 : Reimburse the receiver. 2 : Pay the sender. */ function executeRuling(uint _transactionID, uint _ruling) internal { Transaction storage transaction = transactions[_transactionID]; require(_ruling <= AMOUNT_OF_CHOICES, "Invalid ruling."); // Give the arbitration fee back. // Note that we use send to prevent a party from blocking the execution. if (_ruling == SENDER_WINS) { transaction.sender.send(transaction.senderFee + transaction.amount); } else if (_ruling == RECEIVER_WINS) { transaction.receiver.send(transaction.receiverFee + transaction.amount); } else { uint split_amount = (transaction.senderFee + transaction.amount) / 2; transaction.sender.send(split_amount); transaction.receiver.send(split_amount); } transaction.amount = 0; transaction.senderFee = 0; transaction.receiverFee = 0; transaction.status = Status.Resolved; } // **************************** // // * Constant getters * // // **************************** // /** @dev Getter to know the count of transactions. * @return countTransactions The count of transactions. */ function getCountTransactions() public view returns (uint countTransactions) { return transactions.length; } /** @dev Get IDs for transactions where the specified address is the receiver and/or the sender. * This function must be used by the UI and not by other smart contracts. * Note that the complexity is O(t), where t is amount of arbitrable transactions. * @param _address The specified address. * @return transactionIDs The transaction IDs. */ function getTransactionIDsByAddress(address _address) public view returns (uint[] transactionIDs) { uint count = 0; for (uint i = 0; i < transactions.length; i++) { if (transactions[i].sender == _address || transactions[i].receiver == _address) count++; } transactionIDs = new uint[](count); count = 0; for (uint j = 0; j < transactions.length; j++) { if (transactions[j].sender == _address || transactions[j].receiver == _address) transactionIDs[count++] = j; } } }