Skip to content

Commit e399a03

Browse files
authored
feat(lazer): add solana contract migration script, add message parsing to protocol (#2181)
* feat(lazer): add solana contract migration script, add message parsing to protocol * test(lazer): add ser/de tests
1 parent d5b15f1 commit e399a03

File tree

7 files changed

+194
-4
lines changed

7 files changed

+194
-4
lines changed

lazer/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lazer/contracts/solana/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"test:format": "prettier --check **/*.*",
88
"test:anchor": "CARGO_TARGET_DIR=\"$PWD/target\" anchor test",
99
"test": "pnpm run test:format && pnpm run test:anchor",
10-
"setup": "anchor build && pnpm ts-node scripts/setup.ts"
10+
"setup": "anchor build && pnpm ts-node scripts/setup.ts",
11+
"migrate_from_0_1_0": "pnpm ts-node scripts/migrate_from_0_1_0.ts"
1112
},
1213
"dependencies": {
1314
"@coral-xyz/anchor": "^0.30.1"

lazer/contracts/solana/programs/pyth-lazer-solana-contract/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ no-log-ix-name = []
1919
idl-build = ["anchor-lang/idl-build"]
2020

2121
[dependencies]
22-
pyth-lazer-protocol = { version = "0.1.0", path = "../../../../sdk/rust/protocol" }
22+
pyth-lazer-protocol = { version = "0.1.2", path = "../../../../sdk/rust/protocol" }
2323

2424
anchor-lang = "0.30.1"
2525
bytemuck = "1.20.0"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as anchor from "@coral-xyz/anchor";
2+
import { Program } from "@coral-xyz/anchor";
3+
import { PythLazerSolanaContract } from "../target/types/pyth_lazer_solana_contract";
4+
import * as pythLazerSolanaContractIdl from "../target/idl/pyth_lazer_solana_contract.json";
5+
import yargs from "yargs/yargs";
6+
import { readFileSync } from "fs";
7+
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
8+
9+
// This script tops up the storage PDA and calls `migrateFrom010` on the contract.
10+
async function main() {
11+
let argv = await yargs(process.argv.slice(2))
12+
.options({
13+
url: { type: "string", demandOption: true },
14+
"keypair-path": { type: "string", demandOption: true },
15+
treasury: { type: "string", demandOption: true },
16+
})
17+
.parse();
18+
19+
const keypair = anchor.web3.Keypair.fromSecretKey(
20+
new Uint8Array(JSON.parse(readFileSync(argv.keypairPath, "ascii")))
21+
);
22+
const wallet = new NodeWallet(keypair);
23+
const connection = new anchor.web3.Connection(argv.url, {
24+
commitment: "confirmed",
25+
});
26+
const provider = new anchor.AnchorProvider(connection, wallet);
27+
28+
const program: Program<PythLazerSolanaContract> = new Program(
29+
pythLazerSolanaContractIdl as PythLazerSolanaContract,
30+
provider
31+
);
32+
33+
const storagePdaKey = new anchor.web3.PublicKey(
34+
"3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL"
35+
);
36+
const storagePdaInfo = await provider.connection.getAccountInfo(
37+
storagePdaKey
38+
);
39+
const newStorageSize = 381;
40+
if (storagePdaInfo.data.length == newStorageSize) {
41+
console.log("Already migrated");
42+
const storage = await program.account.storage.all();
43+
console.log("storage account: ", storage);
44+
return;
45+
}
46+
const minBalance =
47+
await provider.connection.getMinimumBalanceForRentExemption(newStorageSize);
48+
if (storagePdaInfo.lamports < minBalance) {
49+
console.log("storage PDA needs top-up");
50+
const transaction = new anchor.web3.Transaction().add(
51+
anchor.web3.SystemProgram.transfer({
52+
fromPubkey: keypair.publicKey,
53+
toPubkey: storagePdaKey,
54+
lamports: minBalance - storagePdaInfo.lamports,
55+
})
56+
);
57+
const signature = await anchor.web3.sendAndConfirmTransaction(
58+
provider.connection,
59+
transaction,
60+
[keypair]
61+
);
62+
console.log("signature:", signature);
63+
} else {
64+
console.log("storage PDA doesn't need top-up");
65+
}
66+
67+
console.log("executing migration");
68+
const signature2 = await program.methods
69+
.migrateFrom010(new anchor.web3.PublicKey(argv.treasury))
70+
.accounts({})
71+
.rpc();
72+
console.log("signature:", signature2);
73+
}
74+
75+
main();

lazer/sdk/rust/protocol/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pyth-lazer-protocol"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
description = "Pyth Lazer SDK - protocol types."
66
license = "Apache-2.0"

lazer/sdk/rust/protocol/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Protocol types.
22
3+
pub mod message;
34
pub mod payload;
45
pub mod publisher;
56
pub mod router;
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use {
2+
crate::payload::{EVM_FORMAT_MAGIC, SOLANA_FORMAT_MAGIC_LE},
3+
anyhow::bail,
4+
byteorder::{ReadBytesExt, WriteBytesExt, BE, LE},
5+
std::io::{Cursor, Read, Write},
6+
};
7+
8+
/// EVM signature enveope.
9+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10+
pub struct EvmMessage {
11+
pub payload: Vec<u8>,
12+
pub signature: [u8; 64],
13+
pub recovery_id: u8,
14+
}
15+
16+
impl EvmMessage {
17+
pub fn serialize(&self, mut writer: impl Write) -> anyhow::Result<()> {
18+
writer.write_u32::<BE>(EVM_FORMAT_MAGIC)?;
19+
writer.write_all(&self.signature)?;
20+
writer.write_u8(self.recovery_id)?;
21+
writer.write_u16::<BE>(self.payload.len().try_into()?)?;
22+
writer.write_all(&self.payload)?;
23+
Ok(())
24+
}
25+
26+
pub fn deserialize_slice(data: &[u8]) -> anyhow::Result<Self> {
27+
Self::deserialize(Cursor::new(data))
28+
}
29+
30+
pub fn deserialize(mut reader: impl Read) -> anyhow::Result<Self> {
31+
let magic = reader.read_u32::<BE>()?;
32+
if magic != EVM_FORMAT_MAGIC {
33+
bail!("magic mismatch");
34+
}
35+
let mut signature = [0u8; 64];
36+
reader.read_exact(&mut signature)?;
37+
let recovery_id = reader.read_u8()?;
38+
let payload_len: usize = reader.read_u16::<BE>()?.into();
39+
let mut payload = vec![0u8; payload_len];
40+
reader.read_exact(&mut payload)?;
41+
Ok(Self {
42+
payload,
43+
signature,
44+
recovery_id,
45+
})
46+
}
47+
}
48+
49+
/// Solana signature envelope.
50+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51+
pub struct SolanaMessage {
52+
pub payload: Vec<u8>,
53+
pub signature: [u8; 64],
54+
pub public_key: [u8; 32],
55+
}
56+
57+
impl SolanaMessage {
58+
pub fn serialize(&self, mut writer: impl Write) -> anyhow::Result<()> {
59+
writer.write_u32::<LE>(SOLANA_FORMAT_MAGIC_LE)?;
60+
writer.write_all(&self.signature)?;
61+
writer.write_all(&self.public_key)?;
62+
writer.write_u16::<LE>(self.payload.len().try_into()?)?;
63+
writer.write_all(&self.payload)?;
64+
Ok(())
65+
}
66+
67+
pub fn deserialize_slice(data: &[u8]) -> anyhow::Result<Self> {
68+
Self::deserialize(Cursor::new(data))
69+
}
70+
71+
pub fn deserialize(mut reader: impl Read) -> anyhow::Result<Self> {
72+
let magic = reader.read_u32::<LE>()?;
73+
if magic != SOLANA_FORMAT_MAGIC_LE {
74+
bail!("magic mismatch");
75+
}
76+
let mut signature = [0u8; 64];
77+
reader.read_exact(&mut signature)?;
78+
let mut public_key = [0u8; 32];
79+
reader.read_exact(&mut public_key)?;
80+
let payload_len: usize = reader.read_u16::<LE>()?.into();
81+
let mut payload = vec![0u8; payload_len];
82+
reader.read_exact(&mut payload)?;
83+
Ok(Self {
84+
payload,
85+
signature,
86+
public_key,
87+
})
88+
}
89+
}
90+
91+
#[test]
92+
fn test_evm_serde() {
93+
let m1 = EvmMessage {
94+
payload: vec![1, 2, 4, 3],
95+
signature: [5; 64],
96+
recovery_id: 1,
97+
};
98+
let mut buf = Vec::new();
99+
m1.serialize(&mut buf).unwrap();
100+
assert_eq!(m1, EvmMessage::deserialize_slice(&buf).unwrap());
101+
}
102+
103+
#[test]
104+
fn test_solana_serde() {
105+
let m1 = SolanaMessage {
106+
payload: vec![1, 2, 4, 3],
107+
signature: [5; 64],
108+
public_key: [6; 32],
109+
};
110+
let mut buf = Vec::new();
111+
m1.serialize(&mut buf).unwrap();
112+
assert_eq!(m1, SolanaMessage::deserialize_slice(&buf).unwrap());
113+
}

0 commit comments

Comments
 (0)