@@ -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