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!("2Wvo8b4oF63csMU45z6qHCN9EZ1qV2ifBb3dwnWow6Ub");
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            emit!(RestakeEvent{
120                    user: ctx.accounts.user.key(),
121                    base_mint: mint_account.base_mint,
122                    restaked_mint: mint_account.restaked_mint,
123                    amount_deposited: amount,
124                    restaked_amount,
125                    exchange_rate: mint_account.exchange_rate,
126                });
127
128        Ok(())
129    }
130
131    pub fn request_unstake(ctx: Context<RequestUnstake>, restaked_amount: u64) -> Result<()> {
132        let user_account = &mut ctx.accounts.user_restaking_account;
133        let _mint_account = &ctx.accounts.mint_account;
134
135            require!(restaked_amount > 0, CustomError::InvalidAmount);
136            require!(user_account.restaked_amount >= restaked_amount, CustomError::InsufficientRestakeBalance);
137
138
139        let cpi_ctx_burn = CpiContext::new(
140            ctx.accounts.token_program.to_account_info(),
141            Burn {
142                mint: ctx.accounts.restaked_mint.to_account_info(),
143                from: ctx.accounts.user_restaked_token.to_account_info(),
144                authority: ctx.accounts.user.to_account_info(),
145            },
146        );
147        token::burn(cpi_ctx_burn, restaked_amount)?;
148
149        user_account.pending_unstake = restaked_amount;
150        user_account.cooldown_end_timestamp = Clock::get()?.unix_timestamp + 300; // 300= 5 mins
151
152          emit!(UnstakeRequestedEvent{
153                user: ctx.accounts.user.key(),
154                restaked_mint: ctx.accounts.mint_account.restaked_mint,
155                restaked_amount,
156                cooldown_end_timestamp: user_account.cooldown_end_timestamp ,
157            });
158
159        Ok(())
160    }
161
162    pub fn claim_unstake(ctx: Context<ClaimUnstake>) -> Result<()> {
163        let vault_account = &mut ctx.accounts.vault_account;
164        let mint_account = &mut ctx.accounts.mint_account;
165        let user_account = &mut ctx.accounts.user_restaking_account;
166
167        let current_timestamp = Clock::get()?.unix_timestamp;
168        require!(
169            current_timestamp >= user_account.cooldown_end_timestamp,
170            CustomError::CooldownNotFinished
171        );
172
173        let restaked_amount = user_account.pending_unstake;
174        require!(restaked_amount > 0, CustomError::NothingToClaim);
175
176        let base_amount = restaked_amount
177            .checked_mul(mint_account.exchange_rate)
178            .unwrap()
179            .checked_div(1_000_000_000)
180            .unwrap();
181
182        let fee = base_amount / 1000;
183        let withdraw_amount = base_amount.checked_sub(fee).unwrap();
184
185        let seeds = &[
186            b"vault_account",
187            mint_account.base_mint.as_ref(),
188            &[vault_account.bump],
189        ];
190        let signer = &[&seeds[..]];
191
192        let cpi_ctx_transfer = CpiContext::new_with_signer(
193            ctx.accounts.token_program.to_account_info(),
194            anchor_spl::token::Transfer {
195                from: ctx.accounts.vault.to_account_info(),
196                to: ctx.accounts.user_base_token.to_account_info(),
197                authority: vault_account.to_account_info(),
198            },
199            signer,
200        );
201        anchor_spl::token::transfer(cpi_ctx_transfer, withdraw_amount)?;
202
203        vault_account.total_deposited = vault_account
204            .total_deposited
205            .checked_sub(base_amount)
206            .unwrap();
207        mint_account.total_minted = mint_account
208            .total_minted
209            .checked_sub(restaked_amount)
210            .unwrap();
211        user_account.pending_unstake = 0;
212        user_account.last_claimed_slot = Clock::get()?.slot;// added because it was missed 
213
214          emit!(UnstakeClaimedEvent{
215                user: ctx.accounts.user.key(),
216                base_mint: mint_account.base_mint,
217                base_amount,
218                fee,
219                withdraw_amount,
220            });
221
222
223        Ok(())
224    }
225
226    pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()> {
227            let user_account = &mut ctx.accounts.user_restaking_account;
228            let treasury = &mut ctx.accounts.treasury;
229
230            let current_slot = Clock::get()?.slot;
231
232           
233            let elapsed_slots = current_slot
234                .checked_sub(user_account.last_claimed_slot)
235                .unwrap();
236
237            require!(elapsed_slots > 0, CustomError::NothingToClaim);
238
239            let reward_rate: u64 = 1; // can adjust for demo
240            let rewards = elapsed_slots
241                .checked_mul(user_account.restaked_amount)
242                .unwrap()
243                .checked_mul(reward_rate)
244                .unwrap();
245
246            require!(
247                **treasury.to_account_info().lamports.borrow() >= rewards,
248                CustomError::InsufficientTreasuryBalance
249            );
250
251            **treasury.to_account_info().try_borrow_mut_lamports()? -= rewards;
252            **ctx.accounts.user.to_account_info().try_borrow_mut_lamports()? += rewards;
253
254      
255            user_account.last_claimed_slot = current_slot;
256
257             emit!(RewardsClaimedEvent{
258                user: ctx.accounts.user.key(),
259                rewards,
260                elapsed_slots,
261            });
262
263            Ok(())
264         }
265    pub fn initialize_operator(ctx: Context<RegisterOperator>, bond_amount: u64, metadata: String) -> Result<()> {
266            let operator_account = &mut ctx.accounts.operator_account;
267            let vault = &mut ctx.accounts.vault;  // ← Get mutable reference to vault
268
269            require!(bond_amount >= 2_000_000_000, CustomError::NotEnoughToken);
270         
271            operator_account.owner = ctx.accounts.operator_key.key();
272            operator_account.bond_amount = bond_amount;
273            operator_account.metadata = metadata;
274            operator_account.active = true;
275            operator_account.avs_count = 0;
276            operator_account.bump = ctx.bumps.operator_account;
277            operator_account.vault_bump = ctx.bumps.vault;
278
279            
280            vault.bump = ctx.bumps.vault;
281
282        
283            let transfer_ix = anchor_lang::solana_program::system_instruction::transfer(
284                &ctx.accounts.operator_key.key(),
285                &ctx.accounts.vault.key(),
286                bond_amount,
287            );
288            
289            anchor_lang::solana_program::program::invoke(
290                &transfer_ix,
291                &[
292                    ctx.accounts.operator_key.to_account_info(),
293                    ctx.accounts.vault.to_account_info(),
294                ],
295            )?;
296
297            emit!(OperatorRegisteredEvent{
298                owner : operator_account.owner,
299                bond_amount,
300                metadata: operator_account.metadata.clone(),
301                active : true,
302                avs_count : operator_account.avs_count,
303            });
304
305            Ok(())
306        }
307
308
309    pub fn update_operator_metadata( ctx: Context<UpdateOperatorMetadata>, metadata : String)->Result<()>{
310
311        let operator_account = &mut ctx.accounts.operator_account;
312        require!(operator_account.owner== ctx.accounts.owner.key(), CustomError::Unauthorized);
313
314        operator_account.metadata = metadata;
315
316        emit!(OperatorMetadataUpdatedEvent{
317                owner: operator_account.owner,
318                metadata: operator_account.metadata.clone(),
319            });
320
321        Ok(())
322    }
323
324    pub fn de_register_operator(ctx: Context<DeRegisterOperator>) -> Result<()> {
325        require!(
326            ctx.accounts.operator_account.owner == ctx.accounts.operator_key.key(), 
327            CustomError::Unauthorized
328        );
329        
330        let bond_amount = ctx.accounts.operator_account.bond_amount;
331
332        **ctx.accounts.operator_key.to_account_info().try_borrow_mut_lamports()? += bond_amount;
333        **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= bond_amount;
334        
335        let operator_account = &mut ctx.accounts.operator_account;
336        operator_account.active = false;
337
338                emit!(OperatorDeRegisteredEvent{
339                        owner: operator_account.owner,
340                        bond_returned: bond_amount,
341                        active: false,
342                    });
343        
344        Ok(())
345    }
346    
347    pub fn slash_operator(ctx: Context<SlashOperator>, operator_owner: Pubkey,amount: u64) -> Result<()> {
348            let operator_account = &mut ctx.accounts.operator_account;
349
350            require!(
351                operator_account.bond_amount >= amount, 
352                CustomError::InsufficientBond
353            );
354
355            operator_account.bond_amount = operator_account
356                .bond_amount
357                .checked_sub(amount)
358                .unwrap();
359
360            **ctx.accounts.vault.to_account_info().try_borrow_mut_lamports()? -= amount;
361            **ctx.accounts.treasury.to_account_info().try_borrow_mut_lamports()? += amount;
362
363                emit!(OperatorSlashedEvent{
364                    operator_owner,
365                    slashed_amount: amount,
366                    remaining_bond: operator_account.bond_amount,
367                });
368
369            Ok(())
370        }
371
372    pub fn initialize_reward_treasury(ctx: Context<InitializeRewardTreasury>) -> Result<()> {
373       let treasury = &mut ctx.accounts.treasury;
374       treasury.authority = ctx.accounts.authority.key();
375       treasury.bump = ctx.bumps.treasury;
376       treasury.total_rewards_distributed = 0;
377       Ok(())
378   }
379
380    pub fn register_avs(ctx: Context<RegisterAvs>, metadata:String , registration_fee: u64)->Result<()>{
381        // get AccountInfos (immutable borrows) and perform the transfer before taking mutable borrows
382        let avs_owner_ai = ctx.accounts.avs_owner.to_account_info();
383        let avs_account_ai = ctx.accounts.avs_account.to_account_info();
384
385        require!(registration_fee >= 3_000_000_000, CustomError::NotEnoughToken);
386        
387        let transfer_sol = anchor_lang::solana_program::system_instruction::transfer(
388            &ctx.accounts.avs_owner.key(), 
389            &ctx.accounts.avs_account.key(), 
390            registration_fee,
391        );
392
393        anchor_lang::solana_program::program::invoke(
394            &transfer_sol,
395            &[avs_owner_ai.clone(), avs_account_ai.clone()],
396        )?;
397
398       
399        let avs_account = &mut ctx.accounts.avs_account;
400        let treasury = &mut ctx.accounts.treasury;
401
402        let treasury_share = registration_fee / 2;
403        **avs_account.to_account_info().try_borrow_mut_lamports()? -= treasury_share;
404        **treasury.to_account_info().try_borrow_mut_lamports()? += treasury_share;
405
406        avs_account.owner = ctx.accounts.avs_owner.key();
407        avs_account.metadata = metadata.clone();
408        avs_account.registration_fee = registration_fee;
409        avs_account.active = true;
410        avs_account.registered_slot = Clock::get()?.slot;
411        avs_account.bump = ctx.bumps.avs_account;
412
413            emit!(AvsRegisteredEvent{
414                    owner: avs_account.owner,
415                    registration_fee,
416                    metadata: avs_account.metadata.clone(),
417                    registered_slot: avs_account.registered_slot,
418                });
419
420        Ok(())
421    }
422
423    pub fn update_avs_metadata(ctx: Context<UpdateAvsMetadata>, metadata: String )-> Result<()>{
424        let avs_account = &mut ctx.accounts.avs_account;
425
426        require!(avs_account.owner == ctx.accounts.avs_owner.key(), CustomError::Unauthorized);
427        avs_account.metadata = metadata;
428
429          emit!(AvsMetadatUpdatedEvent{
430                owner: avs_account.owner,
431                metadata: avs_account.metadata.clone() ,
432            });
433        
434        Ok(())
435    }
436    
437
438    pub fn de_register_avs(ctx: Context<DeRegisterAvs>) -> Result<()> {
439            let avs_account = &mut ctx.accounts.avs_account;
440            require!(avs_account.owner == ctx.accounts.avs_owner.key(), CustomError::Unauthorized);
441
442            avs_account.active = false;
443
444                emit!(AvsDeRegisterEvent{
445                    owner: avs_account.owner,
446                    active : false,
447                });
448
449            Ok(())
450    }
451
452    pub fn operator_opt_in_avs(ctx: Context<OptInAvs>, avs_owner: Pubkey ) -> Result<()> {
453            let operator_account = &mut ctx.accounts.operator_account;
454            let operator_avs_reg = &mut ctx.accounts.operator_avs_registration;
455            let avs_account = &ctx.accounts.avs_account;
456
457          
458            require!(
459                operator_account.active,
460                CustomError::OperatorNotActive
461            );
462
463     
464            require!(
465                avs_account.active,
466                CustomError::AvsNotActive
467            );
468
469
470            operator_avs_reg.operator = ctx.accounts.operator_key.key();
471            operator_avs_reg.avs = avs_account.key();  
472            operator_avs_reg.opted_in_slot = Clock::get()?.slot;
473            operator_avs_reg.active = true;
474            operator_avs_reg.tasks_completed = 0;
475            operator_avs_reg.tasks_failed = 0;
476            operator_avs_reg.bump = ctx.bumps.operator_avs_registration;
477
478
479            operator_account.avs_count = operator_account
480                .avs_count
481                .checked_add(1)
482                .unwrap();
483
484               emit!(OperatorOptedInEvent{
485                    operator: operator_avs_reg.operator,
486                    avs: avs_account.key(),
487                    avs_owner,
488                    opted_in_slot: operator_avs_reg.opted_in_slot,
489                });
490
491            msg!(
492                "✅ Operator {} opted into AVS {} (owner: {})",
493                operator_avs_reg.operator,
494                avs_account.key(),
495                avs_owner
496            );
497
498            Ok(())
499        }
500
501
502}
503
504#[derive(Accounts)]
505pub struct InitializeStateAccount<'info> {
506    #[account(mut)]
507    pub authority: Signer<'info>,
508
509    #[account(
510        init,
511        payer = authority,
512        space = 8 + StateAccount::INIT_SPACE,
513        seeds = [b"state"],
514        bump
515    )]
516    pub state: Account<'info, StateAccount>,
517
518    pub system_program: Program<'info, System>,
519}
520
521#[derive(Accounts)]
522#[instruction(token_mint: Pubkey)]
523pub struct InitializeVault<'info> {
524    #[account(mut)]
525    pub authority: Signer<'info>,
526
527    #[account(
528        init,
529        payer = authority,
530        space = 8 + VaultAccount::INIT_SPACE,
531        seeds = [b"vault_account", token_mint.key().as_ref()],
532        bump
533    )]
534    pub vault_account: Account<'info, VaultAccount>,
535
536    #[account(
537        init,
538        payer = authority,
539        token::mint = token_mint,
540        token::authority = vault_account,
541        seeds = [b"vault_token", token_mint.key().as_ref()],
542        bump
543    )]
544    pub vault: Account<'info, TokenAccount>,
545
546    pub token_mint: Account<'info, Mint>,
547    pub system_program: Program<'info, System>,
548    pub token_program: Program<'info, Token>,
549    pub rent: Sysvar<'info, Rent>,
550}
551
552#[derive(Accounts)]
553#[instruction(base_mint: Pubkey, restaked_mint: Pubkey)]
554pub struct InitializeMintAccount<'info> {
555    #[account(mut)]
556    pub authority: Signer<'info>,
557
558    #[account(
559        init,
560        payer = authority,
561        space = 8 + MintAccount::INIT_SPACE,
562        seeds = [b"mint_account", base_mint.key().as_ref()],
563        bump
564    )]
565    pub mint_account: Account<'info, MintAccount>,
566
567    #[account(mut)]
568    pub vault_account: Account<'info, VaultAccount>,
569
570    pub system_program: Program<'info, System>,
571}
572
573#[derive(Accounts)]
574pub struct Restake<'info> {
575    #[account(mut)]
576    pub user: Signer<'info>,
577
578    #[account(mut)]
579    pub user_base_token: Account<'info, TokenAccount>,
580
581    #[account(
582        mut,
583        seeds = [b"vault_account", mint_account.base_mint.as_ref()],
584        bump = vault_account.bump
585    )]
586    pub vault_account: Account<'info, VaultAccount>,
587
588    #[account(mut)]
589    pub vault: Account<'info, TokenAccount>,
590
591    #[account(
592        mut,
593        seeds = [b"mint_account", mint_account.base_mint.as_ref()],
594        bump = mint_account.bump
595    )]
596    pub mint_account: Account<'info, MintAccount>,
597
598    #[account(mut)]
599    pub restaked_mint: Account<'info, Mint>,
600
601    #[account(mut)]
602    pub user_restaked_token: Account<'info, TokenAccount>,
603
604    #[account(
605        init_if_needed,
606        payer = user,
607        space = 8 + UserRestakingAccount::INIT_SPACE,
608        seeds = [b"user_restaking", user.key().as_ref(), mint_account.restaked_mint.as_ref()],
609        bump
610    )]
611    pub user_restaking_account: Account<'info, UserRestakingAccount>,
612
613    pub token_program: Program<'info, Token>,
614    pub system_program: Program<'info, System>,
615}
616
617
618#[derive(Accounts)]
619pub struct RequestUnstake<'info> {
620    #[account(mut)]
621    pub user: Signer<'info>,
622
623    #[account(
624        mut,
625        constraint = user_restaked_token.owner == user.key(),
626        constraint = user_restaked_token.mint == mint_account.restaked_mint
627    )]
628    pub user_restaked_token: Account<'info, TokenAccount>,
629
630    #[account(
631        mut,
632        constraint = user_restaking_account.user == user.key(),
633        constraint = user_restaking_account.restaked_mint == mint_account.restaked_mint
634    )]
635    pub user_restaking_account: Account<'info, UserRestakingAccount>,
636
637    #[account(mut)]
638    pub restaked_mint: Account<'info, Mint>,
639
640    #[account(mut)]
641    pub mint_account: Account<'info, MintAccount>,
642
643    pub token_program: Program<'info, Token>,
644    pub system_program: Program<'info, System>,
645}
646
647#[derive(Accounts)]
648pub struct ClaimUnstake<'info> {
649    #[account(mut)]
650    pub user: Signer<'info>,
651
652    #[account(
653        mut,
654        constraint = user_base_token.owner == user.key(),
655        constraint = user_base_token.mint == mint_account.base_mint
656    )]
657    pub user_base_token: Account<'info, TokenAccount>,
658
659    #[account(
660        mut,
661        constraint = user_restaking_account.user == user.key(),
662        constraint = user_restaking_account.restaked_mint == mint_account.restaked_mint
663    )]
664    pub user_restaking_account: Account<'info, UserRestakingAccount>,
665
666    #[account(mut)]
667    pub vault_account: Account<'info, VaultAccount>,
668
669    #[account(
670        mut,
671        constraint = vault.mint == mint_account.base_mint
672    )]
673    pub vault: Account<'info, TokenAccount>,
674
675    #[account(mut)]
676    pub mint_account: Account<'info, MintAccount>,
677
678    pub token_program: Program<'info, Token>,
679}
680
681
682#[derive(Accounts)]
683pub struct RegisterOperator<'info> {
684    #[account(mut)]
685    pub operator_key: Signer<'info>,
686
687    #[account(
688        init,
689        payer = operator_key,
690        space = 8 + OperatorAccount::INIT_SPACE,
691        seeds = [b"operator", operator_key.key().as_ref()],
692        bump
693    )]
694    pub operator_account: Account<'info, OperatorAccount>,
695
696    #[account(
697        init,  
698        payer = operator_key,
699        space = 8 + OperatorVault::INIT_SPACE,
700        seeds = [b"vault", operator_key.key().as_ref()],
701        bump
702    )]
703    pub vault: Account<'info, OperatorVault>, 
704
705    pub system_program: Program<'info, System>,
706}
707
708
709#[derive(Accounts)]
710pub struct UpdateOperatorMetadata<'info>{
711    #[account(mut)]
712    pub owner: Signer<'info>,
713
714    #[account(
715        mut ,
716        seeds = [b"operator", owner.key().as_ref()],
717        bump = operator_account.bump,
718        has_one = owner @ CustomError::Unauthorized
719    )]
720    pub operator_account: Account<'info , OperatorAccount>
721}
722
723#[derive(Accounts)]
724pub struct DeRegisterOperator<'info> {
725    #[account(mut)]
726    pub operator_key: Signer<'info>,
727
728    #[account(
729        mut,
730        seeds = [b"operator", operator_key.key().as_ref()],
731        bump = operator_account.bump,
732        close = operator_key
733    )]
734    pub operator_account: Account<'info, OperatorAccount>,
735
736    /// Add the vault here too
737    #[account(
738        mut,
739        seeds = [b"vault", operator_key.key().as_ref()],
740        bump = operator_account.vault_bump,
741        close = operator_key  // Also close the vault
742    )]
743    pub vault: Account<'info, OperatorVault>,
744
745    pub system_program: Program<'info, System>,
746}
747#[derive(Accounts)]
748#[instruction(operator_owner: Pubkey)]
749pub struct SlashOperator<'info> {
750    #[account(mut)]
751    pub authority: Signer<'info>, 
752
753   #[account(
754        mut,
755        seeds = [b"operator", operator_owner.as_ref()],  
756        bump = operator_account.bump,
757    )]
758    pub operator_account: Account<'info, OperatorAccount>,
759
760      #[account(
761        mut,
762        seeds = [b"vault", operator_owner.as_ref()], 
763        bump = operator_account.vault_bump
764    )]
765    pub vault:  Account<'info, OperatorVault>,
766
767    #[account(
768        mut,
769        seeds = [b"reward_treasury"],
770        bump = treasury.bump
771    )]
772    pub treasury: Account<'info, RewardTreasury>,
773    
774    pub system_program: Program<'info, System>,
775}
776#[derive(Accounts)]
777pub struct InitializeRewardTreasury<'info> {
778    #[account(mut)]
779    pub authority: Signer<'info>,
780
781    #[account(
782        init,
783        payer = authority,
784        space = 8 + RewardTreasury::INIT_SPACE,
785        seeds = [b"reward_treasury"],
786        bump
787    )]
788    pub treasury: Account<'info, RewardTreasury>,
789
790    pub system_program: Program<'info, System>,
791}
792
793#[derive(Accounts)]
794pub struct ClaimRewards<'info> {
795    #[account(mut)]
796    pub user: Signer<'info>,
797
798    #[account(
799        mut,
800        constraint = user_restaking_account.user == user.key()
801    )]
802    pub user_restaking_account: Account<'info, UserRestakingAccount>,
803
804    #[account(
805        mut,
806        seeds = [b"reward_treasury"],
807        bump = treasury.bump
808    )]
809    pub treasury: Account<'info, RewardTreasury>,
810}
811
812#[derive(Accounts)]
813pub struct RegisterAvs<'info>{
814
815    #[account(mut)]
816    pub avs_owner : Signer<'info>,
817
818    #[account(
819        init ,
820        payer = avs_owner,
821        space = 8 + AvsAccount::INIT_SPACE,
822        seeds = [b"avs" , avs_owner.key().as_ref()],
823        bump
824    )]
825    pub avs_account : Account<'info , AvsAccount>,
826
827    #[account(
828        mut ,
829        seeds = [b"reward_treasury"],
830        bump = treasury.bump
831    )]
832    pub treasury: Account<'info , RewardTreasury>,
833
834    pub system_program : Program<'info , System>
835}
836
837#[derive(Accounts)]
838pub struct UpdateAvsMetadata<'info>{
839    #[account(mut)]
840    pub avs_owner: Signer<'info>,
841
842    #[account(
843        mut ,
844        seeds = [b"avs", avs_owner.key().as_ref()],
845        bump = avs_account.bump,
846    )]
847    pub avs_account: Account<'info , AvsAccount>
848}
849
850#[derive(Accounts)]
851pub struct DeRegisterAvs<'info>{
852    #[account(mut)]
853    pub avs_owner : Signer<'info>,
854
855    #[account(
856        mut,
857        seeds = [b"avs", avs_owner.key().as_ref()],
858        bump = avs_account.bump,
859        close = avs_owner
860    )]
861    pub avs_account : Account<'info , AvsAccount>
862}
863
864#[derive(Accounts)]
865pub struct GetUserData<'info> {
866    pub user: Signer<'info>,
867
868    #[account(
869        seeds = [b"user_restaking", user.key().as_ref(), restaked_mint.key().as_ref()],
870        bump = user_restaking_account.bump,
871        constraint = user_restaking_account.user == user.key()
872    )]
873    pub user_restaking_account: Account<'info, UserRestakingAccount>,
874
875    pub restaked_mint: Account<'info, Mint>,
876}
877
878
879#[derive(Accounts)]
880#[instruction(avs_owner: Pubkey)]  // ← Changed: this is the AVS owner pubkey
881pub struct OptInAvs<'info> {
882    #[account(mut)]
883    pub operator_key: Signer<'info>,
884
885   
886    #[account(
887        mut,
888        seeds = [b"operator", operator_key.key().as_ref()],
889        bump = operator_account.bump,
890        constraint = operator_account.active @ CustomError::OperatorNotActive,
891        constraint = operator_account.owner == operator_key.key() @ CustomError::Unauthorized
892    )]
893    pub operator_account: Account<'info, OperatorAccount>,
894
895    /// The AVS account - derived using AVS owner pubkey (same as RegisterAvs)
896    #[account(
897        seeds = [b"avs", avs_owner.as_ref()],  // ← Fixed: matches your RegisterAvs seeds
898        bump = avs_account.bump,
899        constraint = avs_account.active @ CustomError::AvsNotActive,
900        constraint = avs_account.owner == avs_owner @ CustomError::Unauthorized
901    )]
902    pub avs_account: Account<'info, AvsAccount>,
903
904    /// The registration account linking operator to AVS
905    #[account(
906        init,
907        payer = operator_key,
908        space = 8 + OperatorAvsRegistration::INIT_SPACE,
909        seeds = [
910            b"operator_avs",
911            operator_key.key().as_ref(),
912            avs_owner.as_ref()  
913        ],
914        bump
915    )]
916    pub operator_avs_registration: Account<'info, OperatorAvsRegistration>,
917
918    pub system_program: Program<'info, System>,
919}
920
921
922#[account]
923#[derive(InitSpace, Debug)]
924pub struct StateAccount {
925    pub authority: Pubkey,
926    pub msol_mint: Pubkey,
927    pub jitosol_mint: Pubkey,
928    pub rm_sol_mint: Pubkey,
929    pub rjito_sol_mint: Pubkey,
930    pub bump: u8,
931}
932
933#[account]
934#[derive(InitSpace, Debug)]
935pub struct VaultAccount {
936    pub token_mint: Pubkey,
937    pub vault: Pubkey,
938    pub bump: u8,
939    pub total_deposited: u64,
940}
941
942#[account]
943#[derive(InitSpace, Debug)]
944pub struct MintAccount {
945    pub base_mint: Pubkey,
946    pub restaked_mint: Pubkey,
947    pub vault: Pubkey,
948    pub bump: u8,
949    pub total_minted: u64,
950    pub exchange_rate: u64,
951    pub last_update_slot: u64,
952}
953#[account]
954#[derive(InitSpace, Debug)]
955pub struct UserRestakingAccount {
956    pub user: Pubkey,
957    pub deposited_mint: Pubkey,
958    pub restaked_mint: Pubkey,
959    pub deposited_amount: u64,
960    pub restaked_amount: u64,
961    pub bump: u8,
962    pub cooldown_end_timestamp: i64,
963    pub pending_unstake: u64,
964    pub reward_debt: u64,
965    pub last_claimed_slot: u64,
966}
967
968#[account]
969#[derive(InitSpace, Debug)]
970pub struct OperatorAccount {
971    pub owner: Pubkey,
972    pub bond_amount: u64,
973    #[max_len(100)]
974    pub metadata: String,
975    pub active: bool,
976    pub avs_count: u32,
977    pub bump: u8,
978    pub vault_bump: u8, 
979}
980
981#[account]
982#[derive(InitSpace, Debug)]
983pub struct OperatorVault {
984    pub bump: u8,
985}
986
987
988#[account]
989#[derive(InitSpace, Debug)]
990pub struct RewardTreasury {
991    pub authority: Pubkey,
992    pub bump: u8,
993    pub total_rewards_distributed: u64,
994}
995
996
997#[account]
998#[derive(InitSpace, Debug)]
999pub struct AvsAccount{
1000    pub owner: Pubkey,
1001    #[max_len(100)]
1002    pub metadata: String,
1003    pub registration_fee : u64,
1004    pub active : bool,
1005    pub slashing_policy : Pubkey,
1006    pub registered_slot : u64,
1007    pub bump: u8,
1008}
1009
1010pub struct SlashingPolicy {
1011    pub misbehavior_type: u8,  // 0 = downtime, 1 = invalid signature, etc.
1012    pub penalty_percent: u8,
1013}
1014
1015#[account]
1016#[derive(InitSpace, Debug)]
1017pub struct OperatorAvsRegistration {
1018    pub operator: Pubkey,
1019    pub avs: Pubkey,
1020    pub opted_in_slot: u64,
1021    pub active: bool,
1022    pub tasks_completed: u64,
1023    pub tasks_failed: u64,
1024    pub bump: u8,
1025}
1026
1027#[event]
1028pub struct OperatorRegisteredEvent{
1029    pub owner : Pubkey,
1030    pub bond_amount: u64,
1031    pub metadata : String,
1032    pub active: bool,
1033    pub avs_count: u32,
1034}
1035
1036#[event]
1037pub struct OperatorMetadataUpdatedEvent{
1038    pub owner: Pubkey,
1039    pub metadata: String,
1040}
1041
1042#[event]
1043pub struct OperatorDeRegisteredEvent{
1044    pub owner: Pubkey,
1045    pub bond_returned: u64,
1046    pub active: bool
1047}
1048
1049#[event]
1050pub struct OperatorSlashedEvent{
1051    pub operator_owner: Pubkey,
1052    pub slashed_amount: u64,
1053    pub remaining_bond: u64,
1054}
1055
1056
1057#[event]
1058pub struct AvsRegisteredEvent{
1059    pub owner: Pubkey,
1060    pub registration_fee: u64,
1061    pub metadata: String,
1062    pub registered_slot: u64,
1063}
1064
1065#[event]
1066pub struct AvsMetadatUpdatedEvent{
1067    pub owner: Pubkey,
1068    pub metadata: String,
1069}
1070
1071#[event]
1072pub struct AvsDeRegisterEvent{
1073    pub owner: Pubkey,
1074    pub active : bool,
1075} 
1076
1077#[event]
1078pub struct OperatorOptedInEvent{
1079    pub operator: Pubkey,
1080    pub avs: Pubkey,
1081    pub avs_owner: Pubkey,
1082    pub opted_in_slot: u64,
1083}
1084
1085#[event]
1086pub struct RestakeEvent{
1087    pub user: Pubkey,
1088    pub base_mint: Pubkey,
1089    pub restaked_mint: Pubkey,
1090    pub amount_deposited: u64,
1091    pub restaked_amount: u64,
1092    pub exchange_rate: u64,
1093}
1094
1095#[event]
1096pub struct UnstakeRequestedEvent{
1097    pub user: Pubkey,
1098    pub restaked_mint: Pubkey,
1099    pub restaked_amount: u64,
1100    pub cooldown_end_timestamp: i64,
1101}
1102
1103#[event]
1104pub struct UnstakeClaimedEvent{
1105    pub user: Pubkey,
1106    pub base_mint: Pubkey,
1107    pub base_amount: u64,
1108    pub fee: u64,
1109    pub withdraw_amount: u64,
1110}
1111
1112#[event]
1113pub struct RewardsClaimedEvent{
1114    pub user: Pubkey,
1115    pub rewards: u64,
1116    pub elapsed_slots: u64,
1117}
1118
1119
1120
1121
1122#[error_code]
1123pub enum CustomError {
1124    #[msg("Cooldown not finished yet")]
1125    CooldownNotFinished,
1126    #[msg("No pending unstake to claim")]
1127    NothingToClaim,
1128    #[msg("Not enough tokens supplied")]
1129    NotEnoughToken,
1130    #[msg("Unauthorized action")]
1131    Unauthorized,
1132    #[msg("Insufficient bond to slash")]
1133    InsufficientBond,
1134    #[msg("Not enough lamports in treasury")]
1135    InsufficientTreasuryBalance,
1136    #[msg("Avs not active to opt")]
1137    AvsNotActive,
1138    #[msg("Operator not active to run nodes")]
1139    OperatorNotActive,
1140    #[msg("Operator not opted into this AVS")]
1141    OperatorNotOptedIn,
1142    #[msg("Task not completed")]
1143    TaskNotCompleted,
1144    #[msg("Challenge already resolved")]
1145    ChallengeAlreadyResolved,
1146    #[msg("Amount must be greater than 0")]
1147    InvalidAmount,
1148    #[msg("Insufficient restaked balance in account")]
1149    InsufficientRestakeBalance,
1150}