messenger/solana working

This commit is contained in:
spacemandev 2022-08-10 05:15:23 -05:00
parent f078fcba50
commit adc338fb3e
17 changed files with 2858 additions and 19 deletions

View File

@ -599,6 +599,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.8.1"
@ -1114,6 +1120,10 @@ name = "solana"
version = "0.1.0"
dependencies = [
"anchor-lang",
"borsh",
"byteorder",
"hex",
"sha3",
]
[[package]]

View File

@ -1,7 +1,7 @@
[package]
name = "solana"
version = "0.1.0"
description = "Created with Anchor"
description = "Cross Chain Messenger App"
edition = "2021"
[lib]
@ -17,3 +17,7 @@ default = []
[dependencies]
anchor-lang = "0.25.0"
sha3 = "0.10.1"
byteorder = "1.4.3"
borsh = "0.9.3"
hex = "0.4.3"

View File

@ -0,0 +1,20 @@
use anchor_lang::prelude::*;
#[account]
#[derive(Default)]
pub struct Config{
pub owner: Pubkey,
pub nonce: u32,
pub current_msg: String
}
#[account]
#[derive(Default)]
pub struct EmitterAddrAccount{
pub chain_id: u16,
pub emitter_addr: String
}
//Empty account, we just need to check that it *exists*
#[account]
pub struct ProcessedVAA {}

View File

@ -0,0 +1 @@
pub const CORE_BRIDGE_ADDRESS: &str = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";

View File

@ -1,4 +1,146 @@
use anchor_lang::prelude::*;
use crate::constant::*;
use crate::account::*;
use std::str::FromStr;
use anchor_lang::solana_program::sysvar::{rent, clock};
use crate::wormhole::*;
use hex::decode;
#[derive(Accounts)]
pub struct Initialize {}
pub struct Initialize<'info> {
#[account(
init,
seeds=[b"config".as_ref()],
payer=owner,
bump,
space=8+32+32+1024
)]
pub config: Account<'info, Config>,
#[account(mut)]
pub owner: Signer<'info>,
pub system_program: Program<'info, System>
}
#[derive(Accounts)]
#[instruction(chain_id:u16, emitter_addr:String)]
pub struct RegisterChain<'info> {
#[account(mut)]
pub owner: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
constraint = config.owner == owner.key()
)]
pub config: Account<'info, Config>,
#[account(
init,
seeds=[b"EmitterAddress".as_ref(), chain_id.to_be_bytes().as_ref()],
payer=owner,
bump,
space=8+2+256
)]
pub emitter_acc: Account<'info, EmitterAddrAccount>,
}
#[derive(Accounts)]
pub struct SendMsg<'info>{
#[account(
constraint = core_bridge.key() == Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
/// CHECK: If someone passes in the wrong account, Guardians won't read the message
pub core_bridge: AccountInfo<'info>,
#[account(
seeds = [
b"Bridge".as_ref()
],
bump,
seeds::program = Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap(),
mut
)]
/// CHECK: If someone passes in the wrong account, Guardians won't read the message
pub wormhole_config: AccountInfo<'info>,
#[account(
seeds = [
b"fee_collector".as_ref()
],
bump,
seeds::program = Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap(),
mut
)]
/// CHECK: If someone passes in the wrong account, Guardians won't read the message
pub wormhole_fee_collector: AccountInfo<'info>,
#[account(
seeds = [
b"emitter".as_ref(),
],
bump,
mut
)]
/// CHECK: If someone passes in the wrong account, Guardians won't read the message
pub wormhole_derived_emitter: AccountInfo<'info>,
#[account(
seeds = [
b"Sequence".as_ref(),
wormhole_derived_emitter.key().to_bytes().as_ref()
],
bump,
seeds::program = Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap(),
mut
)]
/// CHECK: If someone passes in the wrong account, Guardians won't read the message
pub wormhole_sequence: AccountInfo<'info>,
#[account(mut)]
pub wormhole_message_key: Signer<'info>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
constraint = clock.key() == clock::id()
)]
/// CHECK: The account constraint will make sure it's the right clock var
pub clock: AccountInfo<'info>,
#[account(
constraint = rent.key() == rent::id()
)]
/// CHECK: The account constraint will make sure it's the right rent var
pub rent: AccountInfo<'info>,
#[account(mut)]
pub config: Account<'info, Config>,
}
#[derive(Accounts)]
#[instruction()]
pub struct ConfirmMsg<'info>{
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
init,
seeds=[
&decode(&emitter_acc.emitter_addr.as_str()).unwrap()[..],
emitter_acc.chain_id.to_be_bytes().as_ref(),
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).sequence.to_be_bytes().as_ref()
],
payer=payer,
bump,
space=8
)]
pub processed_vaa: Account<'info, ProcessedVAA>,
pub emitter_acc: Account<'info, EmitterAddrAccount>,
/// This requires some fancy hashing, so confirm it's derived address in the function itself.
#[account(
constraint = core_bridge_vaa.to_account_info().owner == &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
/// CHECK: This account is owned by Core Bridge so we trust it
pub core_bridge_vaa: AccountInfo<'info>,
#[account(mut)]
pub config: Account<'info, Config>,
}
#[derive(Accounts)]
pub struct Debug<'info>{
#[account(
constraint = core_bridge_vaa.to_account_info().owner == &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
/// CHECK: This account is owned by Core Bridge so we trust it
pub core_bridge_vaa: AccountInfo<'info>,
}

