From 1afada66effa9dc82b5c1bd948b27c756e0164ac Mon Sep 17 00:00:00 2001 From: Jon C Date: Wed, 20 Aug 2025 23:48:40 +0200 Subject: [PATCH] interface: Remove from the repo #### Problem As part of the upgrade to SDK v3, the cyclical dependency between the sdk crates and the system interface were too much of an annoyance, so the interface was migrated back into the SDK with https://github.com/anza-xyz/solana-sdk/pull/264. But the interface code still lives in this repo as well. #### Summary of changes Remove the interface crate and all references to it. --- .github/workflows/main.yml | 62 -- Cargo.toml | 1 - Makefile | 3 - interface/Cargo.toml | 63 -- interface/README.md | 104 -- interface/src/error.rs | 175 ---- interface/src/instruction.rs | 1718 ---------------------------------- interface/src/lib.rs | 31 - interface/src/wasm.rs | 118 --- 9 files changed, 2275 deletions(-) delete mode 100644 interface/Cargo.toml delete mode 100644 interface/README.md delete mode 100644 interface/src/error.rs delete mode 100644 interface/src/instruction.rs delete mode 100644 interface/src/lib.rs delete mode 100644 interface/src/wasm.rs 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) - } -}