Conversation
Manual Deploy AvailableYou can trigger a manual deploy of this PR branch to testnet: Alternative: Comment
Comment updated automatically when the PR is synchronized. |
📝 WalkthroughWalkthroughAdds end-to-end ephemeral-account support: new MagicBlock instruction variants (CreateEphemeralAccount, ResizeEphemeralAccount, CloseEphemeralAccount) and processor wiring; implements ephemeral account modules (validation, create, resize, close) with rent calculation and transfer logic; sponsor-aware CPI helpers and global sponsor PDA support; EPHEMERAL_VAULT funding and blacklisting; ScheduleCommit guard to reject ephemeral accounts; extensive integration tests covering lifecycle and failure cases; dependency rev bumps and a small lock-type rename (GlobalWriteLock → GlobalSyncLock). Assessment against linked issues
Out-of-scope changes
Suggested reviewers
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@magicblock-api/src/fund_account.rs`:
- Around line 86-101: fund_ephemeral_vault currently relies on unwrap() after
get_account and leaves the vault owner as Default::default(); change it so the
created vault is explicitly owned by the magic program and remove the unwrap:
after calling fund_account(...) fetch the account with get_account and handle
the Option without unwrap (e.g., if let Some(mut vault) =
accountsdb.get_account(&magic_program::EPHEMERAL_VAULT_PUBKEY) {
vault.set_owner(magic_program::ID); vault.set_delegated(true);
vault.set_ephemeral(true); let _ =
accountsdb.insert_account(&magic_program::EPHEMERAL_VAULT_PUBKEY, &vault); }
else { /* return or log an error per existing error-handling conventions */ }),
ensuring you reference fund_ephemeral_vault, fund_account,
magic_program::EPHEMERAL_VAULT_PUBKEY and magic_program::ID.
In `@magicblock-magic-program-api/src/instruction.rs`:
- Around line 119-127: Update the ResizeEphemeralAccount account reference docs
to include the vault account that process_resize.rs expects at index 2;
specifically, add a third bullet like "**2.** `[WRITE]` Vault account
(holds/receives lamports for rent transfer)" so the documentation matches the
implementation that transfers rent to/from the vault when resizing in
process_resize.rs and references the ResizeEphemeralAccount variant.
- Around line 129-134: Update the documentation for the CloseEphemeralAccount
enum variant to include the missing vault account at index 2: annotate that
**2.** is a `[WRITE]` Vault account (source of rent refund). Locate the
CloseEphemeralAccount variant (symbol: CloseEphemeralAccount) and modify its doc
comment so the account reference list includes the new third line describing the
vault account, matching the implementation in process_close.rs that debits the
vault to refund rent.
In `@magicblock-processor/tests/ephemeral_accounts.rs`:
- Around line 902-958: The test function test_create_with_pda_sponsor is
documented as IGNORED but currently runs; either add the #[ignore] attribute
above fn test_create_with_pda_sponsor to actually skip it, or update the block
comment to remove the "NOTE: This test is IGNORED" text so the comment matches
current behavior; locate the test by the function name
test_create_with_pda_sponsor in ephemeral_accounts.rs and apply one of these two
fixes consistently.
In `@programs/magicblock/src/ephemeral_accounts/process_close.rs`:
- Around line 65-68: The close logic currently zeroes lamports, sets owner and
resizes the account but doesn't clear the ephemeral flag; call
acc.set_ephemeral(false) on the same mutable account handle (the same variable
used with ephemeral.borrow_mut()/acc) as part of the close sequence (e.g.,
alongside set_lamports, set_owner, resize) so the ephemeral flag is cleared and
the pubkey can be reused.
In `@programs/magicblock/src/ephemeral_accounts/process_create.rs`:
- Around line 35-40: Replace the owner-only vault check with an explicit pubkey
equality check: use get_instruction_pubkey_with_idx(transaction_context, 2)? to
fetch the account pubkey and compare it against EPHEMERAL_VAULT_PUBKEY,
returning InstructionError::InvalidAccountOwner (or existing error) if they
differ. Update this check in process_create
(ephemeral_accounts/process_create.rs), and apply the same replacement in
process_resize (ephemeral_accounts/process_resize.rs) and process_close
(ephemeral_accounts/process_close.rs), ensuring you remove or keep but do not
rely solely on the previous *vault.borrow().owner() check and instead validate
the exact vault pubkey.
- Around line 23-25: The code calls validate_cpi_only(transaction_context)? but
later uses an unbounded data_len for account resize and rent_for; add a
module-level constant (e.g., MAX_DATA_LEN) and check data_len <= MAX_DATA_LEN at
the start of process_create (or the function handling resizing) and return a
proper error if exceeded. Also use checked arithmetic (checked_add/checked_mul)
when computing total account size or rent to avoid overflow before calling
resize/rent_for; reference the data_len argument, the resize call, and rent_for
call so the guard is applied immediately before those operations.
In `@programs/magicblock/src/ephemeral_accounts/process_resize.rs`:
- Around line 15-77: The resize logic in process_resize_ephemeral_account
performs lamport transfers even when delta == 0; update the function to
short-circuit that work by checking delta == 0 after computing delta
(rent_for(new_data_len) - rent_for(old_len)) and, if zero, skip both
debit/credit calls and proceed directly to ephemeral.borrow_mut().resize(...)
and logging; reference the delta variable and the debit/credit calls
(accounts::debit_instruction_account_at_index and
accounts::credit_instruction_account_at_index) so the zero-case branch is the
only change.
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@magicblock-magic-program-api/src/instruction.rs`:
- Around line 119-134: Update the account reference docs for both
ResizeEphemeralAccount and CloseEphemeralAccount to include the vault account
(account index 2) that the processors expect; specifically state a third
account: **2.** `[WRITE]` Vault account (holds ephemeral assets/rent) so
clients/SDKs supply three accounts when invoking the ResizeEphemeralAccount {
new_data_len: usize } and CloseEphemeralAccount instructions (this matches the
processors that read account index 2).
- Around line 114-127: Change the variable-width usize fields used in the
CreateEphemeralAccount and ResizeEphemeralAccount enum variants to fixed-width
u64 so instruction payloads are architecture independent: replace
CreateEphemeralAccount::data_len: usize and
ResizeEphemeralAccount::new_data_len: usize with u64, update any
serialization/deserialization, constructor calls, and tests that reference these
fields (e.g., where CreateEphemeralAccount or ResizeEphemeralAccount instances
are created or pattern-matched) to use u64 values.
In `@magicblock-processor/tests/ephemeral_accounts.rs`:
- Around line 902-958: The comment says the test is ignored but it actually
runs; update the test declaration for fn test_create_with_pda_sponsor to match
the comment by adding the #[ignore] attribute above the async test (or
alternatively update the comment to remove the "IGNORED" wording). Locate the
async test function test_create_with_pda_sponsor in tests/ephemeral_accounts.rs
and either prepend #[ignore] to the #[tokio::test] or change the note text so it
no longer claims the test is ignored.
In `@programs/guinea/src/lib.rs`:
- Around line 205-319: The three handlers create_ephemeral_account,
resize_ephemeral_account, and close_ephemeral_account duplicate the same
validation of magic_program_info and vault_info; extract that logic into a small
helper (e.g., validate_ephemeral_accounts) that accepts &AccountInfo for
magic_program_info and vault_info and returns ProgramResult, checking
magic_program_info.key against magicblock_magic_program_api::ID and
vault_info.key against EPHEMERAL_VAULT_PUBKEY; then call
validate_ephemeral_accounts(magic_program_info, vault_info)? at the start of
each handler and remove the duplicated checks.
In `@programs/magicblock/src/ephemeral_accounts/process_close.rs`:
- Around line 69-72: The close path leaves the account's ephemeral marker set so
subsequent CreateEphemeralAccount calls will reject it; update the close logic
in process_close.rs (the ephemeral / acc variable) to explicitly clear the
ephemeral flag after zeroing the lamports/size (i.e., after acc.set_lamports(0)
and acc.resize(0, 0)) by calling the account API that removes the ephemeral
marker (for example, acc.clear_ephemeral() or acc.set_ephemeral(false) depending
on the available method).
In `@programs/magicblock/src/ephemeral_accounts/process_create.rs`:
- Around line 44-50: The code currently only checks lamports and acc.ephemeral()
allowing zero-lamport accounts owned by other programs to be reinitialized;
update the validation after acquiring ephemeral (via
accounts::get_instruction_account_with_idx and acc) to also verify the account
owner is the system program (i.e., the account is system-owned) and if not
return InstructionError::InvalidAccountData, so only system-owned zero-lamport
accounts can be converted to ephemeral.
- Around line 37-41: Add a strict pubkey check for the vault: after fetching
vault via accounts::get_instruction_account_with_idx(transaction_context, 2)?
and after verifying owner() == id(), also compare the account's pubkey to the
canonical EPHEMERAL_VAULT_PUBKEY constant and return an appropriate
InstructionError if it doesn't match; apply the same pubkey equality validation
in the corresponding resize and close handlers so only the designated
EPHEMERAL_VAULT_PUBKEY can be used as the vault.
In `@programs/magicblock/src/ephemeral_accounts/process_resize.rs`:
- Around line 35-52: Add an ownership check before performing the resize: after
obtaining ephemeral via
accounts::get_instruction_account_with_idx(transaction_context, 1) and
confirming ephemeral.borrow().ephemeral(), verify that
ephemeral.borrow().owner() equals the current program id from the transaction
context (e.g., transaction_context.get_program_id() or equivalent accessor) and
return InstructionError::IllegalOwner or InstructionError::InvalidAccountOwner
if it does not match; only proceed with computing old_len/delta and
debiting/crediting when the owner check passes.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@programs/magicblock/src/ephemeral_accounts/process_create.rs`:
- Around line 36-41: Update the comment above the account check to correctly
state that the target account must be empty and owned by the system program —
replace "not owned by system program" with wording such as "owned by system
program"; the relevant code to locate is the
accounts::get_instruction_account_with_idx(...) call and the acc.lamports()/
*acc.owner() == system_program::ID check (variables ephemeral and acc).
In `@test-integration/Cargo.toml`:
- Line 107: The Cargo.toml now lists the dependency test-ephemeral-accounts but
the corresponding crate is missing; either create a new crate named
test-ephemeral-accounts (with a Cargo.toml and src/lib.rs/bin) in the workspace
and add "test-ephemeral-accounts" to the workspace members, ensuring the
dependency path matches the crate directory name, or remove the
test-ephemeral-accounts = { path = "test-ephemeral-accounts" } entry from the
test-integration Cargo.toml until the crate is implemented.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@Cargo.toml`:
- Line 141: Replace the Git commit hash refs with the feature branch names for
the ephemeral accounts dependencies: change the solana-account dependency that
currently uses rev = "6eae52b" to use branch = "bmuddha/feat/ephemeral-flag",
and change the solana-svm dependency that uses rev = "bdbaac86" to use branch =
"bmuddha/feat/ephemeral-accounts"; update the Cargo.toml entries for the
solana-account and solana-svm dependencies accordingly so they reference the
named branches instead of commit hashes.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@programs/magicblock/src/ephemeral_accounts/validation.rs`:
- Around line 40-52: The doc comment for validate_sponsor is out of sync: the
function only checks that the sponsor (account index 0) is a signer, not
ownership for PDAs. Update the function doc above validate_sponsor to state that
the sponsor (index 0) — whether an on-curve key or a PDA — must be a signer
(PDAs may be signed via invoke_signed), and remove or replace the incorrect line
about PDA ownership; keep references to validate_sponsor, transaction_context,
get_current_instruction_context, and is_instruction_account_signer to locate the
code.
- Around line 15-37: InstructionContextFrames::try_from currently calls
.expect(...) on current_frame_idx which can panic; change that to return a
proper InstructionError instead of panicking: in the try_from implementation
(the code that sets current_frame_idx) replace the .expect(...) usage on the
current_frame_idx variable with logic that returns
Err(InstructionError::InvalidAccountData) (or another appropriate
InstructionError) when the index is None, so callers like get_caller_program_id
and validate_cpi_only that use InstructionContextFrames::try_from will receive
an error rather than causing a panic.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@programs/magicblock/src/ephemeral_accounts/validation.rs`:
- Around line 26-38: In validate_cpi_only, replace the manual
InstructionContextFrames::try_from(...) +
find_program_id_of_parent_of_current_instruction() check with a call to the
existing get_caller_program_id(transaction_context) helper: call
get_caller_program_id(transaction_context) and if it returns None return
Err(InstructionError::IncorrectProgramId), otherwise Ok(()). This removes
duplication and reuses the shared logic encapsulated by get_caller_program_id.
In `@programs/magicblock/src/mutate_accounts/process_mutate_accounts.rs`:
- Around line 110-114: Add a log message where the code skips ephemeral
accounts: inside the loop guarding account.borrow().ephemeral() in
process_mutate_accounts (the block that currently does "if
account.borrow().ephemeral() { continue; }"), call ic_msg! to record that an
ephemeral account was encountered and skipped and include an identifying value
from the account (e.g., account id/address or a debug string via
account.borrow()) so the log shows which account was skipped for traceability.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@programs/magicblock/src/mutate_accounts/process_mutate_accounts.rs`:
- Around line 110-121: The code currently skips ephemeral accounts via the
continue inside process_mutate_accounts (the account.borrow().ephemeral()
branch) but does not remove their entry from account_mods, leaving stale entries
and breaking the accounts_to_mod_len == account_mods_len invariant; fix by
removing the ephemeral account's modification from account_mods (call
account_mods.remove(&account_key) or account_mods.remove(account_key) using the
key obtained from transaction_context.get_key_of_account_at_index) before
continuing so bookkeeping stays consistent—alternatively, if ephemeral accounts
should never be present, replace the silent continue with an explicit error
return (e.g., Err(...)) to fail loudly.
In `@test-kit/src/lib.rs`:
- Around line 265-283: Extract the duplicated payer-selection logic into a
private helper (e.g., fn next_payer(&self) -> &Keypair) that performs the
fetch_add on self.payer_index and returns &self.payers[index %
self.payers.len()]; then replace the inline payer selection in both
build_transaction and build_transaction_with_signers with a call to
next_payer(), ensuring both functions still use the returned payer for signers
and for Transaction::new_signed_with_payer.
| /// Builds a transaction with additional signers beyond the payer. | ||
| pub fn build_transaction_with_signers( | ||
| &self, | ||
| ixs: &[Instruction], | ||
| additional_signers: &[&Keypair], | ||
| ) -> Transaction { | ||
| let payer = { | ||
| let index = self.payer_index.fetch_add(1, Ordering::Relaxed); | ||
| &self.payers[index % self.payers.len()] | ||
| }; | ||
| let mut signers: Vec<&Keypair> = vec![payer]; | ||
| signers.extend(additional_signers); | ||
| Transaction::new_signed_with_payer( | ||
| ixs, | ||
| Some(&payer.pubkey()), | ||
| &signers, | ||
| self.ledger.latest_blockhash(), | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting payer selection to reduce duplication with build_transaction.
The payer-index logic (lines 271-273) is duplicated from build_transaction (lines 253-255). A small private helper (e.g., next_payer(&self) -> &Keypair) would keep both methods in sync.
♻️ Suggested helper extraction
+ fn next_payer(&self) -> &Keypair {
+ let index = self.payer_index.fetch_add(1, Ordering::Relaxed);
+ &self.payers[index % self.payers.len()]
+ }
+
pub fn build_transaction(&self, ixs: &[Instruction]) -> Transaction {
- let payer = {
- let index = self.payer_index.fetch_add(1, Ordering::Relaxed);
- &self.payers[index % self.payers.len()]
- };
+ let payer = self.next_payer();
Transaction::new_signed_with_payer(
ixs,
Some(&payer.pubkey()),
&[payer],
self.ledger.latest_blockhash(),
)
}
pub fn build_transaction_with_signers(
&self,
ixs: &[Instruction],
additional_signers: &[&Keypair],
) -> Transaction {
- let payer = {
- let index = self.payer_index.fetch_add(1, Ordering::Relaxed);
- &self.payers[index % self.payers.len()]
- };
+ let payer = self.next_payer();
let mut signers: Vec<&Keypair> = vec![payer];
signers.extend(additional_signers);
Transaction::new_signed_with_payer(🤖 Prompt for AI Agents
In `@test-kit/src/lib.rs` around lines 265 - 283, Extract the duplicated
payer-selection logic into a private helper (e.g., fn next_payer(&self) ->
&Keypair) that performs the fetch_add on self.payer_index and returns
&self.payers[index % self.payers.len()]; then replace the inline payer selection
in both build_transaction and build_transaction_with_signers with a call to
next_payer(), ensuring both functions still use the returned payer for signers
and for Transaction::new_signed_with_payer.
There was a problem hiding this comment.
not related to the PR
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@programs/magicblock/src/ephemeral_accounts/process_create.rs`:
- Line 33: The unchecked cast "rent as i64" in the transfer call
(transfer_rent(transaction_context, rent as i64) in process_create.rs) can wrap
if rent grows; replace the cast with a fallible conversion using
i64::try_from(rent).map_err(|_| InstructionError::ArithmeticOverflow)? and pass
that result into transfer_rent. Apply the same pattern in process_close.rs and
process_resize.rs where rent (or any u64 → i64) is cast, using try_from and
mapping failures to InstructionError::ArithmeticOverflow to avoid silent
negative values.
In `@programs/magicblock/src/ephemeral_accounts/validation.rs`:
- Around line 42-49: The validate_vault function currently returns
InstructionError::InvalidAccountOwner when the vault pubkey doesn't match, which
is semantically misleading; change the returned error to
InstructionError::InvalidAccountData (or a domain-specific custom error) in
validate_vault (referencing validate_vault, VAULT_IDX, and
EPHEMERAL_VAULT_PUBKEY) so the mismatch is treated as bad data/identity rather
than ownership, and update any tests or upstream callers that rely on the
previous error variant to expect the new error.
de42924 to
9b65a26
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@magicblock-accounts-db/src/lib.rs`:
- Around line 172-176: The removal check in the upsert path compares
account.owner() to Pubkey::default(), but the close processor in
process_close.rs sets the owner to system_program::id(), so closed ephemeral
accounts are not removed; update the condition in the upsert/remove branch (the
block that calls self.index.remove(pubkey, txn!())) to compare account.owner()
against system_program::id() when account.ephemeral() is true, or alternatively
change the close processor in process_close.rs to set the owner to
Pubkey::default(); pick one approach and make the owner comparison and removal
behavior consistent between the close processor and the code that calls
self.index.remove.
In `@magicblock-processor/tests/ephemeral_accounts.rs`:
- Around line 960-991: The test test_pda_wrong_owner_fails currently creates a
PDA account with 0 lamports so the transaction might fail due to insufficient
funds instead of the owner check; update the account creation that uses
Pubkey::find_program_address and AccountSharedData::new/insert_account to give
the PDA a nonzero balance (e.g., 1_000_000_000 lamports) before inserting it
into env.accountsdb so the failure path exercised by env.execute_transaction and
the assertion targets the ownership validation rather than funding.
- Around line 706-741: Update test_resize_to_zero_size to assert lamport
conservation like other resize tests: capture sponsor and vault balances before
the resize (use env.get_payer().pubkey and EPHEMERAL_VAULT_PUBKEY via
env.get_account), perform the resize using resize_ephemeral_account_ix, then
assert the sponsor's lamports increased by the rent-exempt amount freed for the
original 1000-byte account and the vault's lamports decreased by the same amount
so total lamports are conserved; also assert ephemeral_after.data().len() == 0
as before. Use the same balance-check logic/helpers used in
test_resize_smaller_via_cpi to compute the expected refund/debit amounts so the
zero-size edge case is validated consistently.
- Around line 875-906: The test test_insufficient_balance_fails is missing the
delegated sponsor setup so it may fail for the wrong reason; call
init_sponsor(&env, sponsor) before constructing the
create_ephemeral_account_ix/transaction to ensure the payer is marked as
delegated and the failure (assert result.is_err()) is due to insufficient
balance. Make sure you still set the sponsor lamports to 100 (via
sponsor_acc.set_lamports(100); sponsor_acc.commit();) and then call
init_sponsor(&env, sponsor) so the sponsorship state is initialized while
preserving the low balance before building and executing the transaction.
- Around line 1018-1072: In test_full_lifecycle, capture the initial lamports
for the sponsor and EPHEMERAL_VAULT_PUBKEY (use env.get_account or
env.try_get_account) before calling create_ephemeral_account_ix, then after
executing the close_ephemeral_account_ix assert that the sponsor's lamports
equal the captured initial sponsor lamports and that the vault's lamports are
restored (or unchanged) as expected; update references around
ExecutionTestEnv::new_with_config, init_vault, init_sponsor,
create_ephemeral_account_ix, close_ephemeral_account_ix, and env.try_get_account
to read balances before the create step and perform the equality assertions
after the close step.
In `@programs/magicblock/src/ephemeral_accounts/mod.rs`:
- Around line 63-77: In transfer_rent, avoid doing unnecessary account ops when
amount == 0 by adding an early return at the start of the function: check if
amount == 0 and return Ok(()) before computing abs or calling
accounts::debit_instruction_account_at_index /
accounts::credit_instruction_account_at_index for SPONSOR_IDX and VAULT_IDX;
this prevents redundant account lookups/writes when resize produces delta == 0.
| // The ephemeral account has been closed, remove it from DB | ||
| if account.ephemeral() && account.owner() == &Pubkey::default() { | ||
| self.index.remove(pubkey, txn!())?; | ||
| return Ok(()); | ||
| } |
There was a problem hiding this comment.
Ephemeral account removal condition won't match the close processor's output.
The close processor in process_close.rs sets the owner to system_program::id(), but this check expects Pubkey::default(). Closed ephemeral accounts will never hit this removal path — they'll instead fall through to the normal upsert logic, persisting a stale zero-lamport entry.
Either align this condition with what the close processor actually writes, or update the close processor to set Pubkey::default() as the owner.
- if account.ephemeral() && account.owner() == &Pubkey::default() {
+ if account.ephemeral() && account.owner() == &solana_sdk_ids::system_program::id() {🤖 Prompt for AI Agents
In `@magicblock-accounts-db/src/lib.rs` around lines 172 - 176, The removal check
in the upsert path compares account.owner() to Pubkey::default(), but the close
processor in process_close.rs sets the owner to system_program::id(), so closed
ephemeral accounts are not removed; update the condition in the upsert/remove
branch (the block that calls self.index.remove(pubkey, txn!())) to compare
account.owner() against system_program::id() when account.ephemeral() is true,
or alternatively change the close processor in process_close.rs to set the owner
to Pubkey::default(); pick one approach and make the owner comparison and
removal behavior consistent between the close processor and the code that calls
self.index.remove.
| #[tokio::test] | ||
| async fn test_resize_to_zero_size() { | ||
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | ||
| init_vault(&env); | ||
|
|
||
| let sponsor = env.get_payer().pubkey; | ||
| init_sponsor(&env, sponsor); | ||
| let ephemeral = env.create_account(0); | ||
|
|
||
| // Create with initial size | ||
| let ix = create_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| 1000, | ||
| ); | ||
| let txn = env.build_transaction_with_signers(&[ix], &[&ephemeral]); | ||
| assert!(env.execute_transaction(txn).await.is_ok()); | ||
|
|
||
| // Resize to zero | ||
| let ix = resize_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| 0, | ||
| ); | ||
| let txn = env.build_transaction(&[ix]); | ||
|
|
||
| let result = env.execute_transaction(txn).await; | ||
| assert!(result.is_ok(), "Should allow resize to zero"); | ||
|
|
||
| let ephemeral_after = env.get_account(ephemeral.pubkey()); | ||
| assert_eq!(ephemeral_after.data().len(), 0); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
test_resize_to_zero_size lacks lamport conservation checks.
Other resize tests (e.g., test_resize_smaller_via_cpi) thoroughly verify that sponsor gets refunded, vault is debited, and total lamports are conserved. This test only checks data().len() == 0 but doesn't verify that the full rent for the original 1000 bytes was refunded to the sponsor and debited from the vault.
Consider adding the same conservation/balance assertions for consistency and to catch potential rent-accounting bugs on the zero-size edge case.
🤖 Prompt for AI Agents
In `@magicblock-processor/tests/ephemeral_accounts.rs` around lines 706 - 741,
Update test_resize_to_zero_size to assert lamport conservation like other resize
tests: capture sponsor and vault balances before the resize (use
env.get_payer().pubkey and EPHEMERAL_VAULT_PUBKEY via env.get_account), perform
the resize using resize_ephemeral_account_ix, then assert the sponsor's lamports
increased by the rent-exempt amount freed for the original 1000-byte account and
the vault's lamports decreased by the same amount so total lamports are
conserved; also assert ephemeral_after.data().len() == 0 as before. Use the same
balance-check logic/helpers used in test_resize_smaller_via_cpi to compute the
expected refund/debit amounts so the zero-size edge case is validated
consistently.
| #[tokio::test] | ||
| async fn test_insufficient_balance_fails() { | ||
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | ||
| init_vault(&env); | ||
|
|
||
| // Use the payer but give it very low balance | ||
| let sponsor = env.get_payer().pubkey; | ||
|
|
||
| // Set sponsor balance to very low amount | ||
| let mut sponsor_acc = env.get_account(sponsor); | ||
| sponsor_acc.set_lamports(100); // Only 100 lamports | ||
| sponsor_acc.commit(); | ||
|
|
||
| let ephemeral = env.create_account(0); | ||
|
|
||
| let data_len = 1000; | ||
| let required_rent = rent_for(data_len); | ||
|
|
||
| assert!(required_rent > 100, "Rent should exceed sponsor balance"); | ||
|
|
||
| let ix = create_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| data_len, | ||
| ); | ||
| let txn = env.build_transaction_with_signers(&[ix], &[&ephemeral]); | ||
|
|
||
| let result = env.execute_transaction(txn).await; | ||
| assert!(result.is_err(), "Should fail - insufficient balance"); | ||
| } |
There was a problem hiding this comment.
Missing init_sponsor — test may pass for the wrong reason.
Every other test that uses the payer as sponsor calls init_sponsor(&env, sponsor) to mark the account as delegated. This test skips that step, so the transaction may fail because the sponsor is not delegated rather than because of insufficient balance. The assertion result.is_err() would still pass, but the test wouldn't be validating what it claims to.
Proposed fix
// Set sponsor balance to very low amount
let mut sponsor_acc = env.get_account(sponsor);
sponsor_acc.set_lamports(100); // Only 100 lamports
+ sponsor_acc.set_delegated(true);
sponsor_acc.commit();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[tokio::test] | |
| async fn test_insufficient_balance_fails() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| // Use the payer but give it very low balance | |
| let sponsor = env.get_payer().pubkey; | |
| // Set sponsor balance to very low amount | |
| let mut sponsor_acc = env.get_account(sponsor); | |
| sponsor_acc.set_lamports(100); // Only 100 lamports | |
| sponsor_acc.commit(); | |
| let ephemeral = env.create_account(0); | |
| let data_len = 1000; | |
| let required_rent = rent_for(data_len); | |
| assert!(required_rent > 100, "Rent should exceed sponsor balance"); | |
| let ix = create_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| data_len, | |
| ); | |
| let txn = env.build_transaction_with_signers(&[ix], &[&ephemeral]); | |
| let result = env.execute_transaction(txn).await; | |
| assert!(result.is_err(), "Should fail - insufficient balance"); | |
| } | |
| #[tokio::test] | |
| async fn test_insufficient_balance_fails() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| // Use the payer but give it very low balance | |
| let sponsor = env.get_payer().pubkey; | |
| // Set sponsor balance to very low amount | |
| let mut sponsor_acc = env.get_account(sponsor); | |
| sponsor_acc.set_lamports(100); // Only 100 lamports | |
| sponsor_acc.set_delegated(true); | |
| sponsor_acc.commit(); | |
| let ephemeral = env.create_account(0); | |
| let data_len = 1000; | |
| let required_rent = rent_for(data_len); | |
| assert!(required_rent > 100, "Rent should exceed sponsor balance"); | |
| let ix = create_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| data_len, | |
| ); | |
| let txn = env.build_transaction_with_signers(&[ix], &[&ephemeral]); | |
| let result = env.execute_transaction(txn).await; | |
| assert!(result.is_err(), "Should fail - insufficient balance"); | |
| } |
🤖 Prompt for AI Agents
In `@magicblock-processor/tests/ephemeral_accounts.rs` around lines 875 - 906, The
test test_insufficient_balance_fails is missing the delegated sponsor setup so
it may fail for the wrong reason; call init_sponsor(&env, sponsor) before
constructing the create_ephemeral_account_ix/transaction to ensure the payer is
marked as delegated and the failure (assert result.is_err()) is due to
insufficient balance. Make sure you still set the sponsor lamports to 100 (via
sponsor_acc.set_lamports(100); sponsor_acc.commit();) and then call
init_sponsor(&env, sponsor) so the sponsorship state is initialized while
preserving the low balance before building and executing the transaction.
| #[tokio::test] | ||
| async fn test_pda_wrong_owner_fails() { | ||
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | ||
| init_vault(&env); | ||
|
|
||
| // Create a PDA owned by system program (not guinea) | ||
| let (pda, _bump) = Pubkey::find_program_address( | ||
| &[b"wrong"], | ||
| &solana_sdk_ids::system_program::id(), | ||
| ); | ||
| let mut account = | ||
| AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id()); | ||
| account.set_delegated(true); | ||
| let _ = env.accountsdb.insert_account(&pda, &account); | ||
|
|
||
| let ephemeral = env.create_account(0); | ||
|
|
||
| let ix = Instruction::new_with_bincode( | ||
| guinea::ID, | ||
| &GuineaInstruction::CreateEphemeralAccount { data_len: 1000 }, | ||
| vec![ | ||
| AccountMeta::new_readonly(magicblock_magic_program_api::ID, false), | ||
| AccountMeta::new(pda, false), // NOT a signer, not owned by guinea | ||
| AccountMeta::new(ephemeral.pubkey(), false), | ||
| AccountMeta::new(EPHEMERAL_VAULT_PUBKEY, false), | ||
| ], | ||
| ); | ||
| let txn = env.build_transaction(&[ix]); | ||
|
|
||
| let result = env.execute_transaction(txn).await; | ||
| assert!(result.is_err(), "Should fail - PDA not owned by caller"); | ||
| } |
There was a problem hiding this comment.
Verify the PDA wrong-owner test exercises the right validation path.
The PDA is derived from &[b"wrong"] with system_program::id() as the program, and the account is created with 0 lamports. Since the sponsor has no balance, the test could also fail due to insufficient funds rather than the owner check. Consider giving the PDA account a sufficient balance (e.g., 1_000_000_000) to ensure the test isolates the ownership validation.
Proposed fix
let mut account =
- AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id());
+ AccountSharedData::new(1_000_000_000, 0, &solana_sdk_ids::system_program::id());
account.set_delegated(true);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[tokio::test] | |
| async fn test_pda_wrong_owner_fails() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| // Create a PDA owned by system program (not guinea) | |
| let (pda, _bump) = Pubkey::find_program_address( | |
| &[b"wrong"], | |
| &solana_sdk_ids::system_program::id(), | |
| ); | |
| let mut account = | |
| AccountSharedData::new(0, 0, &solana_sdk_ids::system_program::id()); | |
| account.set_delegated(true); | |
| let _ = env.accountsdb.insert_account(&pda, &account); | |
| let ephemeral = env.create_account(0); | |
| let ix = Instruction::new_with_bincode( | |
| guinea::ID, | |
| &GuineaInstruction::CreateEphemeralAccount { data_len: 1000 }, | |
| vec![ | |
| AccountMeta::new_readonly(magicblock_magic_program_api::ID, false), | |
| AccountMeta::new(pda, false), // NOT a signer, not owned by guinea | |
| AccountMeta::new(ephemeral.pubkey(), false), | |
| AccountMeta::new(EPHEMERAL_VAULT_PUBKEY, false), | |
| ], | |
| ); | |
| let txn = env.build_transaction(&[ix]); | |
| let result = env.execute_transaction(txn).await; | |
| assert!(result.is_err(), "Should fail - PDA not owned by caller"); | |
| } | |
| #[tokio::test] | |
| async fn test_pda_wrong_owner_fails() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| // Create a PDA owned by system program (not guinea) | |
| let (pda, _bump) = Pubkey::find_program_address( | |
| &[b"wrong"], | |
| &solana_sdk_ids::system_program::id(), | |
| ); | |
| let mut account = | |
| AccountSharedData::new(1_000_000_000, 0, &solana_sdk_ids::system_program::id()); | |
| account.set_delegated(true); | |
| let _ = env.accountsdb.insert_account(&pda, &account); | |
| let ephemeral = env.create_account(0); | |
| let ix = Instruction::new_with_bincode( | |
| guinea::ID, | |
| &GuineaInstruction::CreateEphemeralAccount { data_len: 1000 }, | |
| vec![ | |
| AccountMeta::new_readonly(magicblock_magic_program_api::ID, false), | |
| AccountMeta::new(pda, false), // NOT a signer, not owned by guinea | |
| AccountMeta::new(ephemeral.pubkey(), false), | |
| AccountMeta::new(EPHEMERAL_VAULT_PUBKEY, false), | |
| ], | |
| ); | |
| let txn = env.build_transaction(&[ix]); | |
| let result = env.execute_transaction(txn).await; | |
| assert!(result.is_err(), "Should fail - PDA not owned by caller"); | |
| } |
🤖 Prompt for AI Agents
In `@magicblock-processor/tests/ephemeral_accounts.rs` around lines 960 - 991, The
test test_pda_wrong_owner_fails currently creates a PDA account with 0 lamports
so the transaction might fail due to insufficient funds instead of the owner
check; update the account creation that uses Pubkey::find_program_address and
AccountSharedData::new/insert_account to give the PDA a nonzero balance (e.g.,
1_000_000_000 lamports) before inserting it into env.accountsdb so the failure
path exercised by env.execute_transaction and the assertion targets the
ownership validation rather than funding.
| #[tokio::test] | ||
| async fn test_full_lifecycle() { | ||
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | ||
| init_vault(&env); | ||
|
|
||
| let sponsor = env.get_payer().pubkey; | ||
| init_sponsor(&env, sponsor); | ||
| let ephemeral = env.create_account(0); | ||
|
|
||
| // Create → Resize (grow) → Resize (shrink) → Close | ||
| let create_ix = create_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| 1000, | ||
| ); | ||
| let txn = env.build_transaction_with_signers(&[create_ix], &[&ephemeral]); | ||
| assert!(env.execute_transaction(txn).await.is_ok()); | ||
|
|
||
| let grow_ix = resize_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| 2000, | ||
| ); | ||
| let txn = env.build_transaction(&[grow_ix]); | ||
| assert!(env.execute_transaction(txn).await.is_ok()); | ||
|
|
||
| let shrink_ix = resize_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| 500, | ||
| ); | ||
| let txn = env.build_transaction(&[shrink_ix]); | ||
| assert!(env.execute_transaction(txn).await.is_ok()); | ||
|
|
||
| let close_ix = close_ephemeral_account_ix( | ||
| magicblock_magic_program_api::ID, | ||
| sponsor, | ||
| ephemeral.pubkey(), | ||
| EPHEMERAL_VAULT_PUBKEY, | ||
| ); | ||
| let txn = env.build_transaction(&[close_ix]); | ||
| assert!(env.execute_transaction(txn).await.is_ok()); | ||
|
|
||
| // Closed ephemeral accounts are removed from the DB | ||
| assert!( | ||
| env.try_get_account(ephemeral.pubkey()).is_none(), | ||
| "Closed ephemeral account should be removed from DB" | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding end-to-end balance check in full lifecycle test.
This lifecycle test (create → grow → shrink → close) correctly verifies the account is removed from DB, but doesn't check that the sponsor's final balance equals the initial balance (since all rent should be refunded on close). Adding a single conservation assertion at the end would strengthen this as a regression test for the complete flow.
Suggestion
+ // After full lifecycle, sponsor should have all rent refunded
+ let sponsor_final = env.get_account(sponsor);
+ let vault_final = env.get_account(EPHEMERAL_VAULT_PUBKEY);
+ // Capture sponsor_initial and vault_initial before the create step above
+ // and assert conservation here
+
// Closed ephemeral accounts are removed from the DB
assert!(
env.try_get_account(ephemeral.pubkey()).is_none(),
"Closed ephemeral account should be removed from DB"
);You'd need to capture sponsor and vault lamports before the create step and assert equality at the end.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[tokio::test] | |
| async fn test_full_lifecycle() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| let sponsor = env.get_payer().pubkey; | |
| init_sponsor(&env, sponsor); | |
| let ephemeral = env.create_account(0); | |
| // Create → Resize (grow) → Resize (shrink) → Close | |
| let create_ix = create_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 1000, | |
| ); | |
| let txn = env.build_transaction_with_signers(&[create_ix], &[&ephemeral]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let grow_ix = resize_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 2000, | |
| ); | |
| let txn = env.build_transaction(&[grow_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let shrink_ix = resize_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 500, | |
| ); | |
| let txn = env.build_transaction(&[shrink_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let close_ix = close_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| ); | |
| let txn = env.build_transaction(&[close_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| // Closed ephemeral accounts are removed from the DB | |
| assert!( | |
| env.try_get_account(ephemeral.pubkey()).is_none(), | |
| "Closed ephemeral account should be removed from DB" | |
| ); | |
| } | |
| #[tokio::test] | |
| async fn test_full_lifecycle() { | |
| let env = ExecutionTestEnv::new_with_config(0, 1, false); | |
| init_vault(&env); | |
| let sponsor = env.get_payer().pubkey; | |
| init_sponsor(&env, sponsor); | |
| let ephemeral = env.create_account(0); | |
| // Capture initial balances before lifecycle operations | |
| let sponsor_initial = env.get_account(sponsor).lamports; | |
| let vault_initial = env.get_account(EPHEMERAL_VAULT_PUBKEY).lamports; | |
| // Create → Resize (grow) → Resize (shrink) → Close | |
| let create_ix = create_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 1000, | |
| ); | |
| let txn = env.build_transaction_with_signers(&[create_ix], &[&ephemeral]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let grow_ix = resize_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 2000, | |
| ); | |
| let txn = env.build_transaction(&[grow_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let shrink_ix = resize_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| 500, | |
| ); | |
| let txn = env.build_transaction(&[shrink_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| let close_ix = close_ephemeral_account_ix( | |
| magicblock_magic_program_api::ID, | |
| sponsor, | |
| ephemeral.pubkey(), | |
| EPHEMERAL_VAULT_PUBKEY, | |
| ); | |
| let txn = env.build_transaction(&[close_ix]); | |
| assert!(env.execute_transaction(txn).await.is_ok()); | |
| // Closed ephemeral accounts are removed from the DB | |
| assert!( | |
| env.try_get_account(ephemeral.pubkey()).is_none(), | |
| "Closed ephemeral account should be removed from DB" | |
| ); | |
| // After full lifecycle, sponsor and vault should maintain balance conservation | |
| let sponsor_final = env.get_account(sponsor).lamports; | |
| let vault_final = env.get_account(EPHEMERAL_VAULT_PUBKEY).lamports; | |
| assert_eq!(sponsor_initial, sponsor_final, "Sponsor balance should be conserved after full lifecycle"); | |
| assert_eq!(vault_initial, vault_final, "Vault balance should be conserved after full lifecycle"); | |
| } |
🤖 Prompt for AI Agents
In `@magicblock-processor/tests/ephemeral_accounts.rs` around lines 1018 - 1072,
In test_full_lifecycle, capture the initial lamports for the sponsor and
EPHEMERAL_VAULT_PUBKEY (use env.get_account or env.try_get_account) before
calling create_ephemeral_account_ix, then after executing the
close_ephemeral_account_ix assert that the sponsor's lamports equal the captured
initial sponsor lamports and that the vault's lamports are restored (or
unchanged) as expected; update references around
ExecutionTestEnv::new_with_config, init_vault, init_sponsor,
create_ephemeral_account_ix, close_ephemeral_account_ix, and env.try_get_account
to read balances before the create step and perform the equality assertions
after the close step.
| fn transfer_rent( | ||
| tc: &TransactionContext, | ||
| amount: i64, | ||
| ) -> Result<(), InstructionError> { | ||
| if amount > 0 { | ||
| let abs = amount as u64; | ||
| accounts::debit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | ||
| accounts::credit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | ||
| } else { | ||
| let abs = amount.unsigned_abs(); | ||
| accounts::credit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | ||
| accounts::debit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | ||
| } | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
transfer_rent silently no-ops when amount == 0.
When amount is exactly 0, the else branch calls credit/debit with 0 lamports. This is harmless but does unnecessary account lookups and writes. Consider adding an early return for the zero case, especially since resize can produce delta == 0.
Optional: skip zero transfers
fn transfer_rent(
tc: &TransactionContext,
amount: i64,
) -> Result<(), InstructionError> {
+ if amount == 0 {
+ return Ok(());
+ }
if amount > 0 {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn transfer_rent( | |
| tc: &TransactionContext, | |
| amount: i64, | |
| ) -> Result<(), InstructionError> { | |
| if amount > 0 { | |
| let abs = amount as u64; | |
| accounts::debit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | |
| accounts::credit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | |
| } else { | |
| let abs = amount.unsigned_abs(); | |
| accounts::credit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | |
| accounts::debit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | |
| } | |
| Ok(()) | |
| } | |
| fn transfer_rent( | |
| tc: &TransactionContext, | |
| amount: i64, | |
| ) -> Result<(), InstructionError> { | |
| if amount == 0 { | |
| return Ok(()); | |
| } | |
| if amount > 0 { | |
| let abs = amount as u64; | |
| accounts::debit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | |
| accounts::credit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | |
| } else { | |
| let abs = amount.unsigned_abs(); | |
| accounts::credit_instruction_account_at_index(tc, SPONSOR_IDX, abs)?; | |
| accounts::debit_instruction_account_at_index(tc, VAULT_IDX, abs)?; | |
| } | |
| Ok(()) | |
| } |
🤖 Prompt for AI Agents
In `@programs/magicblock/src/ephemeral_accounts/mod.rs` around lines 63 - 77, In
transfer_rent, avoid doing unnecessary account ops when amount == 0 by adding an
early return at the start of the function: check if amount == 0 and return
Ok(()) before computing abs or calling
accounts::debit_instruction_account_at_index /
accounts::credit_instruction_account_at_index for SPONSOR_IDX and VAULT_IDX;
this prevents redundant account lookups/writes when resize produces delta == 0.
Summary
Implements ephemeral accounts — zero-balance accounts that live only in the ephemeral rollup, with rent paid by a sponsor at 32 lamports/byte (~109x cheaper than Solana).
This enables cheap temporary storage for gaming sessions, caching, computation intermediate results, etc. — without base chain prerequisites.
What's included:
CreateEphemeralAccount,ResizeEphemeralAccount,CloseEphemeralAccountSee MIMD-0016 for full spec (rent analysis, safeguards, example usage).
Compatibility
Testing
Checklist
Summary by CodeRabbit
New Features
Validation & Safety
Tests
Chores