Skip to content

Commit cbaa7da

Browse files
feboinspi-writer001zubayr1M-Daeva
authored
token-2022: Add cpi guard instructions (#333)
* token2022: add cpi guard token extension Co-authored-by: zubayr1 <write2zubayr@gmail.com> Co-authored-by: M. Daeva <embedmad@gmail.com> * Nits * Fix formatting * More nits * Address review comments --------- Co-authored-by: inspiration_gx <94226358+inspi-writer001@users.noreply.github.com> Co-authored-by: zubayr1 <write2zubayr@gmail.com> Co-authored-by: M. Daeva <embedmad@gmail.com>
1 parent b7e65b0 commit cbaa7da

File tree

4 files changed

+278
-0
lines changed

4 files changed

+278
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use {
2+
crate::instructions::{extensions::ExtensionDiscriminator, MAX_MULTISIG_SIGNERS},
3+
core::{mem::MaybeUninit, slice},
4+
solana_account_view::AccountView,
5+
solana_address::Address,
6+
solana_instruction_view::{
7+
cpi::{invoke_signed_with_bounds, Signer},
8+
InstructionAccount, InstructionView,
9+
},
10+
solana_program_error::{ProgramError, ProgramResult},
11+
};
12+
13+
/// Allow all token operations to happen via CPI as normal.
14+
///
15+
/// Implicitly initializes the extension in the case where it is not
16+
/// present.
17+
///
18+
/// Accounts expected by this instruction:
19+
///
20+
/// 0. `[writable]` The account to update.
21+
/// 1. `[signer]` The account's owner.
22+
///
23+
/// * Multisignature authority
24+
/// 0. `[writable]` The account to update.
25+
/// 1. `[]` The account's multisignature owner.
26+
/// 2. `..2+M` `[signer]` M signer accounts.
27+
pub struct Disable<'a, 'b, 'c> {
28+
/// The account to update.
29+
pub account: &'a AccountView,
30+
31+
/// The account's owner.
32+
pub authority: &'a AccountView,
33+
34+
/// The signer accounts if the authority is a multisig.
35+
pub multisig_signers: &'c [&'a AccountView],
36+
37+
/// The token program.
38+
pub token_program: &'b Address,
39+
}
40+
41+
impl<'a, 'b, 'c> Disable<'a, 'b, 'c> {
42+
pub const DISCRIMINATOR: u8 = 1;
43+
44+
/// Creates a new `Disable` instruction with a single owner/delegate
45+
/// authority.
46+
#[inline(always)]
47+
pub fn new(
48+
token_program: &'b Address,
49+
account: &'a AccountView,
50+
authority: &'a AccountView,
51+
) -> Self {
52+
Self::with_multisig_signers(token_program, account, authority, &[])
53+
}
54+
55+
/// Creates a new `Disable` instruction with a multisignature owner/delegate
56+
/// authority and signer accounts.
57+
#[inline(always)]
58+
pub fn with_multisig_signers(
59+
token_program: &'b Address,
60+
account: &'a AccountView,
61+
authority: &'a AccountView,
62+
multisig_signers: &'c [&'a AccountView],
63+
) -> Self {
64+
Self {
65+
account,
66+
authority,
67+
multisig_signers,
68+
token_program,
69+
}
70+
}
71+
72+
#[inline(always)]
73+
pub fn invoke(&self) -> ProgramResult {
74+
self.invoke_signed(&[])
75+
}
76+
77+
#[inline(always)]
78+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
79+
if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
80+
return Err(ProgramError::InvalidArgument);
81+
}
82+
83+
let expected_accounts = 2 + self.multisig_signers.len();
84+
85+
// Instruction accounts.
86+
87+
let mut instruction_accounts =
88+
[const { MaybeUninit::<InstructionAccount>::uninit() }; 2 + MAX_MULTISIG_SIGNERS];
89+
90+
instruction_accounts[0].write(InstructionAccount::writable(self.account.address()));
91+
92+
instruction_accounts[1].write(InstructionAccount::new(
93+
self.authority.address(),
94+
false,
95+
self.multisig_signers.is_empty(),
96+
));
97+
98+
for (account, signer) in instruction_accounts[2..]
99+
.iter_mut()
100+
.zip(self.multisig_signers.iter())
101+
{
102+
account.write(InstructionAccount::readonly_signer(signer.address()));
103+
}
104+
105+
// Accounts.
106+
107+
let mut accounts =
108+
[const { MaybeUninit::<&AccountView>::uninit() }; 2 + MAX_MULTISIG_SIGNERS];
109+
110+
accounts[0].write(self.account);
111+
112+
accounts[1].write(self.authority);
113+
114+
for (account, signer) in accounts[2..].iter_mut().zip(self.multisig_signers.iter()) {
115+
account.write(signer);
116+
}
117+
118+
invoke_signed_with_bounds::<{ 2 + MAX_MULTISIG_SIGNERS }>(
119+
&InstructionView {
120+
program_id: self.token_program,
121+
// SAFETY: instruction accounts has `expected_accounts` initialized.
122+
accounts: unsafe {
123+
slice::from_raw_parts(instruction_accounts.as_ptr() as _, expected_accounts)
124+
},
125+
data: &[ExtensionDiscriminator::CpiGuard as u8, Self::DISCRIMINATOR],
126+
},
127+
// SAFETY: accounts has `expected_accounts` initialized.
128+
unsafe {
129+
slice::from_raw_parts(accounts.as_ptr() as *const &AccountView, expected_accounts)
130+
},
131+
signers,
132+
)
133+
}
134+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use {
2+
crate::instructions::{extensions::ExtensionDiscriminator, MAX_MULTISIG_SIGNERS},
3+
core::{mem::MaybeUninit, slice},
4+
solana_account_view::AccountView,
5+
solana_address::Address,
6+
solana_instruction_view::{
7+
cpi::{invoke_signed_with_bounds, Signer},
8+
InstructionAccount, InstructionView,
9+
},
10+
solana_program_error::{ProgramError, ProgramResult},
11+
};
12+
13+
/// Lock certain token operations from taking place within CPI for this
14+
/// Account, namely:
15+
/// * `Transfer` and `Burn` must go through a delegate.
16+
/// * `CloseAccount` can only return lamports to owner.
17+
/// * `SetAuthority` can only be used to remove an existing close authority.
18+
/// * `Approve` is disallowed entirely.
19+
///
20+
/// In addition, CPI Guard cannot be enabled or disabled via CPI.
21+
///
22+
/// Accounts expected by this instruction:
23+
///
24+
/// 0. `[writable]` The account to update.
25+
/// 1. `[signer]` The account's owner.
26+
///
27+
/// * Multisignature authority
28+
/// 0. `[writable]` The account to update.
29+
/// 1. `[]` The account's multisignature owner.
30+
/// 2. `..2+M` `[signer]` M signer accounts.
31+
pub struct Enable<'a, 'b, 'c> {
32+
/// The account to update.
33+
pub account: &'a AccountView,
34+
35+
/// The account's owner.
36+
pub authority: &'a AccountView,
37+
38+
/// The signer accounts if the authority is a multisig.
39+
pub multisig_signers: &'c [&'a AccountView],
40+
41+
/// The token program.
42+
pub token_program: &'b Address,
43+
}
44+
45+
impl<'a, 'b, 'c> Enable<'a, 'b, 'c> {
46+
pub const DISCRIMINATOR: u8 = 0;
47+
48+
/// Creates a new `Enable` instruction with a single owner/delegate
49+
/// authority.
50+
#[inline(always)]
51+
pub fn new(
52+
token_program: &'b Address,
53+
account: &'a AccountView,
54+
authority: &'a AccountView,
55+
) -> Self {
56+
Self::with_multisig_signers(token_program, account, authority, &[])
57+
}
58+
59+
/// Creates a new `Enable` instruction with a multisignature owner/delegate
60+
/// authority and signer accounts.
61+
#[inline(always)]
62+
pub fn with_multisig_signers(
63+
token_program: &'b Address,
64+
account: &'a AccountView,
65+
authority: &'a AccountView,
66+
multisig_signers: &'c [&'a AccountView],
67+
) -> Self {
68+
Self {
69+
account,
70+
authority,
71+
multisig_signers,
72+
token_program,
73+
}
74+
}
75+
76+
#[inline(always)]
77+
pub fn invoke(&self) -> ProgramResult {
78+
self.invoke_signed(&[])
79+
}
80+
81+
#[inline(always)]
82+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
83+
if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
84+
return Err(ProgramError::InvalidArgument);
85+
}
86+
87+
let expected_accounts = 2 + self.multisig_signers.len();
88+
89+
// Instruction accounts.
90+
91+
let mut instruction_accounts =
92+
[const { MaybeUninit::<InstructionAccount>::uninit() }; 2 + MAX_MULTISIG_SIGNERS];
93+
94+
instruction_accounts[0].write(InstructionAccount::writable(self.account.address()));
95+
96+
instruction_accounts[1].write(InstructionAccount::new(
97+
self.authority.address(),
98+
false,
99+
self.multisig_signers.is_empty(),
100+
));
101+
102+
for (account, signer) in instruction_accounts[2..]
103+
.iter_mut()
104+
.zip(self.multisig_signers.iter())
105+
{
106+
account.write(InstructionAccount::readonly_signer(signer.address()));
107+
}
108+
109+
// Accounts.
110+
111+
let mut accounts =
112+
[const { MaybeUninit::<&AccountView>::uninit() }; 2 + MAX_MULTISIG_SIGNERS];
113+
114+
accounts[0].write(self.account);
115+
116+
accounts[1].write(self.authority);
117+
118+
for (account, signer) in accounts[2..].iter_mut().zip(self.multisig_signers.iter()) {
119+
account.write(signer);
120+
}
121+
122+
invoke_signed_with_bounds::<{ 2 + MAX_MULTISIG_SIGNERS }>(
123+
&InstructionView {
124+
program_id: self.token_program,
125+
// SAFETY: instruction accounts has `expected_accounts` initialized.
126+
accounts: unsafe {
127+
slice::from_raw_parts(instruction_accounts.as_ptr() as _, expected_accounts)
128+
},
129+
data: &[ExtensionDiscriminator::CpiGuard as u8, Self::DISCRIMINATOR],
130+
},
131+
// SAFETY: accounts has `expected_accounts` initialized.
132+
unsafe {
133+
slice::from_raw_parts(accounts.as_ptr() as *const &AccountView, expected_accounts)
134+
},
135+
signers,
136+
)
137+
}
138+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub mod disable;
2+
pub mod enable;
3+
4+
pub use {disable::*, enable::*};

programs/token-2022/src/instructions/extensions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod cpi_guard;
12
pub mod default_account_state;
23
pub mod group_member_pointer;
34
pub mod group_pointer;
@@ -19,6 +20,7 @@ pub enum ExtensionDiscriminator {
1920
DefaultAccountState = 28,
2021
MemoTransfer = 30,
2122
InterestBearingMint = 33,
23+
CpiGuard = 34,
2224
PermanentDelegate = 35,
2325
TransferHook = 36,
2426
MetadataPointer = 39,

0 commit comments

Comments
 (0)