diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17195af..09a7e06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,68 +48,6 @@ jobs: - name: Lint / Features run: make lint-features-clients-rust - format_and_lint_interface: - name: Format & Lint Interface - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - toolchain: format, lint - - - name: Format - run: make format-interface - - - name: Lint / Clippy - run: make clippy-interface - - - name: Lint / Docs - run: make lint-docs-interface - - - name: Lint / Features - run: make lint-features-interface - - wasm_interface: - name: Build Interface in WASM - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - cargo-cache-key: cargo-wasm-interface - solana: true - - - name: Install wasm-pack - uses: taiki-e/install-action@v2 - with: - tool: wasm-pack - - - name: Build Interface with wasm-pack - run: make build-wasm-interface - - test_interface: - name: Test Interface - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - toolchain: test - cargo-cache-key: cargo-interface - solana: true - - - name: Test Interface - run: make test-interface - generate_clients: name: Check Client Generation runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 0a5d605..35b78eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ resolver = "2" members = [ "clients/rust", - "interface", ] [workspace.package] diff --git a/Makefile b/Makefile index 3f355a6..dc294bd 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,6 @@ features-%: publish-%: ./scripts/publish-rust.sh $(subst -,/,$*) -build-wasm-interface: - wasm-pack build --target nodejs --dev ./interface --features bincode - lint-docs-%: RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo $(nightly) doc --all-features --no-deps --manifest-path $(subst -,/,$*)/Cargo.toml diff --git a/interface/Cargo.toml b/interface/Cargo.toml deleted file mode 100644 index 2d76073..0000000 --- a/interface/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -name = "solana-system-interface" -version = "1.0.0" -description = "Instructions and constructors for the System program" -readme = "README.md" -authors = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -edition = { workspace = true } - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] -all-features = true -rustdoc-args = ["--cfg=docsrs"] - -[dependencies] -num-traits = "0.2" -serde = { version = "1.0.210", optional = true } -serde_derive = { version = "1.0.210", optional = true } -solana-decode-error = { workspace = true } -solana-frozen-abi = { workspace = true, features = ["frozen-abi"], optional = true } -solana-frozen-abi-macro = { workspace = true, features = ["frozen-abi"], optional = true } -solana-instruction = { workspace = true, features = ["bincode", "std"], optional = true } -solana-logger = { workspace = true, optional = true } -solana-msg = { workspace = true } -solana-program-error = { workspace = true } -solana-pubkey = { workspace = true, default-features = false } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -js-sys = "0.3.72" -wasm-bindgen = "0.2" - -[dev-dependencies] -anyhow = "1.0.89" -borsh = { version = "1.5.1", features = ["derive", "unstable__schema"] } -solana-account-info = { workspace = true } -solana-cpi = { workspace = true } -solana-example-mocks = { workspace = true } -solana-nonce = { workspace = true } -solana-program-entrypoint = { workspace = true } -solana-program-error = { workspace = true, features = ["borsh"] } -solana-pubkey = { workspace = true, features = ["std"] } -solana-system-interface = { path = ".", features = ["bincode"] } -solana-sysvar = { workspace = true } -solana-sysvar-id = { workspace = true } -static_assertions = "1.1.0" -strum = "0.24" -strum_macros = "0.24" - -[features] -bincode = ["dep:solana-instruction", "serde"] -frozen-abi = [ - "dep:solana-frozen-abi", - "dep:solana-frozen-abi-macro", - "dep:solana-logger", - "solana-pubkey/frozen-abi", - "solana-pubkey/std" -] -serde = ["dep:serde", "dep:serde_derive", "solana-pubkey/serde"] - -[lib] -crate-type = ["cdylib", "rlib"] diff --git a/interface/README.md b/interface/README.md deleted file mode 100644 index a8a2441..0000000 --- a/interface/README.md +++ /dev/null @@ -1,104 +0,0 @@ -

- - Solana - -

