Skip to content

Commit 064db89

Browse files
committed
feat(lazer): make treasury configurable, add migration
1 parent be3e12c commit 064db89

File tree

5 files changed

+310
-124
lines changed

5 files changed

+310
-124
lines changed

lazer/contracts/solana/programs/pyth-lazer-solana-contract/src/lib.rs

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod signature;
33
use {
44
crate::signature::VerifiedMessage,
55
anchor_lang::{prelude::*, solana_program::pubkey::PUBKEY_BYTES, system_program},
6+
std::io::Cursor,
67
std::mem::size_of,
78
};
89

@@ -14,18 +15,13 @@ pub use {
1415
declare_id!("pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt");
1516

1617
pub const STORAGE_ID: Pubkey = pubkey!("3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL");
17-
pub const TREASURY_ID: Pubkey = pubkey!("EN4aB3soE5iuCG2fGj2r5fksh4kLRVPV8g7N86vXm8WM");
1818

1919
#[test]
2020
fn test_ids() {
2121
assert_eq!(
2222
Pubkey::find_program_address(&[STORAGE_SEED], &ID).0,
2323
STORAGE_ID
2424
);
25-
assert_eq!(
26-
Pubkey::find_program_address(&[TREASURY_SEED], &ID).0,
27-
TREASURY_ID
28-
);
2925
}
3026

3127
pub const MAX_NUM_TRUSTED_SIGNERS: usize = 2;
@@ -41,17 +37,35 @@ impl TrustedSignerInfo {
4137
}
4238

4339
#[account]
44-
pub struct Storage {
40+
pub struct StorageV1 {
4541
pub top_authority: Pubkey,
4642
pub num_trusted_signers: u8,
47-
pub single_update_fee_in_lamports: u64,
4843
pub trusted_signers: [TrustedSignerInfo; MAX_NUM_TRUSTED_SIGNERS],
4944
}
5045

51-
impl Storage {
46+
impl StorageV1 {
5247
const SERIALIZED_LEN: usize = PUBKEY_BYTES
5348
+ size_of::<u8>()
49+
+ TrustedSignerInfo::SERIALIZED_LEN * MAX_NUM_TRUSTED_SIGNERS;
50+
51+
pub fn initialized_trusted_signers(&self) -> &[TrustedSignerInfo] {
52+
&self.trusted_signers[0..usize::from(self.num_trusted_signers)]
53+
}
54+
}
55+
#[account]
56+
pub struct StorageV2 {
57+
pub top_authority: Pubkey,
58+
pub treasury: Pubkey,
59+
pub single_update_fee_in_lamports: u64,
60+
pub num_trusted_signers: u8,
61+
pub trusted_signers: [TrustedSignerInfo; MAX_NUM_TRUSTED_SIGNERS],
62+
}
63+
64+
impl StorageV2 {
65+
const SERIALIZED_LEN: usize = PUBKEY_BYTES
66+
+ PUBKEY_BYTES
5467
+ size_of::<u64>()
68+
+ size_of::<u8>()
5569
+ TrustedSignerInfo::SERIALIZED_LEN * MAX_NUM_TRUSTED_SIGNERS;
5670

5771
pub fn initialized_trusted_signers(&self) -> &[TrustedSignerInfo] {
@@ -60,18 +74,53 @@ impl Storage {
6074
}
6175

6276
pub const STORAGE_SEED: &[u8] = b"storage";
63-
pub const TREASURY_SEED: &[u8] = b"treasury";
6477

6578
#[program]
6679
pub mod pyth_lazer_solana_contract {
6780
use super::*;
6881

69-
pub fn initialize(ctx: Context<Initialize>, top_authority: Pubkey) -> Result<()> {
82+
pub fn initialize_v1(ctx: Context<InitializeV1>, top_authority: Pubkey) -> Result<()> {
7083
ctx.accounts.storage.top_authority = top_authority;
84+
Ok(())
85+
}
86+
87+
pub fn initialize_v2(
88+
ctx: Context<InitializeV2>,
89+
top_authority: Pubkey,
90+
treasury: Pubkey,
91+
) -> Result<()> {
92+
ctx.accounts.storage.top_authority = top_authority;
93+
ctx.accounts.storage.treasury = treasury;
7194
ctx.accounts.storage.single_update_fee_in_lamports = 1;
7295
Ok(())
7396
}
7497

98+
pub fn migrate_to_storage_v2(ctx: Context<MigrateToStorageV2>, treasury: Pubkey) -> Result<()> {
99+
let old_storage = StorageV1::try_deserialize(&mut &**ctx.accounts.storage.data.borrow())?;
100+
if old_storage.top_authority != ctx.accounts.top_authority.key() {
101+
return Err(ProgramError::MissingRequiredSignature.into());
102+
}
103+
104+
let space = 8 + StorageV2::SERIALIZED_LEN;
105+
ctx.accounts.storage.realloc(space, false)?;
106+
let min_lamports = Rent::get()?.minimum_balance(space);
107+
if ctx.accounts.storage.lamports() < min_lamports {
108+
return Err(ProgramError::AccountNotRentExempt.into());
109+
}
110+
111+
let new_storage = StorageV2 {
112+
top_authority: old_storage.top_authority,
113+
treasury,
114+
single_update_fee_in_lamports: 1,
115+
num_trusted_signers: old_storage.num_trusted_signers,
116+
trusted_signers: old_storage.trusted_signers,
117+
};
118+
new_storage.try_serialize(&mut Cursor::new(
119+
&mut **ctx.accounts.storage.data.borrow_mut(),
120+
))?;
121+
Ok(())
122+
}
123+
75124
pub fn update(ctx: Context<Update>, trusted_signer: Pubkey, expires_at: i64) -> Result<()> {
76125
let num_trusted_signers: usize = ctx.accounts.storage.num_trusted_signers.into();
77126
if num_trusted_signers > ctx.accounts.storage.trusted_signers.len() {
@@ -157,29 +206,45 @@ pub mod pyth_lazer_solana_contract {
157206
}
158207

159208
#[derive(Accounts)]
160-
pub struct Initialize<'info> {
209+
pub struct InitializeV1<'info> {
161210
#[account(mut)]
162211
pub payer: Signer<'info>,
163212
#[account(
164213
init,
165214
payer = payer,
166-
space = 8 + Storage::SERIALIZED_LEN,
215+
space = 8 + StorageV1::SERIALIZED_LEN,
167216
seeds = [STORAGE_SEED],
168217
bump,
169218
)]
170-
pub storage: Account<'info, Storage>,
219+
pub storage: Account<'info, StorageV1>,
220+
pub system_program: Program<'info, System>,
221+
}
222+
223+
#[derive(Accounts)]
224+
pub struct MigrateToStorageV2<'info> {
225+
pub top_authority: Signer<'info>,
226+
#[account(
227+
mut,
228+
seeds = [STORAGE_SEED],
229+
bump,
230+
)]
231+
/// CHECK: top_authority in storage must match top_authority account.
232+
pub storage: AccountInfo<'info>,
233+
pub system_program: Program<'info, System>,
234+
}
235+
236+
#[derive(Accounts)]
237+
pub struct InitializeV2<'info> {
238+
#[account(mut)]
239+
pub payer: Signer<'info>,
171240
#[account(
172241
init,
173242
payer = payer,
174-
space = 0,
175-
owner = system_program::ID,
176-
seeds = [TREASURY_SEED],
243+
space = 8 + StorageV2::SERIALIZED_LEN,
244+
seeds = [STORAGE_SEED],
177245
bump,
178246
)]
179-
/// CHECK: this is a system program account but using anchor's `SystemAccount`
180-
/// results in invalid output from the Accounts proc macro. No extra checks
181-
/// are necessary because all necessary constraints are specified in the attribute.
182-
pub treasury: AccountInfo<'info>,
247+
pub storage: Account<'info, StorageV2>,
183248
pub system_program: Program<'info, System>,
184249
}
185250