View File

@ -0,0 +1,10 @@
use anchor_lang::prelude::*;
#[error_code]
pub enum MessengerError {
#[msg("Posted VAA Key Mismatch")]
VAAKeyMismatch,
#[msg("Posted VAA Emitter Chain ID or Address Mismatch")]
VAAEmitterMismatch,
}

View File

@ -1,25 +1,158 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::instruction::Instruction;
use anchor_lang::solana_program::system_instruction::transfer;
use anchor_lang::solana_program::borsh::try_from_slice_unchecked;
use anchor_lang::solana_program::program::invoke_signed;
use sha3::Digest;
use byteorder::{
BigEndian,
WriteBytesExt,
};
use std::io::{
Cursor,
Write,
};
use std::str::FromStr;
use hex::decode;
mod account;
mod constant;
mod context;
mod constant;
mod account;
mod wormhole;
mod error;
mod event;
use account::*;
use constant::*;
use wormhole::*;
use context::*;
use constant::*;
use error::*;
use event::*;
declare_id!("24FoTeX7BKbhTh3UF3feWusoAVKDPWZneiEqhXLVzZPL");
#[program]
pub mod solana {
use super::*;
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
ctx.accounts.config.owner = ctx.accounts.owner.key();
ctx.accounts.config.nonce = 1;
Ok(())
}
pub fn register_chain(ctx:Context<RegisterChain>, chain_id:u16, emitter_addr:String) -> Result<()> {
ctx.accounts.emitter_acc.chain_id = chain_id;
ctx.accounts.emitter_acc.emitter_addr = emitter_addr;
Ok(())
}
pub fn send_msg(ctx:Context<SendMsg>, msg:String) -> Result<()> {
//Look Up Fee
let bridge_data:BridgeData = try_from_slice_unchecked(&ctx.accounts.wormhole_config.data.borrow_mut())?;
//Send Fee
invoke_signed(
&transfer(
&ctx.accounts.payer.key(),
&ctx.accounts.wormhole_fee_collector.key(),
bridge_data.config.fee
),
&[
ctx.accounts.payer.to_account_info(),
ctx.accounts.wormhole_fee_collector.to_account_info()
],
&[]
)?;
//Send Post Msg Tx
let sendmsg_ix = Instruction {
program_id: ctx.accounts.core_bridge.key(),
accounts: vec![
AccountMeta::new(ctx.accounts.wormhole_config.key(), false),
AccountMeta::new(ctx.accounts.wormhole_message_key.key(), true),
AccountMeta::new_readonly(ctx.accounts.wormhole_derived_emitter.key(), true),
AccountMeta::new(ctx.accounts.wormhole_sequence.key(), false),
AccountMeta::new(ctx.accounts.payer.key(), true),
AccountMeta::new(ctx.accounts.wormhole_fee_collector.key(), false),
AccountMeta::new_readonly(ctx.accounts.clock.key(), false),
AccountMeta::new_readonly(ctx.accounts.rent.key(), false),
AccountMeta::new_readonly(ctx.accounts.system_program.key(), false),
],
data: (
wormhole::Instruction::PostMessage,
PostMessageData {
nonce: ctx.accounts.config.nonce,
payload: msg.as_bytes().try_to_vec()?,
consistency_level: wormhole::ConsistencyLevel::Confirmed,
},
).try_to_vec()?,
};
invoke_signed(
&sendmsg_ix,
&[
ctx.accounts.wormhole_config.to_account_info(),
ctx.accounts.wormhole_message_key.to_account_info(),
ctx.accounts.wormhole_derived_emitter.to_account_info(),
ctx.accounts.wormhole_sequence.to_account_info(),
ctx.accounts.payer.to_account_info(),
ctx.accounts.wormhole_fee_collector.to_account_info(),
ctx.accounts.clock.to_account_info(),
ctx.accounts.rent.to_account_info(),
ctx.accounts.system_program.to_account_info(),
],
&[
&[
&b"emitter".as_ref(),
&[*ctx.bumps.get("wormhole_derived_emitter").unwrap()]
]
]
)?;
ctx.accounts.config.nonce += 1;
Ok(())
}
pub fn confirm_msg(ctx:Context<ConfirmMsg>) -> Result<()> {
//Hash a VAA Extract and derive a VAA Key
let vaa = PostedMessageData::try_from_slice(&ctx.accounts.core_bridge_vaa.data.borrow())?.0;
let serialized_vaa = serialize_vaa(&vaa);
let mut h = sha3::Keccak256::default();
h.write(serialized_vaa.as_slice()).unwrap();
let vaa_hash: [u8; 32] = h.finalize().into();
let (vaa_key, _) = Pubkey::find_program_address(&[
b"PostedVAA",
&vaa_hash
], &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap());
if ctx.accounts.core_bridge_vaa.key() != vaa_key {
return err!(MessengerError::VAAKeyMismatch);
}
msg!("Checking emitter chain and address");
// Already checked that the SignedVaa is owned by core bridge in account constraint logic
//Check that the emitter chain and address match up with the vaa
if vaa.emitter_chain != ctx.accounts.emitter_acc.chain_id ||
vaa.emitter_address != &decode(&ctx.accounts.emitter_acc.emitter_addr.as_str()).unwrap()[..] {
return err!(MessengerError::VAAEmitterMismatch)
}
ctx.accounts.config.current_msg = String::from_utf8(vaa.payload).unwrap();
Ok(())
}
}
// Convert a full VAA structure into the serialization of its unique components, this structure is
// what is hashed and verified by Guardians.
pub fn serialize_vaa(vaa: &MessageData) -> Vec<u8> {
let mut v = Cursor::new(Vec::new());
v.write_u32::<BigEndian>(vaa.vaa_time).unwrap();
v.write_u32::<BigEndian>(vaa.nonce).unwrap();
v.write_u16::<BigEndian>(vaa.emitter_chain.clone() as u16).unwrap();
v.write(&vaa.emitter_address).unwrap();
v.write_u64::<BigEndian>(vaa.sequence).unwrap();
v.write_u8(vaa.consistency_level).unwrap();
v.write(&vaa.payload).unwrap();
v.into_inner()
}

