diff --git a/react/src/swapper/swapper.ts b/react/src/swapper/swapper.ts index 516785a..816c3f9 100644 --- a/react/src/swapper/swapper.ts +++ b/react/src/swapper/swapper.ts @@ -32,6 +32,14 @@ import { WETH_TOKEN_INFO, WMATIC_TOKEN_INFO, } from "../utils/consts"; +import { + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3, + swapExactInFromVaaNativeV2, + swapExactInFromVaaNativeV3, + swapExactInFromVaaTokenV2, + swapExactInFromVaaTokenV3, +} from "util"; import { abi as SWAP_CONTRACT_V2_ABI } from "../abi/contracts/CrossChainSwapV2.json"; import { abi as SWAP_CONTRACT_V3_ABI } from "../abi/contracts/CrossChainSwapV3.json"; import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../addresses/goerli"; @@ -74,18 +82,6 @@ const EXECUTION_PARAMETERS_POLYGON: ExecutionParameters = { }, }; -const CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 = { - gasLimit: "550000", - maxFeePerGas: "250000000000", - maxPriorityFeePerGas: "1690000000", -}; - -const CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 = { - gasLimit: "350000", - maxFeePerGas: "250000000000", - maxPriorityFeePerGas: "1690000000", -}; - function makeExecutionParameters(id: number): ExecutionParameters { switch (id) { case ETH_NETWORK_CHAIN_ID: { @@ -175,7 +171,8 @@ async function approveAndSwapExactIn( srcTokenIn: UniEvmToken, quoteParams: ExactInCrossParameters, srcExecutionParams: ExecutionParameters, - dstExecutionParams: ExecutionParameters + dstExecutionParams: ExecutionParameters, + isNative: boolean ): Promise { const swapContractParams = srcExecutionParams.crossChainSwap; @@ -190,6 +187,7 @@ async function approveAndSwapExactIn( // approve and swap this amount const amountIn = quoteParams.src.amountIn; + /* // approve swap contract to spend our tokens console.info("approving contract to spend token in"); await approveContractTokenSpend( @@ -199,6 +197,7 @@ async function approveAndSwapExactIn( swapContract.address, amountIn ); + */ const address = await srcWallet.getAddress(); @@ -223,29 +222,246 @@ async function approveAndSwapExactIn( // do the swap if (protocol === PROTOCOL_UNISWAP_V2) { - console.info("swapExactInToV3"); - const tx = await contractWithSigner.swapExactInToV3( - swapParams, - pathArray, - quoteParams.relayerFee.amount, - dstWormholeChainId, - dstContractAddress, - bridgeNonce, - CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + // approve swap contract to spend our tokens + console.info("approving contract to spend token in"); + await approveContractTokenSpend( + srcProvider, + srcWallet, + srcTokenIn.getContract(), + swapContract.address, + amountIn ); - return tx.wait(); + + if (isNative) { + const gasPlusValue = { + value: amountIn, + gasLimit: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.gasLimit, + maxFeePerGas: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.maxFeePerGas, + maxPriorityFeePerGas: + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.maxPriorityFeePerGas, + }; + + console.info("swapExactNativeInToV3"); + const tx = await contractWithSigner.swapExactNativeInToV3( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + gasPlusValue + ); + return tx.wait(); + } else { + console.info("swapExactInToV3"); + const tx = await contractWithSigner.swapExactInToV3( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); + } } else { - console.info("swapExactInToV2"); - const tx = await contractWithSigner.swapExactInToV2( - swapParams, - pathArray, - quoteParams.relayerFee.amount, - dstWormholeChainId, - dstContractAddress, - bridgeNonce, - CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + if (isNative) { + const gasPlusValue = { + value: amountIn, + gasLimit: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.gasLimit, + maxFeePerGas: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.maxFeePerGas, + maxPriorityFeePerGas: + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.maxPriorityFeePerGas, + }; + + console.info("swapExactNativeInToV2"); + const tx = await contractWithSigner.swapExactNativeInToV2( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + gasPlusValue + ); + return tx.wait(); + } else { + // approve swap contract to spend our tokens + console.info("approving contract to spend token in"); + await approveContractTokenSpend( + srcProvider, + srcWallet, + srcTokenIn.getContract(), + swapContract.address, + amountIn + ); + console.info("swapExactInToV2"); + const tx = await contractWithSigner.swapExactInToV2( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); + } + } +} + +async function approveAndSwapExactOut( + srcProvider: ethers.providers.Provider, + srcWallet: ethers.Signer, + srcTokenIn: UniEvmToken, + quoteParams: ExactOutCrossParameters, + srcExecutionParams: ExecutionParameters, + dstExecutionParams: ExecutionParameters, + isNative: boolean +): Promise { + const swapContractParams = srcExecutionParams.crossChainSwap; + + const protocol = quoteParams.src.protocol; + const swapContract = makeCrossChainSwapContract( + srcProvider, + protocol, + swapContractParams.address + ); + const contractWithSigner = swapContract.connect(srcWallet); + + // approve and swap this amount + const amountOut = quoteParams.src.amountOut; + const maxAmountIn = quoteParams.src.maxAmountIn; + + /* + // approve swap contract to spend our tokens + console.info('approving contract to spend token in'); + await approveContractTokenSpend( + srcProvider, + srcWallet, + srcTokenIn.getContract(), + swapContract.address, + maxAmountIn, + ); + */ + + /* + struct ExactOutParameters { + uint256 amountOut; + uint256 amountInMaximum; + uint256 targetAmountOut; + address targetChainRecipient; + uint256 deadline; + uint24 poolFee; + } + */ + + const swapParams = [ + amountOut, + maxAmountIn, + quoteParams.dst.amountOut, + srcWallet.address, + quoteParams.src.deadline, + quoteParams.dst.poolFee || quoteParams.src.poolFee, + ]; + const pathArray = quoteParams.src.path.concat(quoteParams.dst.path); + + const dstWormholeChainId = dstExecutionParams.wormhole.chainId; + const dstContractAddress = addressToBytes32( + dstExecutionParams.crossChainSwap.address, + dstWormholeChainId + ); + const bridgeNonce = 69; + + //const fakeRelayerFee = quoteParams.relayerFee.amount; + // do the swap + if (protocol === PROTOCOL_UNISWAP_V2) { + console.info("approving contract to spend token in"); + await approveContractTokenSpend( + srcProvider, + srcWallet, + srcTokenIn.getContract(), + swapContract.address, + maxAmountIn ); - return tx.wait(); + + if (isNative) { + const gasPlusValue = { + value: maxAmountIn, + gasLimit: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.gasLimit, + maxFeePerGas: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.maxFeePerGas, + maxPriorityFeePerGas: + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2.maxPriorityFeePerGas, + }; + + console.info("swapExactNativeOutToV3"); + const tx = await contractWithSigner.swapExactNativeOutToV3( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + gasPlusValue + ); + return tx.wait(); + } else { + console.info("swapExactOutToV3"); + const tx = await contractWithSigner.swapExactOutToV3( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); + } + } else { + if (isNative) { + const gasPlusValue = { + value: maxAmountIn, + gasLimit: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.gasLimit, + maxFeePerGas: CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.maxFeePerGas, + maxPriorityFeePerGas: + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3.maxPriorityFeePerGas, + }; + + console.info("swapExactNativeOutToV2"); + const tx = await contractWithSigner.swapExactNativeOutToV2( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + gasPlusValue + ); + return tx.wait(); + } else { + console.info("approving contract to spend token in"); + await approveContractTokenSpend( + srcProvider, + srcWallet, + srcTokenIn.getContract(), + swapContract.address, + maxAmountIn + ); + + console.info("swapExactOutToV2"); + const tx = await contractWithSigner.swapExactOutToV2( + swapParams, + pathArray, + quoteParams.relayerFee.amount, + dstWormholeChainId, + dstContractAddress, + bridgeNonce, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); + } } } @@ -254,7 +470,8 @@ async function swapExactInFromVaa( dstWallet: ethers.Signer, dstExecutionParams: ExecutionParameters, dstProtocol: string, - signedVAA: Uint8Array + signedVaa: Uint8Array, + isNative: boolean ): Promise { const swapContractParams = dstExecutionParams.crossChainSwap; @@ -266,19 +483,57 @@ async function swapExactInFromVaa( const contractWithSigner = swapContract.connect(dstWallet); if (dstProtocol === PROTOCOL_UNISWAP_V3) { - console.info("swapExactInFromV2"); - const tx = await contractWithSigner.swapExactInFromV2( - signedVAA, - CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 - ); - return tx.wait(); + if (isNative) { + console.info("swapExactNativeInFromV2"); + return swapExactInFromVaaNativeV3(contractWithSigner, signedVaa); + } else { + console.info("swapExactInFromV2"); + return swapExactInFromVaaTokenV3(contractWithSigner, signedVaa); + } } else { - console.info("swapExactInFromV3"); - const tx = await contractWithSigner.swapExactInFromV3( - signedVAA, - CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 - ); - return tx.wait(); + if (isNative) { + console.info("swapExactNativeInFromV3"); + return swapExactInFromVaaNativeV2(contractWithSigner, signedVaa); + } else { + console.info("swapExactInFromV3"); + return swapExactInFromVaaTokenV2(contractWithSigner, signedVaa); + } + } +} + +async function swapExactOutFromVaa( + dstProvider: ethers.providers.Provider, + dstWallet: ethers.Wallet, + dstExecutionParams: ExecutionParameters, + dstProtocol: string, + signedVaa: Uint8Array, + isNative: boolean +): Promise { + const swapContractParams = dstExecutionParams.crossChainSwap; + + const swapContract = makeCrossChainSwapContract( + dstProvider, + dstProtocol, + swapContractParams.address + ); + const contractWithSigner = swapContract.connect(dstWallet); + + if (dstProtocol === PROTOCOL_UNISWAP_V3) { + if (isNative) { + console.info("swapExactNativeInFromV2"); + return swapExactOutFromVaaNativeV3(contractWithSigner, signedVaa); + } else { + console.info("swapExactInFromV2"); + return swapExactOutFromVaaTokenV3(contractWithSigner, signedVaa); + } + } else { + if (isNative) { + console.info("swapExactNativeInFromV3"); + return swapExactOutFromVaaNativeV2(contractWithSigner, signedVaa); + } else { + console.info("swapExactInFromV3"); + return swapExactOutFromVaaTokenV2(contractWithSigner, signedVaa); + } } } @@ -466,14 +721,23 @@ export class UniswapToUniswapExecutor { this.tokens.srcIn, this.cachedExactInParams, this.srcExecutionParams, - this.dstExecutionParams + this.dstExecutionParams, + this.isNative ); } async approveAndSwapExactOut( wallet: ethers.Wallet ): Promise { - throw Error("ExactOut not supported yet"); + return approveAndSwapExactOut( + this.getSrcProvider(), + wallet, + this.tokens.srcIn, + this.cachedExactOutParams, + this.srcExecutionParams, + this.dstExecutionParams, + this.isNative + ); } async approveAndSwap(wallet: ethers.Signer): Promise { @@ -564,11 +828,17 @@ export class UniswapToUniswapExecutor { } async swapExactOutFromVaa( - wallet: ethers.Wallet + wallet: ethers.Signer ): Promise { - throw Error("ExactOut not supported yet"); + return swapExactOutFromVaa( + this.getDstProvider(), + wallet, + this.dstExecutionParams, + this.cachedExactOutParams.dst.protocol, + this.vaaBytes, + this.isNative + ); } - clearState(): void { // TODO: after the whole swap, clear the state of everything this.vaaBytes = undefined; diff --git a/react/src/swapper/util.ts b/react/src/swapper/util.ts new file mode 100644 index 0000000..f83d738 --- /dev/null +++ b/react/src/swapper/util.ts @@ -0,0 +1,106 @@ +import { ethers } from "ethers"; +import { TransactionReceipt } from "@ethersproject/abstract-provider"; + +export const CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 = { + gasLimit: "550000", + maxFeePerGas: "250000000000", + maxPriorityFeePerGas: "1690000000", +}; + +export const CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 = { + gasLimit: "550000", + maxFeePerGas: "250000000000", + maxPriorityFeePerGas: "1690000000", +}; + +// exact in +// +export async function swapExactInFromVaaNativeV3( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactNativeInFromV2( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); +} + +export async function swapExactInFromVaaNativeV2( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactNativeInFromV3( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); +} + +export async function swapExactInFromVaaTokenV3( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactInFromV2( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); +} + +export async function swapExactInFromVaaTokenV2( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactInFromV3( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); +} + +// exact out +// +export async function swapExactOutFromVaaNativeV3( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactNativeOutFromV2( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); +} + +export async function swapExactOutFromVaaNativeV2( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactNativeOutFromV3( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); +} + +export async function swapExactOutFromVaaTokenV3( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactOutFromV2( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V3 + ); + return tx.wait(); +} + +export async function swapExactOutFromVaaTokenV2( + swapContractWithSigner: ethers.Contract, + signedVaa: Uint8Array +): Promise { + const tx = await swapContractWithSigner.swapExactOutFromV3( + signedVaa, + CROSSCHAINSWAP_GAS_PARAMETERS_UNISWAP_V2 + ); + return tx.wait(); +}