[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:
Yunhao Zhang 2023-02-15 10:43:51 -05:00 committed by GitHub
parent 66a7d243ac
commit 16caff1b04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 4781 additions and 0 deletions

7
target_chains/solana/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.anchor
.DS_Store
target
**/*.rs.bk
node_modules
test-ledger

View File

@ -0,0 +1,8 @@
.anchor
.DS_Store
target
node_modules
dist
build
test-ledger

View File

@ -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"

4497
target_chains/solana/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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==

View File

@ -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`.

View File

@ -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"}

View File

@ -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,
},
}

View File

@ -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(())
}

View File

@ -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.
};

View File

@ -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"

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -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 {}