Skip to content

Commit

Permalink
refine code based on comment and add some test functions
Browse files Browse the repository at this point in the history
  • Loading branch information
alex0207s committed Mar 1, 2024
1 parent 4067352 commit 48fbfef
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 31 deletions.
12 changes: 4 additions & 8 deletions contracts/ConditionalSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,29 +82,25 @@ contract ConditionalSwap is IConditionalSwap, Ownable, TokenCollector, EIP712 {
bytes1 settlementType = settlementData[0];
bytes memory strategyData = settlementData[1:];

uint256 returnedAmount;
if (settlementType == 0x0) {
// direct settlement type
_collect(order.takerToken, order.taker, msg.sender, takerTokenAmount, order.takerTokenPermit);
_collect(order.makerToken, msg.sender, order.recipient, makerTokenAmount, order.takerTokenPermit);
} else if (settlementType == 0x01) {
// strategy settlement type

(address strategy, bytes memory data) = abi.decode(strategyData, (address, bytes));
_collect(order.takerToken, order.taker, strategy, takerTokenAmount, order.takerTokenPermit);

uint256 makerTokenBalanceBefore = order.makerToken.getBalance(address(this));
//@todo Create a separate strategy contract specifically for conditionalSwap
IStrategy(strategy).executeStrategy(order.takerToken, order.makerToken, takerTokenAmount, data);
returnedAmount = order.makerToken.getBalance(address(this)) - makerTokenBalanceBefore;
uint256 returnedAmount = order.makerToken.getBalance(address(this)) - makerTokenBalanceBefore;

if (returnedAmount < minMakerTokenAmount) revert InsufficientOutput();
if (returnedAmount < makerTokenAmount) revert InsufficientOutput();
order.makerToken.transferTo(order.recipient, returnedAmount);
} else {
revert(); // specific the error message1
}
} else revert InvalidSettlementType();

_emitConOrderFilled(order, orderHash, takerTokenAmount, returnedAmount);
_emitConOrderFilled(order, orderHash, takerTokenAmount, makerTokenAmount);
}

