Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled diff

Created Diff never expires
55 removals
374 lines
79 additions
398 lines
/**
/**
* @authors: [@eburgos, @n1c01a5]
* @authors: [@n1c01a5, @hellwolf, @satello]
* @reviewers: [@unknownunknown1, @clesaege*, @ferittuncer]
* @reviewers: [@ferittuncer*, @unknownunknown1, @mtsalenc, @clesaege]
* @auditors: []
* @auditors: []
* @bounties: []
* @bounties: []
* @deployments: []
* @deployments: []
*/
*/


/** @title Multiple Arbitrable ERC20 Token Transaction
* This is a contract for multiple arbitrated token transactions which can be reversed by an arbitrator.
* This can be used for buying goods, services and for paying freelancers.
* Parties are identified as "sender" and "receiver".
*/

pragma solidity ^0.4.24;
pragma solidity ^0.4.24;


import "./Arbitrator.sol";
import "./Arbitrator.sol";
import "./IArbitrable.sol";
import "./IArbitrable.sol";


contract MultipleArbitrableTransaction is IArbitrable {
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

contract MultipleArbitrableTokenTransaction is IArbitrable {


// **************************** //
// **************************** //
// * Contract variables * //
// * Contract variables * //
// **************************** //
// **************************** //


uint8 constant AMOUNT_OF_CHOICES = 2;
uint8 constant AMOUNT_OF_CHOICES = 2;
uint8 constant SENDER_WINS = 1;
uint8 constant RECEIVER_WINS = 2;


enum Party {Sender, Receiver}
enum Party {Sender, Receiver}
enum Status {NoDispute, WaitingSender, WaitingReceiver, DisputeCreated, Resolved}
enum Status {NoDispute, WaitingSender, WaitingReceiver, DisputeCreated, Resolved}
enum RulingOptions {NoRuling, SenderWins, ReceiverWins}


struct Transaction {
struct Transaction {
address sender;
address sender;
address receiver;
address receiver;
uint amount;
uint amount;
ERC20 token;
uint timeoutPayment; // Time in seconds after which the transaction can be automatically executed if not disputed.
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 disputeId; // If dispute exists, the ID of the dispute.
uint senderFee; // Total fees paid by the sender.
uint senderFee; // Total fees paid by the sender.
uint receiverFee; // Total fees paid by the receiver.
uint receiverFee; // Total fees paid by the receiver.
uint lastInteraction; // Last interaction for the dispute procedure.
uint lastInteraction; // Last interaction for the dispute procedure.
Status status;
Status status;
}
}


Transaction[] public transactions;
Transaction[] public transactions;
Arbitrator public arbitrator; // Address of the arbitrator contract.
bytes public arbitratorExtraData; // Extra data to set up the arbitration.
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.
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;
mapping (uint => uint) public disputeIDtoTransactionID; // One-to-one relationship between the dispute and the transaction.


// **************************** //
// **************************** //
// * Events * //
// * Events * //
// **************************** //
// **************************** //


/** @dev To be emitted when a party pays or reimburses the other.
/** @dev To be emitted when a party pays or reimburses the other.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _amount The amount paid.
* @param _amount The amount paid.
* @param _party The party that paid.
* @param _party The party that paid.
*/
*/
event Payment(uint indexed _transactionID, uint _amount, address _party);
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.
/** @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 _transactionID The index of the transaction.
* @param _party The party who has to pay.
* @param _party The party who has to pay.
*/
*/
event HasToPayFee(uint indexed _transactionID, Party _party);
event HasToPayFee(uint indexed _transactionID, Party _party);


/** @dev To be raised when a ruling is given.
/** @dev Emitted when the final ruling of a dispute is given by the arbitrator.
* @param _arbitrator The arbitrator giving the ruling.
* @param _arbitrator The arbitrator giving the ruling.
* @param _disputeID ID of the dispute in the Arbitrator contract.
* @param _disputeID ID of the dispute in the Arbitrator contract.
* @param _ruling The ruling which was given.
* @param _ruling The ruling which was given.
*/
*/
event Ruling(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _ruling);
event Ruling(Arbitrator indexed _arbitrator, uint indexed _disputeID, uint _ruling);


// **************************** //
// **************************** //
// * Arbitrable functions * //
// * Arbitrable functions * //
// * Modifying the state * //
// * Modifying the state * //
// **************************** //
// **************************** //


/** @dev Constructor.
/** @dev Constructor.
* @param _arbitrator The arbitrator of the contract.
* @param _arbitrator The arbitrator of the contract.
* @param _arbitratorExtraData Extra data for the arbitrator.
* @param _arbitratorExtraData Extra data for the arbitrator.
* @param _feeTimeout Arbitration fee timeout for the parties.
* @param _feeTimeout Arbitration fee timeout for the parties.
*/
*/
constructor (
constructor (
Arbitrator _arbitrator,
Arbitrator _arbitrator,
bytes _arbitratorExtraData,
bytes _arbitratorExtraData,
uint _feeTimeout
uint _feeTimeout
) public {
) public {
arbitrator = _arbitrator;
arbitrator = _arbitrator;
arbitratorExtraData = _arbitratorExtraData;
arbitratorExtraData = _arbitratorExtraData;
feeTimeout = _feeTimeout;
feeTimeout = _feeTimeout;
}
}


/** @dev Create a transaction.
/** @dev Create a transaction. UNTRUSTED.
* @param _timeoutPayment Time after which a party can automatically execute the arbitrable transaction.
* @param _amount The amount of tokens in this transaction.
* @param _token The ERC20 token contract.
* @param _timeoutPayment Time after which a party automatically loses a dispute.
* @param _receiver The recipient of the transaction.
* @param _receiver The recipient of the transaction.
* @param _metaEvidence Link to the meta-evidence.
* @param _metaEvidence Link to the meta-evidence.
* @return transactionID The index of the transaction.
* @return The index of the transaction.
*/
*/
function createTransaction(
function createTransaction(
uint _amount,
ERC20 _token,
uint _timeoutPayment,
uint _timeoutPayment,
address _receiver,
address _receiver,
string _metaEvidence
string _metaEvidence
) public payable returns (uint transactionID) {
) public returns (uint transactionIndex) {
// Transfers token from sender wallet to contract.
require(_token.transferFrom(msg.sender, address(this), _amount), "Sender does not have enough approved funds.");

transactions.push(Transaction({
transactions.push(Transaction({
sender: msg.sender,
sender: msg.sender,
receiver: _receiver,
receiver: _receiver,
amount: msg.value,
amount: _amount,
token: _token,
timeoutPayment: _timeoutPayment,
timeoutPayment: _timeoutPayment,
disputeId: 0,
disputeId: 0,
senderFee: 0,
senderFee: 0,
receiverFee: 0,
receiverFee: 0,
lastInteraction: now,
lastInteraction: now,
status: Status.NoDispute
status: Status.NoDispute
}));
}));
emit MetaEvidence(transactions.length - 1, _metaEvidence);
emit MetaEvidence(transactions.length - 1, _metaEvidence);


return transactions.length - 1;
return transactions.length - 1;
}
}


/** @dev Pay receiver. To be called if the good or service is provided.
/** @dev Pay receiver. To be called if the good or service is provided. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _amount Amount to pay in wei.
* @param _amount Amount to pay in tokens.
*/
*/
function pay(uint _transactionID, uint _amount) public {
function pay(uint _transactionID, uint _amount) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(transaction.sender == msg.sender, "The caller must be the sender.");
require(transaction.sender == msg.sender, "The caller must be the sender.");
require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed.");
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.");
require(_amount <= transaction.amount, "The amount paid has to be less or equal than the transaction.");


transaction.receiver.transfer(_amount);
transaction.amount -= _amount;
transaction.amount -= _amount;
require(transaction.token.transfer(transaction.receiver, _amount), "The `transfer` function must not fail.");
emit Payment(_transactionID, _amount, msg.sender);
emit Payment(_transactionID, _amount, msg.sender);
}
}


/** @dev Reimburse sender. To be called if the good or service can't be fully provided.
/** @dev Reimburse sender. To be called if the good or service can't be fully provided. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _amountReimbursed Amount to reimburse in wei.
* @param _amountReimbursed Amount to reimburse in tokens.
*/
*/
function reimburse(uint _transactionID, uint _amountReimbursed) public {
function reimburse(uint _transactionID, uint _amountReimbursed) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(transaction.receiver == msg.sender, "The caller must be the receiver.");
require(transaction.receiver == msg.sender, "The caller must be the receiver.");
require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed.");
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.");
require(_amountReimbursed <= transaction.amount, "The amount reimbursed has to be less or equal than the transaction.");


transaction.sender.transfer(_amountReimbursed);
transaction.amount -= _amountReimbursed;
transaction.amount -= _amountReimbursed;
require(transaction.token.transfer(transaction.sender, _amountReimbursed), "The `transfer` function must not fail.");
emit Payment(_transactionID, _amountReimbursed, msg.sender);
emit Payment(_transactionID, _amountReimbursed, msg.sender);
}
}


