Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions src/ArbitrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ contract ArbitrationModule is IArbitrationModule
*/

//ensure that escrow is valid
//EXCEPTION: InvalidEscrow
require(polyEscrow.getEscrow(escrowId).id == escrowId, "InvalidEscrow");

//EXCEPTION: Unauthorized
require (_canProposeArbitration(polyEscrow, escrowId, msg.sender), "Unauthorized");

//EXCEPTION: InvalidEscrowState
require(_escrowStateIsValid(polyEscrow, escrowId), "InvalidEscrowState");

//TODO: should there be a limit on number of open arbitration cases?
Expand All @@ -75,12 +78,14 @@ contract ArbitrationModule is IArbitrationModule
proposals[propId].status = ArbitrationStatus.ACTIVE;
proposals[propId].votesAgainst = 0;
proposals[propId].autoExecute = autoExecute;
proposals[propId].proposer = msg.sender;

//TODO: validate the amount (should be realistic and related to amount in escrow)

//record proposer as an automatic yes vote
proposals[propId].votesFor = 1;
proposalVotes[propId][msg.sender] = true;
//record proposer as an automatic yes vote, if proposer is a voter
if (_canVoteArbitration(polyEscrow, escrowId, msg.sender)) {
_voteArbitration(polyEscrow, proposals[propId], true);
}

//raise event
emit ArbitrationProposed(proposals[propId].id, proposals[propId].escrowId, msg.sender);
Expand All @@ -91,21 +96,25 @@ contract ArbitrationModule is IArbitrationModule
// WHO can vote on arbitration? arbiters only

//get the arbitration proposal
//EXCEPTION: InvalidProposal
ArbitrationProposal storage proposal = proposals[proposalId];
require(proposal.id != bytes32(0), "InvalidProposal");

//get the escrow id
bytes32 escrowId = proposal.escrowId;

//ensure that escrowId is valid with escrow
//EXCEPTION: InvalidEscrow
require(polyEscrow.getEscrow(escrowId).id == escrowId, "InvalidEscrow");

//TODO: ensure that escrow is in correct state to be voted on

//validate rights of voter
//EXCEPTION: Unauthorized
require(_canVoteArbitration(polyEscrow, escrowId, msg.sender), "Unauthorized");

//verify that the proposal is in a state in which it can be voted
//EXCEPTION: InvalidProposalState
require(proposal.status == ArbitrationStatus.ACTIVE, "InvalidProposalState");

//record vote
Expand Down Expand Up @@ -137,27 +146,37 @@ contract ArbitrationModule is IArbitrationModule
}

function cancelArbitration(bytes32 proposalId) external virtual {

//TODO: WHO can cancel arbitration?
//TODO: all arbitration on an escrow should be cancelled if the seller takes any action

//get the arbitration proposal
//TODO: this code is repeated alot; can it be put into its own function
//EXCEPTION: InvalidProposal
ArbitrationProposal storage proposal = proposals[proposalId];
require(proposal.id != bytes32(0), "InvalidProposal");

//TODO: can only cancel if status is ACTIVE
//only the proposer can cancel
//EXCEPTION: Unauthorized
require(proposal.proposer == msg.sender, "Unauthorized");

//TODO: all arbitration on an escrow should be cancelled if the seller takes any action

//can only cancel if status is ACTIVE
//EXCEPTION: NotCancellable
require(proposal.status == ArbitrationStatus.ACTIVE, "NotCancellable");

//can only cancel if no votes have been cast
//EXCEPTION: NotCancellable
require(proposal.votesFor == 0 && proposal.votesAgainst == 0, "NotCancellable");

proposal.status = ArbitrationStatus.CANCELED;
}

function executeArbitration(IPolyEscrow polyEscrow, bytes32 proposalId) external virtual {

//get the arbitration proposal
//EXCEPTION: InvalidProposal
ArbitrationProposal storage proposal = proposals[proposalId];
require(proposal.id != bytes32(0), "InvalidProposal");

//proposal must be accepted
//EXCEPTION: InvalidEscrowState
require(proposal.status == ArbitrationStatus.ACCEPTED, "InvalidEscrowState");

//TODO: re-validate the amount (adjust it if necessary)
Expand All @@ -166,6 +185,7 @@ contract ArbitrationModule is IArbitrationModule
bytes32 escrowId = proposal.escrowId;

//ensure that escrowId is valid with escrow
//EXCEPTION: InvalidEscrow
require(polyEscrow.getEscrow(escrowId).id == escrowId, "InvalidEscrow");

//TODO: ensure that escrow is in correct state to have arbitration executed
Expand Down Expand Up @@ -214,4 +234,34 @@ contract ArbitrationModule is IArbitrationModule

//TODO: should also include Pending?
}

function _voteArbitration(IPolyEscrow polyEscrow, ArbitrationProposal storage proposal, bool vote) internal {

//record vote
if (vote) {
proposal.votesFor += 1;
} else {
proposal.votesAgainst += 1;
}
proposalVotes[proposal.id][msg.sender] = vote;

//change the status; are there enough votes to execute?
Escrow memory escrow = polyEscrow.getEscrow(proposal.escrowId);
uint256 arbiterCount = escrow.arbiters.length;
uint8 arbitersRequired = escrow.arbitersRequired;
if (proposal.votesFor >= arbitersRequired) {
proposal.status = ArbitrationStatus.ACCEPTED;

// Auto-execute if autoExecute flag is true
if (proposal.autoExecute) {
_executeArbitration(polyEscrow, proposal);
}
}
else if (proposal.votesAgainst >= (arbiterCount - arbitersRequired)) {
proposal.status = ArbitrationStatus.REJECTED;
}

//raise event
emit VoteRecorded(proposal.id, proposal.escrowId, msg.sender);
}
}
24 changes: 19 additions & 5 deletions src/PolyEscrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
settings = systemSettings;
defaultArbitrationModule = arbitrationModule;

