Skip to content

Commit 190146a

Browse files
authored
feat: ephemeral is rent exempt (#29)
1 parent dbd63e0 commit 190146a

1 file changed

Lines changed: 61 additions & 5 deletions

File tree

svm/src/transaction_account_state_info.rs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,19 @@ impl TransactionAccountStateInfo {
2525
.accounts()
2626
.try_borrow(i as IndexOfAccount)
2727
{
28-
Some(get_account_rent_state(
29-
rent,
30-
account.lamports(),
31-
account.data().len(),
32-
))
28+
// Ephemeral accounts pay the reduced ephemeral rent
29+
// charged by the magic program at creation, not the
30+
// standard Rent, so the transaction rent check always
31+
// treats them as rent-exempt regardless of balance.
32+
if account.ephemeral() {
33+
Some(RentState::RentExempt)
34+
} else {
35+
Some(get_account_rent_state(
36+
rent,
37+
account.lamports(),
38+
account.data().len(),
39+
))
40+
}
3341
} else {
3442
None
3543
};
@@ -134,6 +142,54 @@ mod test {
134142
);
135143
}
136144

145+
#[test]
146+
fn test_new_ephemeral_is_rent_exempt() {
147+
let rent = Rent::default();
148+
let key1 = Keypair::new();
149+
let key2 = Keypair::new();
150+
let key3 = Keypair::new();
151+
let key4 = Keypair::new();
152+
153+
let message = Message {
154+
account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
155+
header: MessageHeader::default(),
156+
instructions: vec![
157+
CompiledInstruction {
158+
program_id_index: 1,
159+
accounts: vec![0],
160+
data: vec![],
161+
},
162+
CompiledInstruction {
163+
program_id_index: 1,
164+
accounts: vec![2],
165+
data: vec![],
166+
},
167+
],
168+
recent_blockhash: Hash::default(),
169+
};
170+
171+
let sanitized_message =
172+
SanitizedMessage::Legacy(LegacyMessage::new(message, &HashSet::new()));
173+
174+
// Writable account at index 0 is ephemeral and holds far less than the
175+
// standard rent-exempt minimum for its data length.
176+
let mut ephemeral = AccountSharedData::new(1, 200, &key1.pubkey());
177+
ephemeral.set_ephemeral(true);
178+
assert!(!rent.is_exempt(ephemeral.lamports(), ephemeral.data().len()));
179+
180+
let transaction_accounts = vec![
181+
(key1.pubkey(), ephemeral),
182+
(key2.pubkey(), AccountSharedData::default()),
183+
(key3.pubkey(), AccountSharedData::default()),
184+
];
185+
186+
let context = TransactionContext::new(transaction_accounts, rent.clone(), 20, 20);
187+
let result = TransactionAccountStateInfo::new(&context, &sanitized_message, &rent);
188+
// Despite being below the standard rent floor, the ephemeral account is
189+
// reported as rent-exempt so the transaction rent check passes.
190+
assert_eq!(result[0].rent_state, Some(RentState::RentExempt));
191+
}
192+
137193
#[test]
138194
#[should_panic(expected = "message and transaction context out of sync, fatal")]
139195
fn test_new_panic() {

0 commit comments

Comments
 (0)