Browse Source

lang: add system program cpi wrapper functions (#1629)

Paul 3 years ago
parent
commit
d871f39793
4 changed files with 396 additions and 55 deletions
  1. 1 0
      CHANGELOG.md
  2. 4 5
      lang/src/lib.rs
  3. 369 1
      lang/src/system_program.rs
  4. 22 49
      lang/syn/src/codegen/accounts/constraints.rs

+ 1 - 0
CHANGELOG.md

@@ -39,6 +39,7 @@ incremented for features.
 * lang/ts: Events are now emitted using the `sol_log_data` syscall ([#1608](https://github.com/project-serum/anchor/pull/1608)).
 * lang: Remove space calculation using `#[derive(Default)]` ([#1519](https://github.com/project-serum/anchor/pull/1519)).
 * lang: Add support for logging expected and actual values and pubkeys. Add `require_eq` and `require_keys_eq` macros. Add default error code to `require` macro ([#1572](https://github.com/project-serum/anchor/pull/1572)).
+* lang: Add `system_program` CPI wrapper functions. Make `system_program` module public instead of re-exporting `system_program::System`([#1629](https://github.com/project-serum/anchor/pull/1629)).
 
 ## [0.22.1] - 2022-02-28
 

+ 4 - 5
lang/src/lib.rs

@@ -40,9 +40,8 @@ mod ctor;
 pub mod error;
 #[doc(hidden)]
 pub mod idl;
-mod system_program;
+pub mod system_program;
 
-pub use crate::system_program::System;
 mod vec;
 pub use crate::bpf_upgradeable_state::*;
 pub use anchor_attribute_access_control::access_control;
@@ -241,9 +240,9 @@ pub mod prelude {
         context::Context, context::CpiContext, declare_id, emit, err, error, event, interface,
         program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
         require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state,
-        zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize,
-        AnchorSerialize, Id, Key, Owner, ProgramData, Result, System, ToAccountInfo,
-        ToAccountInfos, ToAccountMetas,
+        system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
+        AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result,
+        ToAccountInfo, ToAccountInfos, ToAccountMetas,
     };
     pub use anchor_attribute_error::*;
     pub use borsh;

+ 369 - 1
lang/src/system_program.rs

@@ -1,4 +1,4 @@
-use crate::*;
+use crate::prelude::*;
 use solana_program::pubkey::Pubkey;
 
 pub use solana_program::system_program::ID;
@@ -11,3 +11,371 @@ impl anchor_lang::Id for System {
         ID
     }
 }
+
+pub fn advance_nonce_account<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, AdvanceNonceAccount<'info>>,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::advance_nonce_account(
+        ctx.accounts.nonce.key,
+        ctx.accounts.authorized.key,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[
+            ctx.accounts.nonce,
+            ctx.accounts.recent_blockhashes,
+            ctx.accounts.authorized,
+        ],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct AdvanceNonceAccount<'info> {
+    pub nonce: AccountInfo<'info>,
+    pub authorized: AccountInfo<'info>,
+    pub recent_blockhashes: AccountInfo<'info>,
+}
+
+pub fn allocate<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, Allocate<'info>>,
+    space: u64,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::allocate(
+        ctx.accounts.account_to_allocate.key,
+        space,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.account_to_allocate],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct Allocate<'info> {
+    pub account_to_allocate: AccountInfo<'info>,
+}
+
+pub fn allocate_with_seed<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, AllocateWithSeed<'info>>,
+    seed: &str,
+    space: u64,
+    owner: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::allocate_with_seed(
+        ctx.accounts.account_to_allocate.key,
+        ctx.accounts.base.key,
+        seed,
+        space,
+        owner,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.account_to_allocate, ctx.accounts.base],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct AllocateWithSeed<'info> {
+    pub account_to_allocate: AccountInfo<'info>,
+    pub base: AccountInfo<'info>,
+}
+
+pub fn assign<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, Assign<'info>>,
+    owner: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::assign(
+        ctx.accounts.account_to_assign.key,
+        owner,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.account_to_assign],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct Assign<'info> {
+    pub account_to_assign: AccountInfo<'info>,
+}
+
+pub fn assign_with_seed<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, AssignWithSeed<'info>>,
+    seed: &str,
+    owner: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::assign_with_seed(
+        ctx.accounts.account_to_assign.key,
+        ctx.accounts.base.key,
+        seed,
+        owner,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.account_to_assign, ctx.accounts.base],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct AssignWithSeed<'info> {
+    pub account_to_assign: AccountInfo<'info>,
+    pub base: AccountInfo<'info>,
+}
+
+pub fn authorize_nonce_account<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, AuthorizeNonceAccount<'info>>,
+    new_authority: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::authorize_nonce_account(
+        ctx.accounts.nonce.key,
+        ctx.accounts.authorized.key,
+        new_authority,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.nonce, ctx.accounts.authorized],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct AuthorizeNonceAccount<'info> {
+    pub nonce: AccountInfo<'info>,
+    pub authorized: AccountInfo<'info>,
+}
+
+pub fn create_account<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, CreateAccount<'info>>,
+    lamports: u64,
+    space: u64,
+    owner: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::create_account(
+        ctx.accounts.from.key,
+        ctx.accounts.to.key,
+        lamports,
+        space,
+        owner,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.from, ctx.accounts.to],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct CreateAccount<'info> {
+    pub from: AccountInfo<'info>,
+    pub to: AccountInfo<'info>,
+}
+
+pub fn create_account_with_seed<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, CreateAccountWithSeed<'info>>,
+    seed: &str,
+    lamports: u64,
+    space: u64,
+    owner: &Pubkey,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::create_account_with_seed(
+        ctx.accounts.from.key,
+        ctx.accounts.to.key,
+        ctx.accounts.base.key,
+        seed,
+        lamports,
+        space,
+        owner,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.from, ctx.accounts.to, ctx.accounts.base],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct CreateAccountWithSeed<'info> {
+    pub from: AccountInfo<'info>,
+    pub to: AccountInfo<'info>,
+    pub base: AccountInfo<'info>,
+}
+
+pub fn create_nonce_account<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, CreateNonceAccount<'info>>,
+    lamports: u64,
+    authority: &Pubkey,
+) -> Result<()> {
+    let ixs = crate::solana_program::system_instruction::create_nonce_account(
+        ctx.accounts.from.key,
+        ctx.accounts.nonce.key,
+        authority,
+        lamports,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ixs[0],
+        &[ctx.accounts.from, ctx.accounts.nonce.clone()],
+        ctx.signer_seeds,
+    )?;
+
+    crate::solana_program::program::invoke_signed(
+        &ixs[1],
+        &[
+            ctx.accounts.nonce,
+            ctx.accounts.recent_blockhashes,
+            ctx.accounts.rent,
+        ],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct CreateNonceAccount<'info> {
+    pub from: AccountInfo<'info>,
+    pub nonce: AccountInfo<'info>,
+    pub recent_blockhashes: AccountInfo<'info>,
+    pub rent: AccountInfo<'info>,
+}
+
+pub fn create_nonce_account_with_seed<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, CreateNonceAccountWithSeed<'info>>,
+    lamports: u64,
+    seed: &str,
+    authority: &Pubkey,
+) -> Result<()> {
+    let ixs = crate::solana_program::system_instruction::create_nonce_account_with_seed(
+        ctx.accounts.from.key,
+        ctx.accounts.nonce.key,
+        ctx.accounts.base.key,
+        seed,
+        authority,
+        lamports,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ixs[0],
+        &[
+            ctx.accounts.from,
+            ctx.accounts.nonce.clone(),
+            ctx.accounts.base,
+        ],
+        ctx.signer_seeds,
+    )?;
+
+    crate::solana_program::program::invoke_signed(
+        &ixs[1],
+        &[
+            ctx.accounts.nonce,
+            ctx.accounts.recent_blockhashes,
+            ctx.accounts.rent,
+        ],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct CreateNonceAccountWithSeed<'info> {
+    pub from: AccountInfo<'info>,
+    pub nonce: AccountInfo<'info>,
+    pub base: AccountInfo<'info>,
+    pub recent_blockhashes: AccountInfo<'info>,
+    pub rent: AccountInfo<'info>,
+}
+
+pub fn transfer<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>,
+    lamports: u64,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::transfer(
+        ctx.accounts.from.key,
+        ctx.accounts.to.key,
+        lamports,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.from, ctx.accounts.to],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct Transfer<'info> {
+    pub from: AccountInfo<'info>,
+    pub to: AccountInfo<'info>,
+}
+
+pub fn transfer_with_seed<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, TransferWithSeed<'info>>,
+    from_seed: String,
+    from_owner: &Pubkey,
+    lamports: u64,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::transfer_with_seed(
+        ctx.accounts.from.key,
+        ctx.accounts.base.key,
+        from_seed,
+        from_owner,
+        ctx.accounts.to.key,
+        lamports,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[ctx.accounts.from, ctx.accounts.base, ctx.accounts.to],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct TransferWithSeed<'info> {
+    pub from: AccountInfo<'info>,
+    pub base: AccountInfo<'info>,
+    pub to: AccountInfo<'info>,
+}
+
+pub fn withdraw_nonce_account<'a, 'b, 'c, 'info>(
+    ctx: CpiContext<'a, 'b, 'c, 'info, WithdrawNonceAccount<'info>>,
+    lamports: u64,
+) -> Result<()> {
+    let ix = crate::solana_program::system_instruction::withdraw_nonce_account(
+        ctx.accounts.nonce.key,
+        ctx.accounts.authorized.key,
+        ctx.accounts.to.key,
+        lamports,
+    );
+    crate::solana_program::program::invoke_signed(
+        &ix,
+        &[
+            ctx.accounts.nonce,
+            ctx.accounts.to,
+            ctx.accounts.recent_blockhashes,
+            ctx.accounts.rent,
+            ctx.accounts.authorized,
+        ],
+        ctx.signer_seeds,
+    )
+    .map_err(Into::into)
+}
+
+#[derive(Accounts)]
+pub struct WithdrawNonceAccount<'info> {
+    pub nonce: AccountInfo<'info>,
+    pub to: AccountInfo<'info>,
+    pub recent_blockhashes: AccountInfo<'info>,
+    pub rent: AccountInfo<'info>,
+    pub authorized: AccountInfo<'info>,
+}