function _emitConOrderFilled(ConOrder calldata order, bytes32 orderHash, uint256 takerTokenSettleAmount, uint256 makerTokenSettleAmount) internal {
Expand Down
10 changes: 5 additions & 5 deletions contracts/interfaces/IConditionalSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ pragma solidity 0.8.17;
import { ConOrder } from "../libraries/ConditionalOrder.sol";

interface IConditionalSwap {
// error
error ExpiredOrder();
error InsufficientTimePassed();
error ZeroTokenAmount();
error InvalidSignature();
error ZeroTokenAmount();
error InvalidTakingAmount();
error InvalidMakingAmount();
error InvalidRecipient();
error NotOrderMaker();
error InsufficientOutput();
error NotOrderMaker();
error InvalidRecipient();
error InvalidSettlementType();

// event
/// @notice Emitted when a conditional order is filled
event ConditionalOrderFilled(
bytes32 indexed orderHash,
address indexed taker,
Expand Down
69 changes: 54 additions & 15 deletions test/forkMainnet/ConditionalSwap/Fill.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.17;

import { IConditionalSwap } from "contracts/interfaces/IConditionalSwap.sol";
import { getConOrderHash } from "contracts/libraries/ConditionalOrder.sol";
import { ConditionalOrderSwapTest } from "test/forkMainnet/ConditionalSwap/Setup.t.sol";
import { BalanceSnapshot, Snapshot } from "test/utils/BalanceSnapshot.sol";

Expand All @@ -21,11 +22,23 @@ contract ConFillTest is ConditionalOrderSwapTest {
Snapshot memory recMakerToken = BalanceSnapshot.take({ owner: recipient, token: defaultOrder.makerToken });

// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 1 << 253;
uint256 flags = FLG_PARTIAL_FILL_MASK;
defaultOrder.flagsAndPeriod = flags;

defaultTakerSig = signConOrder(takerPrivateKey, defaultOrder, address(conditionalSwap));

vm.expectEmit(true, true, true, true);
emit ConditionalOrderFilled(
getConOrderHash(defaultOrder),
defaultOrder.taker,
defaultOrder.maker,
defaultOrder.takerToken,
defaultOrder.takerTokenAmount,
defaultOrder.makerToken,
defaultOrder.makerTokenAmount,
recipient
);

vm.startPrank(maker);
conditionalSwap.fillConOrder(defaultOrder, defaultTakerSig, defaultOrder.takerTokenAmount, defaultOrder.makerTokenAmount, defaultSettlementData);
vm.stopPrank();
Expand All @@ -38,7 +51,7 @@ contract ConFillTest is ConditionalOrderSwapTest {
recMakerToken.assertChange(int256(defaultOrder.makerTokenAmount));
}

function testRepayment() external {
function testRepaymentOrDCAOrder() external {
Snapshot memory takerTakerToken = BalanceSnapshot.take({ owner: taker, token: defaultOrder.takerToken });
Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: taker, token: defaultOrder.makerToken });
Snapshot memory makerTakerToken = BalanceSnapshot.take({ owner: maker, token: defaultOrder.takerToken });
Expand All @@ -47,12 +60,26 @@ contract ConFillTest is ConditionalOrderSwapTest {
Snapshot memory recMakerToken = BalanceSnapshot.take({ owner: recipient, token: defaultOrder.makerToken });

// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 7 << 253;
uint256 flags = FLG_SINGLE_AMOUNT_CAP_MASK | FLG_PERIODIC_MASK | FLG_PARTIAL_FILL_MASK;
uint256 period = 12 hours;
uint256 numberOfCycles = (defaultExpiry - block.timestamp) / period;
defaultOrder.flagsAndPeriod = flags | period;

uint256 numberOfCycles = (defaultExpiry - block.timestamp) / period;

defaultTakerSig = signConOrder(takerPrivateKey, defaultOrder, address(conditionalSwap));

vm.expectEmit(true, true, true, true);
emit ConditionalOrderFilled(
getConOrderHash(defaultOrder),
defaultOrder.taker,
defaultOrder.maker,
defaultOrder.takerToken,
defaultOrder.takerTokenAmount,
defaultOrder.makerToken,
defaultOrder.makerTokenAmount,
recipient
);

vm.startPrank(maker);
for (uint256 i; i < numberOfCycles; ++i) {
conditionalSwap.fillConOrder(defaultOrder, defaultTakerSig, defaultOrder.takerTokenAmount, defaultOrder.makerTokenAmount, defaultSettlementData);
Expand All @@ -68,14 +95,6 @@ contract ConFillTest is ConditionalOrderSwapTest {
recMakerToken.assertChange(int256(defaultOrder.makerTokenAmount) * int256(numberOfCycles));
}

function testDCAOrder() public {
// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 7 << 253;
uint256 period = 1 days;

defaultOrder.flagsAndPeriod = flags | period;
}

function testCannotFillExpiredOrder() public {
vm.warp(defaultOrder.expiry + 1);

Expand Down Expand Up @@ -103,7 +122,7 @@ contract ConFillTest is ConditionalOrderSwapTest {

function testCannotFillOrderWithInvalidTotalTakerTokenAmount() public {
// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 1 << 253;
uint256 flags = FLG_PARTIAL_FILL_MASK;
defaultOrder.flagsAndPeriod = flags;

defaultTakerSig = signConOrder(takerPrivateKey, defaultOrder, address(conditionalSwap));
Expand All @@ -120,7 +139,7 @@ contract ConFillTest is ConditionalOrderSwapTest {

function testCannotFillOrderWithInvalidSingleTakerTokenAmount() public {
// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 7 << 253;
uint256 flags = FLG_SINGLE_AMOUNT_CAP_MASK | FLG_PERIODIC_MASK | FLG_PARTIAL_FILL_MASK;
uint256 period = 12 hours;
defaultOrder.flagsAndPeriod = flags | period;

Expand Down Expand Up @@ -153,7 +172,7 @@ contract ConFillTest is ConditionalOrderSwapTest {

function testCannotFillOrderWithinSamePeriod() public {
// craft the `flagAndPeriod` of the defaultOrder for BestBuy case
uint256 flags = 7 << 253;
uint256 flags = FLG_SINGLE_AMOUNT_CAP_MASK | FLG_PERIODIC_MASK | FLG_PARTIAL_FILL_MASK;
uint256 period = 12 hours;
defaultOrder.flagsAndPeriod = flags | period;

Expand All @@ -165,4 +184,24 @@ contract ConFillTest is ConditionalOrderSwapTest {
conditionalSwap.fillConOrder(defaultOrder, defaultTakerSig, defaultOrder.takerTokenAmount, defaultOrder.makerTokenAmount, defaultSettlementData);
vm.stopPrank();
}

function testCannotFillOrderWithInvalidSettlementType() public {
bytes memory settlementData = hex"02";

defaultTakerSig = signConOrder(takerPrivateKey, defaultOrder, address(conditionalSwap));

vm.expectRevert(IConditionalSwap.InvalidSettlementType.selector);
vm.startPrank(maker);
conditionalSwap.fillConOrder(defaultOrder, defaultTakerSig, defaultOrder.takerTokenAmount, defaultOrder.makerTokenAmount, settlementData);
vm.stopPrank();
}

function testCannotFillOrderWithInvalidMakingAmount() public {
defaultTakerSig = signConOrder(takerPrivateKey, defaultOrder, address(conditionalSwap));

vm.expectRevert(IConditionalSwap.InvalidMakingAmount.selector);
vm.startPrank(maker);
conditionalSwap.fillConOrder(defaultOrder, defaultTakerSig, defaultOrder.takerTokenAmount, defaultOrder.makerTokenAmount - 1, defaultSettlementData);
vm.stopPrank();
}
}
20 changes: 17 additions & 3 deletions test/forkMainnet/ConditionalSwap/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
pragma solidity 0.8.17;

import { Test } from "forge-std/Test.sol";
import { IWETH } from "contracts/interfaces/IWETH.sol";
import { IConditionalSwap } from "contracts/interfaces/IConditionalSwap.sol";
import { ConditionalSwap } from "contracts/ConditionalSwap.sol";
import { AllowanceTarget } from "contracts/AllowanceTarget.sol";
import { ConOrder, getConOrderHash } from "contracts/libraries/ConditionalOrder.sol";
import { ConOrder } from "contracts/libraries/ConditionalOrder.sol";
import { Tokens } from "test/utils/Tokens.sol";
import { BalanceUtil } from "test/utils/BalanceUtil.sol";
import { SigHelper } from "test/utils/SigHelper.sol";
import { computeContractAddress } from "test/utils/Addresses.sol";
import { Permit2Helper } from "test/utils/Permit2Helper.sol";

contract ConditionalOrderSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper {
event ConditionalOrderFilled(
bytes32 indexed orderHash,
address indexed taker,
address indexed maker,
address takerToken,
uint256 takerTokenFilledAmount,
address makerToken,
uint256 makerTokenSettleAmount,
address recipient
);

// role
address public conditionalOrderOwner = makeAddr("conditionalOrderOwner");
address public allowanceTargetOwner = makeAddr("allowanceTargetOwner");
Expand All @@ -29,6 +38,11 @@ contract ConditionalOrderSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, S
bytes public defaultTakerSig;
bytes public defaultSettlementData;

// mask for triggering different business logic (e.g. BestBuy, Repayment, DCA)
uint256 public constant FLG_SINGLE_AMOUNT_CAP_MASK = 1 << 255; // ConOrder.amount is the cap of single execution, not total cap
uint256 public constant FLG_PERIODIC_MASK = 1 << 254; // ConOrder can be executed periodically
uint256 public constant FLG_PARTIAL_FILL_MASK = 1 << 253; // ConOrder can be fill partially

ConditionalSwap conditionalSwap;
AllowanceTarget allowanceTarget;
ConOrder defaultOrder;
Expand Down

0 comments on commit 48fbfef

Please sign in to comment.