Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: decouple strategyData from GenericSwapData #336

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 10 additions & 4 deletions contracts/GenericSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
receive() external payable {}

/// @inheritdoc IGenericSwap
function executeSwap(GenericSwapData calldata swapData, bytes calldata takerTokenPermit) external payable returns (uint256 returnAmount) {
returnAmount = _executeSwap(swapData, msg.sender, takerTokenPermit);
function executeSwap(
GenericSwapData calldata swapData,
bytes calldata strategyData,
bytes calldata takerTokenPermit
) external payable returns (uint256 returnAmount) {
returnAmount = _executeSwap(swapData, strategyData, msg.sender, takerTokenPermit);

_emitGSExecuted(getGSDataHash(swapData), swapData, msg.sender, returnAmount);
}

/// @inheritdoc IGenericSwap
function executeSwapWithSig(
GenericSwapData calldata swapData,
bytes calldata strategyData,
bytes calldata takerTokenPermit,
address taker,
bytes calldata takerSig
Expand All @@ -47,7 +52,7 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
filledSwap[swapHash] = true;
if (!SignatureValidator.validateSignature(taker, gs712Hash, takerSig)) revert InvalidSignature();

returnAmount = _executeSwap(swapData, taker, takerTokenPermit);
returnAmount = _executeSwap(swapData, strategyData, taker, takerTokenPermit);

_emitGSExecuted(swapHash, swapData, taker, returnAmount);
}
Expand All @@ -59,6 +64,7 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
/// @return returnAmount The output amount of the swap.
function _executeSwap(
GenericSwapData calldata _swapData,
bytes calldata strategyData,
address _authorizedUser,
bytes calldata _takerTokenPermit
) private returns (uint256 returnAmount) {
Expand All @@ -75,7 +81,7 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
_collect(_inputToken, _authorizedUser, _swapData.maker, _swapData.takerTokenAmount, _takerTokenPermit);
}

IStrategy(_swapData.maker).executeStrategy{ value: msg.value }(_inputToken, _outputToken, _swapData.takerTokenAmount, _swapData.strategyData);
IStrategy(_swapData.maker).executeStrategy{ value: msg.value }(_inputToken, _outputToken, _swapData.takerTokenAmount, strategyData);

returnAmount = _outputToken.getBalance(address(this));
if (returnAmount > 1) {
Expand Down
9 changes: 8 additions & 1 deletion contracts/interfaces/IGenericSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,25 @@ interface IGenericSwap {

/// @notice Executes a swap using provided swap data and taker token permit.
/// @param swapData The swap data containing details of the swap.
/// @param strategyData The strategy data contains the details on how to perform the swap.
/// @param takerTokenPermit The permit for spending taker's tokens.
/// @return returnAmount The amount of tokens returned from the swap.
function executeSwap(GenericSwapData calldata swapData, bytes calldata takerTokenPermit) external payable returns (uint256 returnAmount);
function executeSwap(
GenericSwapData calldata swapData,
bytes calldata strategyData,
bytes calldata takerTokenPermit
) external payable returns (uint256 returnAmount);

/// @notice Executes a swap using provided swap data, taker token permit, taker address, and signature.
/// @param swapData The swap data containing details of the swap.
/// @param strategyData The strategy data contains the details on how to perform the swap.
/// @param takerTokenPermit The permit for spending taker's tokens.
/// @param taker The address of the taker initiating the swap.
/// @param takerSig The signature of the taker authorizing the swap.
/// @return returnAmount The amount of tokens returned from the swap.
function executeSwapWithSig(
GenericSwapData calldata swapData,
bytes calldata strategyData,
bytes calldata takerTokenPermit,
address taker,
bytes calldata takerSig
Expand Down
6 changes: 2 additions & 4 deletions contracts/libraries/GenericSwapData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.0;

string constant GS_DATA_TYPESTRING = string(
"GenericSwapData(address maker,address takerToken,uint256 takerTokenAmount,address makerToken,uint256 makerTokenAmount,uint256 minMakerTokenAmount,uint256 expiry,uint256 salt,address recipient,bytes strategyData)"
"GenericSwapData(address maker,address takerToken,uint256 takerTokenAmount,address makerToken,uint256 makerTokenAmount,uint256 minMakerTokenAmount,uint256 expiry,uint256 salt,address recipient)"
);

bytes32 constant GS_DATA_TYPEHASH = keccak256(bytes(GS_DATA_TYPESTRING));
Expand All @@ -17,7 +17,6 @@ struct GenericSwapData {
uint256 expiry;
uint256 salt;
address payable recipient;
bytes strategyData;
}

// solhint-disable-next-line func-visibility
Expand All @@ -34,8 +33,7 @@ function getGSDataHash(GenericSwapData memory gsData) pure returns (bytes32) {
gsData.minMakerTokenAmount,
gsData.expiry,
gsData.salt,
gsData.recipient,
keccak256(gsData.strategyData)
gsData.recipient
)
);
}
3 changes: 1 addition & 2 deletions test/Signing.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ contract testEIP712Signing is SigHelper {
minMakerTokenAmount: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.minMakerTokenAmount"), (uint256)),
expiry: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.expiry"), (uint256)),
salt: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.salt"), (uint256)),
recipient: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.recipient"), (address)),
strategyData: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.strategyData"), (bytes))
recipient: abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.recipient"), (address))
});
uint256 signingKey = abi.decode(vm.parseJson(genericSwapDataPayloadJson, "$.signingKey"), (uint256));
bytes memory sig = signGenericSwap(signingKey, genericSwapData, chainId, verifyingContract);
Expand Down
39 changes: 19 additions & 20 deletions test/forkMainnet/GenericSwap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { IUniswapSwapRouter02 } from "test/utils/IUniswapSwapRouter02.sol";
import { MockStrategy } from "test/mocks/MockStrategy.sol";
import { GenericSwap } from "contracts/GenericSwap.sol";
import { AllowanceTarget } from "contracts/AllowanceTarget.sol";
import { TokenCollector } from "contracts/abstracts/TokenCollector.sol";
import { SmartOrderStrategy } from "contracts/SmartOrderStrategy.sol";
import { Constant } from "contracts/libraries/Constant.sol";
import { GenericSwapData, getGSDataHash } from "contracts/libraries/GenericSwapData.sol";
Expand Down Expand Up @@ -50,6 +49,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
uint24[] defaultV3Fees = [3000];
bytes defaultTakerPermit;
bytes alicePermit;
bytes strategyData;
SmartOrderStrategy smartStrategy;
GenericSwap genericSwap;
GenericSwapData defaultGSData;
Expand Down Expand Up @@ -104,7 +104,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
value: 0,
data: routerPayload
});
bytes memory swapData = abi.encode(operations);
strategyData = abi.encode(operations);