View File

@ -0,0 +1,111 @@
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use std::{
io::Write,
};
#[derive(AnchorDeserialize, AnchorSerialize)]
pub struct PostMessageData {
/// Unique nonce for this message
pub nonce: u32,
/// Message payload
pub payload: Vec<u8>,
/// Commitment Level required for an attestation to be produced
pub consistency_level: ConsistencyLevel,
}
#[derive(AnchorDeserialize, AnchorSerialize)]
pub enum ConsistencyLevel {
Confirmed,
Finalized
}
#[derive(AnchorDeserialize, AnchorSerialize)]
pub enum Instruction{
Initialize,
PostMessage,
PostVAA,
SetFees,
TransferFees,
UpgradeContract,
UpgradeGuardianSet,
VerifySignatures,
}
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct BridgeData {
/// The current guardian set index, used to decide which signature sets to accept.
pub guardian_set_index: u32,
/// Lamports in the collection account
pub last_lamports: u64,
/// Bridge configuration, which is set once upon initialization.
pub config: BridgeConfig,
}
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct BridgeConfig {
/// Period for how long a guardian set is valid after it has been replaced by a new one. This
/// guarantees that VAAs issued by that set can still be submitted for a certain period. In
/// this period we still trust the old guardian set.
pub guardian_set_expiration_time: u32,
/// Amount of lamports that needs to be paid to the protocol to post a message
pub fee: u64,
}
#[derive(Debug)]
#[repr(transparent)]
pub struct PostedMessageData(pub MessageData);
#[derive(Debug, Default, BorshDeserialize, BorshSerialize)]
pub struct MessageData {
/// Header of the posted VAA
pub vaa_version: u8,
/// Level of consistency requested by the emitter
pub consistency_level: u8,
/// Time the vaa was submitted
pub vaa_time: u32,
/// Account where signatures are stored
pub vaa_signature_account: Pubkey,
/// Time the posted message was created
pub submission_time: u32,
/// Unique nonce for this message
pub nonce: u32,
/// Sequence number of this message
pub sequence: u64,
/// Emitter of the message
pub emitter_chain: u16,
/// Emitter of the message
pub emitter_address: [u8; 32],
/// Message payload
pub payload: Vec<u8>,
}
impl AnchorSerialize for PostedMessageData {
fn serialize<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write(b"msg")?;
BorshSerialize::serialize(&self.0, writer)
}
}
impl AnchorDeserialize for PostedMessageData {
fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
*buf = &buf[3..];
Ok(PostedMessageData(
<MessageData as BorshDeserialize>::deserialize(buf)?,
))
}
}

