restaking_programs/
lib.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{self, Burn, Mint, MintTo, Token, TokenAccount, Transfer};
3
4// This is your program's public key and it will update
5// automatically when you build the project.
6declare_id!("6UqcKJ3U7zfr5JkhzjDZQsunbJeFBxgCKYbuMw8Scv6B");
7
8#[program]
9pub mod restaking_programs{
10    use super::*;
11
12    pub fn initialize_state_account(
13        ctx: Context<InitializeStateAccount>,
14        msol_mint: Pubkey,
15        jitoSol_mint: Pubkey,
16        rm_sol_mint: Pubkey,
17        rjito_sol_mint: Pubkey,
18    ) -> Result<()> {
19        let state = &mut ctx.accounts.state;
20        state.authority = ctx.accounts.authority.key();
21        state.msol_mint = msol_mint;
22        state.jitosol_mint = jitoSol_mint;
23        state.rm_sol_mint = rm_sol_mint;
24        state.rjito_sol_mint = rjito_sol_mint;
25        state.bump = ctx.bumps.state;
26        Ok(())
27    }
28
29    pub fn initialize_vault_account(
30        ctx: Context<InitializeVault>,
31        token_mint: Pubkey,
32    ) -> Result<()> {
33        let vault_account = &mut ctx.accounts.vault_account;
34        vault_account.token_mint = token_mint;
35        vault_account.vault = ctx.accounts.vault.key();
36        vault_account.bump = ctx.bumps.vault_account;
37        vault_account.total_deposited = 0;
38        Ok(())
39    }
40
41    pub fn initialize_mint_account(
42        ctx: Context<InitializeMintAccount>,
43        base_mint: Pubkey,
44        restaked_mint: Pubkey,
45    ) -> Result<()> {
46        let mint_account = &mut ctx.accounts.mint_account;
47        let vault_account = &ctx.accounts.vault_account;
48
49        mint_account.base_mint = base_mint;
50        mint_account.restaked_mint = restaked_mint;
51        mint_account.vault = vault_account.key();
52        mint_account.bump = ctx.bumps.mint_account;
53        mint_account.total_minted = 0;
54        mint_account.exchange_rate = 1_000_000_000;
55        mint_account.last_update_slot = Clock::get()?.slot;
56
57        Ok(())
58    }
59
60    pub fn restake(ctx: Context<Restake>, amount: u64) -> Result<()> {
61        let vault_account = &mut ctx.accounts.vault_account;
62        let mint_account = &mut ctx.accounts.mint_account;
63        let user_account = &mut ctx.accounts.user_restaking_account;
64
65        user_account.bump = ctx.bumps.user_restaking_account;
66
67        let cpi_ctx = CpiContext::new(
68            ctx.accounts.token_program.to_account_info(),
69            Transfer {
70                from: ctx.accounts.user_base_token.to_account_info(),
71                to: ctx.accounts.vault.to_account_info(),
72                authority: ctx.accounts.user.to_account_info(),
73            },
74        );
75        token::transfer(cpi_ctx, amount)?;
76
77        let restaked_amount = amount
78            .checked_mul(1_000_000_000) // scale factor
79            .unwrap()
80            .checked_div(mint_account.exchange_rate)
81            .unwrap();
82
83        let seeds = &[
84            b"mint_account",
85            mint_account.base_mint.as_ref(),
86            &[mint_account.bump],
87        ];
88        let signer = &[&seeds[..]];
89
90        let cpi_ctx_mint = CpiContext::new_with_signer(
91            ctx.accounts.token_program.to_account_info(),
92            MintTo {
93                mint: ctx.accounts.restaked_mint.to_account_info(),
94                to: ctx.accounts.user_restaked_token.to_account_info(),
95                authority: mint_account.to_account_info(),
96            },
97            signer,
98        );
99        token::mint_to(cpi_ctx_mint, restaked_amount)?;
100
101        vault_account.total_deposited = vault_account.total_deposited.checked_add(amount).unwrap();
102
103        mint_account.total_minted = mint_account
104            .total_minted
105            .checked_add(restaked_amount)
106            .unwrap();
107
108        user_account.user = ctx.accounts.user.key();
109        user_account.deposited_mint = mint_account.base_mint;
110        user_account.restaked_mint = mint_account.restaked_mint;
111
112        user_account.deposited_amount = user_account.deposited_amount.checked_add(amount).unwrap();
113
114        user_account.restaked_amount = user_account
115            .restaked_amount
116            .checked_add(restaked_amount)
117            .unwrap();
118
119        Ok(())
120    }
121
122    pub fn request_unstake(ctx: Context<RequestUnstake>, restaked_amount: u64) -> Result<()> {
123        let user_account = &mut ctx.accounts.user_restaking_account;
124        let _mint_account = &ctx.accounts.mint_account;
125
126        let cpi_ctx_burn = CpiContext::new(
127            ctx.accounts.token_program.to_account_info(),
128            Burn {
129                mint: ctx.accounts.restaked_mint.to_account_info(),
130                from: ctx.accounts.user_restaked_token.to_account_info(),
131                authority: ctx.accounts.user.to_account_info(),
132            },
133        );
134        token::burn(cpi_ctx_burn, restaked_amount)?;
135
136        user_account.pending_unstake = restaked_amount;
137        user_account.cooldown_end_slot = Clock::get()?.slot + 500; // e.g. 500 slots (30 secs for demo)
138
139        Ok(())
140    }
141
142  pub fn claim_unstake(ctx: Context<ClaimUnstake>) -> Result<()> {
143        let vault_account = &mut ctx.accounts.vault_account;
144        let mint_account = &mut ctx.accounts.mint_account;
145        let user_account = &mut ctx.accounts.user_restaking_account;
146
147        let current_slot = Clock::get()?.slot;
148        require!(
149            current_slot >= user_account.cooldown_end_slot,
150            CustomError::CooldownNotFinished
151        );
152
153        let restaked_amount = user_account.pending_unstake;
154        require!(restaked_amount > 0, CustomError::NothingToClaim);
155
156        let base_amount = restaked_amount
157            .checked_mul(mint_account.exchange_rate)
158            .unwrap()
159            .checked_div(1_000_000_000)
160            .unwrap();
161
162        let fee = base_amount / 1000;
163        let withdraw_amount = base_amount.checked_sub(fee).unwrap();
164
165        let seeds = &[
166            b"vault_account",
167            mint_account.base_mint.as_ref(),
168            &[vault_account.bump],
169        ];
170        let signer = &[&seeds[..]];
171
172        let cpi_ctx_transfer = CpiContext::new_with_signer(
173            ctx.accounts.token_program.to_account_info(),
174            anchor_spl::token::Transfer {
175                from: ctx.accounts.vault.to_account_info(),
176                to: ctx.accounts.user_base_token.to_account_info(),
177                authority: vault_account.to_account_info(),
178            },
179            signer,
180        );
181        anchor_spl::token::transfer(cpi_ctx_transfer, withdraw_amount)?;
182
183        vault_account.total_deposited = vault_account
184            .total_deposited
185            .checked_sub(base_amount)
186            .unwrap();
187        mint_account.total_minted = mint_account
188            .total_minted
189            .checked_sub(restaked_amount)
190            .unwrap();
191        user_account.pending_unstake = 0;
192        user_account.last_claimed_slot = Clock::get()?.slot;// added because it was missed 
193
194        Ok(())
195    }
196
197    pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()> {
198            let user_account = &mut ctx.accounts.user_restaking_account;
199            let treasury = &mut ctx.accounts.treasury;
200
201            let current_slot = Clock::get()?.slot;
202
203           
204            let elapsed_slots = current_slot
205                .checked_sub(user_account.last_claimed_slot)
206                .unwrap();
207
208            require!(elapsed_slots > 0, CustomError::NothingToClaim);
209
210            let reward_rate: u64 = 1; // can adjust for demo
211            let rewards = elapsed_slots
212                .checked_mul(user_account.restaked_amount)
213                .unwrap()
214                .checked_mul(reward_rate)
215                .unwrap();
216
217            require!(
218                **treasury.to_account_info().lamports.borrow() >= rewards,
219                CustomError::InsufficientTreasuryBalance
220            );
221
222            **treasury.to_account_info().try_borrow_mut_lamports()? -= rewards;
223            **ctx.accounts.user.to_account_info().try_borrow_mut_lamports()? += rewards;
224
225      
226            user_account.last_claimed_slot = current_slot;
227
228            Ok(())
229         }
230
231    pub fn get_cooldown_end_slot(ctx: Context<GetUserData>,) -> Result<u64> {
232                let user_account = &ctx.accounts.user_restaking_account;
233                Ok(user_account.cooldown_end_slot)
234        }
235
236    pub fn get_pending_unstake(ctx: Context<GetUserData>) -> Result<u64> {
237                let user_account = &ctx.accounts.user_restaking_account;
238                let value = user_account.pending_unstake;
239                Ok(value)
240        }
241
242    pub fn get_reward_debt(ctx: Context<GetUserData>,) -> Result<u64> {
243                let user_account = &ctx.accounts.user_restaking_account;
244                let value = user_account.reward_debt;
245                Ok(value)
246        }
247
248
249
250    pub fn initialize_operator(ctx: Context<RegisterOperator>, bond_amount: u64, metadata: String) -> Result<()> {
251            let operator_account = &mut ctx.accounts.operator_account;
252            let vault = &mut ctx.accounts.vault;  // ← Get mutable reference to vault
253
254            require!(bond_amount >= 2_000_000_000, CustomError::NotEnoughToken);
255
256         
257            operator_account.owner = ctx.accounts.operator_key.key();
258            operator_account.bond_amount = bond_amount;
259            operator_account.metadata = metadata;
260            operator_account.active = true;
261            operator_account.avs_count = 0;
262            operator_account.bump = ctx.bumps.operator_account;
263            operator_account.vault_bump = ctx.bumps.vault;
264
265            
266            vault.bump = ctx.bumps.vault;
267
268        
269            let transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
270                &ctx.accounts.operator_key.key(),
271                &ctx.accounts.vault.key(),
272                bond_amount,
273            );
274            
275            anchor_lang::solana_program::program::invoke(
276                &transfer_ix,
277                &[
278                    ctx.accounts.operator_key.to_account_info(),
279                    ctx.accounts.vault.to_account_info(),
280                ],
281            )?;
282
283            Ok(())
284        }
285
286
287    pub fn update_operator_metadata( ctx: Context<UpdateOperatorMetadata>, metadata : String)->Result<()>{
288
289        let operator_account = &mut ctx.accounts.operator_account;
290        require!(operator_account.owner== ctx.accounts.owner.key(), CustomError::Unauthorized);
291
292        operator_account.metadata = metadata;
293
294        Ok(())
295    }
296
297    pub fn de_register_operator(ctx: Context<DeRegisterOperator>) -> Result<()> {
298        require!(
299            ctx.accounts.operator_account.owner == ctx.accounts.operator_key.key(), 
300            CustomError::Unauthorized
301        );
302        
303        let bond_amount = ctx.accounts.operator_account.bond_amount;
304
305        **ctx.accounts.operator_key.to_account_info().try_borrow_mut_lamports()? += bond_amount;
306        **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= bond_amount;
307        
308        let operator_account = &mut ctx.accounts.operator_account;
309        operator_account.active = false;
310        
311        Ok(())
312    }
313    
314    pub fn slash_operator(ctx: Context<SlashOperator>, operator_owner: Pubkey,amount: u64) -> Result<()> {
315            let operator_account = &mut ctx.accounts.operator_account;
316
317            require!(
318                operator_account.bond_amount >= amount, 
319                CustomError::InsufficientBond
320            );
321
322            operator_account.bond_amount = operator_account
323                .bond_amount
324                .checked_sub(amount)
325                .unwrap();
326
327            **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= amount;
328            **ctx.accounts.treasury.to_account_info().try_borrow_mut_lamports()? += amount;
329
330            Ok(())
331        }
332
333    pub fn initialize_reward_treasury(ctx: Context<InitializeRewardTreasury>) -> Result<()> {
334       let treasury = &mut ctx.accounts.treasury;
335       treasury.authority = ctx.accounts.authority.key();
336       treasury.bump = ctx.bumps.treasury;
337       treasury.total_rewards_distributed = 0;
338       Ok(())
339   }
340
341    pub fn register_avs(ctx: Context<RegisterAvs>, metadata:String , registration_fee: u64)->Result<()>{
342        // get AccountInfos (immutable borrows) and perform the transfer before taking mutable borrows
343        let avs_owner_ai = ctx.accounts.avs_owner.to_account_info();
344        let avs_account_ai = ctx.accounts.avs_account.to_account_info();
345
346        require!(registration_fee >= 3_000_000_000, CustomError::NotEnoughToken);
347        
348        let transfer_sol = anchor_lang::solana_program::system_instruction::transfer(
349            &ctx.accounts.avs_owner.key(), 
350            &ctx.accounts.avs_account.key(), 
351            registration_fee,
352        );
353
354        anchor_lang::solana_program::program::invoke(
355            &transfer_sol,
356            &[avs_owner_ai.clone(), avs_account_ai.clone()],
357        )?;
358
359       
360        let avs_account = &mut ctx.accounts.avs_account;
361        let treasury = &mut ctx.accounts.treasury;
362
363        let treasury_share = registration_fee / 2;
364        **avs_account.to_account_info().try_borrow_mut_lamports()? -= treasury_share;
365        **treasury.to_account_info().try_borrow_mut_lamports()? += treasury_share;
366
367        avs_account.owner = ctx.accounts.avs_owner.key();
368        avs_account.metadata = metadata;
369        avs_account.registration_fee = registration_fee;
370        avs_account.active = true;
371        avs_account.registered_slot = Clock::get()?.slot;
372        avs_account.bump = ctx.bumps.avs_account;
373
374        Ok(())
375    }
376
377    pub fn update_avs_metadata(ctx: Context<UpdateAvsMetadata>, metadata: String )-> Result<()>{
378        let avs_account = &mut ctx.accounts.avs_account;
379
380        require!(avs_account.owner == ctx.accounts.avs_owner.key(), CustomError::Unauthorized);
381        avs_account.metadata = metadata;
382        
383        Ok(())
384    }
385    
386
387    pub fn de_register_avs(ctx: Context<DeRegisterAvs>) -> Result<()> {
388            let avs_account = &mut ctx.accounts.avs_account;
389            require!(avs_account.owner == ctx.accounts.avs_owner.key(), CustomError::Unauthorized);
390
391            avs_account.active = false;
392            Ok(())
393    }
394
395    pub fn operator_opt_in_avs(ctx: Context<OptInAvs>, avs_owner: Pubkey ) -> Result<()> {
396            let operator_account = &mut ctx.accounts.operator_account;
397            let operator_avs_reg = &mut ctx.accounts.operator_avs_registration;
398            let avs_account = &ctx.accounts.avs_account;
399
400          
401            require!(
402                operator_account.active,
403                CustomError::OperatorNotActive
404            );
405
406     
407            require!(
408                avs_account.active,
409                CustomError::AvsNotActive
410            );
411
412
413            operator_avs_reg.operator = ctx.accounts.operator_key.key();
414            operator_avs_reg.avs = avs_account.key();  
415            operator_avs_reg.opted_in_slot = Clock::get()?.slot;
416            operator_avs_reg.active = true;
417            operator_avs_reg.tasks_completed = 0;
418            operator_avs_reg.tasks_failed = 0;
419            operator_avs_reg.bump = ctx.bumps.operator_avs_registration;
420
421
422            operator_account.avs_count = operator_account
423                .avs_count
424                .checked_add(1)
425                .unwrap();
426
427            msg!(
428                "✅ Operator {} opted into AVS {} (owner: {})",
429                operator_avs_reg.operator,
430                avs_account.key(),
431                avs_owner
432            );
433
434            Ok(())
435        }
436}
437
438#[derive(Accounts)]
439pub struct InitializeStateAccount<'info> {
440    #[account(mut)]
441    pub authority: Signer<'info>,
442
443    #[account(
444        init,
445        payer = authority,
446        space = 8 + StateAccount::INIT_SPACE,
447        seeds = [b"state"],
448        bump
449    )]
450    pub state: Account<'info, StateAccount>,
451
452    pub system_program: Program<'info, System>,
453}
454
455#[derive(Accounts)]
456#[instruction(token_mint: Pubkey)]
457pub struct InitializeVault<'info> {
458    #[account(mut)]
459    pub authority: Signer<'info>,
460
461    #[account(
462        init,
463        payer = authority,
464        space = 8 + VaultAccount::INIT_SPACE,
465        seeds = [b"vault_account", token_mint.key().as_ref()],
466        bump
467    )]
468    pub vault_account: Account<'info, VaultAccount>,
469
470    #[account(
471        init,
472        payer = authority,
473        token::mint = token_mint,
474        token::authority = vault_account,
475        seeds = [b"vault_token", token_mint.key().as_ref()],
476        bump
477    )]
478    pub vault: Account<'info, TokenAccount>,
479
480    pub token_mint: Account<'info, Mint>,
481    pub system_program: Program<'info, System>,
482    pub token_program: Program<'info, Token>,
483    pub rent: Sysvar<'info, Rent>,
484}
485
486#[derive(Accounts)]
487#[instruction(base_mint: Pubkey, restaked_mint: Pubkey)]
488pub struct InitializeMintAccount<'info> {
489    #[account(mut)]
490    pub authority: Signer<'info>,
491
492    #[account(
493        init,
494        payer = authority,
495        space = 8 + MintAccount::INIT_SPACE,
496        seeds = [b"mint_account", base_mint.key().as_ref()],
497        bump
498    )]
499    pub mint_account: Account<'info, MintAccount>,
500
501    #[account(mut)]
502    pub vault_account: Account<'info, VaultAccount>,
503
504    pub system_program: Program<'info, System>,
505}
506
507#[derive(Accounts)]
508pub struct Restake<'info> {
509    #[account(mut)]
510    pub user: Signer<'info>,
511
512    #[account(mut)]
513    pub user_base_token: Account<'info, TokenAccount>,
514
515    #[account(
516        mut,
517        seeds = [b"vault_account", mint_account.base_mint.as_ref()],
518        bump = vault_account.bump
519    )]
520    pub vault_account: Account<'info, VaultAccount>,
521
522    #[account(mut)]
523    pub vault: Account<'info, TokenAccount>,
524
525    #[account(
526        mut,
527        seeds = [b"mint_account", mint_account.base_mint.as_ref()],
528        bump = mint_account.bump
529    )]
530    pub mint_account: Account<'info, MintAccount>,
531
532    #[account(mut)]
533    pub restaked_mint: Account<'info, Mint>,
534
535    #[account(mut)]
536    pub user_restaked_token: Account<'info, TokenAccount>,
537
538    #[account(
539        init_if_needed,
540        payer = user,
541        space = 8 + UserRestakingAccount::INIT_SPACE,
542        seeds = [b"user_restaking", user.key().as_ref(), mint_account.restaked_mint.as_ref()],
543        bump
544    )]
545    pub user_restaking_account: Account<'info, UserRestakingAccount>,
546
547    pub token_program: Program<'info, Token>,
548    pub system_program: Program<'info, System>,
549}
550
551
552#[derive(Accounts)]
553pub struct RequestUnstake<'info> {
554    #[account(mut)]
555    pub user: Signer<'info>,
556
557    #[account(
558        mut,
559        constraint = user_restaked_token.owner == user.key(),
560        constraint = user_restaked_token.mint == mint_account.restaked_mint
561    )]
562    pub user_restaked_token: Account<'info, TokenAccount>,
563
564    #[account(
565        mut,
566        constraint = user_restaking_account.user == user.key(),
567        constraint = user_restaking_account.restaked_mint == mint_account.restaked_mint
568    )]
569    pub user_restaking_account: Account<'info, UserRestakingAccount>,
570
571    #[account(mut)]
572    pub restaked_mint: Account<'info, Mint>,
573
574    #[account(mut)]
575    pub mint_account: Account<'info, MintAccount>,
576
577    pub token_program: Program<'info, Token>,
578    pub system_program: Program<'info, System>,
579}
580
581#[derive(Accounts)]
582pub struct ClaimUnstake<'info> {
583    #[account(mut)]
584    pub user: Signer<'info>,
585
586    #[account(
587        mut,
588        constraint = user_base_token.owner == user.key(),
589        constraint = user_base_token.mint == mint_account.base_mint
590    )]
591    pub user_base_token: Account<'info, TokenAccount>,
592
593    #[account(
594        mut,
595        constraint = user_restaking_account.user == user.key(),
596        constraint = user_restaking_account.restaked_mint == mint_account.restaked_mint
597    )]
598    pub user_restaking_account: Account<'info, UserRestakingAccount>,
599
600    #[account(mut)]
601    pub vault_account: Account<'info, VaultAccount>,
602
603    #[account(
604        mut,
605        constraint = vault.mint == mint_account.base_mint
606    )]
607    pub vault: Account<'info, TokenAccount>,
608
609    #[account(mut)]
610    pub mint_account: Account<'info, MintAccount>,
611
612    pub token_program: Program<'info, Token>,
613}
614
615
616#[derive(Accounts)]
617pub struct RegisterOperator<'info> {
618    #[account(mut)]
619    pub operator_key: Signer<'info>,
620
621    #[account(
622        init,
623        payer = operator_key,
624        space = 8 + OperatorAccount::INIT_SPACE,
625        seeds = [b"operator", operator_key.key().as_ref()],
626        bump
627    )]
628    pub operator_account: Account<'info, OperatorAccount>,
629
630    #[account(
631        init,  
632        payer = operator_key,
633        space = 8 + OperatorVault::INIT_SPACE,
634        seeds = [b"vault", operator_key.key().as_ref()],
635        bump
636    )]
637    pub vault: Account<'info, OperatorVault>, 
638
639    pub system_program: Program<'info, System>,
640}
641
642
643#[derive(Accounts)]
644pub struct UpdateOperatorMetadata<'info>{
645    #[account(mut)]
646    pub owner: Signer<'info>,
647
648    #[account(
649        mut ,
650        seeds = [b"operator", owner.key().as_ref()],
651        bump = operator_account.bump,
652        has_one = owner @ CustomError::Unauthorized
653    )]
654    pub operator_account: Account<'info , OperatorAccount>
655}
656
657#[derive(Accounts)]
658pub struct DeRegisterOperator<'info> {
659    #[account(mut)]
660    pub operator_key: Signer<'info>,
661
662    #[account(
663        mut,
664        seeds = [b"operator", operator_key.key().as_ref()],
665        bump = operator_account.bump,
666        close = operator_key
667    )]
668    pub operator_account: Account<'info, OperatorAccount>,
669
670    /// Add the vault here too
671    #[account(
672        mut,
673        seeds = [b"vault", operator_key.key().as_ref()],
674        bump = operator_account.vault_bump,
675        close = operator_key  // Also close the vault
676    )]
677    pub vault: Account<'info, OperatorVault>,
678
679    pub system_program: Program<'info, System>,
680}
681#[derive(Accounts)]
682#[instruction(operator_owner: Pubkey)]
683pub struct SlashOperator<'info> {
684    #[account(mut)]
685    pub authority: Signer<'info>, 
686
687   #[account(
688        mut,
689        seeds = [b"operator", operator_owner.as_ref()],  
690        bump = operator_account.bump,
691    )]
692    pub operator_account: Account<'info, OperatorAccount>,
693
694      #[account(
695        mut,
696        seeds = [b"vault", operator_owner.as_ref()], 
697        bump = operator_account.vault_bump
698    )]
699    pub vault:  Account<'info, OperatorVault>,
700
701    #[account(
702        mut,
703        seeds = [b"reward_treasury"],
704        bump = treasury.bump
705    )]
706    pub treasury: Account<'info, RewardTreasury>,
707    
708    pub system_program: Program<'info, System>,
709}
710#[derive(Accounts)]
711pub struct InitializeRewardTreasury<'info> {
712    #[account(mut)]
713    pub authority: Signer<'info>,
714
715    #[account(
716        init,
717        payer = authority,
718        space = 8 + RewardTreasury::INIT_SPACE,
719        seeds = [b"reward_treasury"],
720        bump
721    )]
722    pub treasury: Account<'info, RewardTreasury>,
723
724    pub system_program: Program<'info, System>,
725}
726
727#[derive(Accounts)]
728pub struct ClaimRewards<'info> {
729    #[account(mut)]
730    pub user: Signer<'info>,
731
732    #[account(
733        mut,
734        constraint = user_restaking_account.user == user.key()
735    )]
736    pub user_restaking_account: Account<'info, UserRestakingAccount>,
737
738    #[account(
739        mut,
740        seeds = [b"reward_treasury"],
741        bump = treasury.bump
742    )]
743    pub treasury: Account<'info, RewardTreasury>,
744}
745
746#[derive(Accounts)]
747pub struct RegisterAvs<'info>{
748
749    #[account(mut)]
750    pub avs_owner : Signer<'info>,
751
752    #[account(
753        init ,
754        payer = avs_owner,
755        space = 8 + AvsAccount::INIT_SPACE,
756        seeds = [b"avs" , avs_owner.key().as_ref()],
757        bump
758    )]
759    pub avs_account : Account<'info , AvsAccount>,
760
761    #[account(
762        mut ,
763        seeds = [b"reward_treasury"],
764        bump = treasury.bump
765    )]
766    pub treasury: Account<'info , RewardTreasury>,
767
768    pub system_program : Program<'info , System>
769}
770
771#[derive(Accounts)]
772pub struct UpdateAvsMetadata<'info>{
773    #[account(mut)]
774    pub avs_owner: Signer<'info>,
775
776    #[account(
777        mut ,
778        seeds = [b"avs", avs_owner.key().as_ref()],
779        bump = avs_account.bump,
780    )]
781    pub avs_account: Account<'info , AvsAccount>
782}
783
784#[derive(Accounts)]
785pub struct DeRegisterAvs<'info>{
786    #[account(mut)]
787    pub avs_owner : Signer<'info>,
788
789    #[account(
790        mut,
791        seeds = [b"avs", avs_owner.key().as_ref()],
792        bump = avs_account.bump,
793        close = avs_owner
794    )]
795    pub avs_account : Account<'info , AvsAccount>
796}
797
798#[derive(Accounts)]
799pub struct GetUserData<'info> {
800    pub user: Signer<'info>,
801
802    #[account(
803        seeds = [b"user_restaking", user.key().as_ref(), restaked_mint.key().as_ref()],
804        bump = user_restaking_account.bump,
805        constraint = user_restaking_account.user == user.key()
806    )]
807    pub user_restaking_account: Account<'info, UserRestakingAccount>,
808
809    pub restaked_mint: Account<'info, Mint>,
810}
811
812
813#[derive(Accounts)]
814#[instruction(avs_owner: Pubkey)]  // ← Changed: this is the AVS owner pubkey
815pub struct OptInAvs<'info> {
816    #[account(mut)]
817    pub operator_key: Signer<'info>,
818
819   
820    #[account(
821        mut,
822        seeds = [b"operator", operator_key.key().as_ref()],
823        bump = operator_account.bump,
824        constraint = operator_account.active @ CustomError::OperatorNotActive,
825        constraint = operator_account.owner == operator_key.key() @ CustomError::Unauthorized
826    )]
827    pub operator_account: Account<'info, OperatorAccount>,
828
829    /// The AVS account - derived using AVS owner pubkey (same as RegisterAvs)
830    #[account(
831        seeds = [b"avs", avs_owner.as_ref()],  // ← Fixed: matches your RegisterAvs seeds
832        bump = avs_account.bump,
833        constraint = avs_account.active @ CustomError::AvsNotActive,
834        constraint = avs_account.owner == avs_owner @ CustomError::Unauthorized
835    )]
836    pub avs_account: Account<'info, AvsAccount>,
837
838    /// The registration account linking operator to AVS
839    #[account(
840        init,
841        payer = operator_key,
842        space = 8 + OperatorAvsRegistration::INIT_SPACE,
843        seeds = [
844            b"operator_avs",
845            operator_key.key().as_ref(),
846            avs_owner.as_ref()  
847        ],
848        bump
849    )]
850    pub operator_avs_registration: Account<'info, OperatorAvsRegistration>,
851
852    pub system_program: Program<'info, System>,
853}
854
855
856#[account]
857#[derive(InitSpace, Debug)]
858pub struct StateAccount {
859    pub authority: Pubkey,
860    pub msol_mint: Pubkey,
861    pub jitosol_mint: Pubkey,
862    pub rm_sol_mint: Pubkey,
863    pub rjito_sol_mint: Pubkey,
864    pub bump: u8,
865}
866
867#[account]
868#[derive(InitSpace, Debug)]
869pub struct VaultAccount {
870    pub token_mint: Pubkey,
871    pub vault: Pubkey,
872    pub bump: u8,
873    pub total_deposited: u64,
874}
875
876#[account]
877#[derive(InitSpace, Debug)]
878pub struct MintAccount {
879    pub base_mint: Pubkey,
880    pub restaked_mint: Pubkey,
881    pub vault: Pubkey,
882    pub bump: u8,
883    pub total_minted: u64,
884    pub exchange_rate: u64,
885    pub last_update_slot: u64,
886}
887#[account]
888#[derive(InitSpace, Debug)]
889pub struct UserRestakingAccount {
890    pub user: Pubkey,
891    pub deposited_mint: Pubkey,
892    pub restaked_mint: Pubkey,
893    pub deposited_amount: u64,
894    pub restaked_amount: u64,
895    pub bump: u8,
896    pub cooldown_end_slot: u64,
897    pub pending_unstake: u64,
898    pub reward_debt: u64,
899    pub last_claimed_slot: u64,
900}
901
902#[account]
903#[derive(InitSpace, Debug)]
904pub struct OperatorAccount {
905    pub owner: Pubkey,
906    pub bond_amount: u64,
907    #[max_len(100)]
908    pub metadata: String,
909    pub active: bool,
910    pub avs_count: u32,
911    pub bump: u8,
912    pub vault_bump: u8, 
913}
914
915#[account]
916#[derive(InitSpace, Debug)]
917pub struct OperatorVault {
918    pub bump: u8,
919}
920
921
922#[account]
923#[derive(InitSpace, Debug)]
924pub struct RewardTreasury {
925    pub authority: Pubkey,
926    pub bump: u8,
927    pub total_rewards_distributed: u64,
928}
929
930
931#[account]
932#[derive(InitSpace, Debug)]
933pub struct AvsAccount{
934    pub owner: Pubkey,
935    #[max_len(100)]
936    pub metadata: String,
937    pub registration_fee : u64,
938    pub active : bool,
939    pub slashing_policy : Pubkey,
940    pub registered_slot : u64,
941    pub bump: u8,
942}
943
944pub struct SlashingPolicy {
945    pub misbehavior_type: u8,  // 0 = downtime, 1 = invalid signature, etc.
946    pub penalty_percent: u8,
947}
948
949#[account]
950#[derive(InitSpace, Debug)]
951pub struct OperatorAvsRegistration {
952    pub operator: Pubkey,
953    pub avs: Pubkey,
954    pub opted_in_slot: u64,
955    pub active: bool,
956    pub tasks_completed: u64,
957    pub tasks_failed: u64,
958    pub bump: u8,
959}
960
961#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
962pub struct UserUnstakeInfo {
963    pub cooldown_end_slot: u64,
964    pub pending_unstake: u64,
965    pub reward_debt: u64,
966}
967
968
969#[error_code]
970pub enum CustomError {
971    #[msg("Cooldown not finished yet")]
972    CooldownNotFinished,
973    #[msg("No pending unstake to claim")]
974    NothingToClaim,
975    #[msg("Not enough tokens supplied")]
976    NotEnoughToken,
977    #[msg("Unauthorized action")]
978    Unauthorized,
979    #[msg("Insufficient bond to slash")]
980    InsufficientBond,
981    #[msg("Not enough lamports in treasury")]
982    InsufficientTreasuryBalance,
983    #[msg("Avs not active to opt")]
984    AvsNotActive,
985    #[msg("Operator not active to run nodes")]
986    OperatorNotActive,
987    #[msg("Operator not opted into this AVS")]
988    OperatorNotOptedIn,
989    #[msg("Task not completed")]
990    TaskNotCompleted,
991    #[msg("Challenge already resolved")]
992    ChallengeAlreadyResolved,
993}