deal(taker, 100 ether);
setTokenBalanceAndApprove(taker, UNISWAP_PERMIT2_ADDRESS, tokens, 100000);
Expand All @@ -122,8 +122,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
minMakerTokenAmount: minOutputAmount,
expiry: defaultExpiry,
salt: 5678,
recipient: payable(taker),
strategyData: swapData
recipient: payable(taker)
});

defaultTakerPermit = getTokenlonPermit2Data(taker, takerPrivateKey, defaultGSData.takerToken, address(genericSwap));
Expand Down Expand Up @@ -154,7 +153,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
);

vm.prank(taker);
genericSwap.executeSwap(defaultGSData, defaultTakerPermit);
genericSwap.executeSwap(defaultGSData, strategyData, defaultTakerPermit);

takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount));
// the makerTokenAmount in the defaultGSData is the exact quote from strategy
Expand Down Expand Up @@ -190,7 +189,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
gsData.salt
);
vm.prank(taker);
genericSwap.executeSwap(gsData, defaultTakerPermit);
genericSwap.executeSwap(gsData, strategyData, defaultTakerPermit);

takerTakerToken.assertChange(-int256(gsData.takerTokenAmount));
takerMakerToken.assertChange(int256(realChangedInGS));
Expand Down Expand Up @@ -225,7 +224,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
gsData.salt
);
vm.prank(taker);
genericSwap.executeSwap{ value: gsData.takerTokenAmount }(gsData, defaultTakerPermit);
genericSwap.executeSwap{ value: gsData.takerTokenAmount }(gsData, strategyData, defaultTakerPermit);

takerTakerToken.assertChange(-int256(gsData.takerTokenAmount));
takerMakerToken.assertChange(int256(realChangedInGS));
Expand Down Expand Up @@ -261,7 +260,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
gsData.salt
);
vm.prank(taker);
genericSwap.executeSwap(gsData, defaultTakerPermit);
genericSwap.executeSwap(gsData, strategyData, defaultTakerPermit);

takerTakerToken.assertChange(-int256(gsData.takerTokenAmount));
takerMakerToken.assertChange(int256(realChangedInGS));
Expand All @@ -274,13 +273,13 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper

vm.prank(taker);
vm.expectRevert(IGenericSwap.ExpiredOrder.selector);
genericSwap.executeSwap(defaultGSData, defaultTakerPermit);
genericSwap.executeSwap(defaultGSData, strategyData, defaultTakerPermit);
}

function testCannotSwapWithInvalidETHInput() public {
// case1 : msg.value != 0 when takerToken is not ETH
vm.expectRevert(IGenericSwap.InvalidMsgValue.selector);
genericSwap.executeSwap{ value: 1 }(defaultGSData, defaultTakerPermit);
genericSwap.executeSwap{ value: 1 }(defaultGSData, strategyData, defaultTakerPermit);

// change input token as ETH and update amount
GenericSwapData memory gsData = defaultGSData;
Expand All @@ -290,12 +289,12 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
// case2 : msg.value > takerTokenAmount
vm.prank(taker);
vm.expectRevert(IGenericSwap.InvalidMsgValue.selector);
genericSwap.executeSwap{ value: gsData.takerTokenAmount + 1 }(gsData, defaultTakerPermit);
genericSwap.executeSwap{ value: gsData.takerTokenAmount + 1 }(gsData, strategyData, defaultTakerPermit);

// case3 : msg.value < takerTokenAmount
vm.prank(taker);
vm.expectRevert(IGenericSwap.InvalidMsgValue.selector);
genericSwap.executeSwap{ value: gsData.takerTokenAmount - 1 }(gsData, defaultTakerPermit);
genericSwap.executeSwap{ value: gsData.takerTokenAmount - 1 }(gsData, strategyData, defaultTakerPermit);
}

