Skip to content

Commit 30e7715

Browse files
committed
CustomDEX adapter contract
1 parent fde3ef0 commit 30e7715

25 files changed

+9892
-0
lines changed

src/AssetSwapper.sol

Lines changed: 1268 additions & 0 deletions
Large diffs are not rendered by default.

src/SecureDEXRegistry.sol

Lines changed: 761 additions & 0 deletions
Large diffs are not rendered by default.

src/interfaces/ICurvePool.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface ICurvePool {
5+
function exchange(
6+
int128 i,
7+
int128 j,
8+
uint256 dx,
9+
uint256 min_dy
10+
) external payable returns (uint256);
11+
function get_dy(
12+
int128 i,
13+
int128 j,
14+
uint256 amount
15+
) external view returns (uint256);
16+
}

src/interfaces/IFrxETHMinter.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface IFrxETHMinter {
5+
function submitAndDeposit(
6+
address recipient
7+
) external payable returns (uint256 shares);
8+
}

src/interfaces/IQuoterV2.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface IQuoterV2 {
5+
function quoteExactInputSingle(
6+
address tokenIn,
7+
address tokenOut,
8+
uint24 fee,
9+
uint256 amountIn,
10+
uint160 sqrtPriceLimitX96
11+
) external returns (uint256 amountOut);
12+
}

src/interfaces/ISfrxETH.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface ISfrxETH {
5+
function deposit(
6+
uint256 assets,
7+
address receiver
8+
) external returns (uint256 shares);
9+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface IUniswapV3Router {
5+
struct ExactInputSingleParams {
6+
address tokenIn;
7+
address tokenOut;
8+
uint24 fee;
9+
address recipient;
10+
uint256 deadline;
11+
uint256 amountIn;
12+
uint256 amountOutMinimum;
13+
uint160 sqrtPriceLimitX96;
14+
}
15+
16+
struct ExactInputParams {
17+
bytes path;
18+
address recipient;
19+
uint256 deadline;
20+
uint256 amountIn;
21+
uint256 amountOutMinimum;
22+
}
23+
24+
function exactInputSingle(
25+
ExactInputSingleParams calldata params
26+
) external payable returns (uint256);
27+
function exactInput(
28+
ExactInputParams calldata params
29+
) external payable returns (uint256);
30+
}

src/interfaces/IWETH.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
interface IWETH {
5+
function deposit() external payable;
6+
function withdraw(uint256) external;
7+
}

test/TokenSwap/AssetSwapper.t.sol

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
import "forge-std/Test.sol";
5+
import "../../src/AssetSwapper.sol";
6+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7+
8+
contract AssetSwapperTest is Test {
9+
AssetSwapper public assetSwapper;
10+
11+
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
12+
address constant UNISWAP_ROUTER =
13+
0xE592427A0AEce92De3Edee1F18E0157C05861564;
14+
address constant mETH = 0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa;
15+
address constant rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393;
16+
address _frxETHMinter = 0xbAFA44EFE7901E04E39Dad13167D089C559c1138;
17+
18+
address owner = address(0x1234);
19+
20+
function setUp() public {
21+
vm.createSelectFork(vm.rpcUrl("https://ethereum-rpc.publicnode.com"));
22+
23+
vm.prank(owner);
24+
assetSwapper = new AssetSwapper(WETH, UNISWAP_ROUTER, _frxETHMinter);
25+
26+
// Fund owner with ETH
27+
vm.deal(owner, 100 ether);
28+
}
29+
30+
function testSwapWETHToMETH() public {
31+
uint256 amountIn = 1 ether;
32+
33+
// Wrap ETH to WETH
34+
vm.startPrank(owner);
35+
IWETH(WETH).deposit{value: amountIn}();
36+
IERC20(WETH).approve(address(assetSwapper), amountIn);
37+
38+
// Prepare swap params
39+
bytes memory routeData = abi.encode(
40+
AssetSwapper.UniswapV3Route({
41+
pool: 0x04708077eCa6bb527a5BBbD6358ffb043a9c1C14,
42+
fee: 500,
43+
isMultiHop: false,
44+
path: ""
45+
})
46+
);
47+
48+
AssetSwapper.SwapParams memory params = AssetSwapper.SwapParams({
49+
tokenIn: WETH,
50+
tokenOut: mETH,
51+
amountIn: amountIn,
52+
minAmountOut: 0.9 ether, // 10% slippage
53+
protocol: AssetSwapper.Protocol.UniswapV3,
54+
routeData: routeData
55+
});
56+
57+
// Execute swap
58+
uint256 balanceBefore = IERC20(mETH).balanceOf(owner);
59+
uint256 amountOut = assetSwapper.swapAssets(params);
60+
uint256 balanceAfter = IERC20(mETH).balanceOf(owner);
61+
62+
// Assertions
63+
assertGt(amountOut, 0, "Should receive mETH");
64+
assertEq(
65+
balanceAfter - balanceBefore,
66+
amountOut,
67+
"Balance should increase by amount out"
68+
);
69+
assertGt(amountOut, params.minAmountOut, "Should meet minimum output");
70+
71+
vm.stopPrank();
72+
}
73+
74+
function testPauseUnpause() public {
75+
vm.startPrank(owner);
76+
77+
// Test pause
78+
assetSwapper.pause();
79+
assertTrue(assetSwapper.paused(), "Should be paused");
80+
81+
// Test unpause
82+
assetSwapper.unpause();
83+
assertFalse(assetSwapper.paused(), "Should be unpaused");
84+
85+
vm.stopPrank();
86+
}
87+
88+
function testOnlyOwnerCanSwap() public {
89+
address notOwner = address(0x5678);
90+
91+
AssetSwapper.SwapParams memory params = AssetSwapper.SwapParams({
92+
tokenIn: WETH,
93+
tokenOut: mETH,
94+
amountIn: 1 ether,
95+
minAmountOut: 0.9 ether,
96+
protocol: AssetSwapper.Protocol.UniswapV3,
97+
routeData: ""
98+
});
99+
100+
vm.prank(notOwner);
101+
vm.expectRevert("Ownable: caller is not the owner");
102+
assetSwapper.swapAssets(params);
103+
}
104+
}

0 commit comments

Comments
 (0)