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. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds end-to-end ephemeral-account support: new MagicBlock instructions (CreateEphemeralAccount, ResizeEphemeralAccount, CloseEphemeralAccount) and processor wiring; implements ephemeral account modules (validation, create, resize, close) including rent calculation and transfer logic; sponsor-aware CPI helpers and global sponsor PDA support; EPHEMERAL_VAULT funding and blacklist insertion; ScheduleCommit guard to reject ephemeral accounts; extensive integration tests; minor refactor (GlobalWriteLock → GlobalSyncLock) and dependency revision bumps. 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.
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.
9b65a26 to
37b9138
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@magicblock-processor/tests/ephemeral_accounts.rs`:
- Around line 638-663: The test test_create_with_wrong_vault_fails incorrectly
constructs the ephemeral account as a non-signer (AccountMeta::new(ephemeral,
false)), so validate_ephemeral_signer fails before vault validation; change the
ephemeral AccountMeta to be a signer (AccountMeta::new(ephemeral, true)) and
ensure you pass the original ephemeral keypair (the keypair returned by
env.create_account(0)) as a signer into execute_instruction so the runtime
supplies the signature—this lets validate_ephemeral_signer pass and the test
actually exercise the vault-check path.
In `@programs/guinea/src/lib.rs`:
- Around line 42-48: The code directly indexes instruction.accounts[0] and
allocates a Vec for signer seeds; update the invoke_with_sponsor block to first
safely get a mutable reference with instruction.accounts.get_mut(0) and return
an appropriate error if absent (instead of panicking), then avoid heap
allocation by using stack slices for the PDA seeds (e.g. create a &[u8] slice
for bump and a &[&[u8]] for seeds) and pass those as signer_seeds to
invoke_signed; reference the symbols instruction.accounts[0], sponsor_info,
expected_pda, bump, GLOBAL_SPONSOR_SEED and the invoke_with_sponsor call to
locate where to change.
In `@programs/magicblock/src/utils/instruction_context_frames.rs`:
- Around line 146-148: The code currently maps a missing current_frame_idx to
InstructionError::InvalidAccountData; change the error to a more accurate
variant and return it instead of panicking: replace the use of
current_frame_idx.ok_or(InstructionError::InvalidAccountData)? with a typed
error that reflects a missing transaction/instruction context (e.g.,
InstructionError::GenericError or a custom variant if available) before calling
InstructionContextFrames::new(frames, current_frame_idx) so the function
propagates a proper typed error rather than using expect or an imprecise
InvalidAccountData.
---
Duplicate comments:
In `@Cargo.toml`:
- Line 141: Replace the git rev pins with branch references for the
solana-account and solana-svm dependencies: change the solana-account entry to
use branch = "bmuddha/feat/ephemeral-flag" and the solana-svm entry to use
branch = "bmuddha/feat/ephemeral-accounts" (instead of rev = "<commit>"); apply
the same replacement in both the workspace.dependencies section and the
[patch.crates-io] section so both runtime and patched crates follow the feature
branches rather than fixed commit hashes.
In `@magicblock-api/src/fund_account.rs`:
- Around line 87-97: fund_ephemeral_vault currently unconditionally resets the
vault balance by calling fund_account (which delegates to
fund_account_with_data) and uses expect() on get_account; change it to preserve
existing lamports and avoid panics: call
accountsdb.get_account(&magic_program::EPHEMERAL_VAULT_PUBKEY) and if
Some(vault) update only metadata (vault.set_ephemeral(true);
vault.set_owner(magic_program::ID);) and re-insert without modifying lamports,
while if None create the account via fund_account/fund_account_with_data so
initial lamports are set; replace the expect() with proper error handling
(return Result or log+return) and ensure insert_account result is handled/errors
propagated rather than ignored. Use the symbols fund_ephemeral_vault,
fund_account, fund_account_with_data, EPHEMERAL_VAULT_PUBKEY, set_ephemeral,
set_owner, get_account, and insert_account to locate and modify the code.
In `@magicblock-processor/tests/ephemeral_accounts.rs`:
- Around line 960-991: In test_pda_wrong_owner_fails, the PDA AccountSharedData
is created with zero lamports which can trigger an insufficient-funds failure
before the owner check; change the AccountSharedData::new call that constructs
the PDA (the variable pda used with env.accountsdb.insert_account) to seed the
account with a non-zero balance (e.g., 1_000_000_000 lamports) so the
transaction fails for the wrong-owner path rather than lamport insufficiency.
- Around line 875-906: The test test_insufficient_balance_fails is missing a
call to init_sponsor so the payer isn't marked delegated, which causes the
validator to reject the transaction with InvalidAccountForFee before the magic
program's insufficient-balance logic runs; fix it by invoking init_sponsor(&env,
sponsor) (using the existing init_sponsor helper) after you set up the sponsor
(e.g., after init_vault(&env) or after modifying sponsor_acc) so the sponsor is
properly initialized/delegated before building and executing the transaction.
- Around line 1018-1072: In test_full_lifecycle, capture the sponsor and vault
account lamports before the create step (use env.get_payer().pubkey for sponsor
and EPHEMERAL_VAULT_PUBKEY for the vault or env.get_account) and after the close
step assert that the sponsor's lamports equal the pre-create value and that the
vault's lamports are unchanged or restored as expected; modify the test around
create_ephemeral_account_ix / close_ephemeral_account_ix to read and store
balances (via env.get_account or env.try_get_account) before the first
transaction and compare them to balances read after executing the close_ix to
detect any rent-accounting regressions.
- Around line 706-741: In test_resize_to_zero_size, capture sponsor and vault
lamports before the resize (using env.get_account(sponsor) and
env.get_account(EPHEMERAL_VAULT_PUBKEY)), compute the expected rent/refund for
the original 1000-byte allocation (using the same rent calculation/setup used
elsewhere in tests or env), then after calling resize_ephemeral_account_ix
assert that the sponsor's lamports increased by the expected refund and that the
vault's lamports decreased by the same amount (use env.get_account(...) again to
compare); reference symbols: test_resize_to_zero_size, env, sponsor, ephemeral,
EPHEMERAL_VAULT_PUBKEY, create_ephemeral_account_ix,
resize_ephemeral_account_ix.
In `@programs/magicblock/src/ephemeral_accounts/mod.rs`:
- Around line 63-77: The transfer_rent function currently performs debit/credit
calls even when amount == 0; add an early guard at the start of transfer_rent to
return Ok(()) when amount == 0 to avoid unnecessary
accounts::debit_instruction_account_at_index and
accounts::credit_instruction_account_at_index lookups for SPONSOR_IDX and
VAULT_IDX; keep the existing positive/negative branches unchanged otherwise.
In `@programs/magicblock/src/ephemeral_accounts/process_close.rs`:
- Line 26: The current call transfer_rent(transaction_context, -(refund as i64))
performs an unchecked cast from u64 to i64 which can overflow and flip sign;
replace it by performing a checked conversion from refund to i64 (e.g. use
i64::try_from(refund) or refund.try_into()) and negate the resulting i64 only on
success, returning or propagating an error if the conversion fails, then pass
that safe i64 into transfer_rent; this change should be applied around the
transfer_rent(...) invocation in process_close.rs referring to the refund
variable and transaction_context.
In `@programs/magicblock/src/ephemeral_accounts/process_create.rs`:
- Line 33: The unchecked cast of rent to i64 at the call
transfer_rent(transaction_context, rent as i64)? can overflow silently; replace
the direct cast with a checked conversion (e.g., use i64::try_from(rent) or
rent.try_into()) and handle the Result by returning an error (or mapping to the
existing error type) before calling transfer_rent; update the call to pass the
safely converted value and ensure any error path provides clear context
(reference: variable rent, function transfer_rent, and transaction_context).
In `@programs/magicblock/src/ephemeral_accounts/process_resize.rs`:
- Line 31: The subtraction currently does an unchecked narrowing: replace `let
delta = new_rent as i64 - old_rent as i64;` with a safe conversion using a wider
intermediate type (or checked conversion) to avoid silent wraparound — e.g.
compute `let diff = new_rent as i128 - old_rent as i128;` then clamp or try_into
to i64 (`if diff > i64::MAX { i64::MAX } else if diff < i64::MIN { i64::MIN }
else { diff as i64 }`) and assign to `delta`; alternatively use
`i64::try_from(...)`/`checked_sub` on `u64` and return an error on overflow.
Apply this change in process_resize.rs where `delta`, `new_rent`, and `old_rent`
are used.
In `@programs/magicblock/src/mutate_accounts/process_mutate_accounts.rs`:
- Around line 110-122: Add a short doc comment above the ephemeral-account guard
in process_mutate_accounts (the block that checks account.borrow().ephemeral()
and calls account_mods.remove(key) after fetching the key with
transaction_context.get_key_of_account_at_index) explaining the invariant:
callers must provide a (possibly dummy) entry in account_mods for every
ephemeral account because the earlier check comparing accounts_to_mod_len ==
account_mods_len happens before this loop; this makes it explicit that ephemeral
entries are intentionally removed here and must exist to pass the pre-check.
In `@test-kit/src/lib.rs`:
- Around line 265-283: Extract the repeated payer-selection logic into a private
helper fn next_payer(&self) -> &Keypair that performs the fetch_add + modulo on
self.payer_index and returns the selected payer; then replace the duplicate
blocks in build_transaction and build_transaction_with_signers to call
next_payer() for the payer, ensuring both methods use the same selection logic
and avoid duplication.
37b9138 to
d840bb2
Compare
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