Skip to content

Commit

Permalink
feat: improve test coverage (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
marktoda committed Aug 24, 2023
1 parent ffceda5 commit 6eae896
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
182180
150955
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197271
197261
Original file line number Diff line number Diff line change
@@ -1 +1 @@
207034
207024
Original file line number Diff line number Diff line change
@@ -1 +1 @@
260702
260692
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190797
190787
Original file line number Diff line number Diff line change
@@ -1 +1 @@
178116
146882
54 changes: 54 additions & 0 deletions test/base/ProtocolFees.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {OrderInfoBuilder} from "../util/OrderInfoBuilder.sol";
import {OutputsBuilder} from "../util/OutputsBuilder.sol";
import {MockProtocolFees} from "../util/mock/MockProtocolFees.sol";
import {MockFeeController} from "../util/mock/MockFeeController.sol";
import {MockFeeControllerInputFees} from "../util/mock/MockFeeControllerInputFees.sol";
import {MockFeeControllerDuplicates} from "../util/mock/MockFeeControllerDuplicates.sol";
import {MockFeeControllerZeroFee} from "../util/mock/MockFeeControllerZeroFee.sol";
import {PermitSignature} from "../util/PermitSignature.sol";
Expand Down Expand Up @@ -43,13 +44,15 @@ contract ProtocolFeesTest is Test {
MockERC20 tokenOut2;
MockProtocolFees fees;
MockFeeController feeController;
MockFeeControllerInputFees inputFeeController;

function setUp() public {
fees = new MockProtocolFees(PROTOCOL_FEE_OWNER);
tokenIn = new MockERC20("Input", "IN", 18);
tokenOut = new MockERC20("Output", "OUT", 18);
tokenOut2 = new MockERC20("Output2", "OUT", 18);
feeController = new MockFeeController(RECIPIENT);
inputFeeController = new MockFeeControllerInputFees(RECIPIENT);
vm.prank(PROTOCOL_FEE_OWNER);
fees.setProtocolFeeController(address(feeController));
}
Expand Down Expand Up @@ -99,6 +102,41 @@ contract ProtocolFeesTest is Test {
assertEq(afterFees.outputs[1].recipient, RECIPIENT);
}

function testTakeInputFees() public {
vm.prank(PROTOCOL_FEE_OWNER);
fees.setProtocolFeeController(address(inputFeeController));

ResolvedOrder memory order = createOrder(1 ether, false);
uint256 feeBps = 3;
inputFeeController.setFee(tokenIn, feeBps);

assertEq(order.outputs.length, 1);
ResolvedOrder memory afterFees = fees.takeFees(order);
assertEq(afterFees.outputs.length, 2);
assertEq(afterFees.outputs[0].token, order.outputs[0].token);
assertEq(afterFees.outputs[0].amount, order.outputs[0].amount);
assertEq(afterFees.outputs[0].recipient, order.outputs[0].recipient);
assertEq(afterFees.outputs[1].token, address(order.input.token));
assertEq(afterFees.outputs[1].amount, order.input.amount * feeBps / 10000);
assertEq(afterFees.outputs[1].recipient, RECIPIENT);
}

function testTakeInputTokenFees() public {
ResolvedOrder memory order = createOrder(1 ether, false);
uint256 feeBps = 3;
feeController.setFee(tokenIn, address(tokenOut), feeBps);

assertEq(order.outputs.length, 1);
ResolvedOrder memory afterFees = fees.takeFees(order);
assertEq(afterFees.outputs.length, 2);
assertEq(afterFees.outputs[0].token, order.outputs[0].token);
assertEq(afterFees.outputs[0].amount, order.outputs[0].amount);
assertEq(afterFees.outputs[0].recipient, order.outputs[0].recipient);
assertEq(afterFees.outputs[1].token, order.outputs[0].token);
assertEq(afterFees.outputs[1].amount, order.outputs[0].amount * feeBps / 10000);
assertEq(afterFees.outputs[1].recipient, RECIPIENT);
}

function testTakeFeesFuzzOutputs(uint128 inputAmount, uint128[] memory outputAmounts, uint256 feeBps) public {
vm.assume(feeBps <= 5);
vm.assume(outputAmounts.length > 0);
Expand Down Expand Up @@ -167,6 +205,22 @@ contract ProtocolFeesTest is Test {
fees.takeFees(order);
}

function testTakeInputFeesTooMuch() public {
vm.prank(PROTOCOL_FEE_OWNER);
fees.setProtocolFeeController(address(inputFeeController));

ResolvedOrder memory order = createOrder(1 ether, false);
uint256 feeBps = 10;
inputFeeController.setFee(tokenIn, feeBps);

vm.expectRevert(
abi.encodeWithSelector(
ProtocolFees.FeeTooLarge.selector, address(tokenIn), order.input.amount * 10 / 10000, RECIPIENT
)
);
fees.takeFees(order);
}

function testTakeFeesDuplicate() public {
MockFeeControllerDuplicates controller = new MockFeeControllerDuplicates(RECIPIENT);
vm.prank(PROTOCOL_FEE_OWNER);
Expand Down
85 changes: 85 additions & 0 deletions test/reactors/ExclusiveDutchOrderReactor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,91 @@ contract ExclusiveDutchOrderReactorExecuteTest is PermitSignature, DeployPermit2
return (SignedOrder(abi.encode(order), signOrder(swapperPrivateKey, address(permit2), order)), orderHash);
}

function testExecuteInputDecay() public {
tokenIn.mint(address(swapper), 2 ether);
tokenOut.mint(address(fillContract), 1 ether);
tokenIn.forceApprove(swapper, address(permit2), type(uint256).max);

uint256 inputStartAmount = 1 ether;
uint256 inputEndAmount = 2 ether;
uint256 outputAmount = 1 ether;
ExclusiveDutchOrder memory order = ExclusiveDutchOrder({
info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(block.timestamp + 100),
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 300,
input: DutchInput(tokenIn, inputStartAmount, inputEndAmount),
outputs: OutputsBuilder.singleDutch(address(tokenOut), outputAmount, outputAmount, swapper)
});

bytes memory sig = signOrder(swapperPrivateKey, address(permit2), order);
SignedOrder memory signedOrder = SignedOrder(abi.encode(order), sig);

vm.expectEmit(false, false, false, true);
emit Fill(order.hash(), address(this), swapper, order.info.nonce);
fillContract.execute(signedOrder);
assertEq(tokenIn.balanceOf(swapper), 1 ether);
assertEq(tokenOut.balanceOf(swapper), 1 ether);
assertEq(tokenIn.balanceOf(address(fillContract)), 1 ether);
}

function testExecuteInputDecayMiddle() public {
tokenIn.mint(address(swapper), 2 ether);
tokenOut.mint(address(fillContract), 1 ether);
tokenIn.forceApprove(swapper, address(permit2), type(uint256).max);

uint256 inputStartAmount = 1 ether;
uint256 inputEndAmount = 2 ether;
uint256 outputAmount = 1 ether;
vm.warp(100);
ExclusiveDutchOrder memory order = ExclusiveDutchOrder({
info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(200),
decayStartTime: 50,
decayEndTime: 150,
exclusiveFiller: address(0),
exclusivityOverrideBps: 300,
input: DutchInput(tokenIn, inputStartAmount, inputEndAmount),
outputs: OutputsBuilder.singleDutch(address(tokenOut), outputAmount, outputAmount, swapper)
});

bytes memory sig = signOrder(swapperPrivateKey, address(permit2), order);
SignedOrder memory signedOrder = SignedOrder(abi.encode(order), sig);

vm.expectEmit(false, false, false, true);
emit Fill(order.hash(), address(this), swapper, order.info.nonce);
fillContract.execute(signedOrder);
assertEq(tokenIn.balanceOf(swapper), 0.5 ether);
assertEq(tokenOut.balanceOf(swapper), 1 ether);
assertEq(tokenIn.balanceOf(address(fillContract)), 1.5 ether);
}

function testExecuteInputDecayInsufficientInput() public {
tokenIn.mint(address(swapper), 1 ether);
tokenOut.mint(address(fillContract), 1 ether);
tokenIn.forceApprove(swapper, address(permit2), type(uint256).max);

uint256 inputStartAmount = 1 ether;
uint256 inputEndAmount = 2 ether;
uint256 outputAmount = 1 ether;
vm.warp(100);
ExclusiveDutchOrder memory order = ExclusiveDutchOrder({
info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper).withDeadline(200),
decayStartTime: 50,
decayEndTime: 150,
exclusiveFiller: address(0),
exclusivityOverrideBps: 300,
input: DutchInput(tokenIn, inputStartAmount, inputEndAmount),
outputs: OutputsBuilder.singleDutch(address(tokenOut), outputAmount, outputAmount, swapper)
});

bytes memory sig = signOrder(swapperPrivateKey, address(permit2), order);
SignedOrder memory signedOrder = SignedOrder(abi.encode(order), sig);

vm.expectRevert("TRANSFER_FROM_FAILED");
fillContract.execute(signedOrder);
}

// Execute 3 dutch orders. Have the 3rd one signed by a different swapper.
// Order 1: Input = 1, outputs = [2, 1]
// Order 2: Input = 2, outputs = [3]
Expand Down
32 changes: 32 additions & 0 deletions test/util/mock/MockFeeControllerInputFees.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Owned} from "solmate/src/auth/Owned.sol";
import {ResolvedOrder, OutputToken} from "../../../src/base/ReactorStructs.sol";
import {IProtocolFeeController} from "../../../src/interfaces/IProtocolFeeController.sol";
import {ERC20} from "solmate/src/tokens/ERC20.sol";

/// @notice Mock protocol fee controller taking fee on input tokens
contract MockFeeControllerInputFees is IProtocolFeeController, Owned(msg.sender) {
uint256 private constant BPS = 10000;
address public immutable feeRecipient;

constructor(address _feeRecipient) {
feeRecipient = _feeRecipient;
}

mapping(ERC20 tokenIn => uint256) public fees;

/// @inheritdoc IProtocolFeeController
function getFeeOutputs(ResolvedOrder memory order) external view override returns (OutputToken[] memory result) {
result = new OutputToken[](1);

uint256 fee = fees[order.input.token];
uint256 feeAmount = order.input.amount * fee / BPS;
result[0] = OutputToken({token: address(order.input.token), amount: feeAmount, recipient: feeRecipient});
}

function setFee(ERC20 tokenIn, uint256 fee) external onlyOwner {
fees[tokenIn] = fee;
}
}

0 comments on commit 6eae896

Please sign in to comment.