//EXCEPTION: InvalidArbitrationModule
require(address(arbitrationModule) != address(0), "InvalidArbitrationModule");
require(arbitrationModule.isArbitrationModule(), "InvalidArbitrationModule");
}
Expand Down Expand Up @@ -124,7 +125,7 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow

//TODO: validate input more

// EXCEPTION: reject if existing escrow
// EXCEPTION: DuplicateEscrow if existing escrow
require(escrows[input.id].id != input.id, "DuplicateEscrow");

// Store the escrow
Expand All @@ -149,6 +150,7 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
escrow.status = EscrowStatus.Pending;

if (address(input.arbitrationModule) != address(0)) {
//EXCEPTION: InvalidArbitrationModule
require(input.arbitrationModule.isArbitrationModule(), "InvalidArbitrationModule");
escrow.arbitrationModule = input.arbitrationModule;
}
Expand Down Expand Up @@ -186,20 +188,20 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
function placePayment(PaymentInput calldata paymentInput) public payable whenNotPaused {
_validatePaymentInput(paymentInput);

//EXCEPTION: reject if not existing payment
//EXCEPTION: InvalidEscrow if not existing payment
require(escrows[paymentInput.escrowId].id == paymentInput.escrowId, "InvalidEscrow");

//TODO: EXCEPTION: reject if escrow not in the correct state to accept payments

//EXCEPTION: reject the wrong currency
//EXCEPTION: InvalidCurrency reject the wrong currency
require (paymentInput.currency == escrows[paymentInput.escrowId].currency, "InvalidCurrency");

// Handle payment transfer
if (paymentInput.currency == address(0)) {
//EXCEPTION: wrong amount rejected
//EXCEPTION: InvalidAmount amount rejected
require(msg.value == paymentInput.amount, "InvalidAmount");
} else {
//EXCEPTION: failed payment
//EXCEPTION: TokenPaymentFailed failed payment
require(_handleTokenInflow(paymentInput.currency, msg.sender, paymentInput.amount), "TokenPaymentFailed");
}

Expand Down Expand Up @@ -401,6 +403,7 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
* @param input The payment input
*/
function _validatePaymentInput(PaymentInput calldata input) internal pure {
//EXCEPTION: InvalidAmount
require(input.amount > 0, "InvalidAmount");
}

Expand Down Expand Up @@ -429,13 +432,18 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
}

function _validateArbiters(address[] memory arbiters, address payer, address receiver) internal pure {
//EXCEPTION: MaxArbitersExceeded
require(arbiters.length <= MAX_ARBITERS, "MaxArbitersExceeded");

for (uint256 i = 0; i < arbiters.length; i++) {
//EXCEPTION: InvalidArbiter
require(arbiters[i] != address(0), "InvalidArbiter");
for (uint256 j = i + 1; j < arbiters.length; j++) {
//EXCEPTION: DuplicateArbiter
require(arbiters[i] != arbiters[j], "DuplicateArbiter");
}

//EXCEPTION: InvalidArbiter
require(arbiters[i] != payer, "InvalidArbiter");
require(arbiters[i] != receiver, "InvalidArbiter");
}
Expand All @@ -456,11 +464,13 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
function _refund(bytes32 escrowId, uint256 amount) internal {
Escrow storage escrow = escrows[escrowId];

//EXCEPTION: AlreadyReleased
require(escrow.released == false, "AlreadyReleased");

uint256 activeAmount = _getEscrowAmountRemaining(escrow);

if (amount > activeAmount)
//EXCEPTION: AmountExceeded
revert("AmountExceeded");

//transfer amount back to payer
Expand All @@ -475,10 +485,12 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow
function _release(bytes32 escrowId, uint256 amount) internal {
Escrow storage escrow = escrows[escrowId];

//EXCEPTION: AlreadyReleased
require(escrow.released == false, "AlreadyReleased");

uint256 activeAmount = _getEscrowAmountRemaining(escrow);

//EXCEPTION: AmountExceeded
if (amount > activeAmount)
revert("AmountExceeded");

Expand Down Expand Up @@ -529,8 +541,10 @@ contract PolyEscrow is HasSecurityContext, Pausable, IPolyEscrow

function executeArbitrationProposal(bytes32 escrowId, ArbitrationType proposalType, uint256 amount) external {
Escrow storage escrow = escrows[escrowId];
//EXCEPTION: InvalidEscrow
require(escrow.id != bytes32(0), "InvalidEscrow");

//EXCEPTION: Unauthorized
require(msg.sender == address(escrow.arbitrationModule), "Unauthorized");

if (proposalType == ArbitrationType.REFUND) {
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/IArbitrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct ArbitrationProposal {
bytes32 escrowId;
ArbitrationType proposalType;
ArbitrationStatus status;
address proposer;
uint256 amount;
uint8 votesFor;
uint8 votesAgainst;
Expand Down
Loading
Loading