Skip to content

Commit b12306b

Browse files
authored
Merge b19ecbf into 1eb77cd
2 parents 1eb77cd + b19ecbf commit b12306b

17 files changed

+223
-119
lines changed

contracts/GenericSwap.sol

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity 0.8.26;
33

44
import { EIP712 } from "./abstracts/EIP712.sol";
55
import { TokenCollector } from "./abstracts/TokenCollector.sol";
6+
import { Ownable } from "./abstracts/Ownable.sol";
67

78
import { IGenericSwap } from "./interfaces/IGenericSwap.sol";
89
import { IStrategy } from "./interfaces/IStrategy.sol";
@@ -14,42 +15,84 @@ import { SignatureValidator } from "./libraries/SignatureValidator.sol";
1415
/// @title GenericSwap Contract
1516
/// @author imToken Labs
1617
/// @notice This contract facilitates token swaps using SmartOrderStrategy strategies.
17-
contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
18+
contract GenericSwap is IGenericSwap, TokenCollector, EIP712, Ownable {
1819
using Asset for address;
1920

21+
/// @notice Mapping to track addresses authorized as solvers.
22+
/// @dev Maps each address to a boolean indicating whether it is an authorized solver.
23+
mapping(address solver => bool isSolver) public solvers;
24+
2025
/// @notice Mapping to keep track of filled swaps.
2126
/// @dev Stores the status of swaps to ensure they are not filled more than once.
2227
mapping(bytes32 swapHash => bool isFilled) public filledSwap;
2328

29+
modifier onlySolver() {
30+
if (!solvers[msg.sender]) revert InvalidSolver();
31+
_;
32+
}
33+
2434
/// @notice Constructor to initialize the contract with the permit2 and allowance target.
2535
/// @param _uniswapPermit2 The address for Uniswap permit2.
2636
/// @param _allowanceTarget The address for the allowance target.
27-
constructor(address _uniswapPermit2, address _allowanceTarget) TokenCollector(_uniswapPermit2, _allowanceTarget) {}
37+
constructor(
38+
address _uniswapPermit2,
39+
address _allowanceTarget,
40+
address _owner,
41+
address[] memory _trustedSolvers
42+
) TokenCollector(_uniswapPermit2, _allowanceTarget) Ownable(_owner) {
43+
uint256 length = _trustedSolvers.length;
44+
for (uint256 i; i < length; ++i) {
45+
solvers[_trustedSolvers[i]] = true;
46+
}
47+
}
2848

2949
/// @notice Receive function to receive ETH.
3050
receive() external payable {}
3151

52+
function addSolver(address solver) external onlyOwner {
53+
if (solver == address(0)) revert ZeroAddress();
54+
55+
if (!solvers[solver]) {
56+
solvers[solver] = true;
57+
58+
emit AddSolver(solver);
59+
}
60+
}
61+
62+
function removeSolver(address solver) external onlyOwner {
63+
if (solvers[solver]) {
64+
solvers[solver] = false;
65+
66+
emit RemoveSolver(solver);
67+
}
68+
}
69+
3270
/// @inheritdoc IGenericSwap
33-
function executeSwap(GenericSwapData calldata swapData, bytes calldata takerTokenPermit) external payable returns (uint256 returnAmount) {
34-
returnAmount = _executeSwap(swapData, msg.sender, takerTokenPermit);
71+
function executeSwap(
72+
GenericSwapData calldata swapData,
73+
bytes calldata strategyData,
74+
bytes calldata takerTokenPermit
75+
) external payable returns (uint256 returnAmount) {
76+
returnAmount = _executeSwap(swapData, strategyData, msg.sender, takerTokenPermit);
3577

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

3981
/// @inheritdoc IGenericSwap
4082
function executeSwapWithSig(
4183
GenericSwapData calldata swapData,
84+
bytes calldata strategyData,
4285
bytes calldata takerTokenPermit,
4386
address taker,
4487
bytes calldata takerSig
45-
) external payable returns (uint256 returnAmount) {
88+
) external payable onlySolver returns (uint256 returnAmount) {
4689
bytes32 swapHash = getGSDataHash(swapData);
4790
bytes32 gs712Hash = getEIP712Hash(swapHash);
4891
if (filledSwap[swapHash]) revert AlreadyFilled();
4992
filledSwap[swapHash] = true;
5093
if (!SignatureValidator.validateSignature(taker, gs712Hash, takerSig)) revert InvalidSignature();
5194

52-
returnAmount = _executeSwap(swapData, taker, takerTokenPermit);
95+
returnAmount = _executeSwap(swapData, strategyData, taker, takerTokenPermit);
5396

5497
_emitGSExecuted(swapHash, swapData, taker, returnAmount);
5598
}
@@ -61,6 +104,7 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
61104
/// @return returnAmount The output amount of the swap.
62105
function _executeSwap(
63106
GenericSwapData calldata _swapData,
107+
bytes calldata strategyData,
64108
address _authorizedUser,
65109
bytes calldata _takerTokenPermit
66110
) private returns (uint256 returnAmount) {
@@ -78,7 +122,7 @@ contract GenericSwap is IGenericSwap, TokenCollector, EIP712 {
78122
_collect(_inputToken, _authorizedUser, _swapData.maker, _swapData.takerTokenAmount, _takerTokenPermit);
79123
}
80124

81-
IStrategy(_swapData.maker).executeStrategy{ value: msg.value }(_outputToken, _swapData.strategyData);
125+
IStrategy(_swapData.maker).executeStrategy{ value: msg.value }(_outputToken, strategyData);
82126

83127
returnAmount = _outputToken.getBalance(address(this));
84128
if (returnAmount > 1) {

contracts/interfaces/IGenericSwap.sol

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import { GenericSwapData } from "../libraries/GenericSwapData.sol";
88
/// @notice Interface for a generic swap contract.
99
/// @dev This interface defines functions and events related to executing swaps and handling swap errors.
1010
interface IGenericSwap {
11+
/// @notice Emitted when a new solver is added to the contract.
12+
/// @param solver The address of the solver being added.
13+
event AddSolver(address indexed solver);
14+
15+
/// @notice Emitted when a solver is removed from the contract.
16+
/// @param solver The address of the solver being removed.
17+
event RemoveSolver(address indexed solver);
18+
1119
/// @notice Event emitted when a swap is executed.
1220
/// @param swapHash The hash of the swap data.
1321
/// @param maker The address of the maker initiating the swap.
@@ -30,6 +38,10 @@ interface IGenericSwap {
3038
uint256 salt
3139
);
3240

41+
/// @notice Error to be thrown when an unauthorized address attempts to execute a swap.
42+
/// @dev This error is used to restrict access to certain functions to authorized solvers only
43+
error InvalidSolver();
44+
3345
/// @notice Error to be thrown when a swap is already filled.
3446
/// @dev This error is used when attempting to fill a swap that has already been completed.
3547
error AlreadyFilled();
@@ -60,18 +72,25 @@ interface IGenericSwap {
6072

6173
/// @notice Executes a swap using provided swap data and taker token permit.
6274
/// @param swapData The swap data containing details of the swap.
75+
/// @param strategyData The strategy data contains the details on how to perform the swap.
6376
/// @param takerTokenPermit The permit for spending taker's tokens.
6477
/// @return returnAmount The amount of tokens returned from the swap.
65-
function executeSwap(GenericSwapData calldata swapData, bytes calldata takerTokenPermit) external payable returns (uint256 returnAmount);
78+
function executeSwap(
79+
GenericSwapData calldata swapData,
80+
bytes calldata strategyData,
81+
bytes calldata takerTokenPermit
82+
) external payable returns (uint256 returnAmount);
6683

6784
/// @notice Executes a swap using provided swap data, taker token permit, taker address, and signature.
6885
/// @param swapData The swap data containing details of the swap.
86+
/// @param strategyData The strategy data contains the details on how to perform the swap.
6987
/// @param takerTokenPermit The permit for spending taker's tokens.
7088
/// @param taker The address of the taker initiating the swap.
7189
/// @param takerSig The signature of the taker authorizing the swap.
7290
/// @return returnAmount The amount of tokens returned from the swap.
7391
function executeSwapWithSig(
7492
GenericSwapData calldata swapData,
93+
bytes calldata strategyData,
7594
bytes calldata takerTokenPermit,
7695
address taker,
7796
bytes calldata takerSig

contracts/libraries/GenericSwapData.sol

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.0;
33

44
string constant GS_DATA_TYPESTRING = string(
5-
"GenericSwapData(address maker,address takerToken,uint256 takerTokenAmount,address makerToken,uint256 makerTokenAmount,uint256 minMakerTokenAmount,uint256 expiry,uint256 salt,address recipient,bytes strategyData)"
5+
"GenericSwapData(address maker,address takerToken,uint256 takerTokenAmount,address makerToken,uint256 makerTokenAmount,uint256 minMakerTokenAmount,uint256 expiry,uint256 salt,address recipient)"
66
);
77

88
bytes32 constant GS_DATA_TYPEHASH = keccak256(bytes(GS_DATA_TYPESTRING));
@@ -17,7 +17,6 @@ struct GenericSwapData {
1717
uint256 expiry;
1818
uint256 salt;
1919
address payable recipient;
20-
bytes strategyData;
2120
}
2221

2322
// solhint-disable-next-line func-visibility
@@ -34,8 +33,7 @@ function getGSDataHash(GenericSwapData memory gsData) pure returns (bytes32) {
3433
gsData.minMakerTokenAmount,
3534
gsData.expiry,
3635
gsData.salt,
37-
gsData.recipient,
38-
keccak256(gsData.strategyData)
36+
gsData.recipient
3937
)
4038
);
4139
}

snapshots/AdminManagement.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"approveTokens(): testApproveTokens": "133041",
3-
"rescueTokens(): testRescueTokens": "84308"
2+
"approveTokens(): testApproveTokens": "106201",
3+
"rescueTokens(): testRescueTokens": "59723"
44
}

snapshots/AllowanceTarget.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"pause(): testSpendFromUserToAfterUnpause": "27565",
3-
"spendFromUserTo(): testSpendFromUserTo": "61960",
4-
"spendFromUserTo(): testSpendFromUserToAfterUnpause": "61960",
5-
"spendFromUserTo(): testSpendFromUserToWithDeflationaryToken": "89862",
6-
"spendFromUserTo(): testSpendFromUserToWithNoReturnValueToken": "67278",
7-
"unpause(): testSpendFromUserToAfterUnpause": "27326"
2+
"pause(): testSpendFromUserToAfterUnpause": "6978",
3+
"spendFromUserTo(): testSpendFromUserTo": "36872",
4+
"spendFromUserTo(): testSpendFromUserToAfterUnpause": "34872",
5+
"spendFromUserTo(): testSpendFromUserToWithDeflationaryToken": "65947",
6+
"spendFromUserTo(): testSpendFromUserToWithNoReturnValueToken": "42418",
7+
"unpause(): testSpendFromUserToAfterUnpause": "2175"
88
}

snapshots/Asset.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"getBalance(): testGetBalance": "5799",
3-
"getBalance(): testGetBalance(ETH_ADDRESS)": "510",
4-
"getBalance(): testGetBalance(ZERO_ADDRESS)": "517",
5-
"isETH(): testIsETH(ETH_ADDRESS)": "306",
6-
"isETH(): testIsETH(ZERO_ADDRESS)": "313",
7-
"transferTo(): testDoNothingIfTransferToSelf": "22182",
8-
"transferTo(): testDoNothingIfTransferWithZeroAmount": "22170",
9-
"transferTo(): testTransferETH": "56716",
10-
"transferTo(): testTransferToken": "50378"
2+
"getBalance(): testGetBalance": "7087",
3+
"getBalance(): testGetBalance(ETH_ADDRESS)": "1143",
4+
"getBalance(): testGetBalance(ZERO_ADDRESS)": "1165",
5+
"isETH(): testIsETH(ETH_ADDRESS)": "724",
6+
"isETH(): testIsETH(ZERO_ADDRESS)": "746",
7+
"transferTo(): testDoNothingIfTransferToSelf": "877",
8+
"transferTo(): testDoNothingIfTransferWithZeroAmount": "877",
9+
"transferTo(): testTransferETH": "35651",
10+
"transferTo(): testTransferToken": "31566"
1111
}

snapshots/CoordinatedTaker.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"approveTokens(): testApproveTokens": "53171",
3-
"setCoordinator(): testSetCoordinator": "30166",
4-
"submitLimitOrderFill(): testFillWithETH": "189977",
5-
"submitLimitOrderFill(): testFillWithPermission": "252405"
2+
"approveTokens(): testApproveTokens": "28815",
3+
"setCoordinator(): testSetCoordinator": "8995",
4+
"submitLimitOrderFill(): testFillWithETH": "193271",
5+
"submitLimitOrderFill(): testFillWithPermission": "272753"
66
}

snapshots/EIP712.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnChain": "337",
3-
"EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnDifferentChain": "1074",
4-
"getEIP712Hash(): testGetEIP712Hash": "315"
2+
"EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnChain": "443",
3+
"EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnDifferentChain": "1404",
4+
"getEIP712Hash(): testGetEIP712Hash": "817"
55
}

snapshots/GenericSwap.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
{
2-
"executeSwap(): testGenericSwapWithUniswap": "248141",
3-
"executeSwap(): testLeaveOneWeiWithMultipleUsers(the first deposit)": "248141",
4-
"executeSwap(): testLeaveOneWeiWithMultipleUsers(the second deposit)": "204495",
5-
"executeSwap(): testSwapWithETHInput": "97813",
6-
"executeSwap(): testSwapWithETHOutput": "127973",
7-
"executeSwap(): testSwapWithLessOutputButWithinTolerance": "157429",
8-
"executeSwapWithSig(): testGenericSwapRelayed": "279472"
2+
"addSolver(): testAddSolver": "26392",
3+
"executeSwap(): testGenericSwapWithUniswap": "240706",
4+
"executeSwap(): testLeaveOneWeiWithMultipleUsers(the first deposit)": "234706",
5+
"executeSwap(): testLeaveOneWeiWithMultipleUsers(the second deposit)": "143860",
6+
"executeSwap(): testSwapWithETHInput": "61310",
7+
"executeSwap(): testSwapWithETHOutput": "89483",
8+
"executeSwap(): testSwapWithLessOutputButWithinTolerance": "117249",
9+
"executeSwapWithSig(): testGenericSwapRelayed": "270770",
10+
"removeSolver(): testRemoveSolver": "9261"
911
}

snapshots/LimitOrderSwap.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
{
2-
"cancelOrder(): testCancelOrder": "52957",
3-
"fillLimitOrder(): testFillLimitOrderWithETH": "155159",
4-
"fillLimitOrder(): testFillWithBetterTakingAmount": "212150",
5-
"fillLimitOrder(): testFillWithBetterTakingAmountButGetAdjusted": "212318",
6-
"fillLimitOrder(): testFillWithETHRefund": "162238",
7-
"fillLimitOrder(): testFillWithLargerVolumeAndSettleAsManyAsPossible": "212306",
8-
"fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder": "212150",
9-
"fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder(without makerSig)": "119214",
10-
"fillLimitOrder(): testFullyFillLimitOrder": "212150",
11-
"fillLimitOrder(): testFullyFillLimitOrderUsingAMM": "231466",
12-
"fillLimitOrder(): testPartiallyFillLimitOrder": "212150",
13-
"fillLimitOrderFullOrKill(): testFillWithFOK": "212327",
14-
"fillLimitOrderGroup(): testGroupFillRingTrade": "287118",
15-
"fillLimitOrderGroup(): testGroupFillWithPartialWETHUnwrap": "273557",
16-
"fillLimitOrderGroup(): testGroupFillWithTakerPrefundETH": "195696",
17-
"fillLimitOrderGroup(): testGroupFillWithWETHUnwrap": "195696",
18-
"fillLimitOrderGroup(): testPartialFillLargeOrderWithSmallOrders": "261176",
19-
"testGroupFillWithProfit: fillLimitOrderGroup()": "221442"
2+
"cancelOrder(): testCancelOrder": "35188",
3+
"fillLimitOrder(): testFillLimitOrderWithETH": "147904",
4+
"fillLimitOrder(): testFillWithBetterTakingAmount": "203163",
5+
"fillLimitOrder(): testFillWithBetterTakingAmountButGetAdjusted": "203649",
6+
"fillLimitOrder(): testFillWithETHRefund": "155664",
7+
"fillLimitOrder(): testFillWithLargerVolumeAndSettleAsManyAsPossible": "203649",
8+
"fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder": "220163",
9+
"fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder(without makerSig)": "66600",
10+
"fillLimitOrder(): testFullyFillLimitOrder": "203163",
11+
"fillLimitOrder(): testFullyFillLimitOrderUsingAMM": "230361",
12+
"fillLimitOrder(): testPartiallyFillLimitOrder": "203163",
13+
"fillLimitOrderFullOrKill(): testFillWithFOK": "203208",
14+
"fillLimitOrderGroup(): testGroupFillRingTrade": "312414",
15+
"fillLimitOrderGroup(): testGroupFillWithPartialWETHUnwrap": "286911",
16+
"fillLimitOrderGroup(): testGroupFillWithTakerPrefundETH": "208427",
17+
"fillLimitOrderGroup(): testGroupFillWithWETHUnwrap": "208427",
18+
"fillLimitOrderGroup(): testPartialFillLargeOrderWithSmallOrders": "275390",
19+
"testGroupFillWithProfit: fillLimitOrderGroup()": "235042"
2020
}

0 commit comments

Comments
 (0)