+ 22 - 49
lang/syn/src/codegen/accounts/constraints.rs

@@ -670,21 +670,12 @@ pub fn generate_create_account(
         if __current_lamports == 0 {
             // Create the token account with right amount of lamports and space, and the correct owner.
             let lamports = __anchor_rent.minimum_balance(#space);
-            anchor_lang::solana_program::program::invoke_signed(
-                &anchor_lang::solana_program::system_instruction::create_account(
-                    &payer.key(),
-                    &#field.key(),
-                    lamports,
-                    #space as u64,
-                    #owner,
-                ),
-                &[
-                    payer.to_account_info(),
-                    #field.to_account_info(),
-                    system_program.to_account_info(),
-                ],
-                &[#seeds_with_nonce],
-            )?;
+            let cpi_accounts = anchor_lang::system_program::CreateAccount {
+                from: payer.to_account_info(),
+                to: #field.to_account_info()
+            };
+            let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
+            anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?;
         } else {
             // Fund the account for rent exemption.
             let required_lamports = __anchor_rent
@@ -692,43 +683,25 @@ pub fn generate_create_account(
                 .max(1)
                 .saturating_sub(__current_lamports);
             if required_lamports > 0 {
-                anchor_lang::solana_program::program::invoke(
-                    &anchor_lang::solana_program::system_instruction::transfer(
-                        &payer.key(),
-                        &#field.key(),
-                        required_lamports,
-                    ),
-                    &[
-                        payer.to_account_info(),
-                        #field.to_account_info(),
-                        system_program.to_account_info(),
-                    ],
-                )?;
+                let cpi_accounts = anchor_lang::system_program::Transfer {
+                    from: payer.to_account_info(),
+                    to: #field.to_account_info(),
+                };
+                let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
+                anchor_lang::system_program::transfer(cpi_context, required_lamports)?;
             }
             // Allocate space.
-            anchor_lang::solana_program::program::invoke_signed(
-                &anchor_lang::solana_program::system_instruction::allocate(
-                    &#field.key(),
-                    #space as u64,
-                ),
-                &[
-                    #field.to_account_info(),
-                    system_program.to_account_info(),
-                ],
-                &[#seeds_with_nonce],
-            )?;
+            let cpi_accounts = anchor_lang::system_program::Allocate {
+                account_to_allocate: #field.to_account_info()
+            };
+            let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
+            anchor_lang::system_program::allocate(cpi_context.with_signer(&[#seeds_with_nonce]), #space as u64)?;
             // Assign to the spl token program.
-            anchor_lang::solana_program::program::invoke_signed(
-                &anchor_lang::solana_program::system_instruction::assign(
-                    &#field.key(),
-                    #owner,
-                ),
-                &[
-                    #field.to_account_info(),
-                    system_program.to_account_info(),
-                ],
-                &[#seeds_with_nonce],
-            )?;
+            let cpi_accounts = anchor_lang::system_program::Assign {
+                account_to_assign: #field.to_account_info()
+            };
+            let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts);
+            anchor_lang::system_program::assign(cpi_context.with_signer(&[#seeds_with_nonce]), #owner)?;
         }
     }
 }