Compare commits
5 Commits
59c3a8965c
...
31cb892162
Author | SHA1 | Date |
---|---|---|
Karl Kempe | 31cb892162 | |
Karl Kempe | fafd2ace84 | |
Bruce Riley | a295568e58 | |
Bruce Riley | ea822b5fb2 | |
Karl Kempe | f9870b03de |
|
@ -144,11 +144,18 @@ async function swapEverythingExactIn(
|
||||||
tokenInAddress: string,
|
tokenInAddress: string,
|
||||||
tokenOutAddress: string,
|
tokenOutAddress: string,
|
||||||
isNative: boolean,
|
isNative: boolean,
|
||||||
amountIn: string
|
amountIn: string,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const isTerraSrc = tokenInAddress === UST_TOKEN_INFO.address;
|
||||||
|
|
||||||
|
if (isTerraSrc) {
|
||||||
|
throw Error("cannot use terra source yet");
|
||||||
|
}
|
||||||
// connect src wallet
|
// connect src wallet
|
||||||
const srcWallet = determineWalletFromToken(tokenInAddress);
|
const srcWallet = determineWalletFromToken(tokenInAddress);
|
||||||
console.info(`wallet pubkey: ${await srcWallet.getAddress()}`);
|
console.info(`sender: ${await srcWallet.getAddress()}`);
|
||||||
|
console.info(`recipient: ${recipientAddress}`);
|
||||||
|
|
||||||
// tokens selected, let's initialize
|
// tokens selected, let's initialize
|
||||||
await swapper.initialize(tokenInAddress, tokenOutAddress, isNative);
|
await swapper.initialize(tokenInAddress, tokenOutAddress, isNative);
|
||||||
|
@ -193,9 +200,17 @@ async function swapEverythingExactIn(
|
||||||
logExactInParameters(swapper.quoter, exactInParameters);
|
logExactInParameters(swapper.quoter, exactInParameters);
|
||||||
|
|
||||||
// do the src swap
|
// do the src swap
|
||||||
console.info("approveAndSwap");
|
if (isTerraSrc) {
|
||||||
const srcSwapReceipt = await swapper.evmApproveAndSwap(srcWallet);
|
// do terra method
|
||||||
console.info(`src transaction: ${srcSwapReceipt.transactionHash}`);
|
throw Error("terra src not implemented yet");
|
||||||
|
} else {
|
||||||
|
console.info("approveAndSwap");
|
||||||
|
const srcSwapReceipt = await swapper.evmApproveAndSwap(
|
||||||
|
srcWallet,
|
||||||
|
recipientAddress
|
||||||
|
);
|
||||||
|
console.info(`src transaction: ${srcSwapReceipt.transactionHash}`);
|
||||||
|
}
|
||||||
|
|
||||||
// do the dst swap after fetching vaa
|
// do the dst swap after fetching vaa
|
||||||
// connect dst wallet
|
// connect dst wallet
|
||||||
|
@ -263,7 +278,8 @@ async function swapEverythingExactOut(
|
||||||
tokenInAddress: string,
|
tokenInAddress: string,
|
||||||
tokenOutAddress: string,
|
tokenOutAddress: string,
|
||||||
isNative: boolean,
|
isNative: boolean,
|
||||||
amountOut: string
|
amountOut: string,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// connect src wallet
|
// connect src wallet
|
||||||
const srcWallet = determineWalletFromToken(tokenInAddress);
|
const srcWallet = determineWalletFromToken(tokenInAddress);
|
||||||
|
@ -313,7 +329,10 @@ async function swapEverythingExactOut(
|
||||||
|
|
||||||
// do the src swap
|
// do the src swap
|
||||||
console.info("approveAndSwap");
|
console.info("approveAndSwap");
|
||||||
const srcSwapReceipt = await swapper.evmApproveAndSwap(srcWallet);
|
const srcSwapReceipt = await swapper.evmApproveAndSwap(
|
||||||
|
srcWallet,
|
||||||
|
recipientAddress
|
||||||
|
);
|
||||||
console.info(`src transaction: ${srcSwapReceipt.transactionHash}`);
|
console.info(`src transaction: ${srcSwapReceipt.transactionHash}`);
|
||||||
|
|
||||||
// do the dst swap after fetching vaa
|
// do the dst swap after fetching vaa
|
||||||
|
@ -336,7 +355,11 @@ async function main() {
|
||||||
swapper.setTransport(NodeHttpTransport());
|
swapper.setTransport(NodeHttpTransport());
|
||||||
|
|
||||||
const tokenIn = ETH_TOKEN_INFO;
|
const tokenIn = ETH_TOKEN_INFO;
|
||||||
const tokenOut = AVAX_TOKEN_INFO;
|
//const tokenOut = MATIC_TOKEN_INFO;
|
||||||
|
const tokenOut = UST_TOKEN_INFO;
|
||||||
|
|
||||||
|
//const recipientAddress = "0x4e2dfAD7D7d0076b5A0A41223E4Bee390C33251C";
|
||||||
|
const recipientAddress = "terra1vewnsxcy5fqjslyyy409cw8js550esen38n8ey";
|
||||||
|
|
||||||
if (testExactIn) {
|
if (testExactIn) {
|
||||||
console.info(`testing exact in. native=${isNative}`);
|
console.info(`testing exact in. native=${isNative}`);
|
||||||
|
@ -347,17 +370,23 @@ async function main() {
|
||||||
tokenIn.address,
|
tokenIn.address,
|
||||||
tokenOut.address,
|
tokenOut.address,
|
||||||
isNative,
|
isNative,
|
||||||
determineAmountFromToken(tokenIn.address)
|
determineAmountFromToken(tokenIn.address),
|
||||||
|
recipientAddress
|
||||||
);
|
);
|
||||||
|
|
||||||
console.info(`${tokenOut.name} -> ${tokenIn.name}`);
|
if (tokenOut.address === UST_TOKEN_INFO.address) {
|
||||||
await swapEverythingExactIn(
|
console.warn("not pinging back");
|
||||||
swapper,
|
} else {
|
||||||
tokenOut.address,
|
console.info(`${tokenOut.name} -> ${tokenIn.name}`);
|
||||||
tokenIn.address,
|
await swapEverythingExactIn(
|
||||||
isNative,
|
swapper,
|
||||||
determineAmountFromToken(tokenOut.address)
|
tokenOut.address,
|
||||||
);
|
tokenIn.address,
|
||||||
|
isNative,
|
||||||
|
determineAmountFromToken(tokenOut.address),
|
||||||
|
recipientAddress
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.info(`testing exact out. native=${isNative}`);
|
console.info(`testing exact out. native=${isNative}`);
|
||||||
|
|
||||||
|
@ -367,7 +396,8 @@ async function main() {
|
||||||
tokenIn.address,
|
tokenIn.address,
|
||||||
tokenOut.address,
|
tokenOut.address,
|
||||||
isNative,
|
isNative,
|
||||||
determineAmountFromToken(tokenOut.address)
|
determineAmountFromToken(tokenOut.address),
|
||||||
|
recipientAddress
|
||||||
);
|
);
|
||||||
|
|
||||||
console.info(`${tokenOut.name} -> ${tokenIn.name}`);
|
console.info(`${tokenOut.name} -> ${tokenIn.name}`);
|
||||||
|
@ -376,7 +406,8 @@ async function main() {
|
||||||
tokenOut.address,
|
tokenOut.address,
|
||||||
tokenIn.address,
|
tokenIn.address,
|
||||||
isNative,
|
isNative,
|
||||||
determineAmountFromToken(tokenIn.address)
|
determineAmountFromToken(tokenIn.address),
|
||||||
|
recipientAddress
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,21 +29,11 @@ export async function getEvmGasParametersForContract(
|
||||||
contract: ethers.Contract
|
contract: ethers.Contract
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const chainId = await getChainIdFromContract(contract);
|
const chainId = await getChainIdFromContract(contract);
|
||||||
console.info(`getEvmGasParametersForContract... chainId: ${chainId}`);
|
|
||||||
|
|
||||||
if (EVM_EIP1559_CHAIN_IDS.indexOf(chainId) >= 0) {
|
if (EVM_EIP1559_CHAIN_IDS.indexOf(chainId) >= 0) {
|
||||||
console.info(
|
|
||||||
`eip1559? chainId: ${chainId}, eip1559 chains... ${JSON.stringify(
|
|
||||||
EVM_EIP1559_CHAIN_IDS
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
return CROSSCHAINSWAP_GAS_PARAMETERS_EIP1559;
|
return CROSSCHAINSWAP_GAS_PARAMETERS_EIP1559;
|
||||||
}
|
}
|
||||||
console.info(
|
|
||||||
`not eip1559 chainId: ${chainId}, eip1559 chains... ${JSON.stringify(
|
|
||||||
EVM_EIP1559_CHAIN_IDS
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
return CROSSCHAINSWAP_GAS_PARAMETERS_EVM;
|
return CROSSCHAINSWAP_GAS_PARAMETERS_EVM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,12 +54,6 @@ export async function evmSwapExactInFromVaaNative(
|
||||||
swapContractWithSigner
|
swapContractWithSigner
|
||||||
);
|
);
|
||||||
|
|
||||||
console.info(
|
|
||||||
`evmSwapExactInFromVaaNative... contract: ${
|
|
||||||
swapContractWithSigner.address
|
|
||||||
}, gasParams: ${JSON.stringify(gasParams)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const tx = await swapContractWithSigner.recvAndSwapExactNativeIn(
|
const tx = await swapContractWithSigner.recvAndSwapExactNativeIn(
|
||||||
signedVaa,
|
signedVaa,
|
||||||
gasParams
|
gasParams
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//@ts-nocheck
|
//@ts-nocheckk
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { TransactionReceipt } from "@ethersproject/abstract-provider";
|
import { TransactionReceipt } from "@ethersproject/abstract-provider";
|
||||||
import {
|
import {
|
||||||
|
@ -58,7 +58,16 @@ import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_BSC } from "..
|
||||||
import { makeErc20Contract } from "../route/evm";
|
import { makeErc20Contract } from "../route/evm";
|
||||||
|
|
||||||
// placeholders
|
// placeholders
|
||||||
const CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA = "";
|
const CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA =
|
||||||
|
"terra163shc8unyqrndgcldaj2q9kgnqs82v0kgkhynf";
|
||||||
|
|
||||||
|
function makeNullSwapPath(): any[] {
|
||||||
|
const zeroBuffer = Buffer.alloc(20);
|
||||||
|
const nullAddress = "0x" + zeroBuffer.toString("hex");
|
||||||
|
return [nullAddress, nullAddress];
|
||||||
|
}
|
||||||
|
|
||||||
|
const NULL_SWAP_PATH = makeNullSwapPath();
|
||||||
|
|
||||||
interface SwapContractParameters {
|
interface SwapContractParameters {
|
||||||
address: string;
|
address: string;
|
||||||
|
@ -223,6 +232,48 @@ function addressToBytes32(
|
||||||
return hexToUint8Array(hexString);
|
return hexToUint8Array(hexString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function evmMakeExactInSwapParameters(
|
||||||
|
amountIn: ethers.BigNumber,
|
||||||
|
recipientAddress: string,
|
||||||
|
dstWormholeChainId: ChainId,
|
||||||
|
quoteParams: ExactInCrossParameters
|
||||||
|
): any[] {
|
||||||
|
const src = quoteParams.src;
|
||||||
|
const dst = quoteParams.dst;
|
||||||
|
|
||||||
|
if (dst === undefined) {
|
||||||
|
return [
|
||||||
|
amountIn,
|
||||||
|
src.minAmountOut,
|
||||||
|
0,
|
||||||
|
addressToBytes32(recipientAddress, dstWormholeChainId),
|
||||||
|
src.deadline,
|
||||||
|
src.poolFee || 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
amountIn,
|
||||||
|
src.minAmountOut,
|
||||||
|
dst.minAmountOut,
|
||||||
|
addressToBytes32(recipientAddress, dstWormholeChainId),
|
||||||
|
src.deadline,
|
||||||
|
dst.poolFee || src.poolFee || 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function makePathArray(
|
||||||
|
quoteParams: ExactInCrossParameters | ExactOutCrossParameters
|
||||||
|
): any[] {
|
||||||
|
if (quoteParams.src === undefined) {
|
||||||
|
return NULL_SWAP_PATH.concat(quoteParams.dst.path);
|
||||||
|
} else if (quoteParams.dst === undefined) {
|
||||||
|
return quoteParams.src.path.concat(NULL_SWAP_PATH);
|
||||||
|
} else {
|
||||||
|
return quoteParams.src.path.concat(quoteParams.dst.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function evmApproveAndSwapExactIn(
|
async function evmApproveAndSwapExactIn(
|
||||||
srcProvider: ethers.providers.Provider,
|
srcProvider: ethers.providers.Provider,
|
||||||
srcWallet: ethers.Signer,
|
srcWallet: ethers.Signer,
|
||||||
|
@ -230,7 +281,8 @@ async function evmApproveAndSwapExactIn(
|
||||||
quoteParams: ExactInCrossParameters,
|
quoteParams: ExactInCrossParameters,
|
||||||
srcExecutionParams: ExecutionParameters,
|
srcExecutionParams: ExecutionParameters,
|
||||||
dstExecutionParams: ExecutionParameters,
|
dstExecutionParams: ExecutionParameters,
|
||||||
isNative: boolean
|
isNative: boolean,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<TransactionReceipt> {
|
): Promise<TransactionReceipt> {
|
||||||
const swapContractParams = srcExecutionParams.crossChainSwap;
|
const swapContractParams = srcExecutionParams.crossChainSwap;
|
||||||
|
|
||||||
|
@ -244,21 +296,16 @@ async function evmApproveAndSwapExactIn(
|
||||||
|
|
||||||
// approve and swap this amount
|
// approve and swap this amount
|
||||||
const amountIn = quoteParams.src.amountIn;
|
const amountIn = quoteParams.src.amountIn;
|
||||||
|
|
||||||
const address = await srcWallet.getAddress();
|
|
||||||
|
|
||||||
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
|
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
|
||||||
|
|
||||||
const swapParams = [
|
const swapParams = evmMakeExactInSwapParameters(
|
||||||
amountIn,
|
amountIn,
|
||||||
quoteParams.src.minAmountOut,
|
recipientAddress,
|
||||||
quoteParams.dst.minAmountOut,
|
dstWormholeChainId,
|
||||||
addressToBytes32(address, dstWormholeChainId),
|
quoteParams
|
||||||
quoteParams.src.deadline,
|
);
|
||||||
quoteParams.dst.poolFee || quoteParams.src.poolFee || 0,
|
|
||||||
];
|
|
||||||
|
|
||||||
const pathArray = quoteParams.src.path.concat(quoteParams.dst.path);
|
const pathArray = makePathArray(quoteParams);
|
||||||
|
|
||||||
const dstContractAddress = addressToBytes32(
|
const dstContractAddress = addressToBytes32(
|
||||||
dstExecutionParams.crossChainSwap.address,
|
dstExecutionParams.crossChainSwap.address,
|
||||||
|
@ -306,6 +353,7 @@ async function evmApproveAndSwapExactIn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix to resemble ExactIn
|
||||||
async function evmApproveAndSwapExactOut(
|
async function evmApproveAndSwapExactOut(
|
||||||
srcProvider: ethers.providers.Provider,
|
srcProvider: ethers.providers.Provider,
|
||||||
srcWallet: ethers.Signer,
|
srcWallet: ethers.Signer,
|
||||||
|
@ -313,11 +361,12 @@ async function evmApproveAndSwapExactOut(
|
||||||
quoteParams: ExactOutCrossParameters,
|
quoteParams: ExactOutCrossParameters,
|
||||||
srcExecutionParams: ExecutionParameters,
|
srcExecutionParams: ExecutionParameters,
|
||||||
dstExecutionParams: ExecutionParameters,
|
dstExecutionParams: ExecutionParameters,
|
||||||
isNative: boolean
|
isNative: boolean,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<TransactionReceipt> {
|
): Promise<TransactionReceipt> {
|
||||||
const swapContractParams = srcExecutionParams.crossChainSwap;
|
const swapContractParams = srcExecutionParams.crossChainSwap;
|
||||||
|
|
||||||
const protocol = quoteParams.src.protocol;
|
const protocol = quoteParams.src?.protocol;
|
||||||
const swapContract = makeCrossChainSwapEvmContract(
|
const swapContract = makeCrossChainSwapEvmContract(
|
||||||
srcProvider,
|
srcProvider,
|
||||||
protocol,
|
protocol,
|
||||||
|
@ -326,22 +375,19 @@ async function evmApproveAndSwapExactOut(
|
||||||
const contractWithSigner = swapContract.connect(srcWallet);
|
const contractWithSigner = swapContract.connect(srcWallet);
|
||||||
|
|
||||||
// approve and swap this amount
|
// approve and swap this amount
|
||||||
const amountOut = quoteParams.src.amountOut;
|
const amountOut = quoteParams.src?.amountOut;
|
||||||
const maxAmountIn = quoteParams.src.maxAmountIn;
|
const maxAmountIn = quoteParams.src?.maxAmountIn;
|
||||||
|
|
||||||
const address = await srcWallet.getAddress();
|
|
||||||
|
|
||||||
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
|
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
|
||||||
|
|
||||||
const swapParams = [
|
const swapParams = [
|
||||||
amountOut,
|
amountOut,
|
||||||
maxAmountIn,
|
maxAmountIn,
|
||||||
quoteParams.dst.amountOut,
|
quoteParams.dst.amountOut,
|
||||||
addressToBytes32(address, dstWormholeChainId),
|
addressToBytes32(recipientAddress, dstWormholeChainId),
|
||||||
quoteParams.src.deadline,
|
quoteParams.src.deadline,
|
||||||
quoteParams.dst.poolFee || quoteParams.src.poolFee || 0,
|
quoteParams.dst.poolFee || quoteParams.src.poolFee || 0,
|
||||||
];
|
];
|
||||||
const pathArray = quoteParams.src.path.concat(quoteParams.dst.path);
|
const pathArray = makePathArray(quoteParams);
|
||||||
|
|
||||||
const dstContractAddress = addressToBytes32(
|
const dstContractAddress = addressToBytes32(
|
||||||
dstExecutionParams.crossChainSwap.address,
|
dstExecutionParams.crossChainSwap.address,
|
||||||
|
@ -613,30 +659,34 @@ export class UniswapToUniswapExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async evmApproveAndSwapExactIn(
|
async evmApproveAndSwapExactIn(
|
||||||
wallet: ethers.Signer
|
srcWallet: ethers.Signer,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<TransactionReceipt> {
|
): Promise<TransactionReceipt> {
|
||||||
return evmApproveAndSwapExactIn(
|
return evmApproveAndSwapExactIn(
|
||||||
this.getSrcEvmProvider(),
|
this.getSrcEvmProvider(),
|
||||||
wallet,
|
srcWallet,
|
||||||
this.getTokenInAddress(),
|
this.getTokenInAddress(),
|
||||||
this.cachedExactInParams,
|
this.cachedExactInParams,
|
||||||
this.srcExecutionParams,
|
this.srcExecutionParams,
|
||||||
this.dstExecutionParams,
|
this.dstExecutionParams,
|
||||||
this.isNative
|
this.isNative,
|
||||||
|
recipientAddress
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evmApproveAndSwapExactOut(
|
async evmApproveAndSwapExactOut(
|
||||||
wallet: ethers.Signer
|
srcWallet: ethers.Signer,
|
||||||
|
recipientAddress: string
|
||||||
): Promise<TransactionReceipt> {
|
): Promise<TransactionReceipt> {
|
||||||
return evmApproveAndSwapExactOut(
|
return evmApproveAndSwapExactOut(
|
||||||
this.getSrcEvmProvider(),
|
this.getSrcEvmProvider(),
|
||||||
wallet,
|
srcWallet,
|
||||||
this.getTokenInAddress(),
|
this.getTokenInAddress(),
|
||||||
this.cachedExactOutParams,
|
this.cachedExactOutParams,
|
||||||
this.srcExecutionParams,
|
this.srcExecutionParams,
|
||||||
this.dstExecutionParams,
|
this.dstExecutionParams,
|
||||||
this.isNative
|
this.isNative,
|
||||||
|
recipientAddress
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,18 +697,27 @@ export class UniswapToUniswapExecutor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evmApproveAndSwap(wallet: ethers.Signer): Promise<TransactionReceipt> {
|
async evmApproveAndSwap(
|
||||||
|
wallet: ethers.Signer,
|
||||||
|
recipientAddress: string
|
||||||
|
): Promise<TransactionReceipt> {
|
||||||
const quoteType = this.quoteType;
|
const quoteType = this.quoteType;
|
||||||
|
|
||||||
if (quoteType === QuoteType.ExactIn) {
|
if (quoteType === QuoteType.ExactIn) {
|
||||||
this.srcEvmReceipt = await this.evmApproveAndSwapExactIn(wallet);
|
this.srcEvmReceipt = await this.evmApproveAndSwapExactIn(
|
||||||
|
wallet,
|
||||||
|
recipientAddress
|
||||||
|
);
|
||||||
} else if (quoteType === QuoteType.ExactOut) {
|
} else if (quoteType === QuoteType.ExactOut) {
|
||||||
this.srcEvmReceipt = await this.evmApproveAndSwapExactOut(wallet);
|
this.srcEvmReceipt = await this.evmApproveAndSwapExactOut(
|
||||||
|
wallet,
|
||||||
|
recipientAddress
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw Error("no quote found");
|
throw Error("no quote found");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchAndSetEmitterAndSequence();
|
this.fetchAndSetEvmEmitterAndSequence();
|
||||||
return this.srcEvmReceipt;
|
return this.srcEvmReceipt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,8 @@ export const CORE_BRIDGE_ADDRESS_AVALANCHE =
|
||||||
export const CORE_BRIDGE_ADDRESS_BSC =
|
export const CORE_BRIDGE_ADDRESS_BSC =
|
||||||
"0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D";
|
"0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D";
|
||||||
|
|
||||||
export const CORE_BRIDGE_ADDRESS_TERRA = undefined;
|
export const CORE_BRIDGE_ADDRESS_TERRA =
|
||||||
|
"terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v";
|
||||||
|
|
||||||
// token bridge
|
// token bridge
|
||||||
export const TOKEN_BRIDGE_ADDRESS_ETHEREUM =
|
export const TOKEN_BRIDGE_ADDRESS_ETHEREUM =
|
||||||
|
@ -205,7 +206,8 @@ export const TOKEN_BRIDGE_ADDRESS_BSC =
|
||||||
export const TOKEN_BRIDGE_ADDRESS_AVALANCHE =
|
export const TOKEN_BRIDGE_ADDRESS_AVALANCHE =
|
||||||
"0x61E44E506Ca5659E6c0bba9b678586fA2d729756";
|
"0x61E44E506Ca5659E6c0bba9b678586fA2d729756";
|
||||||
|
|
||||||
export const TOKEN_BRIDGE_ADDRESS_TERRA = undefined;
|
export const TOKEN_BRIDGE_ADDRESS_TERRA =
|
||||||
|
"terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a";
|
||||||
|
|
||||||
// gas
|
// gas
|
||||||
export const APPROVAL_GAS_LIMIT = "100000";
|
export const APPROVAL_GAS_LIMIT = "100000";
|
||||||
|
|
|
@ -1,12 +1,29 @@
|
||||||
# TestNet
|
# TestNet
|
||||||
SPY_SERVICE_HOST=localhost:7073
|
SPY_SERVICE_HOST=localhost:7073
|
||||||
SPY_SERVICE_FILTERS=[{"chain_id":2,"emitter_address":"0x000000000000000000000000f890982f9310df57d00f659cf4fd87e65aded8d7"},{"chain_id":3,"emitter_address":"terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a"},{"chain_id":5,"emitter_address":"0x000000000000000000000000377d55a7928c046e18eebb61977e714d2a76472a"}]
|
# You can omit the following to get signed VAAs from every emitter.
|
||||||
|
#SPY_SERVICE_FILTERS=[{"chain_id":2,"emitter_address":"0x000000000000000000000000f890982f9310df57d00f659cf4fd87e65aded8d7"},{"chain_id":3,"emitter_address":"terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a"},{"chain_id":4,"emitter_address":"0x0000000000000000000000009dcF9D205C9De35334D646BeE44b2D2859712A09"},{"chain_id":5,"emitter_address":"0x000000000000000000000000377d55a7928c046e18eebb61977e714d2a76472a"},{"chain_id":6,"emitter_address":"0x0000000000000000000000007bbcE28e64B3F8b84d876Ab298393c38ad7aac4C"}]
|
||||||
|
|
||||||
|
EVM_CHAINS=ETH,BSC,POLYGON,AVAX
|
||||||
|
|
||||||
ETH_PROVIDER=https://goerli.infura.io/v3/your_project_id
|
ETH_PROVIDER=https://goerli.infura.io/v3/your_project_id
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS=0xF890982f9310df57d00f659cf4fd87e65adEd8d7
|
ETH_TOKEN_BRIDGE_ADDRESS=0xF890982f9310df57d00f659cf4fd87e65adEd8d7
|
||||||
|
ETH_CHAIN_ID=2
|
||||||
|
ETH_ABI=V3
|
||||||
|
|
||||||
|
BSC_PROVIDER=
|
||||||
|
BSC_TOKEN_BRIDGE_ADDRESS=0x9dcF9D205C9De35334D646BeE44b2D2859712A09
|
||||||
|
BSC_CHAIN_ID=4
|
||||||
|
BSC_ABI=V2
|
||||||
|
|
||||||
POLYGON_PROVIDER=https://polygon-mumbai.infura.io/v3/your_project_id
|
POLYGON_PROVIDER=https://polygon-mumbai.infura.io/v3/your_project_id
|
||||||
POLYGON_TOKEN_BRIDGE_ADDRESS=0x377D55a7928c046E18eEbb61977e714d2a76472a
|
POLYGON_TOKEN_BRIDGE_ADDRESS=0x377D55a7928c046E18eEbb61977e714d2a76472a
|
||||||
|
POLYGON_CHAIN_ID=5
|
||||||
|
POLYGON_ABI=V2
|
||||||
|
|
||||||
|
AVAX_PROVIDER=
|
||||||
|
AVAX_TOKEN_BRIDGE_ADDRESS=0x61E44E506Ca5659E6c0bba9b678586fA2d729756
|
||||||
|
AVAX_CHAIN_ID=6
|
||||||
|
AVAX_ABI=V2
|
||||||
|
|
||||||
WALLET_PRIVATE_KEY=your_key_here
|
WALLET_PRIVATE_KEY=your_key_here
|
||||||
|
|
||||||
|
@ -20,6 +37,8 @@ TERRA_TOKEN_BRIDGE_ADDRESS=terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a
|
||||||
#LOG_DIR=/var/logs
|
#LOG_DIR=/var/logs
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
ETH_CONTRACT_ADDRESS=0x61D26732B190bdc5771e2a2b3ADB295e1b5A88BF
|
ETH_CONTRACT_ADDRESS=0x9e7Cae3a46ED297b0a05FCEeb41160fC5218E14f
|
||||||
POLYGON_CONTRACT_ADDRESS=0xc5Ba16A974a0c0E7935285d99F496Ee65eDFB8BA
|
BSC_CONTRACT_ADDRESS=0x0DC183c2eFAA5e1749B85f13621F5cC6aCcDa786
|
||||||
TERRA_CONTRACT_ADDRESS=terra_contract_address
|
POLYGON_CONTRACT_ADDRESS=0x72F2F646dC979a9fA8aA685B8a47b7afe2fE0516
|
||||||
|
TERRA_CONTRACT_ADDRESS=terra163shc8unyqrndgcldaj2q9kgnqs82v0kgkhynf
|
||||||
|
AVAX_CONTRACT_ADDRESS=0x52D8A50AF35b0760335F29a4D6aaF0604B7D7484
|
|
@ -32,7 +32,7 @@
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"ethers": "^5.5.3",
|
"ethers": "^5.5.3",
|
||||||
"@terra-money/terra.js": "^2.0.14",
|
"@terra-money/terra.js": "^3.0.4",
|
||||||
"winston": "^3.3.3"
|
"winston": "^3.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,29 @@
|
||||||
import { Mutex } from "async-mutex";
|
|
||||||
let CondVar = require("condition-variable");
|
|
||||||
|
|
||||||
import { getIsTransferCompletedEth } from "@certusone/wormhole-sdk";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
importCoreWasm,
|
getIsTransferCompletedEth,
|
||||||
setDefaultWasm,
|
hexToUint8Array,
|
||||||
} from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
|
} from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
import { abi as SWAP_CONTRACT_V2_ABI } from "../../react/src/abi/contracts/CrossChainSwapV2.json";
|
import { abi as SWAP_CONTRACT_V2_ABI } from "../../react/src/abi/contracts/CrossChainSwapV2.json";
|
||||||
import { abi as SWAP_CONTRACT_V3_ABI } from "../../react/src/abi/contracts/CrossChainSwapV3.json";
|
import { abi as SWAP_CONTRACT_V3_ABI } from "../../react/src/abi/contracts/CrossChainSwapV3.json";
|
||||||
|
|
||||||
import * as swap from "../../react/src/swapper/util";
|
import * as swap from "../../react/src/swapper/helpers";
|
||||||
|
|
||||||
import { logger, OurEnvironment, Type3Payload } from "./index";
|
import { logger, OurEnvironment, Type3Payload } from "./index";
|
||||||
|
|
||||||
|
export type EvmEnvironment = {
|
||||||
|
name: string;
|
||||||
|
chain_id: number;
|
||||||
|
provider_url: string;
|
||||||
|
contract_address: string;
|
||||||
|
token_bridge_address: string;
|
||||||
|
wallet_private_key: string;
|
||||||
|
abi_version: string;
|
||||||
|
};
|
||||||
|
|
||||||
type EvmContractData = {
|
type EvmContractData = {
|
||||||
|
chain_id: number;
|
||||||
name: string;
|
name: string;
|
||||||
contractAddress: string;
|
contractAddress: string;
|
||||||
tokenBridgeAddress: string;
|
tokenBridgeAddress: string;
|
||||||
|
@ -27,48 +33,136 @@ type EvmContractData = {
|
||||||
contractWithSigner: ethers.Contract;
|
contractWithSigner: ethers.Contract;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ethContractData: EvmContractData = null;
|
let evmContractData = new Map<number, EvmContractData>();
|
||||||
let polygonContractData: EvmContractData = null;
|
|
||||||
|
|
||||||
export function makeEvmContractData(env: OurEnvironment) {
|
export function loadEvmConfig(): EvmEnvironment[] {
|
||||||
ethContractData = makeEthContractData(env);
|
let evm_configs: EvmEnvironment[] = [];
|
||||||
polygonContractData = makePolygonContractData(env);
|
let evms = process.env.EVM_CHAINS.split(",");
|
||||||
|
for (const evm of evms) {
|
||||||
|
let key_chain_id: string = evm + "_CHAIN_ID";
|
||||||
|
let val_chain_id: string = eval("process.env." + key_chain_id);
|
||||||
|
if (!val_chain_id) {
|
||||||
|
logger.error("Missing environment variable " + key_chain_id);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_provider: string = evm + "_PROVIDER";
|
||||||
|
let val_provider: string = eval("process.env." + key_provider);
|
||||||
|
if (!val_provider) {
|
||||||
|
logger.error("Missing environment variable " + key_provider);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_contract_address: string = evm + "_CONTRACT_ADDRESS";
|
||||||
|
let val_contract_address: string = eval(
|
||||||
|
"process.env." + key_contract_address
|
||||||
|
);
|
||||||
|
if (!val_contract_address) {
|
||||||
|
logger.error("Missing environment variable " + key_contract_address);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_token_bridge_address: string = evm + "_TOKEN_BRIDGE_ADDRESS";
|
||||||
|
let val_token_bridge_address: string = eval(
|
||||||
|
"process.env." + key_token_bridge_address
|
||||||
|
);
|
||||||
|
if (!val_token_bridge_address) {
|
||||||
|
logger.error("Missing environment variable " + key_token_bridge_address);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_wallet_private_key: string = evm + "_WALLET_PRIVATE_KEY";
|
||||||
|
let val_wallet_private_key: string = eval(
|
||||||
|
"process.env." + key_wallet_private_key
|
||||||
|
);
|
||||||
|
if (!val_wallet_private_key)
|
||||||
|
val_wallet_private_key = process.env.WALLET_PRIVATE_KEY;
|
||||||
|
if (!val_wallet_private_key) {
|
||||||
|
logger.error(
|
||||||
|
"Missing environment variable " +
|
||||||
|
key_wallet_private_key +
|
||||||
|
" or WALLET_PRIVATE_KEY"
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_abi_version: string = evm + "_ABI";
|
||||||
|
let val_abi_version: string = eval("process.env." + key_abi_version);
|
||||||
|
if (!val_abi_version) {
|
||||||
|
logger.error("Missing environment variable " + key_abi_version);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val_abi_version !== "V2" && val_abi_version !== "V3") {
|
||||||
|
logger.error(
|
||||||
|
"Invalid value of environment variable " +
|
||||||
|
key_abi_version +
|
||||||
|
", is [" +
|
||||||
|
val_abi_version +
|
||||||
|
"], must be either V2 or V3"
|
||||||
|
);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
evm_configs.push({
|
||||||
|
name: evm,
|
||||||
|
chain_id: parseInt(val_chain_id),
|
||||||
|
provider_url: val_provider,
|
||||||
|
contract_address: val_contract_address,
|
||||||
|
token_bridge_address: val_token_bridge_address,
|
||||||
|
wallet_private_key: val_wallet_private_key,
|
||||||
|
abi_version: val_abi_version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return evm_configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethereum (Goerli) set up
|
export function makeEvmContractData(envs: EvmEnvironment[]) {
|
||||||
function makeEthContractData(env: OurEnvironment): EvmContractData {
|
if (!envs) return;
|
||||||
let contractAddress: string = env.eth_contract_address.toLowerCase();
|
for (const evm of envs) {
|
||||||
if (contractAddress.search("0x") == 0) {
|
evmContractData.set(evm.chain_id, makeContractDataForEvm(evm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeContractDataForEvm(env: EvmEnvironment): EvmContractData {
|
||||||
|
let contractAddress: string = env.contract_address.toLowerCase();
|
||||||
|
if (contractAddress.search("0x") === 0) {
|
||||||
contractAddress = contractAddress.substring(2);
|
contractAddress = contractAddress.substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Connecting to Ethereum: contract address: [" +
|
"Connecting to " +
|
||||||
|
env.name +
|
||||||
|
": chain_id: " +
|
||||||
|
env.chain_id +
|
||||||
|
", contract address: [" +
|
||||||
contractAddress +
|
contractAddress +
|
||||||
"], node: [" +
|
"], node: [" +
|
||||||
env.eth_provider_url +
|
env.provider_url +
|
||||||
"], token bridge address: [" +
|
"], token bridge address: [" +
|
||||||
env.eth_token_bridge_address +
|
env.token_bridge_address +
|
||||||
|
"], abi version: [" +
|
||||||
|
env.abi_version +
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
|
|
||||||
const provider = new ethers.providers.StaticJsonRpcProvider(
|
const provider = new ethers.providers.StaticJsonRpcProvider(env.provider_url);
|
||||||
env.eth_provider_url
|
|
||||||
);
|
|
||||||
|
|
||||||
const contract = new ethers.Contract(
|
const contract = new ethers.Contract(
|
||||||
contractAddress,
|
contractAddress,
|
||||||
SWAP_CONTRACT_V3_ABI,
|
env.abi_version == "V2" ? SWAP_CONTRACT_V2_ABI : SWAP_CONTRACT_V3_ABI,
|
||||||
provider
|
provider
|
||||||
);
|
);
|
||||||
|
|
||||||
const wallet = new ethers.Wallet(env.evm_wallet_private_key, provider);
|
const wallet = new ethers.Wallet(env.wallet_private_key, provider);
|
||||||
const contractWithSigner = contract.connect(wallet);
|
const contractWithSigner = contract.connect(wallet);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Ethereum",
|
chain_id: env.chain_id,
|
||||||
|
name: env.name,
|
||||||
contractAddress: contractAddress,
|
contractAddress: contractAddress,
|
||||||
tokenBridgeAddress: env.eth_token_bridge_address,
|
tokenBridgeAddress: env.token_bridge_address,
|
||||||
contract: contract,
|
contract: contract,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
wallet: wallet,
|
wallet: wallet,
|
||||||
|
@ -76,53 +170,12 @@ function makeEthContractData(env: OurEnvironment): EvmContractData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polygon (Mumbai) set up
|
export function isEvmContract(
|
||||||
function makePolygonContractData(env: OurEnvironment): EvmContractData {
|
contractAddress: string,
|
||||||
let contractAddress: string = env.polygon_contract_address.toLowerCase();
|
chain_id: number
|
||||||
if (contractAddress.search("0x") == 0) {
|
): boolean {
|
||||||
contractAddress = contractAddress.substring(2);
|
let ecd = evmContractData.get(chain_id);
|
||||||
}
|
return ecd && ecd.contractAddress === contractAddress;
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"Connecting to Polygon: contract address: [" +
|
|
||||||
contractAddress +
|
|
||||||
"], node: [" +
|
|
||||||
env.polygon_provider_url +
|
|
||||||
"], token bridge address: [" +
|
|
||||||
env.polygon_token_bridge_address +
|
|
||||||
"]"
|
|
||||||
);
|
|
||||||
|
|
||||||
const provider = new ethers.providers.StaticJsonRpcProvider(
|
|
||||||
env.polygon_provider_url
|
|
||||||
);
|
|
||||||
|
|
||||||
const contract = new ethers.Contract(
|
|
||||||
contractAddress,
|
|
||||||
SWAP_CONTRACT_V2_ABI,
|
|
||||||
provider
|
|
||||||
);
|
|
||||||
|
|
||||||
const wallet = new ethers.Wallet(env.evm_wallet_private_key, provider);
|
|
||||||
const contractWithSigner = contract.connect(wallet);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "Polygon",
|
|
||||||
contractAddress: contractAddress,
|
|
||||||
tokenBridgeAddress: env.polygon_token_bridge_address,
|
|
||||||
contract: contract,
|
|
||||||
provider: provider,
|
|
||||||
wallet: wallet,
|
|
||||||
contractWithSigner: contractWithSigner,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isEvmContract(contractAddress: string): boolean {
|
|
||||||
return (
|
|
||||||
(ethContractData && contractAddress === ethContractData.contractAddress) ||
|
|
||||||
(polygonContractData &&
|
|
||||||
contractAddress === polygonContractData.contractAddress)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -177,16 +230,24 @@ export function isEvmContract(contractAddress: string): boolean {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function relayVaaToEvm(
|
export async function relayVaaToEvm(vaaBytes: string, t3Payload: Type3Payload) {
|
||||||
signedVaaArray: Uint8Array,
|
let ecd = evmContractData.get(t3Payload.targetChainId);
|
||||||
t3Payload: Type3Payload
|
if (!ecd) {
|
||||||
) {
|
logger.error(
|
||||||
|
"relayVaaToEvm: chain id " + t3Payload.targetChainId + " does not exist!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let exactIn: boolean = false;
|
let exactIn: boolean = false;
|
||||||
|
let error: boolean = false;
|
||||||
if (t3Payload.swapFunctionType === 1) {
|
if (t3Payload.swapFunctionType === 1) {
|
||||||
exactIn = true;
|
exactIn = true;
|
||||||
} else if (t3Payload.swapFunctionType !== 2) {
|
} else if (t3Payload.swapFunctionType !== 2) {
|
||||||
|
error = true;
|
||||||
logger.error(
|
logger.error(
|
||||||
"relayVaa: unsupported swapFunctionType: [" +
|
"relayVaaTo" +
|
||||||
|
ecd.name +
|
||||||
|
": unsupported swapFunctionType: [" +
|
||||||
t3Payload.swapFunctionType +
|
t3Payload.swapFunctionType +
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
|
@ -196,48 +257,30 @@ export async function relayVaaToEvm(
|
||||||
if (t3Payload.swapCurrencyType === 1) {
|
if (t3Payload.swapCurrencyType === 1) {
|
||||||
native = true;
|
native = true;
|
||||||
} else if (t3Payload.swapCurrencyType !== 2) {
|
} else if (t3Payload.swapCurrencyType !== 2) {
|
||||||
|
error = true;
|
||||||
logger.error(
|
logger.error(
|
||||||
"relayVaa: unsupported swapCurrencyType: [" +
|
"relayVaaTo" +
|
||||||
|
ecd.name +
|
||||||
|
": unsupported swapCurrencyType: [" +
|
||||||
t3Payload.swapCurrencyType +
|
t3Payload.swapCurrencyType +
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error) return;
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"relayVaa: contractAddress: [" +
|
"relayVaaTo" +
|
||||||
|
ecd.name +
|
||||||
|
": chain_id: " +
|
||||||
|
ecd.chain_id +
|
||||||
|
", contractAddress: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], ethContract: [" +
|
|
||||||
ethContractData.contractAddress +
|
|
||||||
"], polygonContract[" +
|
|
||||||
polygonContractData.contractAddress +
|
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (t3Payload.contractAddress === ethContractData.contractAddress) {
|
const signedVaaArray = hexToUint8Array(vaaBytes);
|
||||||
await relayVaaToEvmChain(
|
await relayVaaToEvmChain(t3Payload, ecd, signedVaaArray, exactIn, native);
|
||||||
t3Payload,
|
|
||||||
ethContractData,
|
|
||||||
signedVaaArray,
|
|
||||||
exactIn,
|
|
||||||
native
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
t3Payload.contractAddress === polygonContractData.contractAddress
|
|
||||||
) {
|
|
||||||
await relayVaaToEvmChain(
|
|
||||||
t3Payload,
|
|
||||||
polygonContractData,
|
|
||||||
signedVaaArray,
|
|
||||||
exactIn,
|
|
||||||
native
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logger.error(
|
|
||||||
"relayVaa: unexpected contract: [" +
|
|
||||||
t3Payload.contractAddress +
|
|
||||||
"], this should not happen!"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function relayVaaToEvmChain(
|
async function relayVaaToEvmChain(
|
||||||
|
@ -257,17 +300,21 @@ async function relayVaaToEvmChain(
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (await isRedeemedOnEvm(t3Payload, tcd, signedVaaArray)) {
|
if (await isRedeemedOnEvm(tcd, signedVaaArray)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
", native: " +
|
", native: " +
|
||||||
native +
|
native +
|
||||||
": already transferred"
|
": completed: already transferred"
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -276,7 +323,11 @@ async function relayVaaToEvmChain(
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
|
@ -289,24 +340,28 @@ async function relayVaaToEvmChain(
|
||||||
let receipt: any = null;
|
let receipt: any = null;
|
||||||
if (exactIn) {
|
if (exactIn) {
|
||||||
if (native) {
|
if (native) {
|
||||||
receipt = await swap.swapExactInFromVaaNative(
|
logger.debug("relayVaaTo: calling evmSwapExactInFromVaaNative()");
|
||||||
|
receipt = await swap.evmSwapExactInFromVaaNative(
|
||||||
tcd.contractWithSigner,
|
tcd.contractWithSigner,
|
||||||
signedVaaArray
|
signedVaaArray
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
receipt = await swap.swapExactInFromVaaToken(
|
logger.debug("relayVaaTo: calling evmSwapExactInFromVaaToken()");
|
||||||
|
receipt = await swap.evmSwapExactInFromVaaToken(
|
||||||
tcd.contractWithSigner,
|
tcd.contractWithSigner,
|
||||||
signedVaaArray
|
signedVaaArray
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (native) {
|
if (native) {
|
||||||
receipt = await swap.swapExactOutFromVaaNative(
|
logger.debug("relayVaaTo: calling evmSwapExactOutFromVaaNative()");
|
||||||
|
receipt = await swap.evmSwapExactOutFromVaaNative(
|
||||||
tcd.contractWithSigner,
|
tcd.contractWithSigner,
|
||||||
signedVaaArray
|
signedVaaArray
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
receipt = await swap.swapExactOutFromVaaToken(
|
logger.debug("relayVaaTo: calling evmSwapExactOutFromVaaToken()");
|
||||||
|
receipt = await swap.evmSwapExactOutFromVaaToken(
|
||||||
tcd.contractWithSigner,
|
tcd.contractWithSigner,
|
||||||
signedVaaArray
|
signedVaaArray
|
||||||
);
|
);
|
||||||
|
@ -316,27 +371,35 @@ async function relayVaaToEvmChain(
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
", native: " +
|
", native: " +
|
||||||
native +
|
native +
|
||||||
": success, txHash: " +
|
": completed: success, txHash: " +
|
||||||
receipt.transactionHash
|
receipt.transactionHash
|
||||||
);
|
);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (await isRedeemedOnEvm(t3Payload, tcd, signedVaaArray)) {
|
if (await isRedeemedOnEvm(tcd, signedVaaArray)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
", native: " +
|
", native: " +
|
||||||
native +
|
native +
|
||||||
": relay failed because the vaa has already been redeemed"
|
": completed: relay failed because the vaa has already been redeemed"
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -356,35 +419,42 @@ async function relayVaaToEvmChain(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await isRedeemedOnEvm(t3Payload, tcd, signedVaaArray)) {
|
if (await isRedeemedOnEvm(tcd, signedVaaArray)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
", native: " +
|
", native: " +
|
||||||
native +
|
native +
|
||||||
": redeem succeeded"
|
": redeem confirmed"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.error(
|
logger.error(
|
||||||
"relayVaaTo" +
|
"relayVaaTo" +
|
||||||
tcd.name +
|
tcd.name +
|
||||||
": contract: [" +
|
": srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], exactIn: " +
|
"], exactIn: " +
|
||||||
exactIn +
|
exactIn +
|
||||||
", native: " +
|
", native: " +
|
||||||
native +
|
native +
|
||||||
": redeem failed!"
|
": completed: failed to confirm redeem!"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isRedeemedOnEvm(
|
async function isRedeemedOnEvm(
|
||||||
t3Payload: Type3Payload,
|
|
||||||
tcd: EvmContractData,
|
tcd: EvmContractData,
|
||||||
signedVaaArray: Uint8Array
|
signedVaaArray: Uint8Array
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
getEmitterAddressTerra,
|
getEmitterAddressTerra,
|
||||||
getIsTransferCompletedEth,
|
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -25,17 +24,20 @@ import {
|
||||||
|
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
import { abi as SWAP_CONTRACT_V2_ABI } from "../../react/src/abi/contracts/CrossChainSwapV2.json";
|
import {
|
||||||
import { abi as SWAP_CONTRACT_V3_ABI } from "../../react/src/abi/contracts/CrossChainSwapV3.json";
|
EvmEnvironment,
|
||||||
|
isEvmContract,
|
||||||
import * as swap from "../../react/src/swapper/util";
|
loadEvmConfig,
|
||||||
|
makeEvmContractData,
|
||||||
import { isEvmContract, makeEvmContractData, relayVaaToEvm } from "./evm";
|
relayVaaToEvm,
|
||||||
|
} from "./evm";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isTerraContract,
|
isTerraContract,
|
||||||
|
loadTerraConfig,
|
||||||
makeTerraContractData,
|
makeTerraContractData,
|
||||||
relayVaaToTerra,
|
relayVaaToTerra,
|
||||||
|
TerraEnvironment,
|
||||||
} from "./terra";
|
} from "./terra";
|
||||||
|
|
||||||
export let logger: any;
|
export let logger: any;
|
||||||
|
@ -54,37 +56,12 @@ export type OurEnvironment = {
|
||||||
spy_host: string;
|
spy_host: string;
|
||||||
spy_filters: string;
|
spy_filters: string;
|
||||||
|
|
||||||
eth_provider_url: string;
|
evm_configs: EvmEnvironment[];
|
||||||
eth_contract_address: string;
|
terra_config: TerraEnvironment;
|
||||||
eth_token_bridge_address: string;
|
|
||||||
|
|
||||||
polygon_provider_url: string;
|
|
||||||
polygon_contract_address: string;
|
|
||||||
polygon_token_bridge_address: string;
|
|
||||||
|
|
||||||
evm_wallet_private_key: string;
|
|
||||||
|
|
||||||
terraEnabled: boolean;
|
|
||||||
terra_provider_url: string;
|
|
||||||
terra_chain_id: string;
|
|
||||||
terra_name: string;
|
|
||||||
terra_contract_address: string;
|
|
||||||
terra_token_bridge_address: string;
|
|
||||||
terra_wallet_private_key: string;
|
|
||||||
terra_gas_price_url: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type EvmContractData = {
|
|
||||||
name: string;
|
|
||||||
contractAddress: string;
|
|
||||||
tokenBridgeAddress: string;
|
|
||||||
contract: ethers.Contract;
|
|
||||||
provider: ethers.providers.StaticJsonRpcProvider;
|
|
||||||
wallet: ethers.Wallet;
|
|
||||||
contractWithSigner: ethers.Contract;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Type3Payload = {
|
export type Type3Payload = {
|
||||||
|
sourceChainId: number;
|
||||||
targetChainId: number;
|
targetChainId: number;
|
||||||
contractAddress: string;
|
contractAddress: string;
|
||||||
relayerFee: ethers.BigNumber;
|
relayerFee: ethers.BigNumber;
|
||||||
|
@ -112,16 +89,14 @@ let pendingQueue = new Array<PendingEvent>();
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"swap_relay starting up, will listen for signed VAAs from [" +
|
"swap_relayer starting up, will listen for signed VAAs from [" +
|
||||||
env.spy_host +
|
env.spy_host +
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
makeEvmContractData(env);
|
makeEvmContractData(env.evm_configs);
|
||||||
if (env.terraEnabled) {
|
makeTerraContractData(env.terra_config);
|
||||||
makeTerraContractData(env);
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error("failed to connect to target contracts: %o", e);
|
logger.error("failed to connect to target contracts: %o", e);
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -139,98 +114,22 @@ function loadConfig(): [boolean, OurEnvironment] {
|
||||||
return [false, undefined];
|
return [false, undefined];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process.env.ETH_PROVIDER) {
|
let evm_configs: EvmEnvironment[] = null;
|
||||||
logger.error("Missing environment variable ETH_PROVIDER");
|
if (process.env.EVM_CHAINS) {
|
||||||
return [false, undefined];
|
evm_configs = loadEvmConfig();
|
||||||
}
|
if (!evm_configs) return [false, undefined];
|
||||||
if (!process.env.ETH_CONTRACT_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable ETH_CONTRACT_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
if (!process.env.ETH_TOKEN_BRIDGE_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable ETH_TOKEN_BRIDGE_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process.env.POLYGON_PROVIDER) {
|
let terra_config = loadTerraConfig();
|
||||||
logger.error("Missing environment variable POLYGON_PROVIDER");
|
if (!terra_config) return [false, undefined];
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
if (!process.env.POLYGON_CONTRACT_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable POLYGON_CONTRACT_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
if (!process.env.POLYGON_TOKEN_BRIDGE_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable POLYGON_TOKEN_BRIDGE_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.WALLET_PRIVATE_KEY) {
|
|
||||||
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
|
|
||||||
let terraEnabled: boolean = false;
|
|
||||||
if (process.env.TERRA_PROVIDER) {
|
|
||||||
terraEnabled = true;
|
|
||||||
|
|
||||||
if (!process.env.TERRA_CHAIN_ID) {
|
|
||||||
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
|
|
||||||
return [false, undefined];
|
|
||||||
throw "Missing environment variable TERRA_CHAIN_ID";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TERRA_NAME) {
|
|
||||||
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
|
|
||||||
return [false, undefined];
|
|
||||||
throw "Missing environment variable TERRA_NAME";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TERRA_WALLET_PRIVATE_KEY) {
|
|
||||||
logger.error("Missing environment variable TERRA_WALLET_PRIVATE_KEY");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TERRA_GAS_PRICE_URL) {
|
|
||||||
logger.error("Missing environment variable TERRA_GAS_PRICE_URL");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TERRA_CONTRACT_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable TERRA_CONTRACT_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TERRA_TOKEN_BRIDGE_ADDRESS) {
|
|
||||||
logger.error("Missing environment variable TERRA_TOKEN_BRIDGE_ADDRESS");
|
|
||||||
return [false, undefined];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
spy_host: process.env.SPY_SERVICE_HOST,
|
spy_host: process.env.SPY_SERVICE_HOST,
|
||||||
spy_filters: process.env.SPY_SERVICE_FILTERS,
|
spy_filters: process.env.SPY_SERVICE_FILTERS,
|
||||||
|
evm_configs: evm_configs,
|
||||||
eth_provider_url: process.env.ETH_PROVIDER,
|
terra_config: terra_config,
|
||||||
eth_contract_address: process.env.ETH_CONTRACT_ADDRESS,
|
|
||||||
eth_token_bridge_address: process.env.ETH_TOKEN_BRIDGE_ADDRESS,
|
|
||||||
|
|
||||||
polygon_provider_url: process.env.POLYGON_PROVIDER,
|
|
||||||
polygon_contract_address: process.env.POLYGON_CONTRACT_ADDRESS,
|
|
||||||
polygon_token_bridge_address: process.env.POLYGON_TOKEN_BRIDGE_ADDRESS,
|
|
||||||
|
|
||||||
evm_wallet_private_key: process.env.WALLET_PRIVATE_KEY,
|
|
||||||
|
|
||||||
terraEnabled: terraEnabled,
|
|
||||||
terra_provider_url: process.env.TERRA_PROVIDER,
|
|
||||||
terra_chain_id: process.env.TERRA_CHAIN_ID,
|
|
||||||
terra_name: process.env.TERRA_NAME,
|
|
||||||
terra_contract_address: process.env.TERRA_CONTRACT_ADDRESS,
|
|
||||||
terra_token_bridge_address: process.env.TERRA_TOKEN_BRIDGE_ADDRESS,
|
|
||||||
terra_wallet_private_key: process.env.TERRA_WALLET_PRIVATE_KEY,
|
|
||||||
terra_gas_price_url: process.env.TERRA_GAS_PRICE_URL,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -279,7 +178,7 @@ async function spy_listen() {
|
||||||
processVaa(vaaBytes);
|
processVaa(vaaBytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info("swap_relay waiting for transfer signed VAAs");
|
logger.info("swap_relayer waiting for transfer signed VAAs");
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,10 +199,10 @@ async function encodeEmitterAddress(
|
||||||
|
|
||||||
async function processVaa(vaaBytes: string) {
|
async function processVaa(vaaBytes: string) {
|
||||||
let receiveTime = new Date();
|
let receiveTime = new Date();
|
||||||
logger.debug("processVaa");
|
// logger.debug("processVaa");
|
||||||
const { parse_vaa } = await importCoreWasm();
|
const { parse_vaa } = await importCoreWasm();
|
||||||
const parsedVAA = parse_vaa(hexToUint8Array(vaaBytes));
|
const parsedVAA = parse_vaa(hexToUint8Array(vaaBytes));
|
||||||
logger.debug("processVaa: parsedVAA: %o", parsedVAA);
|
// logger.debug("processVaa: parsedVAA: %o", parsedVAA);
|
||||||
|
|
||||||
let emitter_address: string = uint8ArrayToHex(parsedVAA.emitter_address);
|
let emitter_address: string = uint8ArrayToHex(parsedVAA.emitter_address);
|
||||||
|
|
||||||
|
@ -326,14 +225,14 @@ async function processVaa(vaaBytes: string) {
|
||||||
|
|
||||||
let t3Payload: Type3Payload = null;
|
let t3Payload: Type3Payload = null;
|
||||||
try {
|
try {
|
||||||
t3Payload = decodeSignedVAAPayloadType3(parsedVAA);
|
t3Payload = decodeSignedVAAPayloadType3(parsedVAA, parsedVAA.emitter_chain);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("failed to parse type 3 vaa: %o", e);
|
logger.error("failed to parse type 3 vaa: %o", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t3Payload) {
|
if (t3Payload) {
|
||||||
if (isOurContract(t3Payload.contractAddress)) {
|
if (isOurContract(t3Payload.contractAddress, t3Payload.targetChainId)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
"enqueuing type 3 vaa: emitter: [" +
|
"enqueuing type 3 vaa: emitter: [" +
|
||||||
parsedVAA.emitter_chain +
|
parsedVAA.emitter_chain +
|
||||||
|
@ -341,7 +240,9 @@ async function processVaa(vaaBytes: string) {
|
||||||
emitter_address +
|
emitter_address +
|
||||||
"], seqNum: " +
|
"], seqNum: " +
|
||||||
parsedVAA.sequence +
|
parsedVAA.sequence +
|
||||||
", contractAddress: [" +
|
", target: [" +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
":" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], relayerFee: [" +
|
"], relayerFee: [" +
|
||||||
t3Payload.relayerFee +
|
t3Payload.relayerFee +
|
||||||
|
@ -361,7 +262,9 @@ async function processVaa(vaaBytes: string) {
|
||||||
emitter_address +
|
emitter_address +
|
||||||
"], seqNum: " +
|
"], seqNum: " +
|
||||||
parsedVAA.sequence +
|
parsedVAA.sequence +
|
||||||
", contractAddress: [" +
|
", target: [" +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
":" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"], relayerFee: [" +
|
"], relayerFee: [" +
|
||||||
t3Payload.relayerFee +
|
t3Payload.relayerFee +
|
||||||
|
@ -372,21 +275,24 @@ async function processVaa(vaaBytes: string) {
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
// } else {
|
||||||
logger.debug(
|
// logger.debug(
|
||||||
"dropping vaa: emitter: [" +
|
// "dropping vaa: emitter: [" +
|
||||||
parsedVAA.emitter_chain +
|
// parsedVAA.emitter_chain +
|
||||||
":" +
|
// ":" +
|
||||||
emitter_address +
|
// emitter_address +
|
||||||
"], seqNum: " +
|
// "], seqNum: " +
|
||||||
parsedVAA.sequence +
|
// parsedVAA.sequence +
|
||||||
" payloadType: " +
|
// " payloadType: " +
|
||||||
parsedVAA.payload[0]
|
// parsedVAA.payload[0]
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeSignedVAAPayloadType3(parsedVAA: any): Type3Payload {
|
function decodeSignedVAAPayloadType3(
|
||||||
|
parsedVAA: any,
|
||||||
|
sourceChainId: number
|
||||||
|
): Type3Payload {
|
||||||
const payload = Buffer.from(new Uint8Array(parsedVAA.payload));
|
const payload = Buffer.from(new Uint8Array(parsedVAA.payload));
|
||||||
if (payload[0] !== 3) return undefined;
|
if (payload[0] !== 3) return undefined;
|
||||||
|
|
||||||
|
@ -412,11 +318,6 @@ function decodeSignedVAAPayloadType3(parsedVAA: any): Type3Payload {
|
||||||
payload.slice(67, 67 + 32).toString("hex") +
|
payload.slice(67, 67 + 32).toString("hex") +
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
logger.info(
|
|
||||||
"decodeSignedVAAPayloadType3: terraContractAddr: [" +
|
|
||||||
payload.slice(67, 67 + 32).toString() +
|
|
||||||
"]"
|
|
||||||
);
|
|
||||||
|
|
||||||
contractAddress = payload.slice(67, 67 + 32).toString("hex");
|
contractAddress = payload.slice(67, 67 + 32).toString("hex");
|
||||||
} else {
|
} else {
|
||||||
|
@ -431,21 +332,25 @@ function decodeSignedVAAPayloadType3(parsedVAA: any): Type3Payload {
|
||||||
}
|
}
|
||||||
|
|
||||||
contractAddress = payload.slice(79, 79 + 20).toString("hex");
|
contractAddress = payload.slice(79, 79 + 20).toString("hex");
|
||||||
swapFunctionType = payload.readUInt8(260);
|
swapFunctionType = payload.readUInt8(272);
|
||||||
swapCurrencyType = payload.readUInt8(261);
|
swapCurrencyType = payload.readUInt8(273);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
sourceChainId: sourceChainId,
|
||||||
targetChainId: targetChainId,
|
targetChainId: targetChainId,
|
||||||
contractAddress: payload.slice(79, 79 + 20).toString("hex"),
|
contractAddress: contractAddress,
|
||||||
relayerFee: ethers.BigNumber.from(payload.slice(101, 101 + 32)),
|
relayerFee: ethers.BigNumber.from(payload.slice(101, 101 + 32)),
|
||||||
swapFunctionType: swapFunctionType,
|
swapFunctionType: swapFunctionType,
|
||||||
swapCurrencyType: swapCurrencyType,
|
swapCurrencyType: swapCurrencyType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOurContract(contractAddress: string): boolean {
|
function isOurContract(contractAddress: string, chainId: number): boolean {
|
||||||
return isEvmContract(contractAddress) || isTerraContract(contractAddress);
|
return (
|
||||||
|
isEvmContract(contractAddress, chainId) ||
|
||||||
|
isTerraContract(contractAddress, chainId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postVaa(
|
async function postVaa(
|
||||||
|
@ -533,14 +438,12 @@ async function callBack(err: any, result: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function relayVaa(vaaBytes: string, t3Payload: Type3Payload) {
|
async function relayVaa(vaaBytes: string, t3Payload: Type3Payload) {
|
||||||
const signedVaaArray = hexToUint8Array(vaaBytes);
|
|
||||||
|
|
||||||
if (t3Payload.targetChainId === 3) {
|
if (t3Payload.targetChainId === 3) {
|
||||||
relayVaaToTerra(t3Payload, signedVaaArray);
|
await relayVaaToTerra(t3Payload, vaaBytes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
relayVaaToEvm(signedVaaArray, t3Payload);
|
await relayVaaToEvm(vaaBytes, t3Payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////// Start of logger stuff ///////////////////////////////////////////
|
///////////////////////////////// Start of logger stuff ///////////////////////////////////////////
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
import {
|
import {
|
||||||
|
CHAIN_ID_TERRA,
|
||||||
getIsTransferCompletedTerra,
|
getIsTransferCompletedTerra,
|
||||||
redeemOnTerra,
|
redeemOnTerra,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
import {
|
import axios from "axios";
|
||||||
importCoreWasm,
|
import { bech32 } from "bech32";
|
||||||
setDefaultWasm,
|
import { zeroPad } from "ethers/lib/utils";
|
||||||
} from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
|
import { fromUint8Array } from "js-base64";
|
||||||
|
|
||||||
import * as Terra from "@terra-money/terra.js";
|
import * as Terra from "@terra-money/terra.js";
|
||||||
|
|
||||||
import { logger, OurEnvironment, Type3Payload } from "./index";
|
import { logger, OurEnvironment, Type3Payload } from "./index";
|
||||||
|
|
||||||
|
export type TerraEnvironment = {
|
||||||
|
terra_enabled: boolean;
|
||||||
|
terra_provider_url: string;
|
||||||
|
terra_chain_id: string;
|
||||||
|
terra_name: string;
|
||||||
|
terra_contract_address: string;
|
||||||
|
terra_token_bridge_address: string;
|
||||||
|
terra_wallet_private_key: string;
|
||||||
|
terra_gas_price_url: string;
|
||||||
|
};
|
||||||
|
|
||||||
type TerraContractData = {
|
type TerraContractData = {
|
||||||
name: string;
|
name: string;
|
||||||
contractAddress: string;
|
contractAddress: string;
|
||||||
|
encodedContractAddress: string;
|
||||||
tokenBridgeAddress: string;
|
tokenBridgeAddress: string;
|
||||||
lcdConfig: Terra.LCDClientConfig;
|
lcdConfig: Terra.LCDClientConfig;
|
||||||
lcdClient: Terra.LCDClient;
|
lcdClient: Terra.LCDClient;
|
||||||
|
@ -24,15 +38,70 @@ type TerraContractData = {
|
||||||
|
|
||||||
let terraContractData: TerraContractData = null;
|
let terraContractData: TerraContractData = null;
|
||||||
|
|
||||||
export function makeTerraContractData(env: OurEnvironment) {
|
export function loadTerraConfig(): TerraEnvironment {
|
||||||
|
let terra_enabled: boolean = false;
|
||||||
|
if (process.env.TERRA_PROVIDER) {
|
||||||
|
terra_enabled = true;
|
||||||
|
|
||||||
|
if (!process.env.TERRA_CHAIN_ID) {
|
||||||
|
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.TERRA_NAME) {
|
||||||
|
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.TERRA_WALLET_PRIVATE_KEY) {
|
||||||
|
logger.error("Missing environment variable TERRA_WALLET_PRIVATE_KEY");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.TERRA_GAS_PRICE_URL) {
|
||||||
|
logger.error("Missing environment variable TERRA_GAS_PRICE_URL");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.TERRA_CONTRACT_ADDRESS) {
|
||||||
|
logger.error("Missing environment variable TERRA_CONTRACT_ADDRESS");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.TERRA_TOKEN_BRIDGE_ADDRESS) {
|
||||||
|
logger.error("Missing environment variable TERRA_TOKEN_BRIDGE_ADDRESS");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
terra_enabled: terra_enabled,
|
||||||
|
terra_provider_url: process.env.TERRA_PROVIDER,
|
||||||
|
terra_chain_id: process.env.TERRA_CHAIN_ID,
|
||||||
|
terra_name: process.env.TERRA_NAME,
|
||||||
|
terra_contract_address: process.env.TERRA_CONTRACT_ADDRESS,
|
||||||
|
terra_token_bridge_address: process.env.TERRA_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
terra_wallet_private_key: process.env.TERRA_WALLET_PRIVATE_KEY,
|
||||||
|
terra_gas_price_url: process.env.TERRA_GAS_PRICE_URL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeTerraContractData(env: TerraEnvironment) {
|
||||||
|
if (!env.terra_enabled) return;
|
||||||
let contractAddress: string = env.terra_contract_address.toLowerCase();
|
let contractAddress: string = env.terra_contract_address.toLowerCase();
|
||||||
if (contractAddress.search("0x") == 0) {
|
if (contractAddress.search("0x") == 0) {
|
||||||
contractAddress = contractAddress.substring(2);
|
contractAddress = contractAddress.substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let encodedContractAddress: string = Buffer.from(
|
||||||
|
zeroPad(bech32.fromWords(bech32.decode(contractAddress).words), 32)
|
||||||
|
).toString("hex");
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Connecting to Terra: contract address: [" +
|
"Connecting to Terra: contract address: [" +
|
||||||
contractAddress +
|
contractAddress +
|
||||||
|
"], encoded contract address: [" +
|
||||||
|
encodedContractAddress +
|
||||||
"], node: [" +
|
"], node: [" +
|
||||||
env.terra_provider_url +
|
env.terra_provider_url +
|
||||||
"], token bridge address: [" +
|
"], token bridge address: [" +
|
||||||
|
@ -61,6 +130,7 @@ export function makeTerraContractData(env: OurEnvironment) {
|
||||||
terraContractData = {
|
terraContractData = {
|
||||||
name: "Terra",
|
name: "Terra",
|
||||||
contractAddress: contractAddress,
|
contractAddress: contractAddress,
|
||||||
|
encodedContractAddress: encodedContractAddress,
|
||||||
tokenBridgeAddress: env.terra_token_bridge_address,
|
tokenBridgeAddress: env.terra_token_bridge_address,
|
||||||
lcdConfig: lcdConfig,
|
lcdConfig: lcdConfig,
|
||||||
lcdClient: lcdClient,
|
lcdClient: lcdClient,
|
||||||
|
@ -69,111 +139,221 @@ export function makeTerraContractData(env: OurEnvironment) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTerraContract(contractAddress: string): boolean {
|
export function isTerraContract(
|
||||||
return (
|
contractAddress: string,
|
||||||
terraContractData && contractAddress === terraContractData.contractAddress
|
chain_id: number
|
||||||
|
): boolean {
|
||||||
|
if (chain_id !== CHAIN_ID_TERRA) return false;
|
||||||
|
if (!terraContractData) return false;
|
||||||
|
|
||||||
|
let retVal: boolean =
|
||||||
|
terraContractData &&
|
||||||
|
contractAddress === terraContractData.encodedContractAddress;
|
||||||
|
logger.debug(
|
||||||
|
"isTerraContract: comparing [" +
|
||||||
|
contractAddress +
|
||||||
|
"] to [" +
|
||||||
|
terraContractData.encodedContractAddress +
|
||||||
|
"]: " +
|
||||||
|
retVal
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function relayVaaToTerra(
|
export async function relayVaaToTerra(
|
||||||
t3Payload: Type3Payload,
|
t3Payload: Type3Payload,
|
||||||
signedVaaArray: Uint8Array
|
vaaBytes: string
|
||||||
) {
|
) {
|
||||||
if (!terraContractData) return;
|
if (!terraContractData) return;
|
||||||
|
|
||||||
logger.debug(
|
// logger.debug(
|
||||||
"relayVaaToTerra: checking if already redeemed using tokenBridgeAddress [" +
|
// "relayVaaToTerra: checking if already redeemed using tokenBridgeAddress [" +
|
||||||
terraContractData.tokenBridgeAddress +
|
// terraContractData.tokenBridgeAddress +
|
||||||
"]"
|
// "]"
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (await isRedeemedOnTerra(t3Payload, terraContractData, signedVaaArray)) {
|
// if (await isRedeemedOnTerra(terraContractData, vaaBytes)) {
|
||||||
logger.info(
|
// logger.info(
|
||||||
"relayVaaToTerra: contract: [" +
|
// "relayVaaToTerra: srcChain: " +
|
||||||
t3Payload.contractAddress +
|
// t3Payload.sourceChainId +
|
||||||
"]: already transferred"
|
// ", targetChain: " +
|
||||||
);
|
// t3Payload.targetChainId +
|
||||||
|
// ", contract: [" +
|
||||||
|
// t3Payload.contractAddress +
|
||||||
|
// "]: completed: already redeemed"
|
||||||
|
// );
|
||||||
|
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"relayVaaToTerra: contract: [" +
|
|
||||||
t3Payload.contractAddress +
|
|
||||||
"]: submitting redeem request"
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const msg = await redeemOnTerra(
|
logger.debug(
|
||||||
terraContractData.contractAddress,
|
"relayVaaToTerra: creating message using contract address [" +
|
||||||
terraContractData.wallet.key.accAddress,
|
terraContractData.contractAddress +
|
||||||
signedVaaArray
|
"]"
|
||||||
);
|
);
|
||||||
|
|
||||||
let receipt: any = null;
|
logger.debug("relayVaaToTerra: creating a message");
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"relayVaaToTerra: vaa as hex: [" +
|
||||||
|
Buffer.from(vaaBytes, "hex").toString("hex") +
|
||||||
|
"]"
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"relayVaaToTerra: vaa as base64: [" +
|
||||||
|
Buffer.from(vaaBytes, "hex").toString("base64") +
|
||||||
|
"]"
|
||||||
|
);
|
||||||
|
const msg = new Terra.MsgExecuteContract(
|
||||||
|
terraContractData.wallet.key.accAddress,
|
||||||
|
terraContractData.contractAddress,
|
||||||
|
{
|
||||||
|
redeem_payload: {
|
||||||
|
data: Buffer.from(vaaBytes, "hex").toString("base64"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// logger.debug("relayVaaToTerra: getting gas prices");
|
||||||
|
// const gasPrices = terraContractData.lcdClient.config.gasPrices;
|
||||||
|
|
||||||
|
// logger.debug("relayVaaToTerra: estimating fees");
|
||||||
|
// const feeEstimate = await terraContractData.lcdClient.tx.estimateFee(terraContractData.wallet.key.accAddress, [msg], {
|
||||||
|
// feeDenoms: ["uluna"],
|
||||||
|
// gasPrices,
|
||||||
|
// });
|
||||||
|
|
||||||
|
logger.debug("relayVaaToTerra: creating transaction");
|
||||||
|
const tx = await terraContractData.wallet.createAndSignTx({
|
||||||
|
msgs: [msg],
|
||||||
|
memo: "swap relayer",
|
||||||
|
feeDenoms: ["uluna"],
|
||||||
|
// gasPrices,
|
||||||
|
// fee: feeEstimate,
|
||||||
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"relayVaaToTerra: contract: [" +
|
"relayVaaToTerra: contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"]: success, txHash: " +
|
"]: submitting redeem request"
|
||||||
receipt.transactionHash
|
|
||||||
);
|
);
|
||||||
} catch (e: any) {
|
|
||||||
if (await isRedeemedOnTerra(t3Payload, terraContractData, signedVaaArray)) {
|
|
||||||
logger.info(
|
|
||||||
"relayVaaToTerra: contract: [" +
|
|
||||||
t3Payload.contractAddress +
|
|
||||||
"]: relay failed because the vaa has already been redeemed"
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
const receipt = await terraContractData.lcdClient.tx.broadcast(tx);
|
||||||
}
|
|
||||||
|
logger.info(
|
||||||
|
"relayVaaToTerra: srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
|
t3Payload.contractAddress +
|
||||||
|
"]: completed: success: %o",
|
||||||
|
receipt
|
||||||
|
);
|
||||||
|
|
||||||
|
// logger.info(
|
||||||
|
// "relayVaaToTerra: contract: [" +
|
||||||
|
// t3Payload.contractAddress +
|
||||||
|
// "]: success, txHash: " +
|
||||||
|
// receipt.transactionHash
|
||||||
|
// );
|
||||||
|
} catch (e: any) {
|
||||||
|
// if (await isRedeemedOnTerra(terraContractData, vaaBytes)) {
|
||||||
|
// logger.info(
|
||||||
|
// "relayVaaToTerra: srcChain: " +
|
||||||
|
// t3Payload.sourceChainId +
|
||||||
|
// ", targetChain: " +
|
||||||
|
// t3Payload.targetChainId +
|
||||||
|
// ", contract: [" +
|
||||||
|
// t3Payload.contractAddress +
|
||||||
|
// "]: completed: relay failed because the vaa has already been redeemed"
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
"relayVaaToTerra: contract: [" +
|
"relayVaaToTerra: srcChain: " +
|
||||||
|
t3Payload.sourceChainId +
|
||||||
|
", targetChain: " +
|
||||||
|
t3Payload.targetChainId +
|
||||||
|
", contract: [" +
|
||||||
t3Payload.contractAddress +
|
t3Payload.contractAddress +
|
||||||
"]: transaction failed: %o",
|
"]: completed: transaction failed: %o",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await isRedeemedOnTerra(t3Payload, terraContractData, signedVaaArray)) {
|
// if (await isRedeemedOnTerra(terraContractData, vaaBytes)) {
|
||||||
logger.info(
|
// logger.info(
|
||||||
"relayVaaToTerra: contract: [" +
|
// "relayVaaToTerra: srcChain: " +
|
||||||
t3Payload.contractAddress +
|
// t3Payload.sourceChainId +
|
||||||
"]: redeem succeeded"
|
// ", targetChain: " +
|
||||||
);
|
// t3Payload.targetChainId +
|
||||||
} else {
|
// ", contract: [" +
|
||||||
logger.error(
|
// t3Payload.contractAddress +
|
||||||
"relayVaaToTerra: contract: [" +
|
// "]: redeem confirmed"
|
||||||
t3Payload.contractAddress +
|
// );
|
||||||
"]: redeem failed!"
|
// } else {
|
||||||
);
|
// logger.error(
|
||||||
}
|
// "relayVaaToTerra: srcChain: " +
|
||||||
|
// t3Payload.sourceChainId +
|
||||||
|
// ", targetChain: " +
|
||||||
|
// t3Payload.targetChainId +
|
||||||
|
// ", contract: [" +
|
||||||
|
// t3Payload.contractAddress +
|
||||||
|
// "]: completed: failed to confirm redeem!"
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isRedeemedOnTerra(
|
async function isRedeemedOnTerra(
|
||||||
t3Payload: Type3Payload,
|
|
||||||
terraContractData: TerraContractData,
|
terraContractData: TerraContractData,
|
||||||
signedVaaArray: Uint8Array
|
vaaBytes: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
let redeemed: boolean = false;
|
let msg: Terra.MsgExecuteContract = null;
|
||||||
|
let sequenceNumber: number = 0;
|
||||||
try {
|
try {
|
||||||
redeemed = await await getIsTransferCompletedTerra(
|
msg = new Terra.MsgExecuteContract(
|
||||||
terraContractData.tokenBridgeAddress,
|
|
||||||
signedVaaArray,
|
|
||||||
terraContractData.wallet.key.accAddress,
|
terraContractData.wallet.key.accAddress,
|
||||||
terraContractData.lcdClient,
|
terraContractData.tokenBridgeAddress,
|
||||||
terraContractData.gasPriceUrl
|
{
|
||||||
|
submit_vaa: {
|
||||||
|
data: Buffer.from(vaaBytes, "hex").toString("base64"),
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sequenceNumber = await terraContractData.wallet.sequence();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"relayVaaTo" +
|
"isRedeemedOnTerra: failed to create message or look up sequence number, e: %o",
|
||||||
terraContractData.name +
|
e
|
||||||
": failed to check if transfer is already complete, will attempt the transfer, e: %o",
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await terraContractData.lcdClient.tx.estimateFee(
|
||||||
|
[{ sequenceNumber: sequenceNumber }],
|
||||||
|
{
|
||||||
|
msgs: [msg],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response.data.error.includes("VaaAlreadyExecuted")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error(
|
||||||
|
"isRedeemedOnTerra: failed to check if transfer is already complete, e: %o",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redeemed;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue