[solana] Start to implement the solana receiver contract (#557)
* Start to implement the solana receiver contract * Use "cargo run" instead of the compiled binary under target/debug * Update cargo dependency * Add "pyth-" prefix to crate names * Remove the transfer step in cli which is only necessary for pythnet, not solana
This commit is contained in:
parent
66a7d243ac
commit
16caff1b04
|
@ -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_receiver = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
url = "https://api.apr.dev"
|
||||||
|
|
||||||
|
[provider]
|
||||||
|
cluster = "Localnet"
|
||||||
|
wallet = "/home/yunhao/.config/solana/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,14 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"programs/*",
|
||||||
|
"cli/"
|
||||||
|
]
|
||||||
|
|
||||||
|
[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,4 @@
|
||||||
|
ccli:
|
||||||
|
cd cli; cargo build
|
||||||
|
post_vaa:
|
||||||
|
cd cli; cargo run post-price-vaa -v AQAAAAABAGL2NH+QGX4mcTw+F2uFuH8lC62485K1o/vxftbGKL1gauMUW6Lzb18JJLF4SJtkc9H5MnqC+Mpai+bDoj2Ztw8BY9p6/AAAAAAAAfNGGVrALzfWDU24/6bvdMsb41UAR1Q6Sp7prPTXhpewAAAAAAeb7UQBUDJXSAADAAAAAQIABQCVQxzC/Q70r0vHyF//ri9j1Rsm0WIXloLRSa5hmxIhwAv8MJRn3vpLGYxrW9WcCNtLnfsn3bzDLzFWDyF7T/j8KwAAACRAHYigAAAAAAPYMSD////4AAAAJCpaeTgAAAAAAqjStgEAAAABAAAAFQAAAABj2nr8AAAAAGPaevwAAAAAY9p6/AAAACRAHYigAAAAAAPYMSD0Kq+ITHsUVIlBcL4Krx2zm0t406VqJ/1JvYs57ywz12UQcfjHqyMhtr3TvHm5SlCEGpKm4GX547i5kmqPtaXRAAAAJO/rVQAAAAAABQG9AP////gAAAAk30Wg6AAAAAAFg7x3AQAAAAEAAAALAAAAAGPaevwAAAAAY9p6/AAAAABj2nr8AAAAJO/rVQAAAAAABQG9ABgB6wOAOvAkRSPuKobD8nsSar6JBNtLRagq21/iFwi0yoC6bcMuCNBvGqiGAR7tHXfHe+nrdhzBDXK30KL9V6YAAAAk8MYnegAAAAAD1cL6////+AAAACTf8vQAAAAAAANX/dkBAAAAAgAAAB4AAAAAY9p6/AAAAABj2nr8AAAAAGPaevwAAAAk8MYnegAAAAAD1cL6fd8Ngq9THwrxCdXpzp7Ce6nwDp7oq3HJEq//oW1xWDa3q9Jadt2v/fhHIk8DGYzLknI/kLJCnPM/Duy5bjUqhgAAACRKX0MAAAAAAIiMq2D////4AAAAJGpZPYAAAAAAf92Z9AEAAAACAAAADgAAAABj2nr8AAAAAGPaevwAAAAAY9p6/AAAACRKX0MAAAAAAIP9X0DVpcLzDga9bzjgHCxMjN18p8HBLUenM25Fn8bbQXG65mD9YbLZDrpH8oFQWoiGm2YTPZ3FjyA7AZ9apH8bOTQ+AAAAAAAAAAAAAAAAAAAAAP////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAGPaevwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
@ -0,0 +1,5 @@
|
||||||
|
# pythnet-solana-receiver
|
||||||
|
|
||||||
|
To compile the cli, run `make ccli`.
|
||||||
|
|
||||||
|
To test post_vaa of the cli, run `make post_vaa`.
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "pyth-solana-receiver-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.65"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
shellexpand = "2.1.2"
|
||||||
|
solana-sdk = "1.10.31"
|
||||||
|
solana-client = "1.10.31"
|
||||||
|
anchor-client = "0.26.0"
|
||||||
|
clap = {version ="3.2.22", features = ["derive"]}
|
||||||
|
wormhole-core = { git = "https://github.com/guibescos/wormhole", branch = "reisen/sdk-solana"}
|
||||||
|
wormhole-solana = { git = "https://github.com/guibescos/wormhole", branch = "reisen/sdk-solana"}
|
|
@ -0,0 +1,43 @@
|
||||||
|
//! CLI options
|
||||||
|
use {
|
||||||
|
clap::{
|
||||||
|
Parser,
|
||||||
|
Subcommand,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(
|
||||||
|
about = "A cli for the solana receiver contract",
|
||||||
|
author = "Pyth Network Contributors"
|
||||||
|
)]
|
||||||
|
pub struct Cli {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
pub enum Action {
|
||||||
|
#[clap(about = "Verify and post the price VAA on solana")]
|
||||||
|
PostPriceVAA {
|
||||||
|
#[clap(short = 'v', long,
|
||||||
|
help = "Price VAA from Pythnet")]
|
||||||
|
vaa: String,
|
||||||
|
#[clap(
|
||||||
|
short = 'k', long,
|
||||||
|
default_value = "~/.config/solana/id.json",
|
||||||
|
help = "Keypair of the transaction's funder"
|
||||||
|
)]
|
||||||
|
keypair: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[clap(about = "Invoke the on-chain contract decoding the VAA")]
|
||||||
|
InvokePriceReceiver {
|
||||||
|
#[clap(
|
||||||
|
short = 'k', long,
|
||||||
|
default_value = "~/.config/solana/id.json",
|
||||||
|
help = "Keypair of the transaction's funder"
|
||||||
|
)]
|
||||||
|
keypair: String,
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
pub mod cli;
|
||||||
|
|
||||||
|
use {
|
||||||
|
cli::{
|
||||||
|
Action,
|
||||||
|
Cli,
|
||||||
|
},
|
||||||
|
clap::Parser,
|
||||||
|
anyhow::Result,
|
||||||
|
std::str::FromStr,
|
||||||
|
|
||||||
|
solana_sdk::{
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{
|
||||||
|
read_keypair_file,
|
||||||
|
Keypair,
|
||||||
|
},
|
||||||
|
signer::Signer,
|
||||||
|
instruction::Instruction,
|
||||||
|
transaction::Transaction,
|
||||||
|
},
|
||||||
|
|
||||||
|
wormhole::VAA,
|
||||||
|
wormhole_solana::{
|
||||||
|
Account,
|
||||||
|
GuardianSet,
|
||||||
|
Config as WormholeConfig,
|
||||||
|
instructions::{
|
||||||
|
post_vaa,
|
||||||
|
verify_signatures_txs,
|
||||||
|
PostVAAData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
solana_client::rpc_client::RpcClient,
|
||||||
|
anchor_client::anchor_lang::AnchorDeserialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
match cli.action {
|
||||||
|
Action::PostPriceVAA { vaa, keypair } => {
|
||||||
|
println!("PostPriceVAA is invoked with vaa\"{}\"", vaa);
|
||||||
|
// Hard-coded strings
|
||||||
|
let rpc_client = RpcClient::new("https://api.devnet.solana.com");
|
||||||
|
// Is RpcClient::new_with_commitment necessary?
|
||||||
|
let wormhole = Pubkey::from_str("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5").unwrap();
|
||||||
|
|
||||||
|
println!("Decode the VAA");
|
||||||
|
let vaa_bytes: Vec<u8> = base64::decode(vaa)?;
|
||||||
|
let vaa = VAA::from_bytes(vaa_bytes.clone())?;
|
||||||
|
|
||||||
|
println!("Get wormhole guardian set configuration");
|
||||||
|
let wormhole_config = WormholeConfig::key(&wormhole, ());
|
||||||
|
let wormhole_config_data =
|
||||||
|
WormholeConfig::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
|
||||||
|
|
||||||
|
let guardian_set = GuardianSet::key(&wormhole, wormhole_config_data.guardian_set_index);
|
||||||
|
let guardian_set_data =
|
||||||
|
GuardianSet::try_from_slice(&rpc_client.get_account_data(&guardian_set)?)?;
|
||||||
|
|
||||||
|
println!("Invoke wormhole on solana to verify the VAA");
|
||||||
|
let payer =
|
||||||
|
read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
|
||||||
|
let signature_set_keypair = Keypair::new();
|
||||||
|
let verify_txs = verify_signatures_txs(
|
||||||
|
vaa_bytes.as_slice(),
|
||||||
|
guardian_set_data,
|
||||||
|
wormhole,
|
||||||
|
payer.pubkey(),
|
||||||
|
wormhole_config_data.guardian_set_index,
|
||||||
|
signature_set_keypair.pubkey(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for tx in verify_txs {
|
||||||
|
process_transaction(&rpc_client, tx, &vec![&payer, &signature_set_keypair])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Upload the VAA data to a solana account");
|
||||||
|
let post_vaa_data = PostVAAData {
|
||||||
|
version: vaa.version,
|
||||||
|
guardian_set_index: vaa.guardian_set_index,
|
||||||
|
timestamp: vaa.timestamp,
|
||||||
|
nonce: vaa.nonce,
|
||||||
|
emitter_chain: vaa.emitter_chain.into(),
|
||||||
|
emitter_address: vaa.emitter_address,
|
||||||
|
sequence: vaa.sequence,
|
||||||
|
consistency_level: vaa.consistency_level,
|
||||||
|
payload: vaa.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
process_transaction(
|
||||||
|
&rpc_client,
|
||||||
|
vec![post_vaa(
|
||||||
|
wormhole,
|
||||||
|
payer.pubkey(),
|
||||||
|
signature_set_keypair.pubkey(),
|
||||||
|
post_vaa_data,
|
||||||
|
)?],
|
||||||
|
&vec![&payer],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action::InvokePriceReceiver { keypair } => {
|
||||||
|
println!("TBD, keypair={}", keypair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_transaction(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
instructions: Vec<Instruction>,
|
||||||
|
signers: &Vec<&Keypair>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut transaction =
|
||||||
|
Transaction::new_with_payer(instructions.as_slice(), Some(&signers[0].pubkey()));
|
||||||
|
transaction.sign(signers, rpc_client.get_latest_blockhash()?);
|
||||||
|
let transaction_signature =
|
||||||
|
rpc_client.send_and_confirm_transaction_with_spinner(&transaction)?;
|
||||||
|
println!("Transaction successful : {transaction_signature:?}");
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -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 @@
|
||||||
|
[package]
|
||||||
|
name = "pyth-solana-receiver"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Created with Anchor"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
||||||
|
name = "pyth_solana_receiver"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
no-idl = []
|
||||||
|
no-log-ix-name = []
|
||||||
|
cpi = ["no-entrypoint"]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anchor-lang = "0.26.0"
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,15 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod solana_receiver {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct Initialize {}
|
Loading…
Reference in New Issue