Skip to content

Commit b6fcb03

Browse files
authored
[solana] A working solana receiver program (#632)
* A working version of solana receiver * Cleanup * Minor * Commit for triggering tilt * Add program key starting with pyth * Remove duplicated hard-coded wormhole address * Add check for VAA magic number and emmitter is Pythnet or Solana * Cleaner command for building the cli package * Fix bug and use BatchPriceAttestation for deserialization * minor
1 parent 8ff1ada commit b6fcb03

File tree

12 files changed

+346
-48
lines changed

12 files changed

+346
-48
lines changed

target_chains/solana/Anchor.toml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
[features]
22
seeds = false
33
skip-lint = false
4-
[programs.localnet]
5-
solana_receiver = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
4+
5+
[programs.devnet]
6+
pyth_solana_receiver = "pythKkWXoywbvTQVcWrNDz5ENvWteF7tem7xzW52NBK"
67

78
[registry]
89
url = "https://api.apr.dev"
910

1011
[provider]
11-
cluster = "Localnet"
12-
wallet = "/home/yunhao/.config/solana/id.json"
12+
cluster = "https://api.devnet.solana.com"
13+
wallet = "~/.config/solana/id.json"
1314

1415
[scripts]
15-
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
16+
deploy = "anchor deploy --program-keypair program_address.json --program-name solana-receiver"
17+
cli_build = "cargo build --package pyth-solana-receiver-cli"
18+
cli_test = "cargo run --package pyth-solana-receiver-cli post-and-receive-vaa -v AQAAAAABABPz9W9ARa/V06ZBp68lkoGJ3tlnJRZ2/jYU4Wi1jdQAdQj3ZkPLKIMtzN3EqaCAjKrLG/sKqADTbPoaBtn9zKUBZAYEhwAAAAAAAfNGGVrALzfWDU24/6bvdMsb41UAR1Q6Sp7prPTXhpewAAAAAApC0/gBUDJXSAADAAEAAQIAAwCdfVoraihHxJUfK+NKfkJ4r4DsPSm8uMcxEWv1D/av5l7/DsJkQsV9dFZpW4Q2lOc3mxXPGyULJ+DkfmV/GVWq/wAAAAAAG8iMAAAAAAAAARb////7AAAAAAAbz04AAAAAAAABfQEAAAACAAAACwAAAABkBgSHAAAAAGQGBIcAAAAAZAYEhwAAAAAAG8hzAAAAAAAAAS8AAAAAZAYEhD3tPwvP5dgslVQroHu37+dlyxeoTqcDWIDDMUz5Y0KBMhuk1gj6dbp21tc9qnFavL3rnboCJX8FobWReLSfWZsAAAAAACBQdgAAAAAAAANS////+wAAAAAAID+JAAAAAAAAA00BAAAAAgAAAAsAAAAAZAYEhwAAAABkBgSHAAAAAGQGBIYAAAAAACBQKwAAAAAAAAOdAAAAAGQGBIRPu3srgFy1FVEoeBBWS6f74PhYsJvdbggVRmGyUzFy1DChkVj1pUwK34+3VgYnND8iobyFK4nVa+GszcXb+W0OAAAAAAAcQ44AAAAAAAAAhP////0AAAAAABxBSQAAAAAAAACXAQAAAAIAAAATAAAAAGQGBIcAAAAAZAYEhwAAAABkBgSGAAAAAAAcQ28AAAAAAAAAZgAAAABkBgSE"

target_chains/solana/Cargo.lock

Lines changed: 101 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/solana/Makefile

Lines changed: 0 additions & 4 deletions
This file was deleted.

target_chains/solana/README.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
1-
# pythnet-solana-receiver
1+
# Solana program for receiving price VAA from Pythnet
22

3-
To compile the cli, run `make ccli`.
3+
The program under `cli` receives a VAA string from the shell, verifies the VAA with wormhole, posts the VAA on solana and then invokes the receiver program under `programs`.
4+
The receiver program verifies that the VAA comes from wormhole (through the `owner` function in `state.rs`) and deserializes the price information (in `decode_posted_vaa` function of `lib.rs`).
45

5-
To test post_vaa of the cli, run `make post_vaa`.
6+
```shell
7+
# Generate the program key
8+
# and use the key to replace the following two places
9+
# "pyth_solana_receiver" in Anchor.toml
10+
# "declare_id!()" in programs/solana-receiver/src/lib.rs
11+
> solana-keygen new -o program_address.json
12+
13+
# Build and deploy the receiver program
14+
> anchor build
15+
> anchor run deploy
16+
17+
# Build and test the cli program
18+
> anchor run cli_build
19+
> anchor run cli_test
20+
# Example output
21+
...
22+
[1/5] Decode the VAA
23+
[2/5] Get wormhole guardian set configuration
24+
[3/5] Invoke wormhole on solana to verify the VAA
25+
Transaction successful : 3VbrqQBCf1RsNLxrcvxN3aTb5fZRht4n8XDUVPM8NKniRmo84NZQUu5iFw5groAQgQYox3YCqaMjKc2WTpPU1yqV
26+
[4/5] Post the VAA data onto a solana account
27+
Transaction successful : 3L1vxzSHQv6B6TwtoMv2Y6m7vFGz3hzqApGHEhHSLA9Jn5dNKeWRWKv29UDPDc3vsgt1mYueamUPPt6bHGGEkbxh
28+
[5/5] Receive and deserialize the VAA on solana
29+
Receiver program ID is 5dXnHcDXdXaiEp9QgknCDPsEhJStSqZqJ4ATirWfEqeY
30+
Transaction successful : u5y9Hqc18so3BnjSUvZkLZR4mvA8zkiBgzGKHSEYyWkHQhH3uQatM7xWf4kdrhjZFVGbfBLdR8RJJUmuf28ePtG
31+
```

target_chains/solana/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ solana-sdk = "1.10.31"
1111
solana-client = "1.10.31"
1212
anchor-client = "0.26.0"
1313
clap = {version ="3.2.22", features = ["derive"]}
14+
pyth-solana-receiver = {path = "../programs/solana-receiver"}
1415
wormhole-core = { git = "https://github.com/guibescos/wormhole", branch = "reisen/sdk-solana"}
1516
wormhole-solana = { git = "https://github.com/guibescos/wormhole", branch = "reisen/sdk-solana"}

target_chains/solana/cli/src/cli.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//! CLI options
21
use {
32
clap::{
43
Parser,
@@ -18,26 +17,17 @@ pub struct Cli {
1817

1918
#[derive(Subcommand, Debug)]
2019
pub enum Action {
21-
#[clap(about = "Verify and post the price VAA on solana")]
22-
PostPriceVAA {
20+
#[clap(about = "Verify, post and receive the price VAA on solana")]
21+
PostAndReceiveVAA {
2322
#[clap(short = 'v', long,
2423
help = "Price VAA from Pythnet")]
2524
vaa: String,
2625
#[clap(
2726
short = 'k', long,
2827
default_value = "~/.config/solana/id.json",
29-
help = "Keypair of the transaction's funder"
28+
help = "Keypair of the payer of transactions"
3029
)]
3130
keypair: String,
3231
},
3332

34-
#[clap(about = "Invoke the on-chain contract decoding the VAA")]
35-
InvokePriceReceiver {
36-
#[clap(
37-
short = 'k', long,
38-
default_value = "~/.config/solana/id.json",
39-
help = "Keypair of the transaction's funder"
40-
)]
41-
keypair: String,
42-
},
4333
}

