Skip to content

Commit

Permalink
return penalty to redeemer if revert is not possible
Browse files Browse the repository at this point in the history
  • Loading branch information
hobofan committed Feb 1, 2019
1 parent 0c00ffb commit 214c003
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 4 deletions.
44 changes: 43 additions & 1 deletion contracts/gateway/EIP20CoGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,9 @@ contract EIP20CoGateway is GatewayBase {
);

MessageBus.Message storage message = messages[_messageHash];
MessageBus.MessageStatus outboxMessageStatus =
messageBox.outbox[_messageHash];

redeemer_ = message.sender;
redeemAmount_ = redeems[_messageHash].amount;

MessageBus.progressOutboxWithProof(
Expand All @@ -559,9 +560,50 @@ contract EIP20CoGateway is GatewayBase {
MessageBus.MessageStatus(_messageStatus)
);

uint256 bountyAmount = redeems[_messageHash].bounty;
(redeemer_, redeemAmount_) =
progressRedeemInternal(_messageHash, message, true, bytes32(0));

// Return revert penalty to redeemer if message is already progressed
// and can't be reverted anymore.
tryReturnPenaltyToRedeemer(
address(uint160(redeemer_)), // cast to address payable
outboxMessageStatus,
MessageBus.MessageStatus(_messageStatus),
bountyAmount
);
}

/**
* @notice Return the revert penalty to the redeemer. Only valid for
* a message transition from DeclaredRevocation -> Progressed.
*
* @dev Should only be called from progressRedeemWithProof. This function
* exists to avoid a stack too deep error.
*
* @param _redeemer Redeemer address.
* @param _outboxMessageStatus Message status before progressing.
* @param _inboxMessageStatus Message status after progressing.
* @param _bountyAmount Bounty amount to use for calculating penalty.
*/
function tryReturnPenaltyToRedeemer(
address payable _redeemer,
MessageBus.MessageStatus _outboxMessageStatus,
MessageBus.MessageStatus _inboxMessageStatus,
uint256 _bountyAmount
)
private
{
if (_outboxMessageStatus != MessageBus.MessageStatus.DeclaredRevocation) {
return;
}
if (_inboxMessageStatus != MessageBus.MessageStatus.Progressed) {
return;
}

// Penalty charged to staker for revert redeem.
uint256 penalty = penaltyFromBounty(_bountyAmount);
_redeemer.transfer(penalty);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions contracts/gateway/EIP20Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,9 @@ contract EIP20Gateway is GatewayBase {
// Return revert penalty to staker if message is already progressed
// and can't be reverted anymore.
tryReturnPenaltyToStaker(
message.sender,
staker_,
outboxMessageStatus,
MessageBus.MessageStatus(_messageStatus), // inbox message status
MessageBus.MessageStatus(_messageStatus),
bountyAmount
);
}
Expand Down
75 changes: 74 additions & 1 deletion test/gateway/eip20_cogateway/progress_redeem_with_proof.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const MessageStatusEnum = messageBus.MessageStatusEnum;

contract('EIP20CoGateway.progressRedeemWithProof() ', function (accounts) {

let utilityToken, eip20CoGateway, redeemParams, bountyAmount, owner, facilitator;
let utilityToken, eip20CoGateway, redeemParams, bountyAmount, penaltyAmount, owner, facilitator;
const PENALTY_MULTIPLIER = 1.5;

let setStorageRoot = async function() {

Expand Down Expand Up @@ -81,6 +82,7 @@ contract('EIP20CoGateway.progressRedeemWithProof() ', function (accounts) {
);

bountyAmount = new BN(proofData.co_gateway.constructor.bounty);
penaltyAmount = bountyAmount.muln(PENALTY_MULTIPLIER);

eip20CoGateway = await EIP20CoGateway.new(
proofData.co_gateway.constructor.valueToken,
Expand Down Expand Up @@ -380,6 +382,14 @@ contract('EIP20CoGateway.progressRedeemWithProof() ', function (accounts) {
it('should pass when message inbox status at target is progressed and outbox' +
' status at source is revocation declared', async function () {

await web3.eth.sendTransaction(
{
to: eip20CoGateway.address,
from: facilitator,
value: penaltyAmount,
}
);

await eip20CoGateway.setOutboxStatus(
redeemParams.messageHash,
MessageStatusEnum.DeclaredRevocation,
Expand Down Expand Up @@ -521,6 +531,69 @@ contract('EIP20CoGateway.progressRedeemWithProof() ', function (accounts) {

});

it('should return penalty to redeemer when the message status in source is ' +
'declared revocation and in the target is progressed', async function () {
const facilitator = accounts[8];
const redeemer = redeemParams.redeemer;

await web3.eth.sendTransaction(
{
to: eip20CoGateway.address,
from: facilitator,
value: penaltyAmount,
}
);

await eip20CoGateway.setOutboxStatus(
redeemParams.messageHash,
MessageStatusEnum.DeclaredRevocation,
);

const initialFacilitatorEthBalance = await Utils.getBalance(facilitator);
const initialRedeemerEthBalance = await Utils.getBalance(redeemer);
const initialCoGatewayEthBalance = await Utils.getBalance(eip20CoGateway.address);

await setStorageRoot();

const tx = await eip20CoGateway.progressRedeemWithProof(
redeemParams.messageHash,
proofData.gateway.progress_unstake.proof_data.storageProof[0].serializedProof,
new BN(proofData.gateway.progress_unstake.proof_data.block_number, 16),
MessageStatusEnum.Progressed,
{ from: facilitator },
);

const finalFacilitatorEthBalance = await Utils.getBalance(facilitator);
const finalRedeemerEthBalance = await Utils.getBalance(redeemer);
const finalCoGatewayEthBalance = await Utils.getBalance(eip20CoGateway.address);

const expectedFinalFacilitatorETHBalance = initialFacilitatorEthBalance
.add(bountyAmount)
.subn(tx.receipt.gasUsed);

const expectedFinalRedeemerETHBalance = initialRedeemerEthBalance
.add(penaltyAmount);

assert.strictEqual(
finalFacilitatorEthBalance.eq(expectedFinalFacilitatorETHBalance),
true,
`Facilitator's base token balance ${finalFacilitatorEthBalance.toString(10)} should be equal to ${expectedFinalFacilitatorETHBalance.toString(10)}`,
);

assert.strictEqual(
finalRedeemerEthBalance.eq(expectedFinalRedeemerETHBalance),
true,
`Redeemer's base token balance ${finalRedeemerEthBalance.toString(10)} should be equal to ${expectedFinalRedeemerETHBalance.toString(10)}`,
);

assert.strictEqual(
finalCoGatewayEthBalance.eq(initialCoGatewayEthBalance.sub(bountyAmount).sub(penaltyAmount)),
true,
`CoGateway's base token balance ${finalCoGatewayEthBalance.toString(10)} should be equal to ${initialCoGatewayEthBalance.sub(bountyAmount).sub(penaltyAmount)}.`,
);

});

it('should decrease token supply for utility token', async function () {

let initialTotalSupply = await utilityToken.totalSupply.call();
Expand Down

0 comments on commit 214c003

Please sign in to comment.