|
| 1 | +use pinocchio::{ |
| 2 | + account_info::AccountInfo, |
| 3 | + entrypoint::ProgramResult, |
| 4 | + instruction::{AccountMeta, Instruction, Signer}, |
| 5 | + program::invoke_signed, |
| 6 | + pubkey::Pubkey, |
| 7 | +}; |
| 8 | + |
| 9 | +/// Allocate space for and assign an account at an address derived |
| 10 | +/// from a base public key and a seed. |
| 11 | +/// |
| 12 | +/// ### Accounts: |
| 13 | +/// 0. `[WRITE]` Allocated account |
| 14 | +/// 1. `[SIGNER]` Base account |
| 15 | +pub struct AllocateWithSeed<'a, 'b, 'c> { |
| 16 | + /// Allocated account. |
| 17 | + pub account: &'a AccountInfo, |
| 18 | + |
| 19 | + /// Base account. |
| 20 | + /// |
| 21 | + /// The account matching the base Pubkey below must be provided as |
| 22 | + /// a signer, but may be the same as the funding account and provided |
| 23 | + /// as account 0. |
| 24 | + pub base: &'a AccountInfo, |
| 25 | + |
| 26 | + /// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN`. |
| 27 | + pub seed: &'b str, |
| 28 | + |
| 29 | + /// Number of bytes of memory to allocate. |
| 30 | + pub space: u64, |
| 31 | + |
| 32 | + /// Address of program that will own the new account. |
| 33 | + pub owner: &'c Pubkey, |
| 34 | +} |
| 35 | + |
| 36 | +impl<'a, 'b, 'c> AllocateWithSeed<'a, 'b, 'c> { |
| 37 | + #[inline(always)] |
| 38 | + pub fn invoke(&self) -> ProgramResult { |
| 39 | + self.invoke_signed(&[]) |
| 40 | + } |
| 41 | + |
| 42 | + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { |
| 43 | + // account metadata |
| 44 | + let account_metas: [AccountMeta; 2] = [ |
| 45 | + AccountMeta::writable_signer(self.account.key()), |
| 46 | + AccountMeta::readonly_signer(self.base.key()), |
| 47 | + ]; |
| 48 | + |
| 49 | + // instruction data |
| 50 | + // - [0..4 ]: instruction discriminator |
| 51 | + // - [4..36 ]: base pubkey |
| 52 | + // - [36..40]: seed length |
| 53 | + // - [40.. ]: seed (max 32) |
| 54 | + // - [.. +8]: account space |
| 55 | + // - [.. +32]: owner pubkey |
| 56 | + let mut instruction_data = [0; 112]; |
| 57 | + instruction_data[0] = 9; |
| 58 | + instruction_data[4..36].copy_from_slice(self.base.key()); |
| 59 | + instruction_data[36..40].copy_from_slice(&u32::to_le_bytes(self.seed.len() as u32)); |
| 60 | + |
| 61 | + let offset = 40 + self.seed.len(); |
| 62 | + instruction_data[40..offset].copy_from_slice(self.seed.as_bytes()); |
| 63 | + instruction_data[offset..offset + 8].copy_from_slice(&self.space.to_le_bytes()); |
| 64 | + instruction_data[offset + 8..offset + 40].copy_from_slice(self.owner.as_ref()); |
| 65 | + |
| 66 | + let instruction = Instruction { |
| 67 | + program_id: &crate::ID, |
| 68 | + accounts: &account_metas, |
| 69 | + data: &instruction_data[..offset + 40], |
| 70 | + }; |
| 71 | + |
| 72 | + invoke_signed(&instruction, &[self.account, self.base], signers) |
| 73 | + } |
| 74 | +} |
0 commit comments