function testCannotSwapWithInsufficientOutput() public {
Expand All @@ -306,7 +305,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
mockStrategy.setOutputAmountAndRecipient(gsData.minMakerTokenAmount - 1, payable(address(genericSwap)));
vm.prank(taker);
vm.expectRevert(IGenericSwap.InsufficientOutput.selector);
genericSwap.executeSwap(gsData, defaultTakerPermit);
genericSwap.executeSwap(gsData, strategyData, defaultTakerPermit);
}

function testCannotSwapWithZeroRecipient() public {
Expand All @@ -315,7 +314,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper

vm.prank(taker);
vm.expectRevert(IGenericSwap.ZeroAddress.selector);
genericSwap.executeSwap(gsData, defaultTakerPermit);
genericSwap.executeSwap(gsData, strategyData, defaultTakerPermit);
}

function testGenericSwapRelayed() public {
Expand All @@ -336,7 +335,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
);

bytes memory takerSig = signGenericSwap(takerPrivateKey, defaultGSData, address(genericSwap));
genericSwap.executeSwapWithSig(defaultGSData, defaultTakerPermit, taker, takerSig);
genericSwap.executeSwapWithSig(defaultGSData, strategyData, defaultTakerPermit, taker, takerSig);

takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount));
// the makerTokenAmount in the defaultGSData is the exact quote from strategy
Expand All @@ -349,15 +348,15 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper

vm.expectRevert(IGenericSwap.InvalidSignature.selector);
// submit with user address as expected signer
genericSwap.executeSwapWithSig(defaultGSData, defaultTakerPermit, taker, randomSig);
genericSwap.executeSwapWithSig(defaultGSData, strategyData, defaultTakerPermit, taker, randomSig);
}

function testCannotReplayGenericSwapSig() public {
bytes memory takerSig = signGenericSwap(takerPrivateKey, defaultGSData, address(genericSwap));
genericSwap.executeSwapWithSig(defaultGSData, defaultTakerPermit, taker, takerSig);
genericSwap.executeSwapWithSig(defaultGSData, strategyData, defaultTakerPermit, taker, takerSig);

vm.expectRevert(IGenericSwap.AlreadyFilled.selector);
genericSwap.executeSwapWithSig(defaultGSData, defaultTakerPermit, taker, takerSig);
genericSwap.executeSwapWithSig(defaultGSData, strategyData, defaultTakerPermit, taker, takerSig);
}

function testLeaveOneWeiWithMultipleUsers() public {
Expand Down Expand Up @@ -385,7 +384,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
);

vm.prank(taker);
genericSwap.executeSwap(defaultGSData, defaultTakerPermit);
genericSwap.executeSwap(defaultGSData, strategyData, defaultTakerPermit);

// the second user: Alice
// his makerTokenAmount is recalculate by `quoteExactInput() function base on the current state`
Expand Down Expand Up @@ -418,7 +417,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
);

vm.startPrank(alice);
genericSwap.executeSwap(aliceGSData, alicePermit);
genericSwap.executeSwap(aliceGSData, strategyData, alicePermit);
vm.stopPrank();

takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount));
Expand Down
5 changes: 2 additions & 3 deletions test/utils/payload/genericSwapData.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
"expiry": 3376656000,
"salt": 123,
"recipient": "0xdf5621f59CcD4A58cF487E0C4E3fCDd6Db67E4dA",
"strategyData": "0xcbe324dfbf8d48a7ad9ef3cb0c2bdd411129db5238371b8609272f14921736fb0ab28dcede4f2e1e41393d575ffec60002e565bfbf5eaf896bc490a965c8452d08d9b71b",
"expectedSig": "0xdfe4e0414baa9b0ac4edcefc1d8ceb18dc21230f6b93a83c91188a105d1d7ece7809ca3c67db499fd497e35ba271a0a6689f7683d41b5d4602a5bb7895537a2d1c",
"expectedSig": "0x5a671e6994dbed10c4f8fd34cc4d526a8d74b15363fcefc30a20ad295674ec8a231c5e743da079623545601b37aa09c51c017da1a5cb6c0dde42da5fe7665e4c1b",
"signingKey": "0x1dc4f07f92fa1783042350ffe685439f857a8fdd837589c00c5299e287c4afcb",
"chainId": 5,
"verifyingContract": "0x49eA36C04c109a8c62C9b48fd8C4d13b3EA98B27",
"typehash": "0x1b6f9d7673107802b331a5ab52a40f7d942bdf74fa821744df8b69eead3d26c1"
"typehash": "0x3aaeb6ca9ecb74a956054c8b123c6d2ab5251f97eed10a138611db2aafd0b792"
}
Loading