- -# Solana System Interface - -This crate contains instructions and constructors for interacting with the [System program](https://docs.solanalabs.com/runtime/programs#system-program). - -The System program can be used to create new accounts, allocate account data, assign accounts to owning programs, transfer lamports from System Program owned accounts and pay transaction fees. - -## Getting Started - -From your project folder: - -```bash -cargo add solana-system-interface --features bincode -``` - -This will add the `solana-system-interface` dependency with the `bincode` feature enabled to your `Cargo.toml` file. The `bincode` feature contains the instruction constructors to create instructions for the System program. - -## Examples - -Creating an account: - -```rust -use solana_rpc_client::rpc_client::RpcClient; -use solana_sdk::{ - signature::{Keypair, Signer}, - transaction::Transaction, -}; -use solana_system_interface::instruction; -use anyhow::Result; - -fn create_account( - client: &RpcClient, - payer: &Keypair, - new_account: &Keypair, - owning_program: &Pubkey, - space: u64, -) -> Result<()> { - let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; - let instr = instruction::create_account( - &payer.pubkey(), - &new_account.pubkey(), - rent, - space, - owning_program, - ); - - let blockhash = client.get_latest_blockhash()?; - let tx = Transaction::new_signed_with_payer( - &[instr], - Some(&payer.pubkey()), - &[payer, new_account], - blockhash, - ); - - let _sig = client.send_and_confirm_transaction(&tx)?; - - Ok(()) -} -``` - -Transfer lamports between accounts: - -```rust -use solana_rpc_client::rpc_client::RpcClient; -use solana_pubkey::Pubkey; -use solana_sdk::{ - signature::{Keypair, Signer}, - transaction::Transaction, -}; -use solana_system_interface::instruction; -use anyhow::Result; - -fn transfer( - client: &RpcClient, - from: &Keypair, - recipient: &Pubkey, - lamports: u64, -) -> Result<()> { - let instr = instruction::transfer( - &from.pubkey(), - recipient, - lamports, - ); - - let blockhash = client.get_latest_blockhash()?; - let tx = Transaction::new_signed_with_payer( - &[instr], - Some(&from.pubkey()), - &[from], - blockhash, - ); - - let _sig = client.send_and_confirm_transaction(&tx)?; - - Ok(()) -} -``` - -More examples can be found on the crate [documentation](https://docs.rs/solana-system-interface/latest/solana-system-interface/). diff --git a/interface/src/error.rs b/interface/src/error.rs deleted file mode 100644 index f1365e2..0000000 --- a/interface/src/error.rs +++ /dev/null @@ -1,175 +0,0 @@ -use { - num_traits::{FromPrimitive, ToPrimitive}, - solana_decode_error::DecodeError, - solana_msg::msg, - solana_program_error::{PrintProgramError, ProgramError}, -}; - -// Use strum when testing to ensure our FromPrimitive -// impl is exhaustive -#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Deserialize, serde_derive::Serialize) -)] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SystemError { - /// An account with the same address already exists. - AccountAlreadyInUse, - /// Account does not have enough SOL to perform the operation. - ResultWithNegativeLamports, - /// Cannot assign account to this program id. - InvalidProgramId, - /// Cannot allocate account data of this length. - InvalidAccountDataLength, - /// Length of requested seed is too long. - MaxSeedLengthExceeded, - /// Provided address does not match addressed derived from seed. - AddressWithSeedMismatch, - /// Advancing stored nonce requires a populated RecentBlockhashes sysvar. - NonceNoRecentBlockhashes, - /// Stored nonce is still in recent_blockhashes. - NonceBlockhashNotExpired, - /// Specified nonce does not match stored nonce. - NonceUnexpectedBlockhashValue, -} - -impl FromPrimitive for SystemError { - #[inline] - fn from_i64(n: i64) -> Option { - if n == Self::AccountAlreadyInUse as i64 { - Some(Self::AccountAlreadyInUse) - } else if n == Self::ResultWithNegativeLamports as i64 { - Some(Self::ResultWithNegativeLamports) - } else if n == Self::InvalidProgramId as i64 { - Some(Self::InvalidProgramId) - } else if n == Self::InvalidAccountDataLength as i64 { - Some(Self::InvalidAccountDataLength) - } else if n == Self::MaxSeedLengthExceeded as i64 { - Some(Self::MaxSeedLengthExceeded) - } else if n == Self::AddressWithSeedMismatch as i64 { - Some(Self::AddressWithSeedMismatch) - } else if n == Self::NonceNoRecentBlockhashes as i64 { - Some(Self::NonceNoRecentBlockhashes) - } else if n == Self::NonceBlockhashNotExpired as i64 { - Some(Self::NonceBlockhashNotExpired) - } else if n == Self::NonceUnexpectedBlockhashValue as i64 { - Some(Self::NonceUnexpectedBlockhashValue) - } else { - None - } - } - #[inline] - fn from_u64(n: u64) -> Option { - Self::from_i64(n as i64) - } -} - -impl ToPrimitive for SystemError { - #[inline] - fn to_i64(&self) -> Option { - Some(match *self { - Self::AccountAlreadyInUse => Self::AccountAlreadyInUse as i64, - Self::ResultWithNegativeLamports => Self::ResultWithNegativeLamports as i64, - Self::InvalidProgramId => Self::InvalidProgramId as i64, - Self::InvalidAccountDataLength => Self::InvalidAccountDataLength as i64, - Self::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded as i64, - Self::AddressWithSeedMismatch => Self::AddressWithSeedMismatch as i64, - Self::NonceNoRecentBlockhashes => Self::NonceNoRecentBlockhashes as i64, - Self::NonceBlockhashNotExpired => Self::NonceBlockhashNotExpired as i64, - Self::NonceUnexpectedBlockhashValue => Self::NonceUnexpectedBlockhashValue as i64, - }) - } - #[inline] - fn to_u64(&self) -> Option { - self.to_i64().map(|x| x as u64) - } -} - -impl std::error::Error for SystemError {} - -impl core::fmt::Display for SystemError { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - match self { - SystemError::AccountAlreadyInUse => { - f.write_str("an account with the same address already exists") - } - SystemError::ResultWithNegativeLamports => { - f.write_str("account does not have enough SOL to perform the operation") - } - SystemError::InvalidProgramId => { - f.write_str("cannot assign account to this program id") - } - SystemError::InvalidAccountDataLength => { - f.write_str("cannot allocate account data of this length") - } - SystemError::MaxSeedLengthExceeded => { - f.write_str("length of requested seed is too long") - } - SystemError::AddressWithSeedMismatch => { - f.write_str("provided address does not match addressed derived from seed") - } - SystemError::NonceNoRecentBlockhashes => { - f.write_str("advancing stored nonce requires a populated RecentBlockhashes sysvar") - } - SystemError::NonceBlockhashNotExpired => { - f.write_str("stored nonce is still in recent_blockhashes") - } - SystemError::NonceUnexpectedBlockhashValue => { - f.write_str("specified nonce does not match stored nonce") - } - } - } -} - -impl PrintProgramError for SystemError { - fn print(&self) { - msg!(&self.to_string()); - } -} - -impl From for ProgramError { - fn from(e: SystemError) -> Self { - Self::Custom(e as u32) - } -} - -impl DecodeError for SystemError { - fn type_of() -> &'static str { - "SystemError" - } -} - -impl From for SystemError { - fn from(error: u64) -> Self { - match error { - 0 => SystemError::AccountAlreadyInUse, - 1 => SystemError::ResultWithNegativeLamports, - 2 => SystemError::InvalidProgramId, - 3 => SystemError::InvalidAccountDataLength, - 4 => SystemError::MaxSeedLengthExceeded, - 5 => SystemError::AddressWithSeedMismatch, - 6 => SystemError::NonceNoRecentBlockhashes, - 7 => SystemError::NonceBlockhashNotExpired, - 8 => SystemError::NonceUnexpectedBlockhashValue, - _ => panic!("Unsupported SystemError"), - } - } -} - -#[cfg(test)] -mod tests { - use {super::SystemError, num_traits::FromPrimitive, strum::IntoEnumIterator}; - - #[test] - fn test_system_error_from_primitive_exhaustive() { - for variant in SystemError::iter() { - let variant_i64 = variant.clone() as i64; - assert_eq!( - SystemError::from_repr(variant_i64 as usize), - SystemError::from_i64(variant_i64) - ); - assert_eq!(SystemError::from(variant_i64 as u64), variant); - } - } -} diff --git a/interface/src/instruction.rs b/interface/src/instruction.rs deleted file mode 100644 index 6f8381f..0000000 --- a/interface/src/instruction.rs +++ /dev/null @@ -1,1718 +0,0 @@ -//! Instructions and constructors for the system program. -//! -//! The system program is responsible for the creation of accounts and [nonce -//! accounts][na]. It is responsible for transferring lamports from accounts -//! owned by the system program, including typical user wallet accounts. -//! -//! [na]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces -//! -//! Account creation typically involves three steps: [`allocate`] space, -//! [`transfer`] lamports for rent, [`assign`] to its owning program. The -//! [`create_account`] function does all three at once. All new accounts must -//! contain enough lamports to be [rent exempt], or else the creation -//! instruction will fail. -//! -//! [rent exempt]: https://solana.com/docs/core/accounts#rent-exemption -//! -//! The accounts created by the System program can either be user-controlled, -//! where the secret keys are held outside the blockchain, -//! or they can be [program derived addresses][pda], -//! where write access to accounts is granted by an owning program. -//! -//! [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -//! -//! Most of the functions in this module construct an [`Instruction`], that must -//! be submitted to the runtime for execution, either via RPC, typically with -//! [`RpcClient`], or through [cross-program invocation][cpi]. -//! -//! When invoking through CPI, the [`invoke`] or [`invoke_signed`] instruction -//! requires all account references to be provided explicitly as [`AccountInfo`] -//! values. The account references required are specified in the documentation -//! for the [`SystemInstruction`] variants for each System program instruction, -//! and these variants are linked from the documentation for their constructors. -//! -//! [`RpcClient`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html -//! [cpi]: https://docs.rs/solana-cpi/latest/solana_cpi/index.html -//! [`invoke`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -//! [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -//! [`AccountInfo`]: https://docs.rs/solana-account-info/latest/solana_account_info/struct.AccountInfo.html - -use solana_pubkey::Pubkey; -#[cfg(feature = "bincode")] -use { - crate::program::ID, - solana_instruction::{AccountMeta, Instruction}, -}; - -// Inline some constants to avoid dependencies. -// -// Note: replace these inline IDs with the corresponding value from -// `solana_sdk_ids` once the version is updated to 2.2.0. - -#[cfg(feature = "bincode")] -const RECENT_BLOCKHASHES_ID: Pubkey = - Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111"); - -#[cfg(feature = "bincode")] -const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111"); - -#[cfg(feature = "bincode")] -#[cfg(test)] -static_assertions::const_assert_eq!(solana_nonce::state::State::size(), NONCE_STATE_SIZE); -/// The serialized size of the nonce state. -#[cfg(feature = "bincode")] -const NONCE_STATE_SIZE: usize = 80; - -/// An instruction to the system program. -#[cfg_attr( - feature = "frozen-abi", - solana_frozen_abi_macro::frozen_abi(digest = "2LnVTnJg7LxB1FawNZLoQEY8yiYx3MT3paTdx4s5kAXU"), - derive( - solana_frozen_abi_macro::AbiExample, - solana_frozen_abi_macro::AbiEnumVisitor - ) -)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Deserialize, serde_derive::Serialize) -)] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum SystemInstruction { - /// Create a new account - /// - /// # Account references - /// 0. `[WRITE, SIGNER]` Funding account - /// 1. `[WRITE, SIGNER]` New account - CreateAccount { - /// Number of lamports to transfer to the new account - lamports: u64, - - /// Number of bytes of memory to allocate - space: u64, - - /// Address of program that will own the new account - owner: Pubkey, - }, - - /// Assign account to a program - /// - /// # Account references - /// 0. `[WRITE, SIGNER]` Assigned account public key - Assign { - /// Owner program account - owner: Pubkey, - }, - - /// Transfer lamports - /// - /// # Account references - /// 0. `[WRITE, SIGNER]` Funding account - /// 1. `[WRITE]` Recipient account - Transfer { lamports: u64 }, - - /// Create a new account at an address derived from a base pubkey and a seed - /// - /// # Account references - /// 0. `[WRITE, SIGNER]` Funding account - /// 1. `[WRITE]` Created account - /// 2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be - /// provided as a signer, but may be the same as the funding account - /// and provided as account 0 - CreateAccountWithSeed { - /// Base public key - base: Pubkey, - - /// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN` - seed: String, - - /// Number of lamports to transfer to the new account - lamports: u64, - - /// Number of bytes of memory to allocate - space: u64, - - /// Owner program account address - owner: Pubkey, - }, - - /// Consumes a stored nonce, replacing it with a successor - /// - /// # Account references - /// 0. `[WRITE]` Nonce account - /// 1. `[]` RecentBlockhashes sysvar - /// 2. `[SIGNER]` Nonce authority - AdvanceNonceAccount, - - /// Withdraw funds from a nonce account - /// - /// # Account references - /// 0. `[WRITE]` Nonce account - /// 1. `[WRITE]` Recipient account - /// 2. `[]` RecentBlockhashes sysvar - /// 3. `[]` Rent sysvar - /// 4. `[SIGNER]` Nonce authority - /// - /// The `u64` parameter is the lamports to withdraw, which must leave the - /// account balance above the rent exempt reserve or at zero. - WithdrawNonceAccount(u64), - - /// Drive state of Uninitialized nonce account to Initialized, setting the nonce value - /// - /// # Account references - /// 0. `[WRITE]` Nonce account - /// 1. `[]` RecentBlockhashes sysvar - /// 2. `[]` Rent sysvar - /// - /// The `Pubkey` parameter specifies the entity authorized to execute nonce - /// instruction on the account - /// - /// No signatures are required to execute this instruction, enabling derived - /// nonce account addresses - InitializeNonceAccount(Pubkey), - - /// Change the entity authorized to execute nonce instructions on the account - /// - /// # Account references - /// 0. `[WRITE]` Nonce account - /// 1. `[SIGNER]` Nonce authority - /// - /// The `Pubkey` parameter identifies the entity to authorize - AuthorizeNonceAccount(Pubkey), - - /// Allocate space in a (possibly new) account without funding - /// - /// # Account references - /// 0. `[WRITE, SIGNER]` New account - Allocate { - /// Number of bytes of memory to allocate - space: u64, - }, - - /// Allocate space for and assign an account at an address - /// derived from a base public key and a seed - /// - /// # Account references - /// 0. `[WRITE]` Allocated account - /// 1. `[SIGNER]` Base account - AllocateWithSeed { - /// Base public key - base: Pubkey, - - /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` - seed: String, - - /// Number of bytes of memory to allocate - space: u64, - - /// Owner program account - owner: Pubkey, - }, - - /// Assign account to a program based on a seed - /// - /// # Account references - /// 0. `[WRITE]` Assigned account - /// 1. `[SIGNER]` Base account - AssignWithSeed { - /// Base public key - base: Pubkey, - - /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` - seed: String, - - /// Owner program account - owner: Pubkey, - }, - - /// Transfer lamports from a derived address - /// - /// # Account references - /// 0. `[WRITE]` Funding account - /// 1. `[SIGNER]` Base for funding account - /// 2. `[WRITE]` Recipient account - TransferWithSeed { - /// Amount to transfer - lamports: u64, - - /// Seed to use to derive the funding account address - from_seed: String, - - /// Owner to use to derive the funding account address - from_owner: Pubkey, - }, - - /// One-time idempotent upgrade of legacy nonce versions in order to bump - /// them out of chain blockhash domain. - /// - /// # Account references - /// 0. `[WRITE]` Nonce account - UpgradeNonceAccount, -} - -/// Create an account. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::CreateAccount`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// Account creation typically involves three steps: [`allocate`] space, -/// [`transfer`] lamports for rent, [`assign`] to its owning program. The -/// [`create_account`] function does all three at once. -/// -/// # Required signers -/// -/// The `from_pubkey` and `to_pubkey` signers must sign the transaction. -/// -/// # Examples -/// -/// These examples use a single invocation of -/// [`SystemInstruction::CreateAccount`] to create a new account, allocate some -/// space, transfer it the minimum lamports for rent exemption, and assign it to -/// the system program, -/// -/// ## Example: client-side RPC -/// -/// This example submits the instruction from an RPC client. -/// The `payer` and `new_account` are signers. -/// -/// ``` -/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::{instruction, program}; -/// use anyhow::Result; -/// -/// fn create_account( -/// client: &RpcClient, -/// payer: &Keypair, -/// new_account: &Keypair, -/// space: u64, -/// ) -> Result<()> { -/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; -/// let instr = instruction::create_account( -/// &payer.pubkey(), -/// &new_account.pubkey(), -/// rent, -/// space, -/// &program::ID, -/// ); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// let tx = Transaction::new_signed_with_payer( -/// &[instr], -/// Some(&payer.pubkey()), -/// &[payer, new_account], -/// blockhash, -/// ); -/// -/// let _sig = client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # let payer = Keypair::new(); -/// # let new_account = Keypair::new(); -/// # let client = RpcClient::new(String::new()); -/// # create_account(&client, &payer, &new_account, 0); -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -/// -/// ## Example: on-chain program -/// -/// This example submits the instruction from an on-chain Solana program. The -/// created account is a [program derived address][pda]. The `payer` and -/// `new_account_pda` are signers, with `new_account_pda` being signed for -/// virtually by the program itself via [`invoke_signed`], `payer` being signed -/// for by the client that submitted the transaction. -/// -/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -/// -/// ``` -/// use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_account_info::{next_account_info, AccountInfo}; -/// use solana_cpi::invoke_signed; -/// use solana_program_entrypoint::entrypoint; -/// use solana_program_error::ProgramResult; -/// use solana_pubkey::Pubkey; -/// use solana_system_interface::{instruction, program}; -/// use solana_sysvar::{rent::Rent, Sysvar}; -/// -/// #[derive(BorshSerialize, BorshDeserialize, Debug)] -/// pub struct CreateAccountInstruction { -/// /// The PDA seed used to distinguish the new account from other PDAs -/// pub new_account_seed: [u8; 16], -/// /// The PDA bump seed -/// pub new_account_bump_seed: u8, -/// /// The amount of space to allocate for `new_account_pda` -/// pub space: u64, -/// } -/// -/// entrypoint!(process_instruction); -/// -/// fn process_instruction( -/// program_id: &Pubkey, -/// accounts: &[AccountInfo], -/// instruction_data: &[u8], -/// ) -> ProgramResult { -/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; -/// -/// let account_info_iter = &mut accounts.iter(); -/// -/// let payer = next_account_info(account_info_iter)?; -/// let new_account_pda = next_account_info(account_info_iter)?; -/// let system_account = next_account_info(account_info_iter)?; -/// -/// assert!(payer.is_signer); -/// assert!(payer.is_writable); -/// // Note that `new_account_pda` is not a signer yet. -/// // This program will sign for it via `invoke_signed`. -/// assert!(!new_account_pda.is_signer); -/// assert!(new_account_pda.is_writable); -/// assert!(program::check_id(system_account.key)); -/// -/// let new_account_seed = &instr.new_account_seed; -/// let new_account_bump_seed = instr.new_account_bump_seed; -/// -/// let rent = Rent::get()? -/// .minimum_balance(instr.space.try_into().expect("overflow")); -/// -/// invoke_signed( -/// &instruction::create_account( -/// payer.key, -/// new_account_pda.key, -/// rent, -/// instr.space, -/// &program::ID -/// ), -/// &[payer.clone(), new_account_pda.clone()], -/// &[&[ -/// payer.key.as_ref(), -/// new_account_seed, -/// &[new_account_bump_seed], -/// ]], -/// )?; -/// -/// Ok(()) -/// } -/// ``` -#[cfg(feature = "bincode")] -pub fn create_account( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - space: u64, - owner: &Pubkey, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*from_pubkey, true), - AccountMeta::new(*to_pubkey, true), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::CreateAccount { - lamports, - space, - owner: *owner, - }, - account_metas, - ) -} - -// we accept `to` as a parameter so that callers do their own error handling when -// calling create_with_seed() -#[cfg(feature = "bincode")] -pub fn create_account_with_seed( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner) - base: &Pubkey, - seed: &str, - lamports: u64, - space: u64, - owner: &Pubkey, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*from_pubkey, true), - AccountMeta::new(*to_pubkey, false), - AccountMeta::new_readonly(*base, true), - ]; - - Instruction::new_with_bincode( - ID, - &SystemInstruction::CreateAccountWithSeed { - base: *base, - seed: seed.to_string(), - lamports, - space, - owner: *owner, - }, - account_metas, - ) -} - -/// Assign ownership of an account from the system program. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::Assign`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// # Required signers -/// -/// The `pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// These examples allocate space for an account, transfer it the minimum -/// balance for rent exemption, and assign the account to a program. -/// -/// ## Example: client-side RPC -/// -/// This example submits the instructions from an RPC client. -/// It assigns the account to a provided program account. -/// The `payer` and `new_account` are signers. -/// -/// ``` -/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn create_account( -/// client: &RpcClient, -/// payer: &Keypair, -/// new_account: &Keypair, -/// owning_program: &Pubkey, -/// space: u64, -/// ) -> Result<()> { -/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; -/// -/// let transfer_instr = instruction::transfer( -/// &payer.pubkey(), -/// &new_account.pubkey(), -/// rent, -/// ); -/// -/// let allocate_instr = instruction::allocate( -/// &new_account.pubkey(), -/// space, -/// ); -/// -/// let assign_instr = instruction::assign( -/// &new_account.pubkey(), -/// owning_program, -/// ); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// let tx = Transaction::new_signed_with_payer( -/// &[transfer_instr, allocate_instr, assign_instr], -/// Some(&payer.pubkey()), -/// &[payer, new_account], -/// blockhash, -/// ); -/// -/// let _sig = client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # let client = RpcClient::new(String::new()); -/// # let payer = Keypair::new(); -/// # let new_account = Keypair::new(); -/// # let owning_program = Pubkey::new_unique(); -/// # create_account(&client, &payer, &new_account, &owning_program, 1); -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -/// -/// ## Example: on-chain program -/// -/// This example submits the instructions from an on-chain Solana program. The -/// created account is a [program derived address][pda], funded by `payer`, and -/// assigned to the running program. The `payer` and `new_account_pda` are -/// signers, with `new_account_pda` being signed for virtually by the program -/// itself via [`invoke_signed`], `payer` being signed for by the client that -/// submitted the transaction. -/// -/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -/// -/// ``` -/// use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_account_info::{next_account_info, AccountInfo}; -/// use solana_cpi::invoke_signed; -/// use solana_program_entrypoint::entrypoint; -/// use solana_program_error::ProgramResult; -/// use solana_pubkey::Pubkey; -/// use solana_system_interface::{instruction, program}; -/// use solana_sysvar::{rent::Rent, Sysvar}; -/// -/// #[derive(BorshSerialize, BorshDeserialize, Debug)] -/// pub struct CreateAccountInstruction { -/// /// The PDA seed used to distinguish the new account from other PDAs -/// pub new_account_seed: [u8; 16], -/// /// The PDA bump seed -/// pub new_account_bump_seed: u8, -/// /// The amount of space to allocate for `new_account_pda` -/// pub space: u64, -/// } -/// -/// entrypoint!(process_instruction); -/// -/// fn process_instruction( -/// program_id: &Pubkey, -/// accounts: &[AccountInfo], -/// instruction_data: &[u8], -/// ) -> ProgramResult { -/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; -/// -/// let account_info_iter = &mut accounts.iter(); -/// -/// let payer = next_account_info(account_info_iter)?; -/// let new_account_pda = next_account_info(account_info_iter)?; -/// let system_account = next_account_info(account_info_iter)?; -/// -/// assert!(payer.is_signer); -/// assert!(payer.is_writable); -/// // Note that `new_account_pda` is not a signer yet. -/// // This program will sign for it via `invoke_signed`. -/// assert!(!new_account_pda.is_signer); -/// assert!(new_account_pda.is_writable); -/// assert!(program::check_id(system_account.key)); -/// -/// let new_account_seed = &instr.new_account_seed; -/// let new_account_bump_seed = instr.new_account_bump_seed; -/// -/// let rent = Rent::get()? -/// .minimum_balance(instr.space.try_into().expect("overflow")); -/// -/// invoke_signed( -/// &instruction::create_account( -/// payer.key, -/// new_account_pda.key, -/// rent, -/// instr.space, -/// &program::ID -/// ), -/// &[payer.clone(), new_account_pda.clone()], -/// &[&[ -/// payer.key.as_ref(), -/// new_account_seed, -/// &[new_account_bump_seed], -/// ]], -/// )?; -/// -/// Ok(()) -/// } -/// ``` -#[cfg(feature = "bincode")] -pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { - let account_metas = vec![AccountMeta::new(*pubkey, true)]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::Assign { owner: *owner }, - account_metas, - ) -} - -#[cfg(feature = "bincode")] -pub fn assign_with_seed( - address: &Pubkey, // must match create_with_seed(base, seed, owner) - base: &Pubkey, - seed: &str, - owner: &Pubkey, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*address, false), - AccountMeta::new_readonly(*base, true), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::AssignWithSeed { - base: *base, - seed: seed.to_string(), - owner: *owner, - }, - account_metas, - ) -} - -/// Transfer lamports from an account owned by the system program. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::Transfer`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// # Required signers -/// -/// The `from_pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// These examples allocate space for an account, transfer it the minimum -/// balance for rent exemption, and assign the account to a program. -/// -/// # Example: client-side RPC -/// -/// This example submits the instructions from an RPC client. -/// It assigns the account to a provided program account. -/// The `payer` and `new_account` are signers. -/// -/// ``` -/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn create_account( -/// client: &RpcClient, -/// payer: &Keypair, -/// new_account: &Keypair, -/// owning_program: &Pubkey, -/// space: u64, -/// ) -> Result<()> { -/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; -/// -/// let transfer_instr = instruction::transfer( -/// &payer.pubkey(), -/// &new_account.pubkey(), -/// rent, -/// ); -/// -/// let allocate_instr = instruction::allocate( -/// &new_account.pubkey(), -/// space, -/// ); -/// -/// let assign_instr = instruction::assign( -/// &new_account.pubkey(), -/// owning_program, -/// ); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// let tx = Transaction::new_signed_with_payer( -/// &[transfer_instr, allocate_instr, assign_instr], -/// Some(&payer.pubkey()), -/// &[payer, new_account], -/// blockhash, -/// ); -/// -/// let _sig = client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # let client = RpcClient::new(String::new()); -/// # let payer = Keypair::new(); -/// # let new_account = Keypair::new(); -/// # let owning_program = Pubkey::new_unique(); -/// # create_account(&client, &payer, &new_account, &owning_program, 1); -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -/// -/// ## Example: on-chain program -/// -/// This example submits the instructions from an on-chain Solana program. The -/// created account is a [program derived address][pda], funded by `payer`, and -/// assigned to the running program. The `payer` and `new_account_pda` are -/// signers, with `new_account_pda` being signed for virtually by the program -/// itself via [`invoke_signed`], `payer` being signed for by the client that -/// submitted the transaction. -/// -/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -/// -/// ``` -/// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_account_info::{next_account_info, AccountInfo}; -/// use solana_cpi::invoke_signed; -/// use solana_program_entrypoint::entrypoint; -/// use solana_program_error::ProgramResult; -/// use solana_pubkey::Pubkey; -/// use solana_system_interface::{instruction, program}; -/// use solana_sysvar::{rent::Rent, Sysvar}; -/// -/// #[derive(BorshSerialize, BorshDeserialize, Debug)] -/// # #[borsh(crate = "borsh")] -/// pub struct CreateAccountInstruction { -/// /// The PDA seed used to distinguish the new account from other PDAs -/// pub new_account_seed: [u8; 16], -/// /// The PDA bump seed -/// pub new_account_bump_seed: u8, -/// /// The amount of space to allocate for `new_account_pda` -/// pub space: u64, -/// } -/// -/// entrypoint!(process_instruction); -/// -/// fn process_instruction( -/// program_id: &Pubkey, -/// accounts: &[AccountInfo], -/// instruction_data: &[u8], -/// ) -> ProgramResult { -/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; -/// -/// let account_info_iter = &mut accounts.iter(); -/// -/// let payer = next_account_info(account_info_iter)?; -/// let new_account_pda = next_account_info(account_info_iter)?; -/// let system_account = next_account_info(account_info_iter)?; -/// -/// assert!(payer.is_signer); -/// assert!(payer.is_writable); -/// // Note that `new_account_pda` is not a signer yet. -/// // This program will sign for it via `invoke_signed`. -/// assert!(!new_account_pda.is_signer); -/// assert!(new_account_pda.is_writable); -/// assert!(program::check_id(system_account.key)); -/// -/// let new_account_seed = &instr.new_account_seed; -/// let new_account_bump_seed = instr.new_account_bump_seed; -/// -/// let rent = Rent::get()? -/// .minimum_balance(instr.space.try_into().expect("overflow")); -/// -/// invoke_signed( -/// &instruction::create_account( -/// payer.key, -/// new_account_pda.key, -/// rent, -/// instr.space, -/// &program::ID -/// ), -/// &[payer.clone(), new_account_pda.clone()], -/// &[&[ -/// payer.key.as_ref(), -/// new_account_seed, -/// &[new_account_bump_seed], -/// ]], -/// )?; -/// -/// Ok(()) -/// } -/// ``` -#[cfg(feature = "bincode")] -pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*from_pubkey, true), - AccountMeta::new(*to_pubkey, false), - ]; - Instruction::new_with_bincode(ID, &SystemInstruction::Transfer { lamports }, account_metas) -} - -#[cfg(feature = "bincode")] -pub fn transfer_with_seed( - from_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner) - from_base: &Pubkey, - from_seed: String, - from_owner: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*from_pubkey, false), - AccountMeta::new_readonly(*from_base, true), - AccountMeta::new(*to_pubkey, false), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::TransferWithSeed { - lamports, - from_seed, - from_owner: *from_owner, - }, - account_metas, - ) -} - -/// Allocate space for an account. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::Allocate`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// The transaction will fail if the account already has size greater than 0, -/// or if the requested size is greater than [`super::MAX_PERMITTED_DATA_LENGTH`]. -/// -/// # Required signers -/// -/// The `pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// These examples allocate space for an account, transfer it the minimum -/// balance for rent exemption, and assign the account to a program. -/// -/// # Example: client-side RPC -/// -/// This example submits the instructions from an RPC client. -/// It assigns the account to a provided program account. -/// The `payer` and `new_account` are signers. -/// -/// ``` -/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn create_account( -/// client: &RpcClient, -/// payer: &Keypair, -/// new_account: &Keypair, -/// owning_program: &Pubkey, -/// space: u64, -/// ) -> Result<()> { -/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?; -/// -/// let transfer_instr = instruction::transfer( -/// &payer.pubkey(), -/// &new_account.pubkey(), -/// rent, -/// ); -/// -/// let allocate_instr = instruction::allocate( -/// &new_account.pubkey(), -/// space, -/// ); -/// -/// let assign_instr = instruction::assign( -/// &new_account.pubkey(), -/// owning_program, -/// ); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// let tx = Transaction::new_signed_with_payer( -/// &[transfer_instr, allocate_instr, assign_instr], -/// Some(&payer.pubkey()), -/// &[payer, new_account], -/// blockhash, -/// ); -/// -/// let _sig = client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # let client = RpcClient::new(String::new()); -/// # let payer = Keypair::new(); -/// # let new_account = Keypair::new(); -/// # let owning_program = Pubkey::new_unique(); -/// # create_account(&client, &payer, &new_account, &owning_program, 1); -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -/// -/// ## Example: on-chain program -/// -/// This example submits the instructions from an on-chain Solana program. The -/// created account is a [program derived address][pda], funded by `payer`, and -/// assigned to the running program. The `payer` and `new_account_pda` are -/// signers, with `new_account_pda` being signed for virtually by the program -/// itself via [`invoke_signed`], `payer` being signed for by the client that -/// submitted the transaction. -/// -/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -/// -/// ``` -/// use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_account_info::{next_account_info, AccountInfo}; -/// use solana_cpi::invoke_signed; -/// use solana_program_entrypoint::entrypoint; -/// use solana_program_error::ProgramResult; -/// use solana_pubkey::Pubkey; -/// use solana_system_interface::{instruction, program}; -/// use solana_sysvar::{rent::Rent, Sysvar}; -/// -/// #[derive(BorshSerialize, BorshDeserialize, Debug)] -/// pub struct CreateAccountInstruction { -/// /// The PDA seed used to distinguish the new account from other PDAs -/// pub new_account_seed: [u8; 16], -/// /// The PDA bump seed -/// pub new_account_bump_seed: u8, -/// /// The amount of space to allocate for `new_account_pda` -/// pub space: u64, -/// } -/// -/// entrypoint!(process_instruction); -/// -/// fn process_instruction( -/// program_id: &Pubkey, -/// accounts: &[AccountInfo], -/// instruction_data: &[u8], -/// ) -> ProgramResult { -/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?; -/// -/// let account_info_iter = &mut accounts.iter(); -/// -/// let payer = next_account_info(account_info_iter)?; -/// let new_account_pda = next_account_info(account_info_iter)?; -/// let system_account = next_account_info(account_info_iter)?; -/// -/// assert!(payer.is_signer); -/// assert!(payer.is_writable); -/// // Note that `new_account_pda` is not a signer yet. -/// // This program will sign for it via `invoke_signed`. -/// assert!(!new_account_pda.is_signer); -/// assert!(new_account_pda.is_writable); -/// assert!(program::check_id(system_account.key)); -/// -/// let new_account_seed = &instr.new_account_seed; -/// let new_account_bump_seed = instr.new_account_bump_seed; -/// -/// let rent = Rent::get()? -/// .minimum_balance(instr.space.try_into().expect("overflow")); -/// -/// invoke_signed( -/// &instruction::create_account( -/// payer.key, -/// new_account_pda.key, -/// rent, -/// instr.space, -/// &program::ID -/// ), -/// &[payer.clone(), new_account_pda.clone()], -/// &[&[ -/// payer.key.as_ref(), -/// new_account_seed, -/// &[new_account_bump_seed], -/// ]], -/// )?; -/// -/// Ok(()) -/// } -/// ``` -#[cfg(feature = "bincode")] -pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { - let account_metas = vec![AccountMeta::new(*pubkey, true)]; - Instruction::new_with_bincode(ID, &SystemInstruction::Allocate { space }, account_metas) -} - -#[cfg(feature = "bincode")] -pub fn allocate_with_seed( - address: &Pubkey, // must match create_with_seed(base, seed, owner) - base: &Pubkey, - seed: &str, - space: u64, - owner: &Pubkey, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*address, false), - AccountMeta::new_readonly(*base, true), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::AllocateWithSeed { - base: *base, - seed: seed.to_string(), - space, - owner: *owner, - }, - account_metas, - ) -} - -/// Transfer lamports from an account owned by the system program to multiple accounts. -/// -/// This function produces a vector of [`Instruction`]s which must be submitted -/// in a [`Transaction`] or [invoked] to take effect, containing serialized -/// [`SystemInstruction::Transfer`]s. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// # Required signers -/// -/// The `from_pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// ## Example: client-side RPC -/// -/// This example performs multiple transfers in a single transaction. -/// -/// ``` -/// # use solana_example_mocks::{solana_sdk, solana_rpc_client}; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn transfer_lamports_to_many( -/// client: &RpcClient, -/// from: &Keypair, -/// to_and_amount: &[(Pubkey, u64)], -/// ) -> Result<()> { -/// let instrs = instruction::transfer_many(&from.pubkey(), to_and_amount); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// let tx = Transaction::new_signed_with_payer( -/// &instrs, -/// Some(&from.pubkey()), -/// &[from], -/// blockhash, -/// ); -/// -/// let _sig = client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # let from = Keypair::new(); -/// # let to_and_amount = vec![ -/// # (Pubkey::new_unique(), 1_000), -/// # (Pubkey::new_unique(), 2_000), -/// # (Pubkey::new_unique(), 3_000), -/// # ]; -/// # let client = RpcClient::new(String::new()); -/// # transfer_lamports_to_many(&client, &from, &to_and_amount); -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -/// -/// ## Example: on-chain program -/// -/// This example makes multiple transfers out of a "bank" account, -/// a [program derived address][pda] owned by the calling program. -/// This example submits the instructions from an on-chain Solana program. The -/// created account is a [program derived address][pda], and it is assigned to -/// the running program. The `payer` and `new_account_pda` are signers, with -/// `new_account_pda` being signed for virtually by the program itself via -/// [`invoke_signed`], `payer` being signed for by the client that submitted the -/// transaction. -/// -/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address -/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html -/// -/// ``` -/// # use borsh::{BorshDeserialize, BorshSerialize}; -/// use solana_account_info::{next_account_info, next_account_infos, AccountInfo}; -/// use solana_cpi::invoke_signed; -/// use solana_program_entrypoint::entrypoint; -/// use solana_program_error::ProgramResult; -/// use solana_pubkey::Pubkey; -/// use solana_system_interface::{instruction, program}; -/// -/// /// # Accounts -/// /// -/// /// - 0: bank_pda - writable -/// /// - 1: system_program - executable -/// /// - *: to - writable -/// # #[derive(BorshSerialize, BorshDeserialize, Debug)] -/// # #[borsh(crate = "borsh")] -/// pub struct TransferLamportsToManyInstruction { -/// pub bank_pda_bump_seed: u8, -/// pub amount_list: Vec, -/// } -/// -/// entrypoint!(process_instruction); -/// -/// fn process_instruction( -/// program_id: &Pubkey, -/// accounts: &[AccountInfo], -/// instruction_data: &[u8], -/// ) -> ProgramResult { -/// let instr = TransferLamportsToManyInstruction::deserialize(&mut &instruction_data[..])?; -/// -/// let account_info_iter = &mut accounts.iter(); -/// -/// let bank_pda = next_account_info(account_info_iter)?; -/// let bank_pda_bump_seed = instr.bank_pda_bump_seed; -/// let system_account = next_account_info(account_info_iter)?; -/// -/// assert!(program::check_id(system_account.key)); -/// -/// let to_accounts = next_account_infos(account_info_iter, account_info_iter.len())?; -/// -/// for to_account in to_accounts { -/// assert!(to_account.is_writable); -/// // ... do other verification ... -/// } -/// -/// let to_and_amount = to_accounts -/// .iter() -/// .zip(instr.amount_list.iter()) -/// .map(|(to, amount)| (*to.key, *amount)) -/// .collect::>(); -/// -/// let instrs = instruction::transfer_many(bank_pda.key, to_and_amount.as_ref()); -/// -/// for instr in instrs { -/// invoke_signed(&instr, accounts, &[&[b"bank", &[bank_pda_bump_seed]]])?; -/// } -/// -/// Ok(()) -/// } -/// ``` -#[cfg(feature = "bincode")] -pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec { - to_lamports - .iter() - .map(|(to_pubkey, lamports)| transfer(from_pubkey, to_pubkey, *lamports)) - .collect() -} - -#[cfg(feature = "bincode")] -pub fn create_nonce_account_with_seed( - from_pubkey: &Pubkey, - nonce_pubkey: &Pubkey, - base: &Pubkey, - seed: &str, - authority: &Pubkey, - lamports: u64, -) -> Vec { - vec![ - create_account_with_seed( - from_pubkey, - nonce_pubkey, - base, - seed, - lamports, - NONCE_STATE_SIZE as u64, - &ID, - ), - Instruction::new_with_bincode( - ID, - &SystemInstruction::InitializeNonceAccount(*authority), - vec![ - AccountMeta::new(*nonce_pubkey, false), - #[allow(deprecated)] - AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), - AccountMeta::new_readonly(RENT_ID, false), - ], - ), - ] -} - -/// Create an account containing a durable transaction nonce. -/// -/// This function produces a vector of [`Instruction`]s which must be submitted -/// in a [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::CreateAccount`] and -/// [`SystemInstruction::InitializeNonceAccount`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// A [durable transaction nonce][dtn] is a special account that enables -/// execution of transactions that have been signed in the past. -/// -/// Standard Solana transactions include a [recent blockhash][rbh] (sometimes -/// referred to as a _[nonce]_). During execution the Solana runtime verifies -/// the recent blockhash is approximately less than two minutes old, and that in -/// those two minutes no other identical transaction with the same blockhash has -/// been executed. These checks prevent accidental replay of transactions. -/// Consequently, it is not possible to sign a transaction, wait more than two -/// minutes, then successfully execute that transaction. -/// -/// [dtn]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces -/// [rbh]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash -/// [nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce -/// -/// Durable transaction nonces are an alternative to the standard recent -/// blockhash nonce. They are stored in accounts on chain, and every time they -/// are used their value is changed to a new value for their next use. The -/// runtime verifies that each durable nonce value is only used once, and there -/// are no restrictions on how "old" the nonce is. Because they are stored on -/// chain and require additional instructions to use, transacting with durable -/// transaction nonces is more expensive than with standard transactions. -/// -/// The value of the durable nonce is itself a blockhash and is accessible via -/// the [`blockhash`] field of [`nonce::state::Data`], which is deserialized -/// from the nonce account data. -/// -/// [`blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash -/// [`nonce::state::Data`]: https://docs.rs/solana-nonce/latest/solana_nonce/state/struct.Data.html -/// -/// The basic durable transaction nonce lifecycle is -/// -/// 1) Create the nonce account with the `create_nonce_account` instruction. -/// 2) Submit specially-formed transactions that include the -/// [`advance_nonce_account`] instruction. -/// 3) Destroy the nonce account by withdrawing its lamports with the -/// [`withdraw_nonce_account`] instruction. -/// -/// Nonce accounts have an associated _authority_ account, which is stored in -/// their account data, and can be changed with the [`authorize_nonce_account`] -/// instruction. The authority must sign transactions that include the -/// `advance_nonce_account`, `authorize_nonce_account` and -/// `withdraw_nonce_account` instructions. -/// -/// Nonce accounts are owned by the system program. -/// -/// This constructor creates a [`SystemInstruction::CreateAccount`] instruction -/// and a [`SystemInstruction::InitializeNonceAccount`] instruction. -/// -/// # Required signers -/// -/// The `from_pubkey` and `nonce_pubkey` signers must sign the transaction. -/// -/// # Examples -/// -/// Create a nonce account from an off-chain client: -/// -/// ``` -/// # use solana_example_mocks::solana_keypair; -/// # use solana_example_mocks::solana_signer; -/// # use solana_example_mocks::solana_rpc_client; -/// # use solana_example_mocks::solana_transaction; -/// use solana_keypair::Keypair; -/// use solana_nonce::state::State; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_signer::Signer; -/// use solana_system_interface::instruction; -/// use solana_transaction::Transaction; -/// use anyhow::Result; -/// -/// fn submit_create_nonce_account_tx( -/// client: &RpcClient, -/// payer: &Keypair, -/// ) -> Result<()> { -/// -/// let nonce_account = Keypair::new(); -/// -/// let nonce_rent = client.get_minimum_balance_for_rent_exemption(State::size())?; -/// let instr = instruction::create_nonce_account( -/// &payer.pubkey(), -/// &nonce_account.pubkey(), -/// &payer.pubkey(), // Make the fee payer the nonce account authority -/// nonce_rent, -/// ); -/// -/// let mut tx = Transaction::new_with_payer(&instr, Some(&payer.pubkey())); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// tx.try_sign(&[&nonce_account, payer], blockhash)?; -/// -/// client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let payer = Keypair::new(); -/// # submit_create_nonce_account_tx(&client, &payer)?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -#[cfg(feature = "bincode")] -pub fn create_nonce_account( - from_pubkey: &Pubkey, - nonce_pubkey: &Pubkey, - authority: &Pubkey, - lamports: u64, -) -> Vec { - vec![ - create_account( - from_pubkey, - nonce_pubkey, - lamports, - NONCE_STATE_SIZE as u64, - &ID, - ), - Instruction::new_with_bincode( - ID, - &SystemInstruction::InitializeNonceAccount(*authority), - vec![ - AccountMeta::new(*nonce_pubkey, false), - #[allow(deprecated)] - AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), - AccountMeta::new_readonly(RENT_ID, false), - ], - ), - ] -} - -/// Advance the value of a durable transaction nonce. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::AdvanceNonceAccount`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// Every transaction that relies on a durable transaction nonce must contain a -/// [`SystemInstruction::AdvanceNonceAccount`] instruction as the first -/// instruction in the [`Message`], as created by this function. When included -/// in the first position, the Solana runtime recognizes the transaction as one -/// that relies on a durable transaction nonce and processes it accordingly. The -/// [`Message::new_with_nonce`] function can be used to construct a `Message` in -/// the correct format without calling `advance_nonce_account` directly. -/// -/// When constructing a transaction that includes an `AdvanceNonceInstruction` -/// the [`recent_blockhash`] must be treated differently — instead of -/// setting it to a recent blockhash, the value of the nonce must be retrieved -/// and deserialized from the nonce account, and that value specified as the -/// "recent blockhash". A nonce account can be deserialized with the -/// [`solana_rpc_client_nonce_utils::data_from_account`][dfa] function. -/// -/// For further description of durable transaction nonces see -/// [`create_nonce_account`]. -/// -/// [`Message`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html -/// [`Message::new_with_nonce`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#method.new_with_nonce -/// [`recent_blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash -/// [dfa]: https://docs.rs/solana-rpc-client-nonce-utils/latest/solana_rpc_client_nonce_utils/fn.data_from_account.html -/// -/// # Required signers -/// -/// The `authorized_pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// Create and sign a transaction with a durable nonce: -/// -/// ``` -/// # use solana_example_mocks::solana_sdk; -/// # use solana_example_mocks::solana_rpc_client; -/// # use solana_example_mocks::solana_rpc_client_nonce_utils; -/// # use solana_sdk::account::Account; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// message::Message, -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use std::path::Path; -/// use anyhow::Result; -/// -/// fn create_transfer_tx_with_nonce( -/// client: &RpcClient, -/// nonce_account_pubkey: &Pubkey, -/// payer: &Keypair, -/// receiver: &Pubkey, -/// amount: u64, -/// tx_path: &Path, -/// ) -> Result<()> { -/// -/// let instr_transfer = instruction::transfer( -/// &payer.pubkey(), -/// receiver, -/// amount, -/// ); -/// -/// // In this example, `payer` is `nonce_account_pubkey`'s authority -/// let instr_advance_nonce_account = instruction::advance_nonce_account( -/// nonce_account_pubkey, -/// &payer.pubkey(), -/// ); -/// -/// // The `advance_nonce_account` instruction must be the first issued in -/// // the transaction. -/// let message = Message::new( -/// &[ -/// instr_advance_nonce_account, -/// instr_transfer -/// ], -/// Some(&payer.pubkey()), -/// ); -/// -/// let mut tx = Transaction::new_unsigned(message); -/// -/// // Sign the tx with nonce_account's `blockhash` instead of the -/// // network's latest blockhash. -/// # client.set_get_account_response(*nonce_account_pubkey, Account { -/// # lamports: 1, -/// # data: vec![0], -/// # owner: solana_sdk::system_program::ID, -/// # executable: false, -/// # rent_epoch: 1, -/// # }); -/// let nonce_account = client.get_account(nonce_account_pubkey)?; -/// let nonce_data = solana_rpc_client_nonce_utils::data_from_account(&nonce_account)?; -/// let blockhash = nonce_data.blockhash(); -/// -/// tx.try_sign(&[payer], blockhash)?; -/// -/// // Save the signed transaction locally for later submission. -/// save_tx_to_file(&tx_path, &tx)?; -/// -/// Ok(()) -/// } -/// # -/// # fn save_tx_to_file(path: &Path, tx: &Transaction) -> Result<()> { -/// # Ok(()) -/// # } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let nonce_account_pubkey = Pubkey::new_unique(); -/// # let payer = Keypair::new(); -/// # let receiver = Pubkey::new_unique(); -/// # create_transfer_tx_with_nonce(&client, &nonce_account_pubkey, &payer, &receiver, 1024, Path::new("new_tx"))?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -#[cfg(feature = "bincode")] -pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*nonce_pubkey, false), - #[allow(deprecated)] - AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), - AccountMeta::new_readonly(*authorized_pubkey, true), - ]; - Instruction::new_with_bincode(ID, &SystemInstruction::AdvanceNonceAccount, account_metas) -} - -/// Withdraw lamports from a durable transaction nonce account. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::WithdrawNonceAccount`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// Withdrawing the entire balance of a nonce account will cause the runtime to -/// destroy it upon successful completion of the transaction. -/// -/// Otherwise, nonce accounts must maintain a balance greater than or equal to -/// the minimum required for [rent exemption]. If the result of this instruction -/// would leave the nonce account with a balance less than required for rent -/// exemption, but also greater than zero, then the transaction will fail. -/// -/// [rent exemption]: https://solana.com/docs/core/accounts#rent-exemption -/// -/// This constructor creates a [`SystemInstruction::WithdrawNonceAccount`] -/// instruction. -/// -/// # Required signers -/// -/// The `authorized_pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// ``` -/// # use solana_example_mocks::solana_sdk; -/// # use solana_example_mocks::solana_rpc_client; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn submit_withdraw_nonce_account_tx( -/// client: &RpcClient, -/// nonce_account_pubkey: &Pubkey, -/// authorized_account: &Keypair, -/// ) -> Result<()> { -/// -/// let nonce_balance = client.get_balance(nonce_account_pubkey)?; -/// -/// let instr = instruction::withdraw_nonce_account( -/// nonce_account_pubkey, -/// &authorized_account.pubkey(), -/// &authorized_account.pubkey(), -/// nonce_balance, -/// ); -/// -/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey())); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// tx.try_sign(&[authorized_account], blockhash)?; -/// -/// client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let nonce_account_pubkey = Pubkey::new_unique(); -/// # let payer = Keypair::new(); -/// # submit_withdraw_nonce_account_tx(&client, &nonce_account_pubkey, &payer)?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -#[cfg(feature = "bincode")] -pub fn withdraw_nonce_account( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*nonce_pubkey, false), - AccountMeta::new(*to_pubkey, false), - #[allow(deprecated)] - AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), - AccountMeta::new_readonly(RENT_ID, false), - AccountMeta::new_readonly(*authorized_pubkey, true), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::WithdrawNonceAccount(lamports), - account_metas, - ) -} - -/// Change the authority of a durable transaction nonce account. -/// -/// This function produces an [`Instruction`] which must be submitted in a -/// [`Transaction`] or [invoked] to take effect, containing a serialized -/// [`SystemInstruction::AuthorizeNonceAccount`]. -/// -/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html -/// -/// This constructor creates a [`SystemInstruction::AuthorizeNonceAccount`] -/// instruction. -/// -/// # Required signers -/// -/// The `authorized_pubkey` signer must sign the transaction. -/// -/// # Examples -/// -/// ``` -/// # use solana_example_mocks::solana_sdk; -/// # use solana_example_mocks::solana_rpc_client; -/// use solana_rpc_client::rpc_client::RpcClient; -/// use solana_pubkey::Pubkey; -/// use solana_sdk::{ -/// signature::{Keypair, Signer}, -/// transaction::Transaction, -/// }; -/// use solana_system_interface::instruction; -/// use anyhow::Result; -/// -/// fn authorize_nonce_account_tx( -/// client: &RpcClient, -/// nonce_account_pubkey: &Pubkey, -/// authorized_account: &Keypair, -/// new_authority_pubkey: &Pubkey, -/// ) -> Result<()> { -/// -/// let instr = instruction::authorize_nonce_account( -/// nonce_account_pubkey, -/// &authorized_account.pubkey(), -/// new_authority_pubkey, -/// ); -/// -/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey())); -/// -/// let blockhash = client.get_latest_blockhash()?; -/// tx.try_sign(&[authorized_account], blockhash)?; -/// -/// client.send_and_confirm_transaction(&tx)?; -/// -/// Ok(()) -/// } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let nonce_account_pubkey = Pubkey::new_unique(); -/// # let payer = Keypair::new(); -/// # let new_authority_pubkey = Pubkey::new_unique(); -/// # authorize_nonce_account_tx(&client, &nonce_account_pubkey, &payer, &new_authority_pubkey)?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -#[cfg(feature = "bincode")] -pub fn authorize_nonce_account( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - new_authority: &Pubkey, -) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*nonce_pubkey, false), - AccountMeta::new_readonly(*authorized_pubkey, true), - ]; - Instruction::new_with_bincode( - ID, - &SystemInstruction::AuthorizeNonceAccount(*new_authority), - account_metas, - ) -} - -/// One-time idempotent upgrade of legacy nonce versions in order to bump -/// them out of chain blockhash domain. -#[cfg(feature = "bincode")] -pub fn upgrade_nonce_account(nonce_pubkey: Pubkey) -> Instruction { - let account_metas = vec![AccountMeta::new(nonce_pubkey, /*is_signer:*/ false)]; - Instruction::new_with_bincode(ID, &SystemInstruction::UpgradeNonceAccount, account_metas) -} - -#[cfg(feature = "bincode")] -#[cfg(test)] -mod tests { - use super::*; - use solana_sysvar_id::SysvarId; - - fn get_keys(instruction: &Instruction) -> Vec { - instruction.accounts.iter().map(|x| x.pubkey).collect() - } - - #[allow(deprecated)] - #[test] - fn test_constants() { - // Ensure that the constants are in sync with the solana program. - assert_eq!( - RECENT_BLOCKHASHES_ID, - solana_sysvar::recent_blockhashes::RecentBlockhashes::id(), - ); - - // Ensure that the constants are in sync with the solana rent. - assert_eq!(RENT_ID, solana_sysvar::rent::Rent::id()); - } - - #[test] - fn test_move_many() { - let alice_pubkey = Pubkey::new_unique(); - let bob_pubkey = Pubkey::new_unique(); - let carol_pubkey = Pubkey::new_unique(); - let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)]; - - let instructions = transfer_many(&alice_pubkey, &to_lamports); - assert_eq!(instructions.len(), 2); - assert_eq!(get_keys(&instructions[0]), vec![alice_pubkey, bob_pubkey]); - assert_eq!(get_keys(&instructions[1]), vec![alice_pubkey, carol_pubkey]); - } - - #[test] - fn test_create_nonce_account() { - let from_pubkey = Pubkey::new_unique(); - let nonce_pubkey = Pubkey::new_unique(); - let authorized = nonce_pubkey; - let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42); - assert_eq!(ixs.len(), 2); - let ix = &ixs[0]; - assert_eq!(ix.program_id, crate::program::ID); - let pubkeys: Vec<_> = ix.accounts.iter().map(|am| am.pubkey).collect(); - assert!(pubkeys.contains(&from_pubkey)); - assert!(pubkeys.contains(&nonce_pubkey)); - } -} diff --git a/interface/src/lib.rs b/interface/src/lib.rs deleted file mode 100644 index efeaf39..0000000 --- a/interface/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! The System program interface. - -#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -pub mod error; -pub mod instruction; -#[cfg(target_arch = "wasm32")] -mod wasm; - -#[cfg(test)] -static_assertions::const_assert!(MAX_PERMITTED_DATA_LENGTH <= u32::MAX as u64); -/// Maximum permitted size of account data (10 MiB). -/// -// SBF program entrypoint assumes that the max account data length -// will fit inside a u32. If this constant no longer fits in a u32, -// the entrypoint deserialization code in the SDK must be updated. -pub const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024; - -#[cfg(test)] -static_assertions::const_assert_eq!(MAX_PERMITTED_DATA_LENGTH, 10_485_760); -/// Maximum permitted size of new allocations per transaction, in bytes. -/// -/// The value was chosen such that at least one max sized account could be created, -/// plus some additional resize allocations. -pub const MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION: i64 = - MAX_PERMITTED_DATA_LENGTH as i64 * 2; - -pub mod program { - solana_pubkey::declare_id!("11111111111111111111111111111111"); -} diff --git a/interface/src/wasm.rs b/interface/src/wasm.rs deleted file mode 100644 index 8068bf2..0000000 --- a/interface/src/wasm.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! `SystemInstruction` Javascript interface -#![cfg(target_arch = "wasm32")] -#![allow(non_snake_case)] -use { - crate::instruction::{ - advance_nonce_account, allocate, allocate_with_seed, assign, assign_with_seed, - authorize_nonce_account, create_account, create_account_with_seed, create_nonce_account, - transfer, transfer_with_seed, withdraw_nonce_account, SystemInstruction, - }, - solana_instruction::Instruction, - solana_pubkey::Pubkey, - wasm_bindgen::prelude::*, -}; - -#[wasm_bindgen] -impl SystemInstruction { - pub fn createAccount( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - space: u64, - owner: &Pubkey, - ) -> Instruction { - create_account(from_pubkey, to_pubkey, lamports, space, owner) - } - - pub fn createAccountWithSeed( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, - base: &Pubkey, - seed: &str, - lamports: u64, - space: u64, - owner: &Pubkey, - ) -> Instruction { - create_account_with_seed(from_pubkey, to_pubkey, base, seed, lamports, space, owner) - } - - pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { - assign(pubkey, owner) - } - - pub fn assignWithSeed( - pubkey: &Pubkey, - base: &Pubkey, - seed: &str, - owner: &Pubkey, - ) -> Instruction { - assign_with_seed(pubkey, base, seed, owner) - } - - pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { - transfer(from_pubkey, to_pubkey, lamports) - } - - pub fn transferWithSeed( - from_pubkey: &Pubkey, - from_base: &Pubkey, - from_seed: String, - from_owner: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - ) -> Instruction { - transfer_with_seed( - from_pubkey, - from_base, - from_seed, - from_owner, - to_pubkey, - lamports, - ) - } - - pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { - allocate(pubkey, space) - } - - pub fn allocateWithSeed( - address: &Pubkey, - base: &Pubkey, - seed: &str, - space: u64, - owner: &Pubkey, - ) -> Instruction { - allocate_with_seed(address, base, seed, space, owner) - } - - pub fn createNonceAccount( - from_pubkey: &Pubkey, - nonce_pubkey: &Pubkey, - authority: &Pubkey, - lamports: u64, - ) -> js_sys::Array { - let instructions = create_nonce_account(from_pubkey, nonce_pubkey, authority, lamports); - instructions.into_iter().map(JsValue::from).collect() - } - - pub fn advanceNonceAccount(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { - advance_nonce_account(nonce_pubkey, authorized_pubkey) - } - - pub fn withdrawNonceAccount( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - ) -> Instruction { - withdraw_nonce_account(nonce_pubkey, authorized_pubkey, to_pubkey, lamports) - } - - pub fn authorizeNonceAccount( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - new_authority: &Pubkey, - ) -> Instruction { - authorize_nonce_account(nonce_pubkey, authorized_pubkey, new_authority) - } -}