View File

@ -1,6 +1,6 @@
import * as fs from 'fs';
import { exec } from "child_process";
import { getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth } from '@certusone/wormhole-sdk';
import { getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth, setDefaultWasm } from '@certusone/wormhole-sdk';
import * as ethers from 'ethers';
import fetch from 'node-fetch';
@ -61,6 +61,7 @@ export async function registerApp(src:string, target:string){
targetEmitter = getEmitterAddressEth(targetDeploymentInfo['address']);
break;
case 'solana':
setDefaultWasm("node"); // *sigh*
targetEmitter = await getEmitterAddressSolana(targetDeploymentInfo['address']);
break;
}
@ -144,7 +145,6 @@ export async function sendMsg(src:string, msg:string){
export async function submitVaa(src:string, target:string, idx:string){
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
let srcDeploymentInfo;
let targetDeploymentInfo;

View File

@ -0,0 +1,356 @@
import * as fs from 'fs';
import { exec } from "child_process";
import { Solana as MessengerTypes} from '../chains/solana/target/types/solana';
import fetch from 'node-fetch';
import {
getEmitterAddressEth,
getEmitterAddressSolana,
importCoreWasm,
parseSequenceFromLogSolana,
postVaaSolanaWithRetry,
setDefaultWasm,
getSignedVAAHash,
} from '@certusone/wormhole-sdk';
import * as anchor from '@project-serum/anchor';
import { findProgramAddressSync } from '@project-serum/anchor/dist/cjs/utils/pubkey';
import * as byteify from 'byteify';
import keccak256 from 'keccak256';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
const IDL = JSON.parse(fs.readFileSync("./chains/solana/target/idl/solana.json").toString());
const CONTRACT_ADDRESS = "24FoTeX7BKbhTh3UF3feWusoAVKDPWZneiEqhXLVzZPL";
const KEYPAIR = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(JSON.parse(fs.readFileSync('./chains/solana/keypairs/id.json').toString())));
export async function deploy(src: string){
const rpc = config.networks[src]['rpc'];
//Request Airdrop for saved keypair (because Local Validator probably started with the keypair in ~/.config)
const connection = new anchor.web3.Connection(rpc);
await connection.requestAirdrop(KEYPAIR.publicKey, 1e9*1000); //request 1000 SOL
await new Promise((r) => setTimeout(r, 15000)); // wait for the airdrop to go through
console.log("This will take a couple minutes due to Solana being solana...");
exec(
`cd chains/solana && solana config set -u ${rpc} -k keypairs/id.json && anchor build && anchor deploy --program-name solana --program-keypair keypairs/solana-keypair.json && exit`,
(err, out, errStr) => {
if(err){
throw new Error(err.message);
}
if(out) {
fs.writeFileSync(
`./deployinfo/${src}.deploy.json`,
JSON.stringify({
address: CONTRACT_ADDRESS,
vaas: []
}, null, 4)
);
new Promise((r) => setTimeout(r, 15000)) // wait for the chain to recognize the program
.then(async () => {
//Initalize the Contract
const messenger = new anchor.Program<MessengerTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
new anchor.web3.Connection(rpc),
new anchor.Wallet(KEYPAIR),
{}));
const [configAcc, _] = findProgramAddressSync([
Buffer.from("config")
], messenger.programId);
await messenger.methods.initialize()
.accounts({
config: configAcc,
owner: messenger.provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId
})
.rpc();
})
}
}
)
}
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 messenger = new anchor.Program<MessengerTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
new anchor.web3.Connection(srcNetwork['rpc']),
new anchor.Wallet(KEYPAIR),
{}));
const [emitterAcc, emitterBmp] = findProgramAddressSync([
Buffer.from("EmitterAddress"),
byteify.serializeUint16(targetNetwork.wormholeChainId)
], messenger.programId)
const [configAcc, configBmp] = findProgramAddressSync([
Buffer.from("config")
], messenger.programId);
const tx = await messenger.methods
.registerChain(
targetNetwork.wormholeChainId,
targetEmitter
)
.accounts({
owner: messenger.provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
config: configAcc,
emitterAcc: emitterAcc
})
.rpc();
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 messenger = new anchor.Program<MessengerTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
new anchor.web3.Connection(srcNetwork['rpc']),
new anchor.Wallet(KEYPAIR),
{}
));
const whCoreBridge = new anchor.web3.PublicKey(srcNetwork.bridgeAddress);
const whConfig = findProgramAddressSync([Buffer.from("Bridge")], whCoreBridge)[0];
const whFeeCollector = findProgramAddressSync([Buffer.from("fee_collector")], whCoreBridge)[0];
//The emitter address for Solana contracts is *NOT* the contract address, but rather a PDA of the Contract
const whDerivedEmitter = findProgramAddressSync([Buffer.from("emitter")], messenger.programId)[0];
const whSequence = findProgramAddressSync([Buffer.from("Sequence"), whDerivedEmitter.toBytes()], whCoreBridge)[0];
//Throw away account used to store the message as logs on Solana are not archived for long term retreival
const whMessageKeypair = anchor.web3.Keypair.generate();
const [configAcc, configBmp] = findProgramAddressSync([
Buffer.from("config")
], messenger.programId);
const tx = await messenger.methods
.sendMsg(msg)
.accounts({
coreBridge: whCoreBridge,
wormholeConfig: whConfig,
wormholeFeeCollector: whFeeCollector,
wormholeDerivedEmitter: whDerivedEmitter,
wormholeSequence: whSequence,
wormholeMessageKey: whMessageKeypair.publicKey,
payer: messenger.provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
config: configAcc
})
.signers([
whMessageKeypair
])
.rpc();
const seq = parseSequenceFromLogSolana(await messenger.provider.connection.getTransaction(tx))
setDefaultWasm("node"); // *sigh*
const emitterAddr = await getEmitterAddressSolana(messenger.programId.toString()); //same as whDerivedEmitter
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){
setDefaultWasm("node"); //WASM will be removed very soon, but until then, all the solana functions rely on it
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 messenger = new anchor.Program<MessengerTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
new anchor.web3.Connection(srcNetwork['rpc']),
new anchor.Wallet(KEYPAIR),
{}
));
//Submitting the VAA is a 2 step process
// First we submit it to core bridge to validate the signatures
await postVaaSolanaWithRetry(
messenger.provider.connection,
async (tx) => {
tx.partialSign(KEYPAIR);
return tx;
},
srcNetwork.bridgeAddress,
KEYPAIR.publicKey.toString(),
Buffer.from(vaa, "base64"),
10
);
// Then we submit to our program to go through the program's checks
await new Promise((r) => setTimeout(r, 5000));
const { parse_vaa } = await importCoreWasm(); //this function can only be imported from the WASM right now, not directly
const parsed_vaa = parse_vaa(Buffer.from(vaa, 'base64'));
const vaaHash = getVaaHash(parsed_vaa); //await getSignedVAAHash(Buffer.from(vaa, "base64"));
//console.log("Hash: ", vaaHash, await getSignedVAAHash(Buffer.from(vaa, "base64")));
// Account that we stored the registered foreign emitter
let emitterAddressAcc = findProgramAddressSync([
Buffer.from("EmitterAddress"),
byteify.serializeUint16(parsed_vaa.emitter_chain)
], messenger.programId)[0];
// A blank account we're creating just to keep track of already processed messages
let processedVaaKey = findProgramAddressSync([
Buffer.from(getEmitterAddressEth(targetDeploymentInfo.address), "hex"),
byteify.serializeUint16(parsed_vaa.emitter_chain),
byteify.serializeUint64(parsed_vaa.sequence)
], messenger.programId)[0];
// Account where the core bridge stored the vaa after the signatures checked out
let coreBridgeVaaKey = findProgramAddressSync([
Buffer.from("PostedVAA"),
Buffer.from(vaaHash, 'hex')
], new anchor.web3.PublicKey(srcNetwork.bridgeAddress))[0]
let configAcc = findProgramAddressSync([Buffer.from("config")], messenger.programId)[0];
//Confirm via Messenger Code
// will set the current message to the one in the vaa
const tx = await messenger.methods
.confirmMsg()
.accounts({
payer: KEYPAIR.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
processedVaa: processedVaaKey,
emitterAcc: emitterAddressAcc,
coreBridgeVaa: coreBridgeVaaKey,
config: configAcc
})
.rpc({skipPreflight: true});
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 messenger = new anchor.Program<MessengerTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
new anchor.web3.Connection(srcNetwork['rpc']),
new anchor.Wallet(KEYPAIR),
{}
));
const configAcc = findProgramAddressSync([
Buffer.from('config')
], messenger.programId)[0]
const configAccountData = await messenger.account.config.fetch(configAcc);
return configAccountData.currentMsg;
}
function getVaaHash(parsed_vaa){
//Create VAA Hash to use in core bridge key
let buffer_array = []
buffer_array.push(byteify.serializeUint32(parsed_vaa.timestamp));
buffer_array.push(byteify.serializeUint32(parsed_vaa.nonce));
buffer_array.push(byteify.serializeUint16(parsed_vaa.emitter_chain));
buffer_array.push(Uint8Array.from(parsed_vaa.emitter_address));
buffer_array.push(byteify.serializeUint64(parsed_vaa.sequence));
buffer_array.push(byteify.serializeUint8(parsed_vaa.consistency_level));
buffer_array.push(Uint8Array.from(parsed_vaa.payload));
const hash = keccak256(Buffer.concat(buffer_array));
return hash.toString("hex");
}

