-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial setup for bridge and swap UniSwap Universal Router * Further optimizations * Remove unused dependencies * Sepolia Bridge --------- Co-authored-by: lz.wΔrlock() <[email protected]>
- Loading branch information
1 parent
ce40c4a
commit 9c80a2c
Showing
24 changed files
with
9,728 additions
and
1,391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol"; | ||
import "@layerzerolabs/solidity-examples/contracts/token/oft/IOFTCore.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract SwapAndBridgeUniswapV3 { | ||
uint24 public constant poolFee = 3000; // 0.3% | ||
|
||
IUniversalRouter public immutable universalRouter; | ||
address public immutable weth; | ||
IOFTCore public immutable oft; | ||
|
||
constructor(address _weth, address _oft, address _universalRouter) { | ||
require(_weth != address(0), "SwappableBridge: invalid WETH address"); | ||
require(_oft != address(0), "SwappableBridge: invalid OFT address"); | ||
require(_universalRouter != address(0), "SwappableBridge: invalid Universal Router address"); | ||
|
||
weth = _weth; | ||
oft = IOFTCore(_oft); | ||
universalRouter = IUniversalRouter(_universalRouter); | ||
} | ||
|
||
function swapAndBridge(uint amountIn, uint amountOutMin, uint16 dstChainId, address to, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams) external payable { | ||
require(to != address(0), "SwappableBridge: invalid to address"); | ||
require(msg.value >= amountIn, "SwappableBridge: not enough value sent"); | ||
|
||
// 1) commands | ||
// 0x0b == WRAP_ETH ... https://docs.uniswap.org/contracts/universal-router/technical-reference#wrap_eth | ||
// 0x00 == V3_SWAP_EXACT_IN... https://docs.uniswap.org/contracts/universal-router/technical-reference#v3_swap_exact_in | ||
bytes memory commands = hex"0b00"; | ||
|
||
// 2) inputs | ||
bytes[] memory inputs = new bytes[](2); | ||
// For weth deposit | ||
inputs[0] = abi.encode( | ||
address(universalRouter), // recipient | ||
amountIn // amount | ||
); | ||
// For token swap | ||
inputs[1] = abi.encode( | ||
address(this), // recipient | ||
amountIn, // amount input | ||
amountOutMin, // min amount output | ||
// pathway(inputToken, poolFee, outputToken) | ||
abi.encodePacked(weth, poolFee, address(oft)), | ||
false | ||
); | ||
|
||
// 3) deadline | ||
uint256 deadline = block.timestamp; | ||
|
||
// Call execute on the universal Router | ||
universalRouter.execute{value: amountIn}(commands, inputs, deadline); | ||
|
||
// check balance after to see how many tokens we received | ||
uint256 amountReceived = IERC20(address(oft)).balanceOf(address(this)); | ||
// redundant check to ensure we received enough tokens | ||
require(amountReceived >= amountOutMin, "SwappableBridge: insufficient amount received"); | ||
|
||
oft.sendFrom{value: msg.value - amountIn}( | ||
address(this), | ||
dstChainId, | ||
abi.encodePacked(to), | ||
amountReceived, | ||
refundAddress, | ||
zroPaymentAddress, | ||
adapterParams | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,84 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; | ||
import "@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol"; | ||
import "@layerzerolabs/solidity-examples/contracts/token/oft/IOFTCore.sol"; | ||
import "./IWETH.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "./INativeOFT.sol"; | ||
|
||
contract SwappableBridgeUniswapV3 { | ||
uint24 public constant poolFee = 3000; // 0.3% | ||
|
||
IWETH public immutable weth; | ||
INativeOFT public immutable nativeOft; | ||
IUniversalRouter public immutable universalRouter; | ||
address public immutable weth; | ||
IOFTCore public immutable oft; | ||
ISwapRouter public immutable swapRouter; | ||
|
||
constructor(address _weth, address _oft, address _swapRouter) { | ||
constructor(address _weth, address _oft, address _nativeOft, address _universalRouter) { | ||
require(_weth != address(0), "SwappableBridge: invalid WETH address"); | ||
require(_oft != address(0), "SwappableBridge: invalid OFT address"); | ||
require(_swapRouter != address(0), "SwappableBridge: invalid Swap Router address"); | ||
require(_nativeOft != address(0), "SwappableBridge: invalid Native OFT address"); | ||
require(_universalRouter != address(0), "SwappableBridge: invalid Universal Router address"); | ||
|
||
weth = IWETH(_weth); | ||
weth = _weth; | ||
oft = IOFTCore(_oft); | ||
swapRouter = ISwapRouter(_swapRouter); | ||
nativeOft = INativeOFT(_nativeOft); | ||
universalRouter = IUniversalRouter(_universalRouter); | ||
} | ||
|
||
function swapAndBridge(uint amountIn, uint amountOutMin, uint16 dstChainId, address to, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams) external payable { | ||
require(to != address(0), "SwappableBridge: invalid to address"); | ||
require(msg.value >= amountIn, "SwappableBridge: not enough value sent"); | ||
|
||
weth.deposit{value: amountIn}(); | ||
weth.approve(address(swapRouter), amountIn); | ||
|
||
ISwapRouter.ExactInputSingleParams memory params = | ||
ISwapRouter.ExactInputSingleParams({ | ||
tokenIn: address(weth), | ||
tokenOut: address(oft), | ||
fee: poolFee, | ||
recipient: address(this), | ||
deadline: block.timestamp, | ||
amountIn: amountIn, | ||
amountOutMinimum: amountOutMin, | ||
sqrtPriceLimitX96: 0 | ||
}); | ||
|
||
uint amountOut = swapRouter.exactInputSingle(params); | ||
oft.sendFrom{value: msg.value - amountIn}(address(this), dstChainId, abi.encodePacked(to), amountOut, refundAddress, zroPaymentAddress, adapterParams); | ||
// 1) commands | ||
// 0x0b == WRAP_ETH ... https://docs.uniswap.org/contracts/universal-router/technical-reference#wrap_eth | ||
// 0x00 == V3_SWAP_EXACT_IN... https://docs.uniswap.org/contracts/universal-router/technical-reference#v3_swap_exact_in | ||
bytes memory commands = hex"0b00"; | ||
|
||
// 2) inputs | ||
bytes[] memory inputs = new bytes[](2); | ||
// For weth deposit | ||
inputs[0] = abi.encode( | ||
address(universalRouter), // recipient | ||
amountIn // amount | ||
); | ||
// For token swap | ||
inputs[1] = abi.encode( | ||
address(this), // recipient | ||
amountIn, // amount input | ||
amountOutMin, // min amount output | ||
// pathway(inputToken, poolFee, outputToken) | ||
abi.encodePacked(weth, poolFee, address(oft)), | ||
false | ||
); | ||
|
||
// 3) deadline | ||
uint256 deadline = block.timestamp; | ||
|
||
// Call execute on the universal Router | ||
universalRouter.execute{value: amountIn}(commands, inputs, deadline); | ||
|
||
// check balance after to see how many tokens we received | ||
uint256 amountReceived = IERC20(address(oft)).balanceOf(address(this)); | ||
// redundant check to ensure we received enough tokens | ||
require(amountReceived >= amountOutMin, "SwappableBridge: insufficient amount received"); | ||
|
||
oft.sendFrom{value: msg.value - amountIn}( | ||
address(this), | ||
dstChainId, | ||
abi.encodePacked(to), | ||
amountReceived, | ||
refundAddress, | ||
zroPaymentAddress, | ||
adapterParams | ||
); | ||
} | ||
|
||
function bridge(uint amountIn, uint16 dstChainId, address to, address payable refundAddress, address zroPaymentAddress, bytes calldata adapterParams) external payable { | ||
require(to != address(0), "SwappableBridge: invalid to address"); | ||
require(msg.value >= amountIn, "SwappableBridge: not enough value sent"); | ||
|
||
nativeOft.deposit{value: amountIn}(); | ||
nativeOft.sendFrom{value: msg.value - amountIn}(address(this), dstChainId, abi.encodePacked(to), amountIn, refundAddress, zroPaymentAddress, adapterParams); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
const WETHS = require("../constants/weths.json") | ||
const OFT = { | ||
"ethereum": "0xE71bDfE1Df69284f00EE185cf0d95d0c7680c0d4", | ||
"sepolia-mainnet": "0x4f7A67464B5976d7547c860109e4432d50AfB38e", | ||
"arbitrum": "0xE71bDfE1Df69284f00EE185cf0d95d0c7680c0d4", | ||
"optimism": "0xE71bDfE1Df69284f00EE185cf0d95d0c7680c0d4", | ||
} | ||
const UNIVERSAL_ROUTER = { | ||
"ethereum": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", | ||
"sepolia-mainnet": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", | ||
"arbitrum": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", | ||
"optimism": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", | ||
} | ||
|
||
module.exports = async function ({ deployments, getNamedAccounts }) { | ||
const { deploy } = deployments | ||
const { deployer } = await getNamedAccounts() | ||
console.log(`Deployer Address: ${deployer}`) | ||
|
||
const wethAddress = WETHS[hre.network.name] | ||
console.log(`[${hre.network.name}] WETH Address: ${wethAddress}`) | ||
|
||
const oft = OFT[hre.network.name] | ||
console.log(`[${hre.network.name}] OFT Address: ${oft}`) | ||
|
||
const universalRouter = UNIVERSAL_ROUTER[hre.network.name] | ||
console.log(`[${hre.network.name}] Universal Router Address: ${universalRouter}`) | ||
|
||
await deploy("SwapAndBridgeUniswapV3", { | ||
from: deployer, | ||
args: [wethAddress, oft, universalRouter], | ||
log: true, | ||
waitConfirmations: 1, | ||
}) | ||
} | ||
|
||
module.exports.tags = ["SwapAndBridgeUniswapV3"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.