Compare commits

...

3 Commits

Author SHA1 Message Date
Kevin Peters 2c062ef7f3 Set Avalanche confirms to 1 2022-05-20 16:26:34 +00:00
dsterioti af6fa376a6
Merge pull request #2 from certusone/evm-payload3-update
Update contracts to interact with new payload3 version
2022-03-23 16:37:14 -05:00
Drew Sterioti cb9cce3f89 Update contracts to interact with new payload3 version 2022-03-23 21:33:25 +00:00
5 changed files with 204 additions and 101 deletions

View File

@ -23,8 +23,9 @@ interface TokenBridge {
bytes memory payload
) external payable returns (uint64);
function completeTransferWithPayload(
bytes memory encodedVm
) external returns (IWormhole.VM memory);
bytes memory encodedVm,
address feeRecipient
) external returns (bytes memory);
}
@ -77,21 +78,22 @@ contract CrossChainSwapV2 {
function _getParsedPayload(
bytes calldata encodedVaa,
uint8 swapFunctionType,
uint8 swapCurrencyType
uint8 swapCurrencyType,
address feeRecipient
) private returns (SwapHelper.DecodedVaaParameters memory payload) {
// complete the transfer on the token bridge
IWormhole.VM memory vm = TokenBridge(
bytes memory vmPayload = TokenBridge(
tokenBridgeAddress
).completeTransferWithPayload(encodedVaa);
).completeTransferWithPayload(encodedVaa, feeRecipient);
// make sure payload is the right size
require(
vm.payload.length==expectedVaaLength,
vmPayload.length==expectedVaaLength,
"VAA has the wrong number of bytes"
);
// parse the payload
payload = SwapHelper.decodeVaaPayload(vm);
payload = SwapHelper.decodeVaaPayload(vmPayload);
// sanity check payload parameters
require(
@ -108,14 +110,32 @@ contract CrossChainSwapV2 {
function recvAndSwapExactNativeIn(
bytes calldata encodedVaa
) external payable returns (uint256[] memory amounts) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactIn,
typeNativeSwap
typeNativeSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 swapAmountLessFees = balanceAfter - balanceBefore;
// create dynamic address array
// uniswap won't take fixed size array
address[] memory uniPath = new address[](2);
@ -132,12 +152,6 @@ contract CrossChainSwapV2 {
"tokenOut must be wrapped native asset"
);
// 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],
@ -169,7 +183,7 @@ contract CrossChainSwapV2 {
return amounts;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
swapAmountLessFees
);
@ -190,14 +204,32 @@ contract CrossChainSwapV2 {
function recvAndSwapExactIn(
bytes calldata encodedVaa
) external returns (uint256[] memory amounts) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch the parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactIn,
typeTokenSwap
typeTokenSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 swapAmountLessFees = balanceAfter - balanceBefore;
// create dynamic address array - uniswap won't take fixed size array
address[] memory uniPath = new address[](2);
uniPath[0] = payload.path[0];
@ -206,14 +238,6 @@ contract CrossChainSwapV2 {
// make sure first element in path is UST
require(uniPath[0]==feeTokenAddress, "tokenIn must be UST");
// 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],
@ -241,7 +265,7 @@ contract CrossChainSwapV2 {
return amounts;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
swapAmountLessFees
);
@ -430,13 +454,31 @@ contract CrossChainSwapV2 {
function recvAndSwapExactNativeOut(
bytes calldata encodedVaa
) external returns (uint256 amountInUsed) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactOut,
typeNativeSwap
typeNativeSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 maxAmountInLessFees = balanceAfter - balanceBefore;
// amountOut is the estimated swap amount for exact out methods
uint256 amountOut = payload.estimatedAmount;
@ -455,12 +497,6 @@ contract CrossChainSwapV2 {
payload.path[1]==wrappedNative,
"tokenOut must be wrapped native asset"
);
// 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(
@ -487,7 +523,7 @@ contract CrossChainSwapV2 {
address(swapRouter),
0
);
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees - amountInUsed
);
@ -509,7 +545,7 @@ contract CrossChainSwapV2 {
return amountInUsed;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees
);
@ -530,13 +566,31 @@ contract CrossChainSwapV2 {
function recvAndSwapExactOut(
bytes calldata encodedVaa
) external returns (uint256 amountInUsed) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactOut,
typeTokenSwap
typeTokenSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 maxAmountInLessFees = balanceAfter - balanceBefore;
// amountOut is the estimated swap amount for exact out methods
uint256 amountOut = payload.estimatedAmount;
@ -546,12 +600,6 @@ contract CrossChainSwapV2 {
uniPath[0] = payload.path[0];
uniPath[1] = payload.path[1];
require(uniPath[0]==feeTokenAddress, "tokenIn must be UST");
// 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(
@ -578,7 +626,7 @@ contract CrossChainSwapV2 {
address(swapRouter),
0
);
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees - amountInUsed
);
@ -596,7 +644,7 @@ contract CrossChainSwapV2 {
return amountInUsed;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees
);

View File

@ -23,8 +23,9 @@ interface TokenBridge {
bytes memory payload
) external payable returns (uint64);
function completeTransferWithPayload(
bytes memory encodedVm
) external returns (IWormhole.VM memory);
bytes memory encodedVm,
address feeRecipient
) external returns (bytes memory);
}
@ -81,21 +82,22 @@ contract CrossChainSwapV3 {
function _getParsedPayload(
bytes calldata encodedVaa,
uint8 swapFunctionType,
uint8 swapCurrencyType
uint8 swapCurrencyType,
address feeRecipient
) private returns (SwapHelper.DecodedVaaParameters memory payload) {
// complete the transfer on the token bridge
IWormhole.VM memory vm = TokenBridge(
bytes memory vmPayload = TokenBridge(
tokenBridgeAddress
).completeTransferWithPayload(encodedVaa);
).completeTransferWithPayload(encodedVaa, feeRecipient);
// make sure payload is the right size
require(
vm.payload.length==expectedVaaLength,
vmPayload.length==expectedVaaLength,
"VAA has the wrong number of bytes"
);
// parse the payload
payload = SwapHelper.decodeVaaPayload(vm);
payload = SwapHelper.decodeVaaPayload(vmPayload);
// sanity check payload parameters
require(
@ -112,14 +114,31 @@ contract CrossChainSwapV3 {
function recvAndSwapExactNativeIn(
bytes calldata encodedVaa
) external returns (uint256 amountOut) {
// redeem and fetch parsed payload
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactIn,
typeNativeSwap
typeNativeSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 swapAmountLessFees = balanceAfter - balanceBefore;
// sanity check path
require(
payload.path[0]==feeTokenAddress,
@ -130,12 +149,6 @@ contract CrossChainSwapV3 {
"tokenOut must be wrapped Native"
);
// 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(
payload.path[0],
@ -174,7 +187,7 @@ contract CrossChainSwapV3 {
return amountOut;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
swapAmountLessFees
);
@ -195,23 +208,35 @@ contract CrossChainSwapV3 {
function recvAndSwapExactIn(
bytes calldata encodedVaa
) external returns (uint256 amountOut) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch the parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactIn,
typeTokenSwap
typeTokenSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 swapAmountLessFees = balanceAfter - balanceBefore;
// check path to see if first element is the feeToken
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
// 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(
payload.path[0],
@ -246,7 +271,7 @@ contract CrossChainSwapV3 {
return amountOut;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
swapAmountLessFees
);
@ -448,14 +473,32 @@ contract CrossChainSwapV3 {
function recvAndSwapExactNativeOut(
bytes calldata encodedVaa
) external returns (uint256 amountInUsed) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactOut,
typeNativeSwap
typeNativeSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 maxAmountInLessFees = balanceAfter - balanceBefore;
// sanity check path
require(
payload.path[0]==feeTokenAddress,
@ -469,12 +512,6 @@ contract CrossChainSwapV3 {
// 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 tokens
TransferHelper.safeApprove(
payload.path[0],
@ -504,7 +541,7 @@ contract CrossChainSwapV3 {
address(swapRouter),
0
);
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees - amountInUsed
);
@ -526,7 +563,7 @@ contract CrossChainSwapV3 {
return amountInUsed;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees
);
@ -547,13 +584,31 @@ contract CrossChainSwapV3 {
function recvAndSwapExactOut(
bytes calldata encodedVaa
) external returns (uint256 amountInUsed) {
// check token balance before redeeming the payload
(,bytes memory queriedBalanceBefore) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceBefore = abi.decode(queriedBalanceBefore, (uint256));
// redeem and fetch parsed payload
SwapHelper.DecodedVaaParameters memory payload =
_getParsedPayload(
encodedVaa,
typeExactOut,
typeTokenSwap
typeTokenSwap,
msg.sender // feeRecipient
);
// query token balance after redeeming the payload
(,bytes memory queriedBalanceAfter) = feeTokenAddress.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector,
address(this)
));
uint256 balanceAfter = abi.decode(queriedBalanceAfter, (uint256));
// the balance change is the swap amount (less relayer fees)
uint256 maxAmountInLessFees = balanceAfter - balanceBefore;
// check path to see if first element is the feeToken
require(payload.path[0]==feeTokenAddress, "tokenIn must be UST");
@ -561,12 +616,6 @@ contract CrossChainSwapV3 {
// 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 tokens
TransferHelper.safeApprove(
payload.path[0],
@ -596,7 +645,7 @@ contract CrossChainSwapV3 {
address(swapRouter),
0
);
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees - amountInUsed
);
@ -614,7 +663,7 @@ contract CrossChainSwapV3 {
return amountInUsed;
} catch {
// swap failed - return UST to recipient
feeToken.safeTransfer(
IERC20(feeTokenAddress).safeTransfer(
payload.recipientAddress,
maxAmountInLessFees
);

View File

@ -52,52 +52,52 @@ library SwapHelper {
/// @dev Decodes parameters encoded in a VAA
function decodeVaaPayload(
IWormhole.VM memory encodedVm
bytes memory vmPayload
) public view returns (DecodedVaaParameters memory decoded) {
uint index = 0;
decoded.version = encodedVm.payload.toUint8(index);
decoded.version = vmPayload.toUint8(index);
index += 1;
decoded.swapAmount = encodedVm.payload.toUint256(index);
decoded.swapAmount = vmPayload.toUint256(index);
index += 32;
// skip
index += 46;
decoded.contractAddress = encodedVm.payload.toAddress(index);
decoded.contractAddress = vmPayload.toAddress(index);
index += 20;
// skip
index += 2;
decoded.relayerFee = encodedVm.payload.toUint256(index);
decoded.relayerFee = vmPayload.toUint256(index);
index += 32;
decoded.estimatedAmount = encodedVm.payload.toUint256(index);
decoded.estimatedAmount = vmPayload.toUint256(index);
index += 44;
decoded.recipientAddress = encodedVm.payload.toAddress(index);
decoded.recipientAddress = vmPayload.toAddress(index);
index += 20;
decoded.path[0] = encodedVm.payload.toAddress(index);
decoded.path[0] = vmPayload.toAddress(index);
index += 20;
decoded.path[1] = encodedVm.payload.toAddress(index);
decoded.path[1] = vmPayload.toAddress(index);
index += 20;
decoded.deadline = encodedVm.payload.toUint256(index);
decoded.deadline = vmPayload.toUint256(index);
index += 32;
// skip
index += 1;
decoded.poolFee = encodedVm.payload.toUint16(index);
decoded.poolFee = vmPayload.toUint16(index);
index += 2;
decoded.swapFunctionType = encodedVm.payload.toUint8(index);
decoded.swapFunctionType = vmPayload.toUint8(index);
index += 1;
decoded.swapCurrencyType = encodedVm.payload.toUint8(index);
decoded.swapCurrencyType = vmPayload.toUint8(index);
}
}

View File

@ -1,4 +1,4 @@
#!/bin/bash
set -euo pipefail
npx truffle migrate --config truffle-config.ethereum.js --network goerli --reset
npx truffle migrate --config cfg/truffle-config.ethereum.js --network goerli --reset

View File

@ -1,4 +1,9 @@
import { ChainId, CHAIN_ID_POLYGON, isEVMChain } from "@certusone/wormhole-sdk";
import {
ChainId,
CHAIN_ID_AVAX,
CHAIN_ID_POLYGON,
isEVMChain,
} from "@certusone/wormhole-sdk";
import { LinearProgress, makeStyles, Typography } from "@material-ui/core";
import { useEffect, useState } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
@ -54,7 +59,8 @@ export default function TransactionProgress({
txBlockNumber !== undefined && txBlockNumber && currentBlock
? currentBlock - txBlockNumber
: 0;
const expectedBlocks = chainId === CHAIN_ID_POLYGON ? 512 : 15;
const expectedBlocks =
chainId === CHAIN_ID_POLYGON ? 512 : CHAIN_ID_AVAX ? 1 : 15;
blockDiff = Math.min(Math.max(blockDiff, 0), expectedBlocks);
let value;
let valueBuffer;