View File

@ -1,6 +1,7 @@
import { Command } from 'commander';
import * as fs from 'fs';
import * as evm from './handlers/evm';
import * as solana from './handlers/solana';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString())
@ -25,6 +26,9 @@ program
case "evm":
await evm.deploy(network);
break;
case "solana":
await solana.deploy(network);
break;
}
console.log(`Deploy finished!`);
@ -50,6 +54,8 @@ program
case 'evm':
await evm.registerApp(src,target);
break;
case "solana":
await solana.registerApp(src, target);
}
console.log(`Foreign Network ${target} registered on ${src}`);
@ -73,6 +79,9 @@ program
case 'evm':
await evm.sendMsg(src,msg);
break;
case "solana":
await solana.sendMsg(src, msg);
break;
}
console.log(`Emitted VAA on ${src} network. Submit it using \`submit-vaa\` command on a target network.`)
} catch (e){
@ -101,6 +110,9 @@ program
case 'evm':
await evm.submitVaa(src,target,idx);
break;
case "solana":
await solana.submitVaa(src,target,idx);
break;
}
console.log(`Submitted VAA #${idx} from ${target} to chain ${src}`);
@ -125,6 +137,9 @@ program
case 'evm':
msg = await evm.getCurrentMsg(src);
break;
case "solana":
msg = await solana.getCurrentMsg(src);
break;
}
console.log(`Current Message on Network ${src} is ${msg}`);

