gmsol_timelock/instructions/bypass/
revoke_role.rs1use anchor_lang::prelude::*;
2use gmsol_store::{
3 cpi::{accounts::RevokeRole as StoreRevokeRole, revoke_role},
4 program::GmsolStore,
5 states::{Seed, MAX_ROLE_NAME_LEN},
6 utils::{fixed_str::fixed_str_to_bytes, CpiAuthentication, WithStore},
7 CoreError,
8};
9
10use crate::{
11 roles,
12 states::{Executor, ExecutorWalletSigner},
13};
14
15const NOT_BYPASSABLE_ROLES: [&str; 3] = [
16 roles::TIMELOCKED_ADMIN,
17 roles::TIMELOCK_ADMIN,
18 roles::TIMELOCK_KEEPER,
19];
20
21#[derive(Accounts)]
23pub struct RevokeRole<'info> {
24 pub authority: Signer<'info>,
26 #[account(mut)]
29 pub store: UncheckedAccount<'info>,
30 #[account(
32 has_one = store,
33 constraint = executor.load()?.role_name()? == roles::ADMIN @ CoreError::InvalidArgument,
34 seeds = [
35 Executor::SEED,
36 store.key.as_ref(),
37 &fixed_str_to_bytes::<MAX_ROLE_NAME_LEN>(roles::ADMIN)?,
38 ],
39 bump = executor.load()?.bump,
40 )]
41 pub executor: AccountLoader<'info, Executor>,
42 #[account(
44 mut,
45 seeds = [Executor::WALLET_SEED, executor.key().as_ref()],
46 bump,
47 )]
48 pub wallet: SystemAccount<'info>,
49 pub user: UncheckedAccount<'info>,
52 pub store_program: Program<'info, GmsolStore>,
54}
55
56pub(crate) fn unchecked_revoke_role(ctx: Context<RevokeRole>, role: String) -> Result<()> {
60 require!(
61 !NOT_BYPASSABLE_ROLES.contains(&role.as_str()),
62 CoreError::InvalidArgument
63 );
64 let signer = ExecutorWalletSigner::new(ctx.accounts.executor.key(), ctx.bumps.wallet);
65 let cpi_ctx = ctx.accounts.revoke_role_ctx();
66 revoke_role(
67 cpi_ctx.with_signer(&[&signer.as_seeds()]),
68 ctx.accounts.user.key(),
69 role,
70 )?;
71 Ok(())
72}
73
74impl<'info> WithStore<'info> for RevokeRole<'info> {
75 fn store_program(&self) -> AccountInfo<'info> {
76 self.store_program.to_account_info()
77 }
78
79 fn store(&self) -> AccountInfo<'info> {
80 self.store.to_account_info()
81 }
82}
83
84impl<'info> CpiAuthentication<'info> for RevokeRole<'info> {
85 fn authority(&self) -> AccountInfo<'info> {
86 self.authority.to_account_info()
87 }
88
89 fn on_error(&self) -> Result<()> {
90 err!(CoreError::PermissionDenied)
91 }
92}
93
94impl<'info> RevokeRole<'info> {
95 fn revoke_role_ctx(&self) -> CpiContext<'_, '_, '_, 'info, StoreRevokeRole<'info>> {
96 CpiContext::new(
97 self.store_program.to_account_info(),
98 StoreRevokeRole {
99 authority: self.wallet.to_account_info(),
100 store: self.store.to_account_info(),
101 },
102 )
103 }
104}