113 lines
4.3 KiB
Solidity
113 lines
4.3 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
import "../UniversalCCIPBridge.sol";
|
|
import "./EnhancedSwapRouter.sol";
|
|
|
|
/**
|
|
* @title SwapBridgeSwapCoordinator
|
|
* @notice Coordinates source-chain swap (token A -> bridgeable token) then CCIP bridge in one flow
|
|
* @dev User approves coordinator for sourceToken; coordinator swaps via EnhancedSwapRouter (Dodoex) then calls UniversalCCIPBridge
|
|
*/
|
|
contract SwapBridgeSwapCoordinator is ReentrancyGuard {
|
|
using SafeERC20 for IERC20;
|
|
|
|
EnhancedSwapRouter public immutable swapRouter;
|
|
UniversalCCIPBridge public immutable bridge;
|
|
|
|
event SwapAndBridgeExecuted(
|
|
address indexed sourceToken,
|
|
address indexed bridgeableToken,
|
|
uint256 amountIn,
|
|
uint256 amountBridged,
|
|
uint64 destinationChain,
|
|
address indexed recipient,
|
|
bytes32 messageId
|
|
);
|
|
|
|
error ZeroAddress();
|
|
error ZeroAmount();
|
|
error InsufficientOutput();
|
|
error SameToken();
|
|
|
|
constructor(address _swapRouter, address _bridge) {
|
|
if (_swapRouter == address(0) || _bridge == address(0)) revert ZeroAddress();
|
|
swapRouter = EnhancedSwapRouter(payable(_swapRouter));
|
|
bridge = UniversalCCIPBridge(payable(_bridge));
|
|
}
|
|
|
|
/**
|
|
* @notice Swap source token to bridgeable token then bridge to destination chain
|
|
* @param sourceToken Token user is sending (will be swapped if different from bridgeableToken)
|
|
* @param amountIn Amount of source token
|
|
* @param amountOutMin Minimum bridgeable token from swap (slippage protection; ignored if sourceToken == bridgeableToken)
|
|
* @param bridgeableToken Token to bridge (WETH or stablecoin); must be registered on bridge
|
|
* @param destinationChainSelector CCIP destination chain selector
|
|
* @param recipient Recipient on destination chain
|
|
* @param assetType Asset type hash for bridge (from UniversalAssetRegistry)
|
|
* @param usePMM Whether bridge should use PMM liquidity
|
|
* @param useVault Whether bridge should use vault
|
|
*/
|
|
function swapAndBridge(
|
|
address sourceToken,
|
|
uint256 amountIn,
|
|
uint256 amountOutMin,
|
|
address bridgeableToken,
|
|
uint64 destinationChainSelector,
|
|
address recipient,
|
|
bytes32 assetType,
|
|
bool usePMM,
|
|
bool useVault
|
|
) external payable nonReentrant returns (bytes32 messageId) {
|
|
if (amountIn == 0) revert ZeroAmount();
|
|
if (sourceToken == address(0) || bridgeableToken == address(0) || recipient == address(0)) revert ZeroAddress();
|
|
|
|
uint256 amountToBridge;
|
|
|
|
if (sourceToken == bridgeableToken) {
|
|
IERC20(sourceToken).safeTransferFrom(msg.sender, address(this), amountIn);
|
|
amountToBridge = amountIn;
|
|
} else {
|
|
IERC20(sourceToken).safeTransferFrom(msg.sender, address(this), amountIn);
|
|
IERC20(sourceToken).approve(address(swapRouter), amountIn);
|
|
amountToBridge = swapRouter.swapTokenToToken(sourceToken, bridgeableToken, amountIn, amountOutMin);
|
|
if (amountToBridge < amountOutMin) revert InsufficientOutput();
|
|
}
|
|
|
|
UniversalCCIPBridge.BridgeOperation memory op = UniversalCCIPBridge.BridgeOperation({
|
|
token: bridgeableToken,
|
|
amount: amountToBridge,
|
|
destinationChain: destinationChainSelector,
|
|
recipient: recipient,
|
|
assetType: assetType,
|
|
usePMM: usePMM,
|
|
useVault: useVault,
|
|
complianceProof: "",
|
|
vaultInstructions: ""
|
|
});
|
|
|
|
IERC20(bridgeableToken).approve(address(bridge), amountToBridge);
|
|
(bool ok, bytes memory result) = address(bridge).call{value: msg.value}(
|
|
abi.encodeWithSelector(bridge.bridge.selector, op)
|
|
);
|
|
require(ok, "SwapBridgeSwapCoordinator: bridge failed");
|
|
messageId = abi.decode(result, (bytes32));
|
|
|
|
emit SwapAndBridgeExecuted(
|
|
sourceToken,
|
|
bridgeableToken,
|
|
amountIn,
|
|
amountToBridge,
|
|
destinationChainSelector,
|
|
recipient,
|
|
messageId
|
|
);
|
|
return messageId;
|
|
}
|
|
|
|
receive() external payable {}
|
|
}
|