View File

@ -7,8 +7,12 @@
"license": "MIT",
"dependencies": {
"@certusone/wormhole-sdk": "^0.6.0",
"@project-serum/anchor": "^0.25.0",
"@types/node-fetch": "^2.6.2",
"byteify": "^2.0.10",
"commander": "^9.4.0",
"ethers": "^5.6.9",
"keccak256": "^1.0.6",
"node-fetch": "2",
"ts-node": "^10.9.1"
},

View File

@ -0,0 +1,26 @@
# Warning
echo "Due to the nature of Solana accounts, you'll need to reset the Solana Local Validator (evm can stay as is) every time you run this test, otherwise it'll error saying accounts are already created and in use."
# Deploy evm0 and sol0
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy sol0
# Register evm0 on sol0 and vice versa
ts-node orchestrator.ts register-network evm0 sol0
ts-node orchestrator.ts register-network sol0 evm0
# Emit VAA from evm0
ts-node orchestrator.ts emit-msg evm0 "Hello from evm0"
# Emit VAA from sol0
ts-node orchestrator.ts emit-msg sol0 "Hello from sol0"
# Submit evm0 VAA to sol0
ts-node orchestrator.ts submit-vaa sol0 evm0 latest
ts-node orchestrator.ts submit-vaa evm0 sol0 latest
# Wait a couple blocks for confirmation
sleep 3
# Get Current Messages from evm0 and sol0
ts-node orchestrator.ts get-msg evm0
ts-node orchestrator.ts get-msg sol0