@@ -192,7 +257,7 @@ pub struct Update<'info> {
192257
bump,
193258
has_one = top_authority,
194259
)]
195-
pub storage: Account<'info, Storage>,
260+
pub storage: Account<'info, StorageV2>,
196261
}
197262

198263
#[derive(Accounts)]
@@ -202,17 +267,10 @@ pub struct VerifyMessage<'info> {
202267
#[account(
203268
seeds = [STORAGE_SEED],
204269
bump,
270+
has_one = treasury
205271
)]
206-
pub storage: Account<'info, Storage>,
207-
#[account(
208-
mut,
209-
owner = system_program::ID,
210-
seeds = [TREASURY_SEED],
211-
bump,
212-
)]
213-
/// CHECK: this is a system program account but using anchor's `SystemAccount`
214-
/// results in invalid output from the Accounts proc macro. No extra checks
215-
/// are necessary because all necessary constraints are specified in the attribute.
272+
pub storage: Account<'info, StorageV2>,
273+
/// CHECK: this account doesn't need additional constraints.
216274
pub treasury: AccountInfo<'info>,
217275
pub system_program: Program<'info, System>,
218276
/// CHECK: account ID is checked in Solana SDK during calls

lazer/contracts/solana/programs/pyth-lazer-solana-contract/src/signature.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use {
2-
crate::Storage,
2+
crate::StorageV2,
33
anchor_lang::{
44
prelude::{borsh, AccountInfo, Clock, ProgramError, Pubkey, SolanaSysvar},
55
solana_program::{ed25519_program, pubkey::PUBKEY_BYTES, sysvar},
@@ -158,7 +158,7 @@ impl From<SignatureVerificationError> for anchor_lang::error::Error {
158158
/// - `message_offset` is the offset of the signed message within the
159159
/// input data for the current instruction.
160160
pub fn verify_message(
161-
storage: &Storage,
161+
storage: &StorageV2,
162162
instructions_sysvar: &AccountInfo,
163163
message_data: &[u8],
164164
ed25519_instruction_index: u16,

0 commit comments

Comments
 (0)