xdapp-book/projects/messenger/handlers/evm.ts

216 lines
6.9 KiB
TypeScript

import * as fs from 'fs';
import { exec } from "child_process";
import { getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth } from '@certusone/wormhole-sdk';
import * as ethers from 'ethers';
import fetch from 'node-fetch';
import {getEmitterAddress as getEmitterAddressAptos} from './aptos';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
export async function deploy(chain: string){
const rpc = config.networks[chain]['rpc'];
const privateKey = config.networks[chain]['privateKey'];
const bridgeAddress = config.networks[chain]['bridgeAddress'];
exec(
`cd chains/evm && forge build && forge create --legacy --rpc-url ${rpc} --private-key ${privateKey} src/Messenger.sol:Messenger --constructor-args ${bridgeAddress} && exit`,
(err, out, errStr) => {
if (err) {
throw new Error(err.message);
}
if (out) {
console.log(out);
const deploymentAddress = out
.split("Deployed to: ")[1]
.split("\n")[0]
.trim();
const emittedVAAs = []; //Resets the emittedVAAs
fs.writeFileSync(
`./deployinfo/${chain}.deploy.json`,
JSON.stringify({
address: deploymentAddress,
vaas: emittedVAAs
}, null, 4)
);
}
}
);
}
export async function registerApp(src:string, target:string){
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
let srcDeploymentInfo;
let targetDeploymentInfo;
let targetEmitter;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
try{
targetDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
} catch (e){
throw new Error(`${target} is not deployed yet`);
}
switch (targetNetwork['type']){
case 'evm':
targetEmitter = getEmitterAddressEth(targetDeploymentInfo['address']);
break;
case 'solana':
targetEmitter = await getEmitterAddressSolana(targetDeploymentInfo['address']);
break;
case 'aptos':
targetEmitter = await getEmitterAddressAptos(targetNetwork.rpc, targetDeploymentInfo['address']);
break;
}
const emitterBuffer = Buffer.from(targetEmitter, 'hex');
const signer = new ethers.Wallet(srcNetwork.privateKey).connect(
new ethers.providers.JsonRpcProvider(srcNetwork.rpc)
);
const messenger = new ethers.Contract(
srcDeploymentInfo.address,
JSON.parse(
fs
.readFileSync(
"./chains/evm/out/Messenger.sol/Messenger.json"
)
.toString()
).abi,
signer
);
const tx = await messenger.registerApplicationContracts(
targetNetwork.wormholeChainId,
emitterBuffer
);
return tx;
}
export async function sendMsg(src:string, msg:string){
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
const signer = new ethers.Wallet(srcNetwork.privateKey).connect(
new ethers.providers.JsonRpcProvider(srcNetwork.rpc)
);
const messenger = new ethers.Contract(
srcDeploymentInfo.address,
JSON.parse(
fs
.readFileSync(
"./chains/evm/out/Messenger.sol/Messenger.json"
)
.toString()
).abi,
signer
);
const tx = await (await messenger.sendMsg(Buffer.from(msg), {gasLimit: 150000})).wait();
const seq = parseSequenceFromLogEth(tx, srcNetwork['bridgeAddress']);
const emitterAddr = getEmitterAddressEth(srcDeploymentInfo['address']);
await new Promise((r) => setTimeout(r, 5000)); //wait for Guardian to pick up message
console.log(
"Searching for: ",
`${config.wormhole.restAddress}/v1/signed_vaa/${srcNetwork.wormholeChainId}/${emitterAddr}/${seq}`
);
const vaaBytes = await (
await fetch(
`${config.wormhole.restAddress}/v1/signed_vaa/${srcNetwork.wormholeChainId}/${emitterAddr}/${seq}`
)
).json();
if(!vaaBytes['vaaBytes']){
throw new Error("VAA not found!");
}
if(!srcDeploymentInfo['vaas']){
srcDeploymentInfo['vaas'] = [vaaBytes['vaaBytes']]
} else {
srcDeploymentInfo['vaas'].push(vaaBytes['vaaBytes'])
}
fs.writeFileSync(
`./deployinfo/${src}.deploy.json`,
JSON.stringify(srcDeploymentInfo, null, 4)
);
return vaaBytes['vaaBytes'];
}
export async function submitVaa(src:string, target:string, idx:string){
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
let targetDeploymentInfo;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
try{
targetDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
} catch (e){
throw new Error(`${target} is not deployed yet`);
}
const vaa = isNaN(parseInt(idx))
? targetDeploymentInfo.vaas.pop()
: targetDeploymentInfo.vaas[parseInt(idx)];
const signer = new ethers.Wallet(srcNetwork.privateKey).connect(
new ethers.providers.JsonRpcProvider(srcNetwork.rpc)
);
const messenger = new ethers.Contract(
srcDeploymentInfo.address,
JSON.parse(
fs
.readFileSync(
"./chains/evm/out/Messenger.sol/Messenger.json"
)
.toString()
).abi,
signer
);
const tx = await messenger.receiveEncodedMsg(Buffer.from(vaa, "base64"));
return tx;
}
export async function getCurrentMsg(src:string){
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
const signer = new ethers.Wallet(srcNetwork.privateKey).connect(
new ethers.providers.JsonRpcProvider(srcNetwork.rpc)
);
const messenger = new ethers.Contract(
srcDeploymentInfo.address,
JSON.parse(
fs
.readFileSync(
"./chains/evm/out/Messenger.sol/Messenger.json"
)
.toString()
).abi,
signer
);
return await messenger.getCurrentMsg();
}