View File

@ -2,5 +2,6 @@
"compilerOptions": {
"types": ["node"],
"moduleResolution": "node",
"esModuleInterop": true
}
}

File diff suppressed because it is too large Load Diff

View File

@ -421,6 +421,35 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@project-serum/anchor@^0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0.tgz#88ee4843336005cf5a64c80636ce626f0996f503"
integrity sha512-E6A5Y/ijqpfMJ5psJvbw0kVTzLZFUcOFgs6eSM2M2iWE1lVRF18T6hWZVNl6zqZsoz98jgnNHtVGJMs+ds9A7A==
dependencies:
"@project-serum/borsh" "^0.2.5"
"@solana/web3.js" "^1.36.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
camelcase "^5.3.1"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
js-sha256 "^0.9.0"
pako "^2.0.3"
snake-case "^3.0.4"
superstruct "^0.15.4"
toml "^3.0.0"
"@project-serum/borsh@^0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663"
integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==
dependencies:
bn.js "^5.1.2"
buffer-layout "^1.2.0"
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@ -493,7 +522,7 @@
buffer-layout "^1.2.0"
dotenv "10.0.0"
"@solana/web3.js@^1.21.0", "@solana/web3.js@^1.24.0":
"@solana/web3.js@^1.21.0", "@solana/web3.js@^1.24.0", "@solana/web3.js@^1.36.0":
version "1.50.1"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.50.1.tgz#dae726a06267d1bcd88b1e3cd8ae44c709302dcf"
integrity sha512-1l9N/nS8pJEA2YibNT8wa072718O0/A1eKWE0+pdWC5wDGQgBNxZSLuv7Cq5Dcn46WsZ5J5ZstK89q8J/ZZaQA==
@ -601,6 +630,14 @@
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
"@types/node-fetch@^2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.0.3", "@types/node@^18.6.4":
version "18.6.4"
resolved "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz"
@ -720,7 +757,7 @@ base-x@^3.0.2:
dependencies:
safe-buffer "^5.0.1"
base64-js@^1.3.1:
base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@ -782,7 +819,7 @@ bn.js@^4.11.8, bn.js@^4.11.9:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.2.0, bn.js@^5.2.1:
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
@ -830,7 +867,7 @@ bs58check@<3.0.0, bs58check@^2.1.1:
create-hash "^1.1.0"
safe-buffer "^5.1.2"
buffer-layout@^1.2.0:
buffer-layout@^1.2.0, buffer-layout@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5"
integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==
@ -843,7 +880,7 @@ buffer@6.0.1:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@6.0.3, buffer@^6.0.2, buffer@~6.0.3:
buffer@6.0.3, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
@ -866,6 +903,11 @@ bufferutil@^4.0.1, bufferutil@^4.0.3:
dependencies:
node-gyp-build "^4.3.0"
byteify@^2.0.10:
version "2.0.10"
resolved "https://registry.yarnpkg.com/byteify/-/byteify-2.0.10.tgz#2a77702887605439741d10dea4494172925089b4"
integrity sha512-clrE0NtRB/YwjQcmrUU9qpxRIQ5Jc1HGedv6/LWd08upE0FV0S4YvPBkmKEsTaquqGmhx34LkRBO+lXpTgwYgw==
call-bind@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@ -874,6 +916,11 @@ call-bind@^1.0.0:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
@ -942,6 +989,18 @@ create-require@^1.1.0:
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
crypto-hash@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247"
integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==
debug@^4.1.1:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -969,6 +1028,14 @@ diff@^4.0.1:
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dotenv@10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
@ -1249,6 +1316,24 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
keccak256@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58"
integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==
dependencies:
bn.js "^5.2.0"
buffer "^6.0.3"
keccak "^3.0.2"
keccak@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0"
integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==
dependencies:
node-addon-api "^2.0.0"
node-gyp-build "^4.2.0"
readable-stream "^3.6.0"
lodash@^4.17.20:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -1264,6 +1349,13 @@ long@^5.0.0:
resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61"
integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@ -1334,12 +1426,20 @@ nan@^2.13.2:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916"
integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
node-addon-api@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-fetch@2:
node-fetch@2, node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -1363,6 +1463,11 @@ once@^1.3.0:
dependencies:
wrappy "1"
pako@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d"
integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@ -1535,6 +1640,14 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@ -1564,6 +1677,11 @@ superstruct@^0.14.2:
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b"
integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==
superstruct@^0.15.4:
version "0.15.5"
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab"
integrity sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==
text-encoding-utf-8@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
@ -1592,6 +1710,11 @@ tmp@^0.2.1:
dependencies:
rimraf "^3.0.0"
toml@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee"
integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -1616,7 +1739,7 @@ ts-node@^10.9.1:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tslib@^2.1.0:
tslib@^2.0.3, tslib@^2.1.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==

View File

@ -3,7 +3,7 @@
set -euo pipefail
# Start Solana
npx pm2 stop solana 2> /dev/null || true
npx pm2 delete solana 2> /dev/null || true
### Uses prebuilt binaries to boot up STV. Leave the solana-accounts folder in there for those that want to use it with Anchor Validator
npx pm2 start "solana-test-validator" --name solana -- -r \