/** @dev Transfer the transaction's amount to the receiver if the timeout has passed.
/** @dev Transfer the transaction's amount to the receiver if the timeout has passed. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function executeTransaction(uint _transactionID) public {
function executeTransaction(uint _transactionID) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(now - transaction.lastInteraction >= transaction.timeoutPayment, "The timeout has not passed yet.");
require(now - transaction.lastInteraction >= transaction.timeoutPayment, "The timeout has not passed yet.");
require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed.");
require(transaction.status == Status.NoDispute, "The transaction shouldn't be disputed.");


transaction.receiver.transfer(transaction.amount);
uint amount = transaction.amount;
transaction.amount = 0;
transaction.amount = 0;


transaction.status = Status.Resolved;
transaction.status = Status.Resolved;

require(transaction.token.transfer(transaction.receiver, amount), "The `transfer` function must not fail.");
}
}


/** @dev Reimburse sender if receiver fails to pay the fee.
/** @dev Reimburse sender if receiver fails to pay the fee. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function timeOutBySender(uint _transactionID) public {
function timeOutBySender(uint _transactionID) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(transaction.status == Status.WaitingReceiver, "The transaction is not waiting on the receiver.");
require(transaction.status == Status.WaitingReceiver, "The transaction is not waiting on the receiver.");
require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet.");
require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet.");


if (transaction.receiverFee != 0) {
if (transaction.receiverFee != 0) {
transaction.receiver.send(transaction.receiverFee);
transaction.receiver.send(transaction.receiverFee);
transaction.receiverFee = 0;
transaction.receiverFee = 0;
}
}
executeRuling(_transactionID, SENDER_WINS);
executeRuling(_transactionID, uint(RulingOptions.SenderWins));
}
}


/** @dev Pay receiver if sender fails to pay the fee.
/** @dev Pay receiver if sender fails to pay the fee. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function timeOutByReceiver(uint _transactionID) public {
function timeOutByReceiver(uint _transactionID) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(transaction.status == Status.WaitingSender, "The transaction is not waiting on the sender.");
require(transaction.status == Status.WaitingSender, "The transaction is not waiting on the sender.");
require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet.");
require(now - transaction.lastInteraction >= feeTimeout, "Timeout time has not passed yet.");


if (transaction.senderFee != 0) {
if (transaction.senderFee != 0) {
transaction.sender.send(transaction.senderFee);
transaction.sender.send(transaction.senderFee);
transaction.senderFee = 0;
transaction.senderFee = 0;
}
}
executeRuling(_transactionID, RECEIVER_WINS);
executeRuling(_transactionID, uint(RulingOptions.ReceiverWins));
}
}


/** @dev Pay the arbitration fee to raise a dispute. To be called by the sender. UNTRUSTED.
/** @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.
* 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.
* This is not a vulnerability as the arbitrator can rule in favor of one party anyway.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function payArbitrationFeeBySender(uint _transactionID) public payable {
function payArbitrationFeeBySender(uint _transactionID) public payable {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);

require(transaction.status < Status.DisputeCreated, "Dispute has already been created.");
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.");
require(msg.sender == transaction.sender, "The caller must be the sender.");


transaction.senderFee += msg.value;
transaction.senderFee += msg.value;
// Require that the total pay at least the arbitration cost.
// Require that the total paid to be at least the arbitration cost.
require(transaction.senderFee >= arbitrationCost, "The sender fee must cover arbitration costs.");
require(transaction.senderFee >= arbitrationCost, "The sender fee must cover arbitration costs.");


transaction.lastInteraction = now;
transaction.lastInteraction = now;

// The receiver still has to pay. This can also happen if he has paid, but `arbitrationCost` has increased.
// The receiver still has to pay. This can also happen if he has paid, but arbitrationCost has increased.
if (transaction.receiverFee < arbitrationCost) {
if (transaction.receiverFee < arbitrationCost) {
transaction.status = Status.WaitingReceiver;
transaction.status = Status.WaitingReceiver;
emit HasToPayFee(_transactionID, Party.Receiver);
emit HasToPayFee(_transactionID, Party.Receiver);
} else { // The receiver has also paid the fee. We create the dispute.
} else { // The receiver has also paid the fee. We create the dispute.
raiseDispute(_transactionID, arbitrationCost);
raiseDispute(_transactionID, arbitrationCost);
}
}
}
}


/** @dev Pay the arbitration fee to raise a dispute. To be called by the receiver. UNTRUSTED.
/** @dev Pay the arbitration fee to raise a dispute. To be called by the receiver. UNTRUSTED.
* Note that this function mirrors payArbitrationFeeBySender.
* Note that this function mirrors payArbitrationFeeBySender.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function payArbitrationFeeByReceiver(uint _transactionID) public payable {
function payArbitrationFeeByReceiver(uint _transactionID) public payable {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);

require(transaction.status < Status.DisputeCreated, "Dispute has already been created.");
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.");
require(msg.sender == transaction.receiver, "The caller must be the receiver.");


transaction.receiverFee += msg.value;
transaction.receiverFee += msg.value;
// Require that the total paid to be at least the arbitration cost.
// Require that the total paid to be at least the arbitration cost.
require(transaction.receiverFee >= arbitrationCost, "The receiver fee must cover arbitration costs.");
require(transaction.receiverFee >= arbitrationCost, "The receiver fee must cover arbitration costs.");


transaction.lastInteraction = now;
transaction.lastInteraction = now;
// The sender still has to pay. This can also happen if he has paid, but arbitrationCost has increased.
// The sender still has to pay. This can also happen if he has paid, but arbitrationCost has increased.
if (transaction.senderFee < arbitrationCost) {
if (transaction.senderFee < arbitrationCost) {
transaction.status = Status.WaitingSender;
transaction.status = Status.WaitingSender;
emit HasToPayFee(_transactionID, Party.Sender);
emit HasToPayFee(_transactionID, Party.Sender);
} else { // The sender has also paid the fee. We create the dispute.
} else { // The sender has also paid the fee. We create the dispute.
raiseDispute(_transactionID, arbitrationCost);
raiseDispute(_transactionID, arbitrationCost);
}
}
}
}


/** @dev Create a dispute. UNTRUSTED.
/** @dev Create a dispute. UNTRUSTED.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _arbitrationCost Amount to pay the arbitrator.
* @param _arbitrationCost Amount to pay the arbitrator.
*/
*/
function raiseDispute(uint _transactionID, uint _arbitrationCost) internal {
function raiseDispute(uint _transactionID, uint _arbitrationCost) internal {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
transaction.status = Status.DisputeCreated;
transaction.status = Status.DisputeCreated;
transaction.disputeId = arbitrator.createDispute.value(_arbitrationCost)(AMOUNT_OF_CHOICES, arbitratorExtraData);
transaction.disputeId = arbitrator.createDispute.value(_arbitrationCost)(AMOUNT_OF_CHOICES, arbitratorExtraData);
disputeIDtoTransactionID[transaction.disputeId] = _transactionID;
disputeIDtoTransactionID[transaction.disputeId] = _transactionID;
emit Dispute(arbitrator, transaction.disputeId, _transactionID, _transactionID);
emit Dispute(arbitrator, transaction.disputeId, _transactionID, _transactionID);


// Refund sender if it overpaid.
// Refund sender if it overpaid.
if (transaction.senderFee > _arbitrationCost) {
if (transaction.senderFee > _arbitrationCost) {
uint extraFeeSender = transaction.senderFee - _arbitrationCost;
uint extraFeeSender = transaction.senderFee - _arbitrationCost;
transaction.senderFee = _arbitrationCost;
transaction.senderFee = _arbitrationCost;
transaction.sender.send(extraFeeSender);
transaction.sender.send(extraFeeSender);
}
}


// Refund receiver if it overpaid.
// Refund receiver if it overpaid.
if (transaction.receiverFee > _arbitrationCost) {
if (transaction.receiverFee > _arbitrationCost) {
uint extraFeeReceiver = transaction.receiverFee - _arbitrationCost;
uint extraFeeReceiver = transaction.receiverFee - _arbitrationCost;
transaction.receiverFee = _arbitrationCost;
transaction.receiverFee = _arbitrationCost;
transaction.receiver.send(extraFeeReceiver);
transaction.receiver.send(extraFeeReceiver);
}
}
}
}


