334 lines
14 KiB
JavaScript
334 lines
14 KiB
JavaScript
import { exec } from "child_process";
|
|
import fs from "fs";
|
|
import { ethers } from 'ethers';
|
|
import algo from "algosdk";
|
|
import {
|
|
getEmitterAddressAlgorand,
|
|
getEmitterAddressEth,
|
|
getEmitterAddressSolana,
|
|
getEmitterAddressTerra,
|
|
parseSequenceFromLogEth,
|
|
parseSequenceFromLogAlgorand,
|
|
uint8ArrayToHex
|
|
} from "@certusone/wormhole-sdk";
|
|
|
|
import {
|
|
optin,
|
|
submitVAAHeader
|
|
} from "@certusone/wormhole-sdk/lib/cjs/algorand/Algorand.js";
|
|
|
|
import fetch from 'node-fetch';
|
|
|
|
async function main() {
|
|
let config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
|
|
|
|
let network = config.networks[process.argv[2]];
|
|
if (!network){
|
|
throw new Error("Network not defined in config file.")
|
|
}
|
|
|
|
if(process.argv[3] == "deploy") {
|
|
if(network.type == "evm"){
|
|
console.log(`Deploying EVM network: ${process.argv[2]} to ${network.rpc}`);
|
|
exec(
|
|
`cd chains/evm && forge build && forge create --legacy --rpc-url ${network.rpc} --private-key ${network.privateKey} src/Messenger.sol:Messenger && exit`,
|
|
((err, out, errStr) => {
|
|
if(err){
|
|
throw new Error(err);
|
|
}
|
|
|
|
if(out) {
|
|
console.log(out);
|
|
network.deployedAddress = out.split("Deployed to: ")[1].split('\n')[0].trim();
|
|
network.emittedVAAs = []; //Resets the emittedVAAs
|
|
config.networks[process.argv[2]] = network;
|
|
fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 4));
|
|
}
|
|
})
|
|
);
|
|
} else if (network.type == "algorand"){
|
|
console.log(`Deploying Algorand network: ${process.argv[2]} to ${network.rpc}`);
|
|
exec(
|
|
`cd chains/algorand && python3 messenger.py ${network.bridgeAddress} '${network.mnemonic}' ${network.rpc}:${network.port}`,
|
|
((err,out,errStr) => {
|
|
if(err) {
|
|
throw new Error(err);
|
|
}
|
|
|
|
if(out){
|
|
console.log(out);
|
|
network.appId = parseInt(out.split("App ID:")[1].split("Address")[0].trim());
|
|
network.deployedAddress = out.split("Address: ")[1].trim();
|
|
network.emittedVAAs = [];
|
|
config.networks[process.argv[2]] = network;
|
|
fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 4));
|
|
}
|
|
})
|
|
)
|
|
} else if (network.type == "solana") {
|
|
//node exec solana deployer
|
|
/**
|
|
* solana config set --url $TILT_RPC_IP:8899
|
|
cd solana-project && anchor build && solana airdrop 100 -k test_keypair.json && sleep 5 && cd ../
|
|
cd solana-deployer && cargo build --release && cargo run --release -- -m=8 --payer=../solana-project/test_keypair.json --program-kp-path=../solana-project/solana_project-keypair.json --program-path=../solana-project/target/deploy/solana_project.so -r=$TILT_RPC_IP:8899 -s=1 -t=5 --thread-count=8 && cd ../
|
|
sleep 10
|
|
*/
|
|
} else {
|
|
throw new Error("Invalid Network Type!");
|
|
}
|
|
} else if (process.argv[3] == "register_chain") {
|
|
if(!network.deployedAddress){
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const targetNetwork = config.networks[process.argv[4]];
|
|
if(!targetNetwork.deployedAddress){
|
|
throw new Error("Target Network not deployed yet!");
|
|
}
|
|
|
|
let emitterAddr;
|
|
if(targetNetwork.type == "evm"){
|
|
emitterAddr = Buffer.from(getEmitterAddressEth(targetNetwork.deployedAddress), "hex");
|
|
} else if (targetNetwork.type == "algorand") {
|
|
emitterAddr = Buffer.from(getEmitterAddressAlgorand(targetNetwork.appId), "hex");
|
|
} else if (targetNetwork.type == "solana") {
|
|
emitterAddr = Buffer.from(await getEmitterAddressSolana(targetNetwork.deployedAddress), "hex");
|
|
} else if (targetNetwork.type == "terra") {
|
|
emitterAddr = Buffer.from(await getEmitterAddressTerra(targetNetwork.deployedAddress), "hex");
|
|
}
|
|
|
|
|
|
if(network.type == "evm"){
|
|
const signer = new ethers.Wallet(network.privateKey)
|
|
.connect(new ethers.providers.JsonRpcProvider(network.rpc));
|
|
|
|
const messenger = new ethers.Contract(
|
|
network.deployedAddress,
|
|
JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi,
|
|
signer,
|
|
{
|
|
gasPrice: '2000000000'
|
|
}
|
|
);
|
|
await messenger.registerApplicationContracts(targetNetwork.wormholeChainId, emitterAddr);
|
|
} else if (network.type == "algorand"){
|
|
const algodClient = new algo.Algodv2(
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
network.rpc,
|
|
network.port
|
|
);
|
|
const sender = algo.mnemonicToSecretKey(network.mnemonic);
|
|
const params = await algodClient.getTransactionParams().do();
|
|
let txs = [];
|
|
txs.push({
|
|
tx: algo.makeApplicationCallTxnFromObject({
|
|
appArgs: [
|
|
Uint8Array.from(Buffer.from("registerEmitter")),
|
|
Uint8Array.from(emitterAddr),
|
|
algo.bigIntToBytes(BigInt(targetNetwork.wormholeChainId), 2)
|
|
],
|
|
appIndex: network.appId,
|
|
from: sender.addr,
|
|
onComplete: algo.OnApplicationComplete.NoOpOC,
|
|
suggestedParams: params,
|
|
}),
|
|
signer: null,
|
|
});
|
|
await signSendAndConfirmAlgorand(algodClient, txs, sender);
|
|
}
|
|
console.log(`Network(${process.argv[2]}) Registered Emitter: ${targetNetwork.deployedAddress} from Chain: ${targetNetwork.wormholeChainId}`);
|
|
} else if (process.argv[3] == "send_msg") {
|
|
if(!network.deployedAddress){
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
if(network.type == "evm"){
|
|
const signer = new ethers.Wallet(network.privateKey)
|
|
.connect(new ethers.providers.JsonRpcProvider(network.rpc));
|
|
const messenger = new ethers.Contract(
|
|
network.deployedAddress,
|
|
JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi,
|
|
signer,
|
|
{
|
|
gasPrice: '2000000000'
|
|
}
|
|
);
|
|
const tx = await (await messenger.sendMsg(Buffer.from(process.argv[4]), {gasPrice: '2000000000'})).wait();
|
|
await new Promise((r) => setTimeout(r, 5000));
|
|
const emitterAddr = getEmitterAddressEth(messenger.address);
|
|
const seq = parseSequenceFromLogEth(
|
|
tx,
|
|
network.bridgeAddress
|
|
);
|
|
console.log(`${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`);
|
|
const vaaBytes = await (
|
|
await fetch(
|
|
`${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`
|
|
)
|
|
).json();
|
|
if(!network.emittedVAAs){
|
|
network.emittedVAAs = [vaaBytes.vaaBytes];
|
|
} else {
|
|
network.emittedVAAs.push(vaaBytes.vaaBytes);
|
|
}
|
|
config.networks[process.argv[2]] = network;
|
|
fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 2));
|
|
console.log(`Network(${process.argv[2]}) Emitted VAA: `, vaaBytes.vaaBytes);
|
|
} else if (network.type == "algorand"){
|
|
const algodClient = new algo.Algodv2(
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
network.rpc,
|
|
network.port
|
|
);
|
|
const sender = algo.mnemonicToSecretKey(network.mnemonic);
|
|
const params = await algodClient.getTransactionParams().do();
|
|
let txs = [];
|
|
|
|
//Opt In to allow core brige to store data with Algo Contract
|
|
const messengerPubkey = uint8ArrayToHex(algo.decodeAddress(network.deployedAddress).publicKey);
|
|
const { addr: emitterAddr, txs: emitterOptInTxs } = await optin(
|
|
algodClient,
|
|
sender.addr,
|
|
BigInt(network.bridgeAddress),
|
|
BigInt(0),
|
|
messengerPubkey
|
|
);
|
|
txs.push(...emitterOptInTxs);
|
|
let accts = [
|
|
emitterAddr,
|
|
algo.getApplicationAddress(network.bridgeAddress),
|
|
];
|
|
let appTxn = algo.makeApplicationCallTxnFromObject({
|
|
appArgs: [
|
|
Uint8Array.from(Buffer.from("sendMessage")),
|
|
Uint8Array.from(Buffer.from(process.argv[4]))
|
|
],
|
|
accounts: accts,
|
|
appIndex: network.appId,
|
|
foreignApps: [network.bridgeAddress],
|
|
from: sender.addr,
|
|
onComplete: algo.OnApplicationComplete.NoOpOC,
|
|
suggestedParams: params,
|
|
});
|
|
appTxn.fee *= 2;
|
|
txs.push({tx: appTxn, signer: null});
|
|
const receipt = await signSendAndConfirmAlgorand(algodClient, txs, sender);
|
|
const emitAddr = getEmitterAddressAlgorand(network.appId);
|
|
const seq = parseSequenceFromLogAlgorand(receipt);
|
|
await new Promise((r) => setTimeout(r, 10000));
|
|
const vaaBytes = await (
|
|
await fetch(
|
|
`${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitAddr}/${seq}`
|
|
)
|
|
).json();
|
|
if(!network.emittedVAAs){
|
|
network.emittedVAAs = [vaaBytes.vaaBytes];
|
|
} else {
|
|
network.emittedVAAs.push(vaaBytes.vaaBytes);
|
|
}
|
|
config.networks[process.argv[2]] = network;
|
|
fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 2));
|
|
console.log(`Network(${process.argv[2]}) Emitted VAA: `, vaaBytes.vaaBytes);
|
|
}
|
|
} else if (process.argv[3] == "submit_vaa") {
|
|
if(!network.deployedAddress){
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
const targetNetwork = config.networks[process.argv[4]];
|
|
const vaaBytes = isNaN(parseInt(process.argv[5])) ?
|
|
targetNetwork.emittedVAAs.pop() :
|
|
targetNetwork.emittedVAAs[parseInt(process.argv[5])];
|
|
|
|
if(network.type == "evm"){
|
|
|
|
const signer = new ethers.Wallet(network.privateKey)
|
|
.connect(new ethers.providers.JsonRpcProvider(network.rpc));
|
|
const messenger = new ethers.Contract(
|
|
network.deployedAddress,
|
|
JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi,
|
|
signer,
|
|
{
|
|
gasPrice: '2000000000'
|
|
}
|
|
);
|
|
|
|
const tx = await messenger.receiveEncodedMsg(Buffer.from(vaaBytes, "base64"));
|
|
console.log(`Submitted VAA: ${vaaBytes}\nTX: ${tx.hash}`);
|
|
} else if (network.type == "algorand"){
|
|
const algodClient = new algo.Algodv2(
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
network.rpc,
|
|
network.port
|
|
);
|
|
const sender = algo.mnemonicToSecretKey(network.mnemonic);
|
|
const params = await algodClient.getTransactionParams().do();
|
|
let txs = [];
|
|
|
|
let sstate = await submitVAAHeader(algodClient, BigInt(network.bridgeAddress), Uint8Array.from(Buffer.from(vaaBytes, "base64")), sender.addr, BigInt(network.appId))
|
|
txs = sstate.txs;
|
|
let accts = sstate.accounts;
|
|
|
|
txs.push({
|
|
tx: algo.makeApplicationCallTxnFromObject({
|
|
appArgs: [
|
|
Uint8Array.from(Buffer.from(("receiveMessage"))),
|
|
Uint8Array.from(Buffer.from(vaaBytes, "base64"))
|
|
],
|
|
accounts: accts,
|
|
appIndex: network.appId,
|
|
from: sender.addr,
|
|
onComplete: algo.OnApplicationComplete.NoOpOC,
|
|
suggestedParams: params,
|
|
}),
|
|
signer: null,
|
|
});
|
|
|
|
ret = await signSendAndConfirmAlgorand(algodClient, txs, sender);
|
|
console.log(ret);
|
|
}
|
|
} else if (process.argv[3] == "get_current_msg") {
|
|
if(!network.deployedAddress){
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
if(network.type == "evm"){
|
|
const signer = new ethers.Wallet(network.privateKey)
|
|
.connect(new ethers.providers.JsonRpcProvider(network.rpc));
|
|
const messenger = new ethers.Contract(
|
|
network.deployedAddress,
|
|
JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi,
|
|
signer,
|
|
{
|
|
gasPrice: '2000000000'
|
|
}
|
|
);
|
|
console.log(`${process.argv[2]} Current Msg: `, await messenger.getCurrentMsg());
|
|
}
|
|
} else {
|
|
throw new Error("Unkown command!")
|
|
}
|
|
}
|
|
|
|
async function signSendAndConfirmAlgorand(
|
|
algodClient,
|
|
txs,
|
|
wallet
|
|
) {
|
|
algo.assignGroupID(txs.map((tx) => tx.tx));
|
|
const signedTxns = [];
|
|
for (const tx of txs) {
|
|
if (tx.signer) {
|
|
signedTxns.push(await tx.signer.signTxn(tx.tx));
|
|
} else {
|
|
signedTxns.push(tx.tx.signTxn(wallet.sk));
|
|
}
|
|
}
|
|
await algodClient.sendRawTransaction(signedTxns).do();
|
|
const result = await algo.waitForConfirmation(
|
|
algodClient,
|
|
txs[txs.length - 1].tx.txID(),
|
|
1
|
|
);
|
|
return result;
|
|
}
|
|
|
|
main(); |