@@ -623,64 +623,67 @@ def test_handles_expensive_abi_calls_with_ensure_budget(self) -> None:
623623 assert len (result .confirmation .get ("inner-txns" , [])) == 9 # type: ignore[union-attr]
624624 self ._assert_min_fee (self .app_client1 , params , expected_fee )
625625
626- def test_readonly_handles_expensive_abi_calls_with_ensure_budget (self ) -> None :
627- """Test fee handling with expensive readonly ABI method calls that use ensure_budget to op-up"""
626+ @pytest .mark .parametrize ("cover_inner_fees" , [True , False ])
627+ def test_readonly_uses_fixed_opcode_budget_without_op_up_inner_transactions (self , cover_inner_fees : bool ) -> None : # noqa: FBT001
628+ """Test that readonly calls use fixed opcode budget and don't require inner transactions for op-ups
629+ regardless of fee coverage setting"""
628630
629- expected_fee = 12_000
630631 params = AppClientMethodCallParams (
631632 method = "burn_ops_readonly" ,
632- args = [6200 ],
633- max_fee = AlgoAmount .from_micro_algo (expected_fee ),
633+ args = [6200 ], # This would normally require op-ups via inner transactions
634634 )
635- result = self .app_client1 .send .call (params , send_params = {"cover_app_call_inner_transaction_fees" : True })
636-
637- assert result .transaction .raw .fee == expected_fee
638- assert len (result .confirmation .get ("inner-txns" , [])) == 9 # type: ignore[union-attr]
639-
640- def test_readonly_throws_when_no_max_fee (self ) -> None :
641- """Test that error is thrown when no max fee is supplied for a readonly method call"""
642- with pytest .raises (
643- ValueError ,
644- match = "Please provide a `max_fee` for the transaction when `cover_app_call_inner_transaction_fees` is enabled" , # noqa: E501
645- ):
646- self .app_client1 .send .call (
647- AppClientMethodCallParams (
648- method = "burn_ops_readonly" ,
649- args = [6200 ],
650- ),
651- send_params = {
652- "cover_app_call_inner_transaction_fees" : True ,
653- },
654- )
655-
656- def test_readonly_throws_when_inner_fees_not_covered (self ) -> None :
657- """Test that error is thrown when a readonly method call inner transaction fees are not covered"""
658-
659- expected_fee = 7000
660- params = AppClientMethodCallParams (
661- method = "burn_ops_readonly" ,
662- args = [6200 ],
663- max_fee = AlgoAmount .from_micro_algo (expected_fee ),
635+ result = self .app_client1 .send .call (
636+ params , send_params = {"cover_app_call_inner_transaction_fees" : cover_inner_fees }
637+ )
638+
639+ # No op-up inner transactions needed regardless of fee coverage setting
640+ assert len (result .confirmation .get ("inner-txns" , [])) == 0 # type: ignore[union-attr]
641+ assert result .transaction .raw .fee == 1_000
642+ assert len (result .tx_ids ) == 1
643+
644+ def test_readonly_alters_fee_handling_inner_transactions (self ) -> None :
645+ """Test that inner transaction can be covered using the max_fee"""
646+ # Force `send_inners_with_fees` to be marked as readonly
647+ for method in self .app_client1 ._app_spec .methods : # noqa: SLF001
648+ if method .name == "send_inners_with_fees" :
649+ method .readonly = True
650+ break
651+
652+ # The expected_fee differs to non readonly method call,as we don't want to
653+ # run simulate twice (once for resolving the minimum fee and once for the actual transaction result).
654+ # Because no fees are actually paid with readonly calls,
655+ # we simply use the maxFee value (if set) and skip any minimum fee calculations.
656+ # If this method is running in a non readonly context, the minimum fee would be calculated as 5300n.
657+ expected_fee = 12_000
658+ result = self .app_client1 .send .call (
659+ AppClientMethodCallParams (
660+ method = "send_inners_with_fees" ,
661+ args = [self .app_client2 .app_id , self .app_client3 .app_id , [1000 , 0 , 200 , 0 , [500 , 0 ]]],
662+ max_fee = AlgoAmount .from_micro_algo (expected_fee ),
663+ ),
664+ send_params = {
665+ "cover_app_call_inner_transaction_fees" : True ,
666+ },
664667 )
665668
666- with pytest .raises (Exception , match = "fee too small" ):
667- self .app_client1 .send .call (
668- params ,
669- send_params = {
670- "cover_app_call_inner_transaction_fees" : False ,
671- },
672- )
669+ assert result .transaction .raw .fee == expected_fee
670+ assert len (result .confirmation .get ("inner-txns" , [])) == 4 # type: ignore[union-attr]
671+ assert len (result .tx_ids ) == 1
673672
674673 def test_readonly_throws_when_max_fee_too_small (self ) -> None :
675674 """Test that error is thrown when readonly method call max fee is too small to cover inner transaction fees"""
676675
677- expected_fee = 7000
676+ # Force `send_inners_with_fees` to be marked as readonly
677+ for method in self .app_client1 ._app_spec .methods : # noqa: SLF001
678+ if method .name == "send_inners_with_fees" :
679+ method .readonly = True
680+ break
681+
678682 params = AppClientMethodCallParams (
679- method = "burn_ops_readonly " ,
680- args = [6200 ],
681- max_fee = AlgoAmount .from_micro_algo (expected_fee ),
683+ method = "send_inners_with_fees " ,
684+ args = [self . app_client2 . app_id , self . app_client3 . app_id , [ 1000 , 0 , 200 , 0 , [ 500 , 0 ]] ],
685+ max_fee = AlgoAmount .from_micro_algo (2000 ),
682686 )
683-
684687 with pytest .raises (ValueError , match = "Fees were too small. You may need to increase the transaction `maxFee`." ):
685688 self .app_client1 .send .call (
686689 params ,
0 commit comments