/** @dev Submit a reference to evidence. EVENT.
/** @dev Submit a reference to evidence. EVENT.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _evidence A link to an evidence using its URI.
* @param _evidence A link to an evidence using its URI.
*/
*/
function submitEvidence(uint _transactionID, string _evidence) public {
function submitEvidence(uint _transactionID, string _evidence) public {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(
require(
msg.sender == transaction.sender || msg.sender == transaction.receiver,
msg.sender == transaction.receiver || msg.sender == transaction.sender,
"The caller must be the sender or the receiver."
"The caller must be the receiver or the sender."
);
);
require(
require(
transaction.status < Status.Resolved,
transaction.status < Status.Resolved,
"Must not send evidence if the dispute is resolved."
"Must not send evidence if the dispute is resolved."
);
);


emit Evidence(arbitrator, _transactionID, msg.sender, _evidence);
emit Evidence(arbitrator, _transactionID, msg.sender, _evidence);
}
}


/** @dev Appeal an appealable ruling.
/** @dev Appeal an appealable ruling. UNTRUSTED.
* Transfer the funds to the arbitrator.
* Transfer the funds to the arbitrator.
* Note that no checks are required as the checks are done by the arbitrator.
* Note that no checks are required as the checks are done by the arbitrator.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
*/
*/
function appeal(uint _transactionID) public payable {
function appeal(uint _transactionID) public payable {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];


arbitrator.appeal.value(msg.value)(transaction.disputeId, arbitratorExtraData);
arbitrator.appeal.value(msg.value)(transaction.disputeId, arbitratorExtraData);
}
}


