Update contracts
This commit is contained in:
parent
da50adb5e4
commit
9c1042fa80
|
@ -23,21 +23,83 @@ interface TokenBridge {
|
||||||
function completeTransferWithPayload(bytes memory encodedVm) external returns (IWormhole.VM memory);
|
function completeTransferWithPayload(bytes memory encodedVm) external returns (IWormhole.VM memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IWMATIC is IERC20 {
|
||||||
|
function deposit() external payable;
|
||||||
|
function withdraw(uint amount) external;
|
||||||
|
}
|
||||||
|
|
||||||
contract CrossChainSwapV2 {
|
contract CrossChainSwapV2 {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
using BytesLib for bytes;
|
using BytesLib for bytes;
|
||||||
uint8 public immutable typeExactIn = 1;
|
uint8 public immutable typeExactIn = 1;
|
||||||
uint8 public immutable typeExactOut = 2;
|
uint8 public immutable typeExactOut = 2;
|
||||||
uint16 public immutable expectedVaaLength = 261;
|
uint8 public immutable typeNativeSwap = 1;
|
||||||
|
uint8 public immutable typeTokenSwap = 2;
|
||||||
|
uint16 public immutable expectedVaaLength = 262;
|
||||||
IUniswapV2Router02 public immutable swapRouter;
|
IUniswapV2Router02 public immutable swapRouter;
|
||||||
address public immutable feeTokenAddress;
|
address public immutable feeTokenAddress;
|
||||||
address public immutable tokenBridgeAddress;
|
address public immutable tokenBridgeAddress;
|
||||||
|
address public immutable wrappedMatic;
|
||||||
|
|
||||||
constructor(address _swapRouterAddress, address _feeTokenAddress, address _tokenBridgeAddress) {
|
constructor(
|
||||||
|
address _swapRouterAddress,
|
||||||
|
address _feeTokenAddress,
|
||||||
|
address _tokenBridgeAddress,
|
||||||
|
address _wrappedMaticAddress
|
||||||
|
) {
|
||||||
swapRouter = IUniswapV2Router02(_swapRouterAddress);
|
swapRouter = IUniswapV2Router02(_swapRouterAddress);
|
||||||
feeTokenAddress = _feeTokenAddress;
|
feeTokenAddress = _feeTokenAddress;
|
||||||
tokenBridgeAddress = _tokenBridgeAddress;
|
tokenBridgeAddress = _tokenBridgeAddress;
|
||||||
|
wrappedMatic = _wrappedMaticAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
event SwapFromV3(address indexed _recipient, address indexed _tokenOut, address indexed _from, uint256 _amountOut);
|
||||||
|
|
||||||
|
function swapExactNativeInFromV3(
|
||||||
|
bytes calldata encodedVaa
|
||||||
|
) external payable returns (uint256[] memory amounts) {
|
||||||
|
// complete the transfer on the token bridge
|
||||||
|
IWormhole.VM memory vm = TokenBridge(tokenBridgeAddress).completeTransferWithPayload(encodedVaa);
|
||||||
|
require(vm.payload.length==expectedVaaLength, "VAA has the wrong number of bytes");
|
||||||
|
|
||||||
|
// parse the payload
|
||||||
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
|
require(payload.swapFunctionType==typeExactIn, "swap must be type ExactIn");
|
||||||
|
require(payload.swapCurrencyType==typeNativeSwap, "swap must be native to native");
|
||||||
|
|
||||||
|
// create dynamic address array - uniswap won't take fixed size array
|
||||||
|
address[] memory uniPath = new address[](2);
|
||||||
|
uniPath[0] = payload.path[0];
|
||||||
|
uniPath[1] = payload.path[1];
|
||||||
|
require(uniPath[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
require(uniPath[1]==wrappedMatic, "tokenOut must be wMATIC");
|
||||||
|
|
||||||
|
// pay relayer before attempting to do the swap
|
||||||
|
// reflect payment in second swap amount
|
||||||
|
IERC20 feeToken = IERC20(feeTokenAddress);
|
||||||
|
feeToken.safeTransfer(msg.sender, payload.relayerFee);
|
||||||
|
uint256 swapAmountLessFees = payload.swapAmount - payload.relayerFee;
|
||||||
|
|
||||||
|
// approve the router to spend tokens
|
||||||
|
TransferHelper.safeApprove(uniPath[0], address(swapRouter), swapAmountLessFees);
|
||||||
|
|
||||||
|
// try to perform the swap
|
||||||
|
try swapRouter.swapExactTokensForTokens(
|
||||||
|
swapAmountLessFees,
|
||||||
|
payload.estimatedAmount,
|
||||||
|
uniPath,
|
||||||
|
address(this), // send wMATIC to this contract, then unwrap and send to recipient
|
||||||
|
payload.deadline
|
||||||
|
) returns (uint256[] memory amounts) {
|
||||||
|
IWMATIC(wrappedMatic).withdraw(amounts[1]);
|
||||||
|
payable(payload.recipientAddress).transfer(amounts[1]);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[1], msg.sender, amounts[1]);
|
||||||
|
return amounts;
|
||||||
|
} catch {
|
||||||
|
// swap failed - return UST to recipient
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[0], msg.sender, swapAmountLessFees);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function swapExactInFromV3(
|
function swapExactInFromV3(
|
||||||
|
@ -49,7 +111,8 @@ contract CrossChainSwapV2 {
|
||||||
|
|
||||||
// parse the payload
|
// parse the payload
|
||||||
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
require(payload.swapType==typeExactIn, "swap must be type ExactIn");
|
require(payload.swapFunctionType==typeExactIn, "swap must be type ExactIn");
|
||||||
|
require(payload.swapCurrencyType==typeTokenSwap, "swap must be token to token");
|
||||||
|
|
||||||
// create dynamic address array - uniswap won't take fixed size array
|
// create dynamic address array - uniswap won't take fixed size array
|
||||||
address[] memory uniPath = new address[](2);
|
address[] memory uniPath = new address[](2);
|
||||||
|
@ -74,10 +137,12 @@ contract CrossChainSwapV2 {
|
||||||
payload.recipientAddress,
|
payload.recipientAddress,
|
||||||
payload.deadline
|
payload.deadline
|
||||||
) returns (uint256[] memory amounts) {
|
) returns (uint256[] memory amounts) {
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[1], msg.sender, amounts[1]);
|
||||||
return amounts;
|
return amounts;
|
||||||
} catch {
|
} catch {
|
||||||
// swap failed - return UST to recipient
|
// swap failed - return UST to recipient
|
||||||
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[0], msg.sender, swapAmountLessFees);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +199,8 @@ contract CrossChainSwapV2 {
|
||||||
path[3],
|
path[3],
|
||||||
swapParams.deadline,
|
swapParams.deadline,
|
||||||
swapParams.poolFee,
|
swapParams.poolFee,
|
||||||
typeExactIn
|
typeExactIn,
|
||||||
|
typeTokenSwap
|
||||||
);
|
);
|
||||||
|
|
||||||
// approve token bridge to spend feeTokens (UST)
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
@ -146,6 +212,112 @@ contract CrossChainSwapV2 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function swapExactNativeInToV3(
|
||||||
|
SwapHelper.ExactInParameters calldata swapParams,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 relayerFee,
|
||||||
|
uint16 targetChainId,
|
||||||
|
bytes32 targetContractAddress,
|
||||||
|
uint32 nonce
|
||||||
|
) external payable {
|
||||||
|
require(swapParams.amountOutMinimum > relayerFee, "insufficient amountOutMinimum to pay relayer");
|
||||||
|
require(path[0]==wrappedMatic, "tokenIn must be wMATIC for first swap");
|
||||||
|
require(path[1]==feeTokenAddress, "tokenOut must be UST for first swap");
|
||||||
|
require(msg.value > 0, "must pass non 0 MATIC amount");
|
||||||
|
|
||||||
|
// wrap MATIC
|
||||||
|
IWMATIC(wrappedMatic).deposit{
|
||||||
|
value : msg.value
|
||||||
|
}();
|
||||||
|
|
||||||
|
// peform the first swap
|
||||||
|
uint256 amountOut = _swapExactInBeforeTransfer(
|
||||||
|
msg.value,
|
||||||
|
swapParams.amountOutMinimum,
|
||||||
|
msg.sender,
|
||||||
|
path[0:2],
|
||||||
|
swapParams.deadline
|
||||||
|
);
|
||||||
|
|
||||||
|
// encode payload for second swap
|
||||||
|
bytes memory payload = abi.encodePacked(
|
||||||
|
swapParams.targetAmountOutMinimum,
|
||||||
|
swapParams.targetChainRecipient,
|
||||||
|
path[2],
|
||||||
|
path[3],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee,
|
||||||
|
typeExactIn,
|
||||||
|
typeNativeSwap
|
||||||
|
);
|
||||||
|
|
||||||
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, tokenBridgeAddress, amountOut);
|
||||||
|
|
||||||
|
// send transfer with payload to the TokenBridge
|
||||||
|
TokenBridge(tokenBridgeAddress).transferTokensWithPayload(
|
||||||
|
feeTokenAddress, amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactNativeOutFromV3(
|
||||||
|
bytes calldata encodedVaa
|
||||||
|
) external returns (uint256 amountInUsed) {
|
||||||
|
// complete the transfer on the token bridge
|
||||||
|
IWormhole.VM memory vm = TokenBridge(tokenBridgeAddress).completeTransferWithPayload(encodedVaa);
|
||||||
|
require(vm.payload.length==expectedVaaLength, "VAA has the wrong number of bytes");
|
||||||
|
|
||||||
|
// parse the payload
|
||||||
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
|
require(payload.swapFunctionType==typeExactOut, "swap must be type ExactOut");
|
||||||
|
require(payload.swapCurrencyType==typeNativeSwap, "swap must be token to token");
|
||||||
|
|
||||||
|
// amountOut is the estimated swap amount for exact out methods
|
||||||
|
uint256 amountOut = payload.estimatedAmount;
|
||||||
|
|
||||||
|
// create dynamic address array - uniswap won't take fixed size array
|
||||||
|
address[] memory uniPath = new address[](2);
|
||||||
|
uniPath[0] = payload.path[0];
|
||||||
|
uniPath[1] = payload.path[1];
|
||||||
|
require(uniPath[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
require(payload.path[1]==wrappedMatic, "tokenOut must be wMATIC");
|
||||||
|
|
||||||
|
// pay relayer before attempting to do the swap
|
||||||
|
// reflect payment in second swap amount
|
||||||
|
IERC20 feeToken = IERC20(feeTokenAddress);
|
||||||
|
feeToken.safeTransfer(msg.sender, payload.relayerFee);
|
||||||
|
uint256 maxAmountInLessFees = payload.swapAmount - payload.relayerFee;
|
||||||
|
|
||||||
|
// approve the router to spend tokens
|
||||||
|
TransferHelper.safeApprove(uniPath[0], address(swapRouter), maxAmountInLessFees);
|
||||||
|
|
||||||
|
// try to perform the swap
|
||||||
|
try swapRouter.swapTokensForExactTokens(
|
||||||
|
amountOut,
|
||||||
|
maxAmountInLessFees,
|
||||||
|
uniPath,
|
||||||
|
address(this), // send wMATIC to this contract, then unwrap and send to recipient
|
||||||
|
payload.deadline
|
||||||
|
) returns (uint256[] memory amounts) {
|
||||||
|
// amountIn used is first element in array
|
||||||
|
amountInUsed = amounts[0];
|
||||||
|
|
||||||
|
// refund recipient with any UST not used in the swap
|
||||||
|
if (amountInUsed < maxAmountInLessFees) {
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
||||||
|
}
|
||||||
|
// unwrap the wMATIC this contract received from the swap and send to the recipient
|
||||||
|
IWMATIC(wrappedMatic).withdraw(amounts[1]);
|
||||||
|
payable(payload.recipientAddress).transfer(amounts[1]);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[1], msg.sender, amounts[1]);
|
||||||
|
return amountInUsed;
|
||||||
|
} catch {
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[0], msg.sender, maxAmountInLessFees);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function swapExactOutFromV3(
|
function swapExactOutFromV3(
|
||||||
bytes calldata encodedVaa
|
bytes calldata encodedVaa
|
||||||
) external returns (uint256 amountInUsed) {
|
) external returns (uint256 amountInUsed) {
|
||||||
|
@ -155,7 +327,8 @@ contract CrossChainSwapV2 {
|
||||||
|
|
||||||
// parse the payload
|
// parse the payload
|
||||||
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
require(payload.swapType==typeExactOut, "swap must be type ExactOut");
|
require(payload.swapFunctionType==typeExactOut, "swap must be type ExactOut");
|
||||||
|
require(payload.swapCurrencyType==typeTokenSwap, "swap must be token to token");
|
||||||
|
|
||||||
// amountOut is the estimated swap amount for exact out methods
|
// amountOut is the estimated swap amount for exact out methods
|
||||||
uint256 amountOut = payload.estimatedAmount;
|
uint256 amountOut = payload.estimatedAmount;
|
||||||
|
@ -191,9 +364,11 @@ contract CrossChainSwapV2 {
|
||||||
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
||||||
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
||||||
}
|
}
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[1], msg.sender, amounts[1]);
|
||||||
return amountInUsed;
|
return amountInUsed;
|
||||||
} catch {
|
} catch {
|
||||||
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
||||||
|
emit SwapFromV3(payload.recipientAddress, uniPath[0], msg.sender, maxAmountInLessFees);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +433,8 @@ contract CrossChainSwapV2 {
|
||||||
path[3],
|
path[3],
|
||||||
swapParams.deadline,
|
swapParams.deadline,
|
||||||
swapParams.poolFee,
|
swapParams.poolFee,
|
||||||
typeExactOut
|
typeExactOut,
|
||||||
|
typeTokenSwap
|
||||||
);
|
);
|
||||||
|
|
||||||
// approve token bridge to spend feeTokens (UST)
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
@ -269,4 +445,90 @@ contract CrossChainSwapV2 {
|
||||||
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _swapExactNativeOutBeforeTransfer(
|
||||||
|
uint256 amountOut,
|
||||||
|
uint256 amountInMaximum,
|
||||||
|
address contractCaller,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 deadline
|
||||||
|
) public payable {
|
||||||
|
// path[0] is the tokenIn
|
||||||
|
IERC20 token = IERC20(path[0]);
|
||||||
|
token.safeTransferFrom(contractCaller, address(this), amountInMaximum);
|
||||||
|
|
||||||
|
// approve the router to spend tokens
|
||||||
|
TransferHelper.safeApprove(path[0], address(swapRouter), amountInMaximum);
|
||||||
|
|
||||||
|
// perform the swap
|
||||||
|
uint256[] memory amounts = swapRouter.swapTokensForExactTokens(
|
||||||
|
amountOut,
|
||||||
|
amountInMaximum,
|
||||||
|
path,
|
||||||
|
address(this),
|
||||||
|
deadline
|
||||||
|
);
|
||||||
|
|
||||||
|
// amountIn used is first element in array
|
||||||
|
uint256 amountInUsed = amounts[0];
|
||||||
|
|
||||||
|
// refund contractCaller with any amountIn that wasn't spent
|
||||||
|
if (amountInUsed < amountInMaximum) {
|
||||||
|
TransferHelper.safeApprove(path[0], address(swapRouter), 0);
|
||||||
|
// unwrap remaining matic and send to contractCaller
|
||||||
|
IWMATIC(wrappedMatic).withdraw(amountInMaximum - amountInUsed);
|
||||||
|
payable(contractCaller).transfer(amountInMaximum - amountInUsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactNativeOutToV3(
|
||||||
|
SwapHelper.ExactOutParameters calldata swapParams,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 relayerFee,
|
||||||
|
uint16 targetChainId,
|
||||||
|
bytes32 targetContractAddress,
|
||||||
|
uint32 nonce
|
||||||
|
) external payable {
|
||||||
|
require(swapParams.amountOut > relayerFee, "insufficient amountOut to pay relayer");
|
||||||
|
require(path[0]==wrappedMatic, "tokenIn must be wMATIC for first swap");
|
||||||
|
require(path[1]==feeTokenAddress, "tokenOut must be UST for first swap");
|
||||||
|
require(msg.value > 0, "must pass non 0 MATIC amount");
|
||||||
|
|
||||||
|
// wrap MATIC
|
||||||
|
IWMATIC(wrappedMatic).deposit{
|
||||||
|
value : msg.value
|
||||||
|
}();
|
||||||
|
|
||||||
|
// peform the first swap
|
||||||
|
_swapExactNativeOutBeforeTransfer(
|
||||||
|
swapParams.amountOut,
|
||||||
|
msg.value, // MATIC value sent in transaction is the maximumAmountIn
|
||||||
|
msg.sender,
|
||||||
|
path[0:2],
|
||||||
|
swapParams.deadline
|
||||||
|
);
|
||||||
|
|
||||||
|
// encode payload for second swap
|
||||||
|
bytes memory payload = abi.encodePacked(
|
||||||
|
swapParams.targetAmountOut,
|
||||||
|
swapParams.targetChainRecipient,
|
||||||
|
path[2],
|
||||||
|
path[3],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee,
|
||||||
|
typeExactOut,
|
||||||
|
typeNativeSwap
|
||||||
|
);
|
||||||
|
|
||||||
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, tokenBridgeAddress, swapParams.amountOut);
|
||||||
|
|
||||||
|
// send transfer with payload to the TokenBridge
|
||||||
|
TokenBridge(tokenBridgeAddress).transferTokensWithPayload(
|
||||||
|
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to accept ETH sends to unwrap WETH
|
||||||
|
receive() external payable {}
|
||||||
}
|
}
|
|
@ -23,21 +23,88 @@ interface TokenBridge {
|
||||||
function completeTransferWithPayload(bytes memory encodedVm) external returns (IWormhole.VM memory);
|
function completeTransferWithPayload(bytes memory encodedVm) external returns (IWormhole.VM memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IWETH is IERC20 {
|
||||||
|
function withdraw(uint amount) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IUniswapRouter is ISwapRouter {
|
||||||
|
function refundETH() external payable;
|
||||||
|
}
|
||||||
|
|
||||||
contract CrossChainSwapV3 {
|
contract CrossChainSwapV3 {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
using BytesLib for bytes;
|
using BytesLib for bytes;
|
||||||
uint8 public immutable typeExactIn = 1;
|
uint8 public immutable typeExactIn = 1;
|
||||||
uint8 public immutable typeExactOut = 2;
|
uint8 public immutable typeExactOut = 2;
|
||||||
uint16 public immutable expectedVaaLength = 261;
|
uint8 public immutable typeNativeSwap = 1;
|
||||||
ISwapRouter public immutable swapRouter;
|
uint8 public immutable typeTokenSwap = 2;
|
||||||
|
uint16 public immutable expectedVaaLength = 262;
|
||||||
|
IUniswapRouter public immutable swapRouter;
|
||||||
address public immutable feeTokenAddress;
|
address public immutable feeTokenAddress;
|
||||||
address public immutable tokenBridgeAddress;
|
address public immutable tokenBridgeAddress;
|
||||||
|
address public immutable wrappedEth;
|
||||||
|
|
||||||
constructor(address _swapRouterAddress, address _feeTokenAddress, address _tokenBridgeAddress) {
|
constructor(
|
||||||
swapRouter = ISwapRouter(_swapRouterAddress);
|
address _swapRouterAddress,
|
||||||
|
address _feeTokenAddress,
|
||||||
|
address _tokenBridgeAddress,
|
||||||
|
address _wrappedEth
|
||||||
|
) {
|
||||||
|
swapRouter = IUniswapRouter(_swapRouterAddress);
|
||||||
feeTokenAddress = _feeTokenAddress;
|
feeTokenAddress = _feeTokenAddress;
|
||||||
tokenBridgeAddress = _tokenBridgeAddress;
|
tokenBridgeAddress = _tokenBridgeAddress;
|
||||||
|
wrappedEth = _wrappedEth;
|
||||||
|
}
|
||||||
|
|
||||||
|
event SwapFromV2(address indexed _recipient, address indexed _tokenOut, address indexed _from, uint256 _amountOut);
|
||||||
|
|
||||||
|
function swapExactNativeInFromV2(
|
||||||
|
bytes calldata encodedVaa
|
||||||
|
) external returns (uint256 amountOut) {
|
||||||
|
// complete the transfer on the token bridge
|
||||||
|
IWormhole.VM memory vm = TokenBridge(tokenBridgeAddress).completeTransferWithPayload(encodedVaa);
|
||||||
|
require(vm.payload.length==expectedVaaLength, "VAA has the wrong number of bytes");
|
||||||
|
|
||||||
|
// parse the payload
|
||||||
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
|
require(payload.swapFunctionType==typeExactIn, "swap must be type ExactIn");
|
||||||
|
require(payload.swapCurrencyType==typeNativeSwap, "swap must be native to native");
|
||||||
|
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
require(payload.path[1]==wrappedEth, "tokenOut must be wETH");
|
||||||
|
|
||||||
|
// pay relayer before attempting to do the swap
|
||||||
|
// reflect payment in second swap amount
|
||||||
|
IERC20 feeToken = IERC20(feeTokenAddress);
|
||||||
|
feeToken.safeTransfer(msg.sender, payload.relayerFee);
|
||||||
|
uint256 swapAmountLessFees = payload.swapAmount - payload.relayerFee;
|
||||||
|
|
||||||
|
// approve the router to spend tokens based on amountIn
|
||||||
|
TransferHelper.safeApprove(payload.path[0], address(swapRouter), swapAmountLessFees);
|
||||||
|
|
||||||
|
// set swap options with user params
|
||||||
|
ISwapRouter.ExactInputSingleParams memory params =
|
||||||
|
ISwapRouter.ExactInputSingleParams({
|
||||||
|
tokenIn: payload.path[0],
|
||||||
|
tokenOut: payload.path[1],
|
||||||
|
fee: payload.poolFee,
|
||||||
|
recipient: address(this), // send wETH to this contract, then unwrap and send to recipient
|
||||||
|
deadline: payload.deadline,
|
||||||
|
amountIn: swapAmountLessFees,
|
||||||
|
amountOutMinimum: payload.estimatedAmount,
|
||||||
|
sqrtPriceLimitX96: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// the call to `exactInputSingle` executes the swap
|
||||||
|
try swapRouter.exactInputSingle(params) returns (uint256 amountOut) {
|
||||||
|
IWETH(wrappedEth).withdraw(amountOut);
|
||||||
|
payable(payload.recipientAddress).transfer(amountOut);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[1], msg.sender, amountOut);
|
||||||
|
return amountOut;
|
||||||
|
} catch {
|
||||||
|
// swap failed - return UST to recipient
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[0], msg.sender, swapAmountLessFees);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function swapExactInFromV2(
|
function swapExactInFromV2(
|
||||||
|
@ -49,7 +116,8 @@ contract CrossChainSwapV3 {
|
||||||
|
|
||||||
// parse the payload
|
// parse the payload
|
||||||
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
require(payload.swapType==typeExactIn, "swap must be type ExactIn");
|
require(payload.swapFunctionType==typeExactIn, "swap must be type ExactIn");
|
||||||
|
require(payload.swapCurrencyType==typeTokenSwap, "swap must be token to token");
|
||||||
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
|
||||||
// pay relayer before attempting to do the swap
|
// pay relayer before attempting to do the swap
|
||||||
|
@ -76,10 +144,12 @@ contract CrossChainSwapV3 {
|
||||||
|
|
||||||
// the call to `exactInputSingle` executes the swap
|
// the call to `exactInputSingle` executes the swap
|
||||||
try swapRouter.exactInputSingle(params) returns (uint256 amountOut) {
|
try swapRouter.exactInputSingle(params) returns (uint256 amountOut) {
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[1], msg.sender, amountOut);
|
||||||
return amountOut;
|
return amountOut;
|
||||||
} catch {
|
} catch {
|
||||||
// swap failed - return UST to recipient
|
// swap failed - return UST to recipient
|
||||||
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
feeToken.safeTransfer(payload.recipientAddress, swapAmountLessFees);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[0], msg.sender, swapAmountLessFees);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +215,8 @@ contract CrossChainSwapV3 {
|
||||||
path[3],
|
path[3],
|
||||||
swapParams.deadline,
|
swapParams.deadline,
|
||||||
swapParams.poolFee,
|
swapParams.poolFee,
|
||||||
typeExactIn
|
typeExactIn,
|
||||||
|
typeTokenSwap
|
||||||
);
|
);
|
||||||
|
|
||||||
// approve token bridge to spend feeTokens (UST)
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
@ -157,6 +228,129 @@ contract CrossChainSwapV3 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _swapExactNativeInBeforeTransfer(
|
||||||
|
uint256 amountOutMinimum,
|
||||||
|
address contractCaller,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 deadline,
|
||||||
|
uint24 poolFee
|
||||||
|
) public payable returns (uint256 amountOut) {
|
||||||
|
// set swap options with user params
|
||||||
|
ISwapRouter.ExactInputSingleParams memory params =
|
||||||
|
ISwapRouter.ExactInputSingleParams({
|
||||||
|
tokenIn: path[0],
|
||||||
|
tokenOut: path[1],
|
||||||
|
fee: poolFee,
|
||||||
|
recipient: address(this),
|
||||||
|
deadline: deadline,
|
||||||
|
amountIn: msg.value,
|
||||||
|
amountOutMinimum: amountOutMinimum,
|
||||||
|
sqrtPriceLimitX96: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// the call to `exactInputSingle` executes the swap
|
||||||
|
amountOut = swapRouter.exactInputSingle{ value: msg.value }(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactNativeInToV2(
|
||||||
|
SwapHelper.ExactInParameters calldata swapParams,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 relayerFee,
|
||||||
|
uint16 targetChainId,
|
||||||
|
bytes32 targetContractAddress,
|
||||||
|
uint32 nonce
|
||||||
|
) external payable {
|
||||||
|
require(swapParams.amountOutMinimum > relayerFee, "insufficient amountOutMinimum to pay relayer");
|
||||||
|
require(path[0]==wrappedEth, "tokenIn must be wETH for first swap");
|
||||||
|
require(path[1]==feeTokenAddress, "tokenOut must be UST for first swap");
|
||||||
|
require(msg.value > 0, "must pass non 0 ETH amount");
|
||||||
|
|
||||||
|
// peform the first swap
|
||||||
|
uint256 amountOut = _swapExactNativeInBeforeTransfer(
|
||||||
|
swapParams.amountOutMinimum,
|
||||||
|
msg.sender,
|
||||||
|
path[0:2],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee
|
||||||
|
);
|
||||||
|
|
||||||
|
// encode payload for second swap
|
||||||
|
bytes memory payload = abi.encodePacked(
|
||||||
|
swapParams.targetAmountOutMinimum,
|
||||||
|
swapParams.targetChainRecipient,
|
||||||
|
path[2],
|
||||||
|
path[3],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee,
|
||||||
|
typeExactIn,
|
||||||
|
typeNativeSwap
|
||||||
|
);
|
||||||
|
|
||||||
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, tokenBridgeAddress, amountOut);
|
||||||
|
|
||||||
|
// send transfer with payload to the TokenBridge
|
||||||
|
TokenBridge(tokenBridgeAddress).transferTokensWithPayload(
|
||||||
|
feeTokenAddress, amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactNativeOutFromV2(
|
||||||
|
bytes calldata encodedVaa
|
||||||
|
) external returns (uint256 amountInUsed) {
|
||||||
|
// complete the transfer on the token bridge
|
||||||
|
IWormhole.VM memory vm = TokenBridge(tokenBridgeAddress).completeTransferWithPayload(encodedVaa);
|
||||||
|
require(vm.payload.length==expectedVaaLength, "VAA has the wrong number of bytes");
|
||||||
|
|
||||||
|
// parse the payload
|
||||||
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
|
require(payload.swapFunctionType==typeExactOut, "swap must be type ExactOut");
|
||||||
|
require(payload.swapCurrencyType==typeNativeSwap, "swap must be token to token");
|
||||||
|
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
require(payload.path[1]==wrappedEth, "tokenOut must be wETH");
|
||||||
|
|
||||||
|
// amountOut is the estimated swap amount for exact out methods
|
||||||
|
uint256 amountOut = payload.estimatedAmount;
|
||||||
|
|
||||||
|
// pay relayer before attempting to do the swap
|
||||||
|
// reflect payment in second swap amount
|
||||||
|
IERC20 feeToken = IERC20(feeTokenAddress);
|
||||||
|
feeToken.safeTransfer(msg.sender, payload.relayerFee);
|
||||||
|
uint256 maxAmountInLessFees = payload.swapAmount - payload.relayerFee;
|
||||||
|
|
||||||
|
// approve the router to spend swapAmount - which is maxAmountIn in payload
|
||||||
|
TransferHelper.safeApprove(payload.path[0], address(swapRouter), maxAmountInLessFees);
|
||||||
|
|
||||||
|
// set swap options with user params
|
||||||
|
ISwapRouter.ExactOutputSingleParams memory params =
|
||||||
|
ISwapRouter.ExactOutputSingleParams({
|
||||||
|
tokenIn: payload.path[0],
|
||||||
|
tokenOut: payload.path[1],
|
||||||
|
fee: payload.poolFee,
|
||||||
|
recipient: address(this), // send wETH to this contract, then unwrap and send to recipient
|
||||||
|
deadline: payload.deadline,
|
||||||
|
amountOut: amountOut,
|
||||||
|
amountInMaximum: maxAmountInLessFees,
|
||||||
|
sqrtPriceLimitX96: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
try swapRouter.exactOutputSingle(params) returns (uint256 amountInUsed) {
|
||||||
|
// refund recipient with any UST not used in the swap
|
||||||
|
if (amountInUsed < maxAmountInLessFees) {
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
||||||
|
}
|
||||||
|
// unwrap the wETH this contract received from the swap and send to recipient
|
||||||
|
IWETH(wrappedEth).withdraw(amountOut);
|
||||||
|
payable(payload.recipientAddress).transfer(amountOut);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[1], msg.sender, amountOut);
|
||||||
|
return amountInUsed;
|
||||||
|
} catch {
|
||||||
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[0], msg.sender, maxAmountInLessFees);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function swapExactOutFromV2(
|
function swapExactOutFromV2(
|
||||||
bytes calldata encodedVaa
|
bytes calldata encodedVaa
|
||||||
) external returns (uint256 amountInUsed) {
|
) external returns (uint256 amountInUsed) {
|
||||||
|
@ -166,7 +360,8 @@ contract CrossChainSwapV3 {
|
||||||
|
|
||||||
// parse the payload
|
// parse the payload
|
||||||
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
SwapHelper.DecodedVaaParameters memory payload = SwapHelper.decodeVaaPayload(vm);
|
||||||
require(payload.swapType==typeExactOut, "swap must be type ExactOut");
|
require(payload.swapFunctionType==typeExactOut, "swap must be type ExactOut");
|
||||||
|
require(payload.swapCurrencyType==typeTokenSwap, "swap must be token to token");
|
||||||
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
|
||||||
|
|
||||||
// amountOut is the estimated swap amount for exact out methods
|
// amountOut is the estimated swap amount for exact out methods
|
||||||
|
@ -200,9 +395,11 @@ contract CrossChainSwapV3 {
|
||||||
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
TransferHelper.safeApprove(feeTokenAddress, address(swapRouter), 0);
|
||||||
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees - amountInUsed);
|
||||||
}
|
}
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[1], msg.sender, amountOut);
|
||||||
return amountInUsed;
|
return amountInUsed;
|
||||||
} catch {
|
} catch {
|
||||||
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
feeToken.safeTransfer(payload.recipientAddress, maxAmountInLessFees);
|
||||||
|
emit SwapFromV2(payload.recipientAddress, payload.path[0], msg.sender, maxAmountInLessFees);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +470,8 @@ contract CrossChainSwapV3 {
|
||||||
path[3],
|
path[3],
|
||||||
swapParams.deadline,
|
swapParams.deadline,
|
||||||
swapParams.poolFee,
|
swapParams.poolFee,
|
||||||
typeExactOut
|
typeExactOut,
|
||||||
|
typeTokenSwap
|
||||||
);
|
);
|
||||||
|
|
||||||
// approve token bridge to spend feeTokens (UST)
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
@ -284,4 +482,86 @@ contract CrossChainSwapV3 {
|
||||||
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _swapExactNativeOutBeforeTransfer(
|
||||||
|
uint256 amountOut,
|
||||||
|
uint256 amountInMaximum,
|
||||||
|
address contractCaller,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 deadline,
|
||||||
|
uint24 poolFee
|
||||||
|
) public payable {
|
||||||
|
// set swap options with user params
|
||||||
|
ISwapRouter.ExactOutputSingleParams memory params =
|
||||||
|
ISwapRouter.ExactOutputSingleParams({
|
||||||
|
tokenIn: path[0],
|
||||||
|
tokenOut: path[1],
|
||||||
|
fee: poolFee,
|
||||||
|
recipient: address(this),
|
||||||
|
deadline: deadline,
|
||||||
|
amountOut: amountOut,
|
||||||
|
amountInMaximum: amountInMaximum,
|
||||||
|
sqrtPriceLimitX96: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// executes the swap returning the amountIn needed to spend to receive the desired amountOut
|
||||||
|
uint256 amountInUsed = swapRouter.exactOutputSingle{ value: amountInMaximum }(params);
|
||||||
|
|
||||||
|
// ask for our money back -_-
|
||||||
|
swapRouter.refundETH();
|
||||||
|
|
||||||
|
// refund contractCaller with any amountIn that's not spent
|
||||||
|
if (amountInUsed < amountInMaximum) {
|
||||||
|
TransferHelper.safeApprove(path[0], address(swapRouter), 0);
|
||||||
|
// refund contractCaller with unused ether
|
||||||
|
payable(contractCaller).transfer(amountInMaximum - amountInUsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapExactNativeOutToV2(
|
||||||
|
SwapHelper.ExactOutParameters calldata swapParams,
|
||||||
|
address[] calldata path,
|
||||||
|
uint256 relayerFee,
|
||||||
|
uint16 targetChainId, // make sure target contract address belongs to targeChainId
|
||||||
|
bytes32 targetContractAddress,
|
||||||
|
uint32 nonce
|
||||||
|
) external payable {
|
||||||
|
require(swapParams.amountOut > relayerFee, "insufficient amountOut to pay relayer");
|
||||||
|
require(path[0]==wrappedEth, "tokenIn must be wETH for first swap");
|
||||||
|
require(path[1]==feeTokenAddress, "tokenOut must be UST for first swap");
|
||||||
|
require(msg.value > 0, "must pass non 0 ETH amount");
|
||||||
|
|
||||||
|
// peform the first swap
|
||||||
|
_swapExactNativeOutBeforeTransfer(
|
||||||
|
swapParams.amountOut,
|
||||||
|
msg.value, // ETH value sent in transaction is the maximumAmountIn
|
||||||
|
msg.sender,
|
||||||
|
path[0:2],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee
|
||||||
|
);
|
||||||
|
|
||||||
|
// encode payload for second swap
|
||||||
|
bytes memory payload = abi.encodePacked(
|
||||||
|
swapParams.targetAmountOut,
|
||||||
|
swapParams.targetChainRecipient,
|
||||||
|
path[2],
|
||||||
|
path[3],
|
||||||
|
swapParams.deadline,
|
||||||
|
swapParams.poolFee,
|
||||||
|
typeExactOut,
|
||||||
|
typeNativeSwap
|
||||||
|
);
|
||||||
|
|
||||||
|
// approve token bridge to spend feeTokens (UST)
|
||||||
|
TransferHelper.safeApprove(feeTokenAddress, tokenBridgeAddress, swapParams.amountOut);
|
||||||
|
|
||||||
|
// send transfer with payload to the TokenBridge
|
||||||
|
TokenBridge(tokenBridgeAddress).transferTokensWithPayload(
|
||||||
|
feeTokenAddress, swapParams.amountOut, targetChainId, targetContractAddress, relayerFee, nonce, payload
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to accept ETH sends to unwrap WETH
|
||||||
|
receive() external payable {}
|
||||||
}
|
}
|
Loading…
Reference in New Issue