Update relayer to handle new payload

This commit is contained in:
Drew 2022-07-01 20:36:07 +00:00
parent 162dd6ce9e
commit f7ab415cf2
No known key found for this signature in database
GPG Key ID: 14D9F383FC9F839E
2 changed files with 40 additions and 258 deletions

View File

@ -1,7 +1,4 @@
import { import { getIsTransferCompletedEth, hexToUint8Array } from "@certusone/wormhole-sdk";
getIsTransferCompletedEth,
hexToUint8Array,
} from "@certusone/wormhole-sdk";
import { ethers } from "ethers"; import { ethers } from "ethers";
@ -54,35 +51,24 @@ export function loadEvmConfig(): EvmEnvironment[] {
} }
let key_contract_address: string = evm + "_CONTRACT_ADDRESS"; let key_contract_address: string = evm + "_CONTRACT_ADDRESS";
let val_contract_address: string = eval( let val_contract_address: string = eval("process.env." + key_contract_address);
"process.env." + key_contract_address
);
if (!val_contract_address) { if (!val_contract_address) {
logger.error("Missing environment variable " + key_contract_address); logger.error("Missing environment variable " + key_contract_address);
return undefined; return undefined;
} }
let key_token_bridge_address: string = evm + "_TOKEN_BRIDGE_ADDRESS"; let key_token_bridge_address: string = evm + "_TOKEN_BRIDGE_ADDRESS";
let val_token_bridge_address: string = eval( let val_token_bridge_address: string = eval("process.env." + key_token_bridge_address);
"process.env." + key_token_bridge_address
);
if (!val_token_bridge_address) { if (!val_token_bridge_address) {
logger.error("Missing environment variable " + key_token_bridge_address); logger.error("Missing environment variable " + key_token_bridge_address);
return undefined; return undefined;
} }
let key_wallet_private_key: string = evm + "_WALLET_PRIVATE_KEY"; let key_wallet_private_key: string = evm + "_WALLET_PRIVATE_KEY";
let val_wallet_private_key: string = eval( let val_wallet_private_key: string = eval("process.env." + key_wallet_private_key);
"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)
val_wallet_private_key = process.env.WALLET_PRIVATE_KEY;
if (!val_wallet_private_key) { if (!val_wallet_private_key) {
logger.error( logger.error("Missing environment variable " + key_wallet_private_key + " or WALLET_PRIVATE_KEY");
"Missing environment variable " +
key_wallet_private_key +
" or WALLET_PRIVATE_KEY"
);
return undefined; return undefined;
} }
@ -170,72 +156,15 @@ function makeContractDataForEvm(env: EvmEnvironment): EvmContractData {
}; };
} }
export function isEvmContract( export function isEvmContract(contractAddress: string, chain_id: number): boolean {
contractAddress: string,
chain_id: number
): boolean {
let ecd = evmContractData.get(chain_id); let ecd = evmContractData.get(chain_id);
return ecd && ecd.contractAddress === contractAddress; return ecd && ecd.contractAddress === contractAddress;
} }
/*
// GOERLI_PROVIDER = Ethereum
// MUMBAI_PROVIDER = Polygon
if (t3Payload.contractAddress === CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM) {
// Use one of the V3 swap methods.
} else if (t3Payload.contractAddress === CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON) {
// Use one of the V2 swap methods.
} else {
// Error
}
if (t3Payload.swapFunctionType === 1 && t3Payload.swapCurrencyType === 1) {
// swapExactInFromVaaNative
} else if (t3Payload.swapFunctionType === 1 && t3Payload.swapCurrencyType === 2) {
// swapExactInFromVaaToken
} else if (
t3Payload.swapFunctionType === 2 && t3Payload.swapCurrencyType === 1) {
// swapExactOutFromVaaNative
} else if (t3Payload.swapFunctionType === 2 && t3Payload.swapCurrencyType === 2) {
// swapExactOutFromVaaToken
} else {
// error
}
*/
/*
// GOERLI_PROVIDER = Ethereum
// MUMBAI_PROVIDER = Polygon
if (t3Payload.contractAddress === CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM) {
// Use one of the V3 swap methods.
} else if (t3Payload.contractAddress === CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON) {
// Use one of the V2 swap methods.
} else {
// Error
}
if (t3Payload.swapFunctionType === 1 && t3Payload.swapCurrencyType === 1) {
// swapExactInFromVaaNative
} else if (t3Payload.swapFunctionType === 1 && t3Payload.swapCurrencyType === 2) {
// swapExactInFromVaaToken
} else if (
t3Payload.swapFunctionType === 2 && t3Payload.swapCurrencyType === 1) {
// swapExactOutFromVaaNative
} else if (t3Payload.swapFunctionType === 2 && t3Payload.swapCurrencyType === 2) {
// swapExactOutFromVaaToken
} else {
// error
}
*/
export async function relayVaaToEvm(vaaBytes: string, t3Payload: Type3Payload) { export async function relayVaaToEvm(vaaBytes: string, t3Payload: Type3Payload) {
let ecd = evmContractData.get(t3Payload.targetChainId); let ecd = evmContractData.get(t3Payload.targetChainId);
if (!ecd) { if (!ecd) {
logger.error( logger.error("relayVaaToEvm: chain id " + t3Payload.targetChainId + " does not exist!");
"relayVaaToEvm: chain id " + t3Payload.targetChainId + " does not exist!"
);
} }
let exactIn: boolean = false; let exactIn: boolean = false;
@ -244,51 +173,23 @@ export async function relayVaaToEvm(vaaBytes: string, t3Payload: Type3Payload) {
exactIn = true; exactIn = true;
} else if (t3Payload.swapFunctionType !== 2) { } else if (t3Payload.swapFunctionType !== 2) {
error = true; error = true;
logger.error( logger.error("relayVaaTo" + ecd.name + ": unsupported swapFunctionType: [" + t3Payload.swapFunctionType + "]");
"relayVaaTo" +
ecd.name +
": unsupported swapFunctionType: [" +
t3Payload.swapFunctionType +
"]"
);
} }
let native: boolean = false;
if (t3Payload.swapCurrencyType === 1) {
native = true;
} else if (t3Payload.swapCurrencyType !== 2) {
error = true;
logger.error(
"relayVaaTo" +
ecd.name +
": unsupported swapCurrencyType: [" +
t3Payload.swapCurrencyType +
"]"
);
}
if (error) return; if (error) return;
logger.debug( logger.debug(
"relayVaaTo" + "relayVaaTo" + ecd.name + ": chain_id: " + ecd.chain_id + ", contractAddress: [" + t3Payload.contractAddress + "]"
ecd.name +
": chain_id: " +
ecd.chain_id +
", contractAddress: [" +
t3Payload.contractAddress +
"]"
); );
const signedVaaArray = hexToUint8Array(vaaBytes); const signedVaaArray = hexToUint8Array(vaaBytes);
await relayVaaToEvmChain(t3Payload, ecd, signedVaaArray, exactIn, native); await relayVaaToEvmChain(t3Payload, ecd, signedVaaArray, exactIn);
} }
async function relayVaaToEvmChain( async function relayVaaToEvmChain(
t3Payload: Type3Payload, t3Payload: Type3Payload,
tcd: EvmContractData, tcd: EvmContractData,
signedVaaArray: Uint8Array, signedVaaArray: Uint8Array,
exactIn: boolean, exactIn: boolean
native: boolean
) { ) {
logger.debug( logger.debug(
"relayVaaTo" + "relayVaaTo" +
@ -312,8 +213,6 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": completed: already transferred" ": completed: already transferred"
); );
@ -331,41 +230,17 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": submitting redeem request" ": submitting redeem request"
); );
try { try {
let receipt: any = null; let receipt: any = null;
if (exactIn) { if (exactIn) {
if (native) { logger.debug("relayVaaTo: calling evmSwapExactInFromVaaNative()");
logger.debug("relayVaaTo: calling evmSwapExactInFromVaaNative()"); receipt = await swap.evmSwapExactInFromVaaNative(tcd.contractWithSigner, signedVaaArray);
receipt = await swap.evmSwapExactInFromVaaNative(
tcd.contractWithSigner,
signedVaaArray
);
} else {
logger.debug("relayVaaTo: calling evmSwapExactInFromVaaToken()");
receipt = await swap.evmSwapExactInFromVaaToken(
tcd.contractWithSigner,
signedVaaArray
);
}
} else { } else {
if (native) { logger.debug("relayVaaTo: calling evmSwapExactOutFromVaaNative()");
logger.debug("relayVaaTo: calling evmSwapExactOutFromVaaNative()"); receipt = await swap.evmSwapExactOutFromVaaNative(tcd.contractWithSigner, signedVaaArray);
receipt = await swap.evmSwapExactOutFromVaaNative(
tcd.contractWithSigner,
signedVaaArray
);
} else {
logger.debug("relayVaaTo: calling evmSwapExactOutFromVaaToken()");
receipt = await swap.evmSwapExactOutFromVaaToken(
tcd.contractWithSigner,
signedVaaArray
);
}
} }
logger.info( logger.info(
@ -379,8 +254,6 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": completed: success, txHash: " + ": completed: success, txHash: " +
receipt.transactionHash receipt.transactionHash
); );
@ -397,8 +270,6 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": completed: relay failed because the vaa has already been redeemed" ": completed: relay failed because the vaa has already been redeemed"
); );
@ -412,8 +283,6 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": transaction failed: %o", ": transaction failed: %o",
e e
); );
@ -431,8 +300,6 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": redeem confirmed" ": redeem confirmed"
); );
} else { } else {
@ -447,29 +314,18 @@ async function relayVaaToEvmChain(
t3Payload.contractAddress + t3Payload.contractAddress +
"], exactIn: " + "], exactIn: " +
exactIn + exactIn +
", native: " +
native +
": completed: failed to confirm redeem!" ": completed: failed to confirm redeem!"
); );
} }
} }
async function isRedeemedOnEvm( async function isRedeemedOnEvm(tcd: EvmContractData, signedVaaArray: Uint8Array): Promise<boolean> {
tcd: EvmContractData,
signedVaaArray: Uint8Array
): Promise<boolean> {
let redeemed: boolean = false; let redeemed: boolean = false;
try { try {
redeemed = await getIsTransferCompletedEth( redeemed = await getIsTransferCompletedEth(tcd.tokenBridgeAddress, tcd.provider, signedVaaArray);
tcd.tokenBridgeAddress,
tcd.provider,
signedVaaArray
);
} catch (e) { } catch (e) {
logger.error( logger.error(
"relayVaaTo" + "relayVaaTo" + tcd.name + ": failed to check if transfer is already complete, will attempt the transfer, e: %o",
tcd.name +
": failed to check if transfer is already complete, will attempt the transfer, e: %o",
e e
); );
} }

View File

@ -12,33 +12,11 @@ import {
getEmitterAddressTerra, getEmitterAddressTerra,
} from "@certusone/wormhole-sdk"; } from "@certusone/wormhole-sdk";
import { import { importCoreWasm, setDefaultWasm } from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
importCoreWasm, import { createSpyRPCServiceClient, subscribeSignedVAA } from "@certusone/wormhole-spydk";
setDefaultWasm,
} from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
import {
createSpyRPCServiceClient,
subscribeSignedVAA,
} from "@certusone/wormhole-spydk";
import { ethers } from "ethers"; import { ethers } from "ethers";
import { EvmEnvironment, isEvmContract, loadEvmConfig, makeEvmContractData, relayVaaToEvm } from "./evm";
import { import { isTerraContract, loadTerraConfig, makeTerraContractData, relayVaaToTerra, TerraEnvironment } from "./terra";
EvmEnvironment,
isEvmContract,
loadEvmConfig,
makeEvmContractData,
relayVaaToEvm,
} from "./evm";
import {
isTerraContract,
loadTerraConfig,
makeTerraContractData,
relayVaaToTerra,
TerraEnvironment,
} from "./terra";
export let logger: any; export let logger: any;
@ -66,7 +44,6 @@ export type Type3Payload = {
contractAddress: string; contractAddress: string;
relayerFee: ethers.BigNumber; relayerFee: ethers.BigNumber;
swapFunctionType: number; swapFunctionType: number;
swapCurrencyType: number;
}; };
type PendingEvent = { type PendingEvent = {
@ -88,11 +65,7 @@ let condition = new CondVar();
let pendingQueue = new Array<PendingEvent>(); let pendingQueue = new Array<PendingEvent>();
if (success) { if (success) {
logger.info( logger.info("swap_relayer starting up, will listen for signed VAAs from [" + env.spy_host + "]");
"swap_relayer starting up, will listen for signed VAAs from [" +
env.spy_host +
"]"
);
try { try {
makeEvmContractData(env.evm_configs); makeEvmContractData(env.evm_configs);
@ -143,10 +116,7 @@ async function spy_listen() {
var myFilters = []; var myFilters = [];
for (var i = 0; i < parsedJsonFilters.length; i++) { for (var i = 0; i < parsedJsonFilters.length; i++) {
var myChainId = parseInt(parsedJsonFilters[i].chain_id) as ChainId; var myChainId = parseInt(parsedJsonFilters[i].chain_id) as ChainId;
var myEmitterAddress = await encodeEmitterAddress( var myEmitterAddress = await encodeEmitterAddress(myChainId, parsedJsonFilters[i].emitter_address);
myChainId,
parsedJsonFilters[i].emitter_address
);
var myEmitterFilter = { var myEmitterFilter = {
emitterFilter: { emitterFilter: {
chainId: myChainId, chainId: myChainId,
@ -182,10 +152,7 @@ async function spy_listen() {
})(); })();
} }
async function encodeEmitterAddress( async function encodeEmitterAddress(myChainId, emitterAddressStr): Promise<string> {
myChainId,
emitterAddressStr
): Promise<string> {
if (myChainId === CHAIN_ID_SOLANA) { if (myChainId === CHAIN_ID_SOLANA) {
return await getEmitterAddressSolana(emitterAddressStr); return await getEmitterAddressSolana(emitterAddressStr);
} }
@ -206,17 +173,11 @@ async function processVaa(vaaBytes: string) {
let emitter_address: string = uint8ArrayToHex(parsedVAA.emitter_address); let emitter_address: string = uint8ArrayToHex(parsedVAA.emitter_address);
let seqNumKey: string = let seqNumKey: string = parsedVAA.emitter_chain.toString() + ":" + emitter_address;
parsedVAA.emitter_chain.toString() + ":" + emitter_address;
let lastSeqNum = seqMap.get(seqNumKey); let lastSeqNum = seqMap.get(seqNumKey);
if (lastSeqNum) { if (lastSeqNum) {
if (lastSeqNum >= parsedVAA.sequence) { if (lastSeqNum >= parsedVAA.sequence) {
logger.debug( logger.debug("ignoring duplicate: emitter: [" + seqNumKey + "], seqNum: " + parsedVAA.sequence);
"ignoring duplicate: emitter: [" +
seqNumKey +
"], seqNum: " +
parsedVAA.sequence
);
return; return;
} }
} }
@ -248,8 +209,6 @@ async function processVaa(vaaBytes: string) {
t3Payload.relayerFee + t3Payload.relayerFee +
"], swapFunctionType: [" + "], swapFunctionType: [" +
t3Payload.swapFunctionType + t3Payload.swapFunctionType +
"], swapCurrencyType: [" +
t3Payload.swapCurrencyType +
"]" "]"
); );
@ -270,8 +229,6 @@ async function processVaa(vaaBytes: string) {
t3Payload.relayerFee + t3Payload.relayerFee +
"], swapFunctionType: [" + "], swapFunctionType: [" +
t3Payload.swapFunctionType + t3Payload.swapFunctionType +
"], swapCurrencyType: [" +
t3Payload.swapCurrencyType +
"]" "]"
); );
} }
@ -289,10 +246,7 @@ async function processVaa(vaaBytes: string) {
} }
} }
function decodeSignedVAAPayloadType3( function decodeSignedVAAPayloadType3(parsedVAA: any, sourceChainId: number): Type3Payload {
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;
@ -310,18 +264,13 @@ function decodeSignedVAAPayloadType3(
let contractAddress: string = ""; let contractAddress: string = "";
let swapFunctionType: number = 0; let swapFunctionType: number = 0;
let swapCurrencyType: number = 0;
if (targetChainId === 3) { if (targetChainId === 3) {
logger.info( logger.info("decodeSignedVAAPayloadType3: terraContractAddr: [" + payload.slice(67, 67 + 32).toString("hex") + "]");
"decodeSignedVAAPayloadType3: terraContractAddr: [" +
payload.slice(67, 67 + 32).toString("hex") +
"]"
);
contractAddress = payload.slice(67, 67 + 32).toString("hex"); contractAddress = payload.slice(67, 67 + 32).toString("hex");
} else { } else {
if (payload.length < 262) { if (payload.length < 272) {
logger.error( logger.error(
"decodeSignedVAAPayloadType3: dropping type 3 vaa because the payload is too short to extract the contract fields, length: " + "decodeSignedVAAPayloadType3: dropping type 3 vaa because the payload is too short to extract the contract fields, length: " +
payload.length + payload.length +
@ -330,34 +279,24 @@ function decodeSignedVAAPayloadType3(
); );
return undefined; return undefined;
} }
contractAddress = payload.slice(79, 79 + 20).toString("hex"); contractAddress = payload.slice(79, 79 + 20).toString("hex");
swapFunctionType = payload.readUInt8(272); swapFunctionType = payload.readUInt8(272);
swapCurrencyType = payload.readUInt8(273);
} }
return { return {
sourceChainId: sourceChainId, sourceChainId: sourceChainId,
targetChainId: targetChainId, targetChainId: targetChainId,
contractAddress: contractAddress, contractAddress: contractAddress,
relayerFee: ethers.BigNumber.from(payload.slice(101, 101 + 32)), relayerFee: ethers.BigNumber.from(payload.slice(273, 273 + 32)),
swapFunctionType: swapFunctionType, swapFunctionType: swapFunctionType,
swapCurrencyType: swapCurrencyType,
}; };
} }
function isOurContract(contractAddress: string, chainId: number): boolean { function isOurContract(contractAddress: string, chainId: number): boolean {
return ( return isEvmContract(contractAddress, chainId) || isTerraContract(contractAddress, chainId);
isEvmContract(contractAddress, chainId) ||
isTerraContract(contractAddress, chainId)
);
} }
async function postVaa( async function postVaa(vaaBytes: any, t3Payload: Type3Payload, receiveTime: Date) {
vaaBytes: any,
t3Payload: Type3Payload,
receiveTime: Date
) {
let event: PendingEvent = { let event: PendingEvent = {
vaaBytes: vaaBytes, vaaBytes: vaaBytes,
t3Payload: t3Payload, t3Payload: t3Payload,
@ -366,9 +305,7 @@ async function postVaa(
await mutex.runExclusive(() => { await mutex.runExclusive(() => {
pendingQueue.push(event); pendingQueue.push(event);
logger.debug( logger.debug("posting event, there are now " + pendingQueue.length + " enqueued events");
"posting event, there are now " + pendingQueue.length + " enqueued events"
);
if (condition) { if (condition) {
logger.debug("hitting condition variable."); logger.debug("hitting condition variable.");
condition.complete(true); condition.complete(true);
@ -419,16 +356,12 @@ async function callBack(err: any, result: any) {
await mutex.runExclusive(async () => { await mutex.runExclusive(async () => {
if (pendingQueue.length === 0) { if (pendingQueue.length === 0) {
logger.debug( logger.debug("in callback, no more pending events, rearming the condition.");
"in callback, no more pending events, rearming the condition."
);
done = true; done = true;
condition = new CondVar(); condition = new CondVar();
await condition.wait(COND_VAR_TIMEOUT, callBack); await condition.wait(COND_VAR_TIMEOUT, callBack);
} else { } else {
logger.debug( logger.debug("in callback, there are " + pendingQueue.length + " pending events.");
"in callback, there are " + pendingQueue.length + " pending events."
);
} }
}); });
} }
@ -455,8 +388,7 @@ function initLogger() {
let logFileName: string = ""; let logFileName: string = "";
if (process.env.LOG_DIR) { if (process.env.LOG_DIR) {
useConsole = false; useConsole = false;
logFileName = logFileName = process.env.LOG_DIR + "/swap_relay." + new Date().toISOString() + ".log";
process.env.LOG_DIR + "/swap_relay." + new Date().toISOString() + ".log";
} }
let logLevel = "info"; let logLevel = "info";
@ -472,11 +404,7 @@ function initLogger() {
level: logLevel, level: logLevel,
}); });
} else { } else {
console.log( console.log("swap_relay is logging to [%s] at level [%s]", logFileName, logLevel);
"swap_relay is logging to [%s] at level [%s]",
logFileName,
logLevel
);
transport = new winston.transports.File({ transport = new winston.transports.File({
filename: logFileName, filename: logFileName,
@ -492,9 +420,7 @@ function initLogger() {
winston.format.timestamp({ winston.format.timestamp({
format: "YYYY-MM-DD HH:mm:ss.SSS", format: "YYYY-MM-DD HH:mm:ss.SSS",
}), }),
winston.format.printf( winston.format.printf((info: any) => `${[info.timestamp]}|${info.level}|${info.message}`)
(info: any) => `${[info.timestamp]}|${info.level}|${info.message}`
)
), ),
}; };