messenger/evm is working
This commit is contained in:
parent
f5d290ad59
commit
4d0b827b65
|
@ -0,0 +1 @@
|
||||||
|
deployinfo/*.deploy.json
|
|
@ -0,0 +1,3 @@
|
||||||
|
cache/
|
||||||
|
out/
|
||||||
|
!src/Wormhole/
|
|
@ -4,13 +4,14 @@
|
||||||
"artifacts": "out",
|
"artifacts": "out",
|
||||||
"sources": "src",
|
"sources": "src",
|
||||||
"tests": "test",
|
"tests": "test",
|
||||||
|
"scripts": "script",
|
||||||
"libraries": [
|
"libraries": [
|
||||||
"lib"
|
"lib"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"src/Messenger.sol": {
|
"src/Messenger.sol": {
|
||||||
"lastModificationDate": 1654006013773,
|
"lastModificationDate": 1659683988145,
|
||||||
"contentHash": "b21dd2ab010d42c4abc588eed32658bd",
|
"contentHash": "b21dd2ab010d42c4abc588eed32658bd",
|
||||||
"sourceName": "src/Messenger.sol",
|
"sourceName": "src/Messenger.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
|
@ -35,7 +36,8 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"evmVersion": "london"
|
"evmVersion": "london",
|
||||||
|
"libraries": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imports": [
|
"imports": [
|
||||||
|
@ -45,12 +47,12 @@
|
||||||
"versionRequirement": "^0.8.0",
|
"versionRequirement": "^0.8.0",
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
"Messenger": {
|
"Messenger": {
|
||||||
"0.8.10+commit.fc410830.Linux.gcc": "Messenger.sol/Messenger.json"
|
"0.8.10+commit.fc410830.Darwin.appleclang": "Messenger.sol/Messenger.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/Wormhole/IWormhole.sol": {
|
"src/Wormhole/IWormhole.sol": {
|
||||||
"lastModificationDate": 1654006013783,
|
"lastModificationDate": 1659683988146,
|
||||||
"contentHash": "7c930d4b68538e731af28a8d1979f44e",
|
"contentHash": "7c930d4b68538e731af28a8d1979f44e",
|
||||||
"sourceName": "src/Wormhole/IWormhole.sol",
|
"sourceName": "src/Wormhole/IWormhole.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
|
@ -75,7 +77,8 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"evmVersion": "london"
|
"evmVersion": "london",
|
||||||
|
"libraries": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imports": [
|
"imports": [
|
||||||
|
@ -84,12 +87,12 @@
|
||||||
"versionRequirement": "^0.8.0",
|
"versionRequirement": "^0.8.0",
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
"IWormhole": {
|
"IWormhole": {
|
||||||
"0.8.10+commit.fc410830.Linux.gcc": "IWormhole.sol/IWormhole.json"
|
"0.8.10+commit.fc410830.Darwin.appleclang": "IWormhole.sol/IWormhole.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/Wormhole/Structs.sol": {
|
"src/Wormhole/Structs.sol": {
|
||||||
"lastModificationDate": 1654006013783,
|
"lastModificationDate": 1659683988147,
|
||||||
"contentHash": "4e824cc204959f4fcab06c4e806448c4",
|
"contentHash": "4e824cc204959f4fcab06c4e806448c4",
|
||||||
"sourceName": "src/Wormhole/Structs.sol",
|
"sourceName": "src/Wormhole/Structs.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
|
@ -114,14 +117,15 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"evmVersion": "london"
|
"evmVersion": "london",
|
||||||
|
"libraries": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"versionRequirement": "^0.8.0",
|
"versionRequirement": "^0.8.0",
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
"Structs": {
|
"Structs": {
|
||||||
"0.8.10+commit.fc410830.Linux.gcc": "Structs.sol/Structs.json"
|
"0.8.10+commit.fc410830.Darwin.appleclang": "Structs.sol/Structs.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
[default]
|
||||||
|
src = 'src'
|
||||||
|
out = 'out'
|
||||||
|
libs = ['lib']
|
||||||
|
solc_version = '0.8.10'
|
||||||
|
|
||||||
|
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
|
|
@ -0,0 +1,53 @@
|
||||||
|
//SPDX-License-Identifier: Unlicense
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "./Wormhole/IWormhole.sol";
|
||||||
|
|
||||||
|
contract Messenger {
|
||||||
|
string private current_msg;
|
||||||
|
address private wormhole_core_bridge_address = address(0xC89Ce4735882C9F0f0FE26686c53074E09B0D550);
|
||||||
|
IWormhole core_bridge = IWormhole(wormhole_core_bridge_address);
|
||||||
|
uint32 nonce = 0;
|
||||||
|
mapping(uint16 => bytes32) _applicationContracts;
|
||||||
|
address owner;
|
||||||
|
mapping(bytes32 => bool) _completedMessages;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
owner = msg.sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMsg(bytes memory str) public returns (uint64 sequence) {
|
||||||
|
sequence = core_bridge.publishMessage(nonce, str, 1);
|
||||||
|
nonce = nonce+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function receiveEncodedMsg(bytes memory encodedMsg) public {
|
||||||
|
(IWormhole.VM memory vm, bool valid, string memory reason) = core_bridge.parseAndVerifyVM(encodedMsg);
|
||||||
|
|
||||||
|
//1. Check Wormhole Guardian Signatures
|
||||||
|
// If the VM is NOT valid, will return the reason it's not valid
|
||||||
|
// If the VM IS valid, reason will be blank
|
||||||
|
require(valid, reason);
|
||||||
|
|
||||||
|
//2. Check if the Emitter Chain contract is registered
|
||||||
|
require(_applicationContracts[vm.emitterChainId] == vm.emitterAddress, "Invalid Emitter Address!");
|
||||||
|
|
||||||
|
//3. Check that the message hasn't already been processed
|
||||||
|
require(!_completedMessages[vm.hash], "Message already processed");
|
||||||
|
_completedMessages[vm.hash] = true;
|
||||||
|
|
||||||
|
//Do the thing
|
||||||
|
current_msg = string(vm.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentMsg() public view returns (string memory){
|
||||||
|
return current_msg;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
Registers it's sibling applications on other chains as the only ones that can send this instance messages
|
||||||
|
*/
|
||||||
|
function registerApplicationContracts(uint16 chainId, bytes32 applicationAddr) public {
|
||||||
|
require(msg.sender == owner, "Only owner can register new chains!");
|
||||||
|
_applicationContracts[chainId] = applicationAddr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// contracts/Messages.sol
|
||||||
|
// SPDX-License-Identifier: Apache 2
|
||||||
|
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "./Structs.sol";
|
||||||
|
|
||||||
|
interface IWormhole is Structs {
|
||||||
|
event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
|
||||||
|
|
||||||
|
function publishMessage(
|
||||||
|
uint32 nonce,
|
||||||
|
bytes memory payload,
|
||||||
|
uint8 consistencyLevel
|
||||||
|
) external payable returns (uint64 sequence);
|
||||||
|
|
||||||
|
function parseAndVerifyVM(bytes calldata encodedVM) external view returns (Structs.VM memory vm, bool valid, string memory reason);
|
||||||
|
|
||||||
|
function verifyVM(Structs.VM memory vm) external view returns (bool valid, string memory reason);
|
||||||
|
|
||||||
|
function verifySignatures(bytes32 hash, Structs.Signature[] memory signatures, Structs.GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason) ;
|
||||||
|
|
||||||
|
function parseVM(bytes memory encodedVM) external pure returns (Structs.VM memory vm);
|
||||||
|
|
||||||
|
function getGuardianSet(uint32 index) external view returns (Structs.GuardianSet memory) ;
|
||||||
|
|
||||||
|
function getCurrentGuardianSetIndex() external view returns (uint32) ;
|
||||||
|
|
||||||
|
function getGuardianSetExpiry() external view returns (uint32) ;
|
||||||
|
|
||||||
|
function governanceActionIsConsumed(bytes32 hash) external view returns (bool) ;
|
||||||
|
|
||||||
|
function isInitialized(address impl) external view returns (bool) ;
|
||||||
|
|
||||||
|
function chainId() external view returns (uint16) ;
|
||||||
|
|
||||||
|
function governanceChainId() external view returns (uint16);
|
||||||
|
|
||||||
|
function governanceContract() external view returns (bytes32);
|
||||||
|
|
||||||
|
function messageFee() external view returns (uint256) ;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// contracts/Structs.sol
|
||||||
|
// SPDX-License-Identifier: Apache 2
|
||||||
|
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
interface Structs {
|
||||||
|
struct Provider {
|
||||||
|
uint16 chainId;
|
||||||
|
uint16 governanceChainId;
|
||||||
|
bytes32 governanceContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GuardianSet {
|
||||||
|
address[] keys;
|
||||||
|
uint32 expirationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Signature {
|
||||||
|
bytes32 r;
|
||||||
|
bytes32 s;
|
||||||
|
uint8 v;
|
||||||
|
uint8 guardianIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VM {
|
||||||
|
uint8 version;
|
||||||
|
uint32 timestamp;
|
||||||
|
uint32 nonce;
|
||||||
|
uint16 emitterChainId;
|
||||||
|
bytes32 emitterAddress;
|
||||||
|
uint64 sequence;
|
||||||
|
uint8 consistencyLevel;
|
||||||
|
bytes payload;
|
||||||
|
|
||||||
|
uint32 guardianSetIndex;
|
||||||
|
Signature[] signatures;
|
||||||
|
|
||||||
|
bytes32 hash;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
.anchor
|
||||||
|
.DS_Store
|
||||||
|
target
|
||||||
|
**/*.rs.bk
|
||||||
|
node_modules
|
||||||
|
test-ledger
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
.anchor
|
||||||
|
.DS_Store
|
||||||
|
target
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
test-ledger
|
|
@ -0,0 +1,15 @@
|
||||||
|
[features]
|
||||||
|
seeds = false
|
||||||
|
skip-lint = false
|
||||||
|
[programs.localnet]
|
||||||
|
solana = "24FoTeX7BKbhTh3UF3feWusoAVKDPWZneiEqhXLVzZPL"
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
url = "https://api.apr.dev"
|
||||||
|
|
||||||
|
[provider]
|
||||||
|
cluster = "localnet"
|
||||||
|
wallet = "keypairs/id.json"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"programs/*"
|
||||||
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
overflow-checks = true
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
[profile.release.build-override]
|
||||||
|
opt-level = 3
|
||||||
|
incremental = false
|
||||||
|
codegen-units = 1
|
|
@ -0,0 +1 @@
|
||||||
|
[40,148,125,159,224,157,172,169,55,161,127,226,88,78,71,229,217,201,39,206,158,115,118,202,14,123,164,207,238,75,132,230,182,36,37,126,2,94,41,70,100,148,232,150,11,41,158,177,192,236,3,96,36,8,15,61,46,248,128,167,184,106,42,24]
|
|
@ -0,0 +1 @@
|
||||||
|
[208,42,49,103,247,30,120,52,59,87,100,149,183,99,116,225,88,94,168,108,203,46,196,252,4,147,13,136,178,97,159,130,15,177,42,56,142,141,170,79,183,156,130,147,48,241,148,173,6,109,136,29,40,28,120,159,255,113,29,219,240,142,226,151]
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Migrations are an early feature. Currently, they're nothing more than this
|
||||||
|
// single deploy script that's invoked from the CLI, injecting a provider
|
||||||
|
// configured from the workspace's Anchor.toml.
|
||||||
|
|
||||||
|
const anchor = require("@project-serum/anchor");
|
||||||
|
|
||||||
|
module.exports = async function (provider) {
|
||||||
|
// Configure client to use the provider.
|
||||||
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
|
// Add your deploy script here.
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
|
||||||
|
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@project-serum/anchor": "^0.25.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^4.3.4",
|
||||||
|
"mocha": "^9.0.3",
|
||||||
|
"ts-mocha": "^10.0.0",
|
||||||
|
"@types/bn.js": "^5.1.0",
|
||||||
|
"@types/chai": "^4.3.0",
|
||||||
|
"@types/mocha": "^9.0.0",
|
||||||
|
"typescript": "^4.3.5",
|
||||||
|
"prettier": "^2.6.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "solana"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Created with Anchor"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
||||||
|
name = "solana"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
no-idl = []
|
||||||
|
no-log-ix-name = []
|
||||||
|
cpi = ["no-entrypoint"]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anchor-lang = "0.25.0"
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,4 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct Initialize {}
|
|
@ -0,0 +1,25 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
mod account;
|
||||||
|
mod constant;
|
||||||
|
mod context;
|
||||||
|
mod error;
|
||||||
|
mod event;
|
||||||
|
|
||||||
|
use account::*;
|
||||||
|
use constant::*;
|
||||||
|
use context::*;
|
||||||
|
use error::*;
|
||||||
|
use event::*;
|
||||||
|
|
||||||
|
declare_id!("24FoTeX7BKbhTh3UF3feWusoAVKDPWZneiEqhXLVzZPL");
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod solana {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import * as anchor from "@project-serum/anchor";
|
||||||
|
import { Program } from "@project-serum/anchor";
|
||||||
|
import { Solana } from "../target/types/solana";
|
||||||
|
|
||||||
|
describe("solana", () => {
|
||||||
|
// Configure the client to use the local cluster.
|
||||||
|
anchor.setProvider(anchor.AnchorProvider.env());
|
||||||
|
|
||||||
|
const program = anchor.workspace.Solana as Program<Solana>;
|
||||||
|
|
||||||
|
it("Is initialized!", async () => {
|
||||||
|
// Add your test here.
|
||||||
|
const tx = await program.methods.initialize().rpc();
|
||||||
|
console.log("Your transaction signature", tx);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["mocha", "chai"],
|
||||||
|
"typeRoots": ["./node_modules/@types"],
|
||||||
|
"lib": ["es2015"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"esModuleInterop": true
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,212 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
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'];
|
||||||
|
|
||||||
|
exec(
|
||||||
|
`cd chains/evm && forge build && forge create --legacy --rpc-url ${rpc} --private-key ${privateKey} src/Messenger.sol:Messenger && 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))).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];
|
||||||
|
const targetNetwork = config.networks[target];
|
||||||
|
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();
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
import { Command } from 'commander';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as evm from './handlers/evm';
|
||||||
|
|
||||||
|
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString())
|
||||||
|
|
||||||
|
const program = new Command();
|
||||||
|
program
|
||||||
|
.name('xMessenger')
|
||||||
|
.description("Cross chain messaging example project.")
|
||||||
|
.version("0.2.0");
|
||||||
|
|
||||||
|
// $ deploy <chain>
|
||||||
|
program
|
||||||
|
.command('deploy')
|
||||||
|
.argument('<network>', 'name of the network in xdapp.config.json to deploy')
|
||||||
|
.action(async (network) => {
|
||||||
|
if(!config.networks[network]){
|
||||||
|
console.error(`ERROR: ${network} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`Deploying ${network}...`);
|
||||||
|
|
||||||
|
switch(config.networks[network].type){
|
||||||
|
case "evm":
|
||||||
|
await evm.deploy(network);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Deploy finished!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// $ register-network <source> <target>
|
||||||
|
program
|
||||||
|
.command("register-network")
|
||||||
|
.argument('<source>', 'The network you are registering on.')
|
||||||
|
.argument('<target>', 'The foreign network that you want to register.')
|
||||||
|
.action(async (src, target) => {
|
||||||
|
if(!config.networks[src]){
|
||||||
|
console.error(`ERROR: ${src} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!config.networks[target]){
|
||||||
|
console.error(`ERROR: ${target} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch(config.networks[src].type){
|
||||||
|
case 'evm':
|
||||||
|
await evm.registerApp(src,target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Foreign Network ${target} registered on ${src}`);
|
||||||
|
} catch (e){
|
||||||
|
console.error(`ERROR: ${e}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $ send-msg <source> <msg>
|
||||||
|
program
|
||||||
|
.command('emit-msg')
|
||||||
|
.argument('<source>', 'The network you want to emit the message from')
|
||||||
|
.argument('<msg>', 'The message you want to emit')
|
||||||
|
.action(async (src, msg) => {
|
||||||
|
if(!config.networks[src]){
|
||||||
|
console.error(`ERROR: ${src} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch(config.networks[src].type){
|
||||||
|
case 'evm':
|
||||||
|
await evm.sendMsg(src,msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log(`Emitted VAA on ${src} network. Submit it using \`submit-vaa\` command on a target network.`)
|
||||||
|
} catch (e){
|
||||||
|
console.error(`ERROR: ${e}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $ submit-vaa <source> <target> <vaa#>
|
||||||
|
program
|
||||||
|
.command('submit-vaa')
|
||||||
|
.argument('<source>', 'The network you want to submit the VAA on')
|
||||||
|
.argument('<target>', 'The network you want to submit the VAA from')
|
||||||
|
.argument('<vaa#>', 'The index of the VAA in the list of emitted VAAs that you want to submit. Use \'latest\' to submit the latest VAA')
|
||||||
|
.action(async (src, target, idx) => {
|
||||||
|
if(!config.networks[src]){
|
||||||
|
console.error(`ERROR: ${src} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!config.networks[target]){
|
||||||
|
console.error(`ERROR: ${target} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch(config.networks[src].type){
|
||||||
|
case 'evm':
|
||||||
|
await evm.submitVaa(src,target,idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Submitted VAA #${idx} from ${target} to chain ${src}`);
|
||||||
|
} catch (e){
|
||||||
|
console.error(`ERROR: ${e}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $ get-msg <source>
|
||||||
|
program
|
||||||
|
.command('get-msg')
|
||||||
|
.argument('<source>', 'The chain to fetch the current message from')
|
||||||
|
.action(async (src) => {
|
||||||
|
if(!config.networks[src]){
|
||||||
|
console.error(`ERROR: ${src} not found in xdapp.config.json`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = "ERROR!";
|
||||||
|
try {
|
||||||
|
switch(config.networks[src].type){
|
||||||
|
case 'evm':
|
||||||
|
msg = await evm.getCurrentMsg(src);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Current Message on Network ${src} is ${msg}`);
|
||||||
|
} catch (e){
|
||||||
|
console.error(`ERROR: ${e}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
program.parse();
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "messenger",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A simple example of cross chain messaging with wormhole",
|
||||||
|
"main": "orchestrator.ts",
|
||||||
|
"author": "Dev Bharel",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@certusone/wormhole-sdk": "^0.6.0",
|
||||||
|
"commander": "^9.4.0",
|
||||||
|
"ethers": "^5.6.9",
|
||||||
|
"node-fetch": "2",
|
||||||
|
"ts-node": "^10.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.6.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Deploy evm0 and evm1
|
||||||
|
ts-node orchestrator.ts deploy evm0
|
||||||
|
ts-node orchestrator.ts deploy evm1
|
||||||
|
|
||||||
|
# Register evm0 on evm1 and vice versa
|
||||||
|
ts-node orchestrator.ts register-network evm0 evm1
|
||||||
|
ts-node orchestrator.ts register-network evm1 evm0
|
||||||
|
|
||||||
|
# Emit VAA from evm0
|
||||||
|
ts-node orchestrator.ts emit-msg evm0 "Hello from evm0"
|
||||||
|
# Emit VAA from evm1
|
||||||
|
ts-node orchestrator.ts emit-msg evm1 "Hello from evm1"
|
||||||
|
|
||||||
|
# Submit evm0 VAA to evm1
|
||||||
|
ts-node orchestrator.ts submit-vaa evm1 evm0 latest
|
||||||
|
ts-node orchestrator.ts submit-vaa evm0 evm1 latest
|
||||||
|
|
||||||
|
# Wait a couple blocks for confirmation
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Get Current Messages from evm0 and evm1
|
||||||
|
ts-node orchestrator.ts get-msg evm0
|
||||||
|
ts-node orchestrator.ts get-msg evm1
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"networks": {
|
"networks": {
|
||||||
"eth0": {
|
"evm0": {
|
||||||
"type": "evm",
|
"type": "evm",
|
||||||
"wormholeChainId": 2,
|
"wormholeChainId": 2,
|
||||||
"rpc": "http://localhost:8545",
|
"rpc": "http://localhost:8545",
|
||||||
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
|
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
|
||||||
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
|
||||||
},
|
},
|
||||||
"eth1": {
|
"evm1": {
|
||||||
"type": "evm",
|
"type": "evm",
|
||||||
"wormholeChainId": 4,
|
"wormholeChainId": 4,
|
||||||
"rpc": "http://localhost:8546",
|
"rpc": "http://localhost:8546",
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
"type": "solana",
|
"type": "solana",
|
||||||
"wormholeChainId": 1,
|
"wormholeChainId": 1,
|
||||||
"rpc": "http://localhost:8899",
|
"rpc": "http://localhost:8899",
|
||||||
"privateKey": "",
|
"privateKey": "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
|
||||||
"bridgeAddress": ""
|
"bridgeAddress": "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wormhole": {
|
"wormhole": {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Start EVM Chain 0
|
|
||||||
npx pm2 delete evm0 2> /dev/null || true
|
|
||||||
npx pm2 start 'npx ganache -p 8545 -m "myth like bonus scare over problem client lizard pioneer submit female collect" --block-time 1' --name evm0
|
|
||||||
|
|
||||||
# Start EVM Chain 1
|
|
||||||
npx pm2 delete evm1 2> /dev/null || true
|
|
||||||
npx pm2 start 'npx ganache -p 8546 -m "myth like bonus scare over problem client lizard pioneer submit female collect" --block-time 1' --name evm1
|
|
||||||
|
|
||||||
cd wormhole/ethereum
|
|
||||||
|
|
||||||
#Install Wormhole Eth Dependencies
|
|
||||||
make .env build
|
|
||||||
|
|
||||||
# Deploy Wormhole Contracts to EVM Chain 0
|
|
||||||
npm run migrate && npx truffle exec scripts/deploy_test_token.js && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_eth_chain.js
|
|
||||||
|
|
||||||
# Deploy Wormhole Contracts to EVM Chain 1
|
|
||||||
perl -pi -e 's/CHAIN_ID=0x2/CHAIN_ID=0x4/g' .env && perl -pi -e 's/8545/8546/g' truffle-config.js
|
|
||||||
npm run migrate && npx truffle exec scripts/deploy_test_token.js && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_eth_chain.js
|
|
||||||
perl -pi -e 's/CHAIN_ID=0x4/CHAIN_ID=0x2/g' .env && perl -pi -e 's/8546/8545/g' truffle-config.js
|
|
|
@ -1,52 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Run Guardiand
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# dev.v2 for now (until we make a release)
|
|
||||||
DOCKER_IMAGE="ghcr.io/certusone/guardiand:dev.v2"
|
|
||||||
|
|
||||||
DOCKER_FLAGS=
|
|
||||||
HOST=
|
|
||||||
TERRAD_HOST=
|
|
||||||
if [ "$(uname -m)" = "arm64" ]; then
|
|
||||||
DOCKER_FLAGS="-p 7070:7070 -p 7071:7071 -p 7073:7073 --platform linux/amd64"
|
|
||||||
HOST="host.docker.internal"
|
|
||||||
TERRAD_HOST="host.docker.internal"
|
|
||||||
else
|
|
||||||
DOCKER_FLAGS="--network host"
|
|
||||||
TERRAD_HOST="terra-terrad"
|
|
||||||
HOST="localhost"
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker run --rm --name guardiand $DOCKER_FLAGS --hostname guardian-0 --cap-add=IPC_LOCK "$DOCKER_IMAGE" node \
|
|
||||||
--unsafeDevMode --guardianKey /bridge.key --publicRPC "[::]:7070" --publicWeb "[::]:7071" --adminSocket /admin.sock --dataDir /data \
|
|
||||||
--ethRPC ws://$HOST:8545 \
|
|
||||||
--ethContract "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" \
|
|
||||||
--bscRPC ws://$HOST:8546 \
|
|
||||||
--bscContract "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" \
|
|
||||||
--polygonRPC ws://$HOST:8545 \
|
|
||||||
--avalancheRPC ws://$HOST:8545 \
|
|
||||||
--auroraRPC ws://$HOST:8545 \
|
|
||||||
--fantomRPC ws://$HOST:8545 \
|
|
||||||
--oasisRPC ws://$HOST:8545 \
|
|
||||||
--karuraRPC ws://$HOST:8545 \
|
|
||||||
--acalaRPC ws://$HOST:8545 \
|
|
||||||
--klaytnRPC ws://$HOST:8545 \
|
|
||||||
--celoRPC ws://$HOST:8545 \
|
|
||||||
--moonbeamRPC ws://$HOST:8545 \
|
|
||||||
--neonRPC ws://$HOST:8545 \
|
|
||||||
--terraWS ws://$HOST:8545 \
|
|
||||||
--terra2WS ws://$HOST:8545 \
|
|
||||||
--terraLCD https://$TERRAD_HOST:1317 \
|
|
||||||
--terra2LCD http://$HOST:1317 \
|
|
||||||
--terraContract terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5 \
|
|
||||||
--terra2Contract terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5 \
|
|
||||||
--solanaContract Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o \
|
|
||||||
--solanaWS ws://$HOST:8900 \
|
|
||||||
--solanaRPC http://$HOST:8899 \
|
|
||||||
--algorandIndexerRPC ws://$HOST:8545 \
|
|
||||||
--algorandIndexerToken "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
|
||||||
--algorandAlgodToken "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
|
||||||
--algorandAlgodRPC https://$HOST:4001 \
|
|
||||||
--algorandAppID "4"
|
|
|
@ -4,10 +4,10 @@
|
||||||
"description": "A simple template for getting started with xDapps.",
|
"description": "A simple template for getting started with xDapps.",
|
||||||
"main": "starter.js",
|
"main": "starter.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"setup": "sh setup.sh",
|
"setup": "bash setup.bash",
|
||||||
"wormhole": "npm run setup && sh wormhole.sh",
|
"wormhole": "npm run setup && bash wormhole.bash",
|
||||||
"evm": "npm run setup && sh evm.sh",
|
"evm": "npm run setup && bash evm.bash",
|
||||||
"solana": "npm run setup && sh solana.sh",
|
"solana": "npm run setup && bash solana-binary-deploy.bash",
|
||||||
"cleanup": "npx pm2 kill"
|
"cleanup": "npx pm2 kill"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Check if wormhole/ repo exists.
|
|
||||||
# If it doesn't then clone
|
|
||||||
if [ ! -d "./wormhole" ]
|
|
||||||
then
|
|
||||||
git clone https://github.com/certusone/wormhole
|
|
||||||
fi
|
|
|
@ -1,16 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Start Solana
|
|
||||||
npx pm2 stop solana 2> /dev/null || true
|
|
||||||
|
|
||||||
npx pm2 start "solana-test-validator" --name solana -- -r \
|
|
||||||
--bpf-program Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o ./solana-accounts/core/core_bridge.so \
|
|
||||||
--account FKoMTctsC7vJbEqyRiiPskPnuQx2tX1kurmvWByq5uZP ./solana-accounts/core/bridge_config.json \
|
|
||||||
--account GXBsgBD3LDn3vkRZF6TfY5RqgajVZ4W5bMAdiAaaUARs ./solana-accounts/core/fee_collector.json \
|
|
||||||
--account 6MxkvoEwgB9EqQRLNhvYaPGhfcLtBtpBqdQugr3AZUgD ./solana-accounts/core/guardian_set.json \
|
|
||||||
--bpf-program B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE ./solana-accounts/token/token_bridge.so \
|
|
||||||
--account 3GwVs8GSLdo4RUsoXTkGQhojauQ1sXcDNjm7LSDicw19 ./solana-accounts/token/token_config.json \
|
|
||||||
--account 7UqWgfVW1TrjrqauMfDoNMcw8kEStSsQXWNoT2BbhDS5 ./solana-accounts/token/emitter_eth.json \
|
|
||||||
--account BmRvjCA2cQ1qUNAMVAnPgmjATSBPa2pxE3Q7bRoSGFED ./solana-accounts/token/emitter_bsc.json
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Start guardiand
|
|
||||||
|
|
||||||
npx pm2 delete guardiand 2> /dev/null || true
|
|
||||||
npx pm2 start './guardiand.sh' --name guardiand
|
|
Loading…
Reference in New Issue