/** @dev Give a ruling for a dispute. Must be called by the arbitrator.
/** @dev Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling.
* The purpose of this function is to ensure that the address calling it has the right to rule on the contract.
* 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 _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".
* @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 {
function rule(uint _disputeID, uint _ruling) public {
uint transactionID = disputeIDtoTransactionID[_disputeID];
uint transactionID = disputeIDtoTransactionID[_disputeID];
Transaction storage transaction = transactions[transactionID];
Transaction storage transaction = transactions[transactionID];
require(msg.sender == address(arbitrator), "The caller must be the arbitrator.");
require(msg.sender == address(arbitrator), "The caller must be the arbitrator.");
require(transaction.status == Status.DisputeCreated, "The dispute has already been resolved.");
require(transaction.status == Status.DisputeCreated, "The dispute has already been resolved.");


emit Ruling(Arbitrator(msg.sender), _disputeID, _ruling);
emit Ruling(Arbitrator(msg.sender), _disputeID, _ruling);


executeRuling(transactionID, _ruling);
executeRuling(transactionID, _ruling);
}
}


/** @dev Execute a ruling of a dispute. It reimburses the fee to the winning party.
/** @dev Execute a ruling of a dispute. It reimburses the fee to the winning party.
* @param _transactionID The index of the transaction.
* @param _transactionID The index of the transaction.
* @param _ruling Ruling given by the arbitrator. 1 : Reimburse the receiver. 2 : Pay the sender.
* @param _ruling Ruling given by the arbitrator. 1: Reimburse the receiver. 2: Pay the sender.
*/
*/
function executeRuling(uint _transactionID, uint _ruling) internal {
function executeRuling(uint _transactionID, uint _ruling) internal {
Transaction storage transaction = transactions[_transactionID];
Transaction storage transaction = transactions[_transactionID];
require(_ruling <= AMOUNT_OF_CHOICES, "Invalid ruling.");
require(_ruling <= AMOUNT_OF_CHOICES, "Invalid ruling.");


// Give the arbitration fee back.
uint amount = transaction.amount;
// Note that we use send to prevent a party from blocking the execution.
uint senderFee = transaction.senderFee;
if (_ruling == SENDER_WINS) {
uint receiverFee = transaction.receiverFee;
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.amount = 0;
transaction.senderFee = 0;
transaction.senderFee = 0;
transaction.receiverFee = 0;
transaction.receiverFee = 0;
transaction.status = Status.Resolved;
transaction.status = Status.Resolved;

// Give the arbitration fee back.
// Note that we use `send` to prevent a party from blocking the execution.
if (_ruling == uint(RulingOptions.SenderWins)) {
transaction.sender.send(senderFee);
require(transaction.token.transfer(transaction.sender, amount), "The `transfer` function must not fail.");
} else if (_ruling == uint(RulingOptions.ReceiverWins)) {
transaction.receiver.send(receiverFee);
require(transaction.token.transfer(transaction.receiver, amount), "The `transfer` function must not fail.");
} else {
// `senderFee` and `receiverFee` are equal to the arbitration cost.
uint splitArbitrationFee = senderFee / 2;
transaction.receiver.send(splitArbitrationFee);
transaction.sender.send(splitArbitrationFee);
// In the case of an uneven token amount, one basic token unit can be burnt.
require(transaction.token.transfer(transaction.receiver, amount / 2), "The `transfer` function must not fail.");
require(transaction.token.transfer(transaction.sender, amount / 2), "The `transfer` function must not fail.");
}
}
}


// **************************** //
// **************************** //
// * Constant getters * //
// * Constant getters * //
// **************************** //
// **************************** //


/** @dev Getter to know the count of transactions.
/** @dev Getter to know the count of transactions.
* @return countTransactions The count of transactions.
* @return countTransactions The count of transactions.
*/
*/
function getCountTransactions() public view returns (uint countTransactions) {
function getCountTransactions() public view returns (uint countTransactions) {
return transactions.length;
return transactions.length;
}
}


/** @dev Get IDs for transactions where the specified address is the receiver and/or the sender.
/** @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.
* 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.
* Note that the complexity is O(t), where t is amount of arbitrable transactions.
* @param _address The specified address.
* @param _address The specified address.
* @return transactionIDs The transaction IDs.
* @return transactionIDs The transaction IDs.
*/
*/
function getTransactionIDsByAddress(address _address) public view returns (uint[] transactionIDs) {
function getTransactionIDsByAddress(address _address) public view returns (uint[] transactionIDs) {
uint count = 0;
uint count = 0;
for (uint i = 0; i < transactions.length; i++) {
for (uint i = 0; i < transactions.length; i++) {
if (transactions[i].sender == _address || transactions[i].receiver == _address)
if (transactions[i].sender == _address || transactions[i].receiver == _address)
count++;
count++;
}
}


transactionIDs = new uint[](count);
transactionIDs = new uint[](count);


count = 0;
count = 0;


for (uint j = 0; j < transactions.length; j++) {
for (uint j = 0; j < transactions.length; j++) {
if (transactions[j].sender == _address || transactions[j].receiver == _address)
if (transactions[j].sender == _address || transactions[j].receiver == _address)
transactionIDs[count++] = j;
transactionIDs[count++] = j;
}
}
}
}
}
}