target_chains/solana/cli/src/main.rs

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ use {
77
},
88
clap::Parser,
99
anyhow::Result,
10-
std::str::FromStr,
1110

1211
solana_sdk::{
13-
pubkey::Pubkey,
1412
signature::{
1513
read_keypair_file,
1614
Keypair,
@@ -22,36 +20,47 @@ use {
2220

2321
wormhole::VAA,
2422
wormhole_solana::{
25-
Account,
26-
GuardianSet,
27-
Config as WormholeConfig,
2823
instructions::{
2924
post_vaa,
3025
verify_signatures_txs,
3126
PostVAAData,
3227
},
28+
Account,
29+
GuardianSet,
30+
Config as WormholeConfig,
31+
VAA as WormholeSolanaVAA,
32+
},
33+
34+
pyth_solana_receiver::{
35+
ID,
36+
state::AnchorVaa,
37+
accounts::DecodePostedVaa,
38+
},
39+
40+
anchor_client::anchor_lang::{
41+
Owner,
42+
ToAccountMetas,
43+
InstructionData,
44+
AnchorDeserialize,
3345
},
3446

3547
solana_client::rpc_client::RpcClient,
36-
anchor_client::anchor_lang::AnchorDeserialize,
3748
};
3849

3950
fn main() -> Result<()> {
4051
let cli = Cli::parse();
4152

4253
match cli.action {
43-
Action::PostPriceVAA { vaa, keypair } => {
44-
println!("PostPriceVAA is invoked with vaa\"{}\"", vaa);
45-
// Hard-coded strings
54+
Action::PostAndReceiveVAA { vaa, keypair } => {
55+
let wormhole = AnchorVaa::owner();
4656
let rpc_client = RpcClient::new("https://api.devnet.solana.com");
47-
// Is RpcClient::new_with_commitment necessary?
48-
let wormhole = Pubkey::from_str("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5").unwrap();
4957

50-
println!("Decode the VAA");
58+
println!("[1/5] Decode the VAA");
5159
let vaa_bytes: Vec<u8> = base64::decode(vaa)?;
5260
let vaa = VAA::from_bytes(vaa_bytes.clone())?;
61+
let posted_vaa_key = WormholeSolanaVAA::key(&wormhole, vaa.digest().unwrap().hash);
5362

54-
println!("Get wormhole guardian set configuration");
63+
println!("[2/5] Get wormhole guardian set configuration");
5564
let wormhole_config = WormholeConfig::key(&wormhole, ());
5665
let wormhole_config_data =
5766
WormholeConfig::try_from_slice(&rpc_client.get_account_data(&wormhole_config)?)?;
@@ -60,7 +69,7 @@ fn main() -> Result<()> {
6069
let guardian_set_data =
6170
GuardianSet::try_from_slice(&rpc_client.get_account_data(&guardian_set)?)?;
6271

63-
println!("Invoke wormhole on solana to verify the VAA");
72+
println!("[3/5] Invoke wormhole on solana to verify the VAA");
6473
let payer =
6574
read_keypair_file(&*shellexpand::tilde(&keypair)).expect("Keypair not found");
6675
let signature_set_keypair = Keypair::new();
@@ -77,7 +86,7 @@ fn main() -> Result<()> {
7786
process_transaction(&rpc_client, tx, &vec![&payer, &signature_set_keypair])?;
7887
}
7988

80-
println!("Upload the VAA data to a solana account");
89+
println!("[4/5] Post the VAA data onto a solana account");
8190
let post_vaa_data = PostVAAData {
8291
version: vaa.version,
8392
guardian_set_index: vaa.guardian_set_index,
@@ -100,10 +109,23 @@ fn main() -> Result<()> {
100109
)?],
101110
&vec![&payer],
102111
)?;
103-
}
104112

105-
Action::InvokePriceReceiver { keypair } => {
106-
println!("TBD, keypair={}", keypair);
113+
println!("[5/5] Receive and deserialize the VAA on solana");
114+
let account_metas = DecodePostedVaa::populate(&payer.pubkey(), &posted_vaa_key)
115+
.to_account_metas(None);
116+
117+
println!("Receiver program ID is {}", ID);
118+
let invoke_receiver_instruction = Instruction {
119+
program_id: ID,
120+
accounts: account_metas,
121+
data: pyth_solana_receiver::instruction::DecodePostedVaa.data(),
122+
};
123+
124+
process_transaction(
125+
&rpc_client,
126+
vec![invoke_receiver_instruction],
127+
&vec![&payer],
128+
)?;
107129
}
108130
}
109131

@@ -118,8 +140,10 @@ pub fn process_transaction(
118140
let mut transaction =
119141
Transaction::new_with_payer(instructions.as_slice(), Some(&signers[0].pubkey()));
120142
transaction.sign(signers, rpc_client.get_latest_blockhash()?);
143+
121144
let transaction_signature =
122145
rpc_client.send_and_confirm_transaction_with_spinner(&transaction)?;
123146
println!("Transaction successful : {transaction_signature:?}");
147+
124148
Ok(())
125149
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[
2+
60, 149, 65, 82, 222, 44, 5, 183, 190, 133, 174, 157, 116, 169, 223, 187, 176,
3+
116, 180, 249, 122, 201, 107, 232, 238, 112, 238, 241, 8, 143, 97, 41, 12, 74,
4+
160, 14, 126, 203, 129, 160, 107, 74, 220, 154, 139, 58, 58, 125, 87, 41, 190,
5+
231, 142, 176, 98, 77, 107, 78, 193, 20, 114, 34, 71, 146
6+
]

0 commit comments

Comments
 (0)