Make swap relayer actually relay

This commit is contained in:
Bruce Riley 2022-01-21 00:22:40 +00:00
parent 0e49f18d54
commit 237d06b558
6 changed files with 385 additions and 61 deletions

View File

@ -92,7 +92,8 @@ $ docker run --platform linux/amd64 --network=host spy_guardian \
```
$ cd swap_relayer
$ # Edit the .env.sample, be sure to set a valid wallet private key.
$ cp .env.sample .env
$ # Edit the parameters in .env as appropriate.
$ npm ci
$ npm run build
$ npm run start

View File

@ -2,12 +2,14 @@
SPY_SERVICE_HOST=localhost:7073
SPY_SERVICE_FILTERS=[{"chain_id":2,"emitter_address":"0x000000000000000000000000f890982f9310df57d00f659cf4fd87e65aded8d7"},{"chain_id":5,"emitter_address":"0x000000000000000000000000377d55a7928c046e18eebb61977e714d2a76472a"}]
EVM_CHAIN_ID=2
EVM_NODE_URL=ws://localhost:8545
ETH_PROVIDER=https://goerli.infura.io/v3/ff29215fde9c4193bc25b344f10cefc0
POLYGON_PROVIDER=https://polygon-mumbai.infura.io/v3/ff29215fde9c4193bc25b344f10cefc0
# TestNet
EVM_PRIVATE_KEY=your_key_here
EVM_CONTRACT_ADDRESS=0x0290FB167208Af455bB137780163b7B7a9a10C16
# If these are defined, use them instead of the ones defined in the code.
#ETH_CONTRACT_ADDRESS=0x51687A2F951Fa9A78D5B4F278A9B23BEFeA2CC4f
#POLYGON_CONTRACT_ADDRESS=0x10C6B4c7669f1604459d59D29559D05003a07143
#LOG_DIR=/home/briley/logs
WALLET_PRIVATE_KEY=your_key_here
#LOG_DIR=/var/logs
LOG_LEVEL=debug

View File

@ -1 +1,2 @@
/lib
/lib
.env

View File

@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"build": "tsc",
"start": "node lib/index.js"
"start": "node lib/swap_relayer/src/index.js"
},
"author": "",
"license": "Apache-2.0",
@ -16,7 +16,7 @@
"@types/node": "^16.6.1",
"axios": "^0.24.0",
"esm": "^3.2.25",
"ethers": "^5.4.4",
"ethers": "^5.5.3",
"jest": "^27.3.1",
"prettier": "^2.3.2",
"ts-jest": "^27.0.7",

View File

@ -7,8 +7,6 @@ import {
getEmitterAddressEth,
getEmitterAddressSolana,
getEmitterAddressTerra,
getIsTransferCompletedEth,
redeemOnEth,
} from "@certusone/wormhole-sdk";
import {
@ -21,9 +19,18 @@ import {
subscribeSignedVAA,
} from "@certusone/wormhole-spydk";
import { ethers } from "ethers";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../../react/src/addresses/goerli";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON } from "../../react/src/addresses/mumbai";
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 * as swap from "../../react/src/swapper/util";
let logger: any;
let configFile: string = ".env.sample";
let configFile: string = ".env";
if (process.env.SWAP_RELAY_CONFIG) {
configFile = process.env.SWAP_RELAY_CONFIG;
}
@ -36,10 +43,19 @@ initLogger();
type OurEnvironment = {
spy_host: string;
spy_filters: string;
target_chain_id: number;
target_node_url: string;
target_private_key: string;
target_contract_address: string;
eth_provider_url: string;
polygon_provider_url: string;
wallet_private_key: string;
eth_contract_address: string;
polygon_contract_address: string;
};
type TargetContractData = {
contractAddress: string;
contract: ethers.Contract;
provider: ethers.providers.StaticJsonRpcProvider;
wallet: ethers.Wallet;
contractWithSigner: ethers.Contract;
};
setDefaultWasm("node");
@ -48,6 +64,9 @@ let success: boolean;
let env: OurEnvironment;
[success, env] = loadConfig();
let ethContractData: TargetContractData = null;
let polygonContractData: TargetContractData = null;
if (success) {
logger.info(
"swap_relay starting up, will listen for signed VAAs from [" +
@ -55,17 +74,17 @@ if (success) {
"]"
);
logger.info(
"will relay to EVM chainId: [" +
env.target_chain_id +
"], nodeUrl: [" +
env.target_node_url +
"], contractAddress: [" +
env.target_contract_address +
"]"
);
try {
ethContractData = makeEthContractData();
polygonContractData = makePolygonContractData();
} catch (e: any) {
logger.error("failed to connect to target contracts: %o", e);
success = false;
}
spy_listen();
if (success) {
spy_listen();
}
}
function loadConfig(): [boolean, OurEnvironment] {
@ -73,20 +92,16 @@ function loadConfig(): [boolean, OurEnvironment] {
logger.error("Missing environment variable SPY_SERVICE_HOST");
return [false, undefined];
}
if (!process.env.EVM_CHAIN_ID) {
logger.error("Missing environment variable EVM_CHAIN_ID");
if (!process.env.ETH_PROVIDER) {
logger.error("Missing environment variable ETH_PROVIDER");
return [false, undefined];
}
if (!process.env.EVM_NODE_URL) {
logger.error("Missing environment variable EVM_NODE_URL");
if (!process.env.POLYGON_PROVIDER) {
logger.error("Missing environment variable POLYGON_PROVIDER");
return [false, undefined];
}
if (!process.env.EVM_PRIVATE_KEY) {
logger.error("Missing environment variable EVM_PRIVATE_KEY");
return [false, undefined];
}
if (!process.env.EVM_CONTRACT_ADDRESS) {
logger.error("Missing environment variable EVM_CONTRACT_ADDRESS");
if (!process.env.WALLET_PRIVATE_KEY) {
logger.error("Missing environment variable WALLET_PRIVATE_KEY");
return [false, undefined];
}
@ -95,10 +110,11 @@ function loadConfig(): [boolean, OurEnvironment] {
{
spy_host: process.env.SPY_SERVICE_HOST,
spy_filters: process.env.SPY_SERVICE_FILTERS,
target_chain_id: parseInt(process.env.EVM_CHAIN_ID),
target_node_url: process.env.EVM_NODE_URL,
target_private_key: process.env.EVM_PRIVATE_KEY,
target_contract_address: process.env.EVM_CONTRACT_ADDRESS,
eth_provider_url: process.env.ETH_PROVIDER,
polygon_provider_url: process.env.POLYGON_PROVIDER,
wallet_private_key: process.env.WALLET_PRIVATE_KEY,
eth_contract_address: process.env.ETH_CONTRACT_ADDRESS,
polygon_contract_address: process.env.POLYGON_CONTRACT_ADDRESS,
},
];
}
@ -205,7 +221,7 @@ async function processVaa(vaaBytes) {
);
try {
//await relayVaa(vaaBytes);
await relayVaa(vaaBytes, t3Payload);
} catch (e) {
logger.error("failed to relay type 3 vaa: %o", e);
}
@ -243,31 +259,334 @@ function decodeSignedVAAPayloadType3(parsedVAA: any): Type3Payload {
};
}
import { ethers } from "ethers";
// Ethereum (Goerli) set up
function makeEthContractData(): TargetContractData {
let overridden: boolean = false;
let contractAddress: string = CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM;
if (env.eth_contract_address) {
contractAddress = env.eth_contract_address;
overridden = true;
}
async function relayVaa(vaaBytes: string) {
contractAddress = contractAddress.toLowerCase();
if (contractAddress.search("0x") == 0) {
contractAddress = contractAddress.substring(2);
}
if (overridden) {
logger.info(
"Connecting to Ethereum: node [" +
env.eth_provider_url +
"], overriding contract address to [" +
contractAddress +
"]"
);
} else {
logger.info(
"Connecting to Ethereum: node [" +
env.eth_provider_url +
"], contract address [" +
contractAddress +
"]"
);
}
const provider = new ethers.providers.StaticJsonRpcProvider(
env.eth_provider_url
);
const contract = new ethers.Contract(
contractAddress,
SWAP_CONTRACT_V3_ABI,
provider
);
const wallet = new ethers.Wallet(env.wallet_private_key, provider);
const contractWithSigner = contract.connect(wallet);
return {
contractAddress: contractAddress,
contract: contract,
provider: provider,
wallet: wallet,
contractWithSigner: contractWithSigner,
};
}
// Polygon (Mumbai) set up
function makePolygonContractData(): TargetContractData {
let overridden: boolean = false;
let contractAddress: string = CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON;
if (env.polygon_contract_address) {
contractAddress = env.polygon_contract_address;
overridden = true;
}
contractAddress = contractAddress.toLowerCase();
if (contractAddress.search("0x") == 0) {
contractAddress = contractAddress.substring(2);
}
if (overridden) {
logger.info(
"Connecting to Polygon: node [" +
env.polygon_provider_url +
"], overriding contract address to [" +
contractAddress +
"]"
);
} else {
logger.info(
"Connecting to Polygon: node [" +
env.polygon_provider_url +
"], contract address [" +
contractAddress +
"]"
);
}
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.wallet_private_key, provider);
const contractWithSigner = contract.connect(wallet);
return {
contractAddress: contractAddress,
contract: contract,
provider: provider,
wallet: wallet,
contractWithSigner: contractWithSigner,
};
}
/*
// 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
}
*/
async function relayVaa(vaaBytes: string, t3Payload: Type3Payload) {
const signedVaaArray = hexToUint8Array(vaaBytes);
const provider = new ethers.providers.WebSocketProvider(env.target_node_url);
const signer = new ethers.Wallet(env.target_private_key, provider);
const receipt = await redeemOnEth(
env.target_contract_address,
signer,
signedVaaArray
);
let exactIn: boolean = false;
if (t3Payload.swapFunctionType === 1) {
exactIn = true;
} else if (t3Payload.swapFunctionType !== 2) {
logger.error(
"unable to relay vaa: unsupported swapFunctionType: [" +
t3Payload.swapFunctionType +
"]"
);
}
let success = await getIsTransferCompletedEth(
env.target_contract_address,
provider,
signedVaaArray
);
provider.destroy();
let native: boolean = false;
if (t3Payload.swapCurrencyType === 1) {
native = true;
} else if (t3Payload.swapCurrencyType !== 2) {
logger.error(
"unable to relay vaa: unsupported swapCurrencyType: [" +
t3Payload.swapCurrencyType +
"]"
);
}
logger.info(
"redeemed on evm: success: " + success + ", receipt: %o",
receipt
"relayVaa: contractAddress: [" +
t3Payload.contractAddress +
"], ethContract: [" +
ethContractData.contractAddress +
"], polygonContract[" +
polygonContractData.contractAddress +
"]"
);
if (t3Payload.contractAddress === ethContractData.contractAddress) {
await relayVaaToEth(signedVaaArray, exactIn, native);
} else if (
t3Payload.contractAddress === polygonContractData.contractAddress
) {
await relayVaaToPolygon(signedVaaArray, exactIn, native);
} else {
logger.error(
"unable to relay vaa: unsupported contract: [" +
t3Payload.contractAddress +
"]"
);
}
}
async function relayVaaToEth(
signedVaaArray: Uint8Array,
exactIn: boolean,
native: boolean
) {
logger.info("relayVaaToEth: exactIn: " + exactIn + ", native: " + native);
if (exactIn) {
if (native) {
await swap
.swapExactInFromVaaNativeV3(
ethContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToEth: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToEth: transaction failed: %o",
error.transactionHash
);
});
} else {
await swap
.swapExactInFromVaaTokenV3(
ethContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToEth: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToEth: transaction failed: %o",
error.transactionHash
);
});
}
} else {
if (native) {
await swap
.swapExactOutFromVaaNativeV3(
ethContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToEth: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToEth: transaction failed: %o",
error.transactionHash
);
});
} else {
await swap
.swapExactOutFromVaaTokenV3(
ethContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToEth: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToEth: transaction failed: %o",
error.transactionHash
);
});
}
}
}
async function relayVaaToPolygon(
signedVaaArray: Uint8Array,
exactIn: boolean,
native: boolean
) {
logger.info("relayVaaToPolygon: exactIn: " + exactIn + ", native: " + native);
if (exactIn) {
if (native) {
await swap
.swapExactInFromVaaNativeV2(
polygonContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToPolygon: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToPolygon: transaction failed: %o",
error.transactionHash
);
});
} else {
await swap
.swapExactInFromVaaTokenV2(
polygonContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToPolygon: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToPolygon: transaction failed: %o",
error.transactionHash
);
});
}
} else {
if (native) {
await swap
.swapExactOutFromVaaNativeV2(
polygonContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToPolygon: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToPolygon: transaction failed: %o",
error.transactionHash
);
});
} else {
await swap
.swapExactOutFromVaaTokenV2(
polygonContractData.contractWithSigner,
signedVaaArray
)
.then((receipt) => {
logger.info("relayVaaToPolygon: %o", receipt.transactionHash);
})
.catch((error) => {
logger.error(
"relayVaaToPolygon: transaction failed: %o",
error.transactionHash
);
});
}
}
}
///////////////////////////////// Start of logger stuff ///////////////////////////////////////////

View File

@ -6,7 +6,8 @@
"moduleResolution": "node",
"lib": ["es2019"],
"skipLibCheck": true,
"allowJs": true
"allowJs": true,
"resolveJsonModule": true
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]