@@ -55,7 +55,7 @@ type ProxyCoreView = (
5555 ) ,
5656 ( i128 , i128 , bool ) ,
5757 ( i128 , i128 , i128 , i128 ) ,
58- ( i128 , u64 , i128 , i128 ) ,
58+ ( i128 , u64 , i128 , i128 , i128 ) ,
5959) ;
6060type ProxyPolicyView = (
6161 soroban_sdk:: Vec < u32 > ,
@@ -174,6 +174,20 @@ impl<'a> VaultProxy<'a> {
174174 . 7 )
175175 }
176176
177+ fn max_deposit ( & self ) -> Result < i128 , templar_soroban_runtime:: ContractError > {
178+ Ok ( self
179+ . view ( soroban_sdk:: Address :: generate ( self . env ) , 0 , 0 ) ?
180+ . 2
181+ . 2 )
182+ }
183+
184+ fn max_mint ( & self ) -> Result < i128 , templar_soroban_runtime:: ContractError > {
185+ Ok ( self
186+ . view ( soroban_sdk:: Address :: generate ( self . env ) , 0 , 0 ) ?
187+ . 2
188+ . 3 )
189+ }
190+
177191 fn execute (
178192 & self ,
179193 command : & VaultCommand ,
@@ -587,6 +601,165 @@ fn soroban_contract_proxy_view_does_not_inflate_from_zero_fee_anchor(
587601 } ) ;
588602}
589603
604+ #[ rstest]
605+ fn soroban_contract_proxy_view_reports_fee_growth_cap_and_checked_fee_fields (
606+ soroban_contract_fixture : SorobanContractFixture ,
607+ ) {
608+ let env = soroban_contract_fixture. env ;
609+ let contract_id = soroban_contract_fixture. contract_id ;
610+ let proxy = VaultProxy :: new ( & env) ;
611+ let owner = soroban_sdk:: Address :: generate ( & env) ;
612+
613+ env. as_contract ( & contract_id, || {
614+ let fees = FeesSpec :: new (
615+ FeeSlot :: new ( Wad :: one ( ) / 5 , Address ( [ 1u8 ; 32 ] ) ) ,
616+ FeeSlot :: new ( Wad :: one ( ) / 10 , Address ( [ 2u8 ; 32 ] ) ) ,
617+ Some ( Wad :: one ( ) / 20 ) ,
618+ ) ;
619+ let mut bytes = Vec :: with_capacity ( 113 ) ;
620+ bytes. extend_from_slice ( & fees. performance . fee_wad . as_u128_trunc ( ) . to_le_bytes ( ) ) ;
621+ bytes. extend_from_slice ( fees. performance . recipient . as_bytes ( ) ) ;
622+ bytes. extend_from_slice ( & fees. management . fee_wad . as_u128_trunc ( ) . to_le_bytes ( ) ) ;
623+ bytes. extend_from_slice ( fees. management . recipient . as_bytes ( ) ) ;
624+ bytes. push ( 1 ) ;
625+ bytes. extend_from_slice (
626+ & fees
627+ . max_total_assets_growth_rate
628+ . expect ( "growth cap configured" )
629+ . as_u128_trunc ( )
630+ . to_le_bytes ( ) ,
631+ ) ;
632+ env. storage ( ) . instance ( ) . set (
633+ & templar_soroban_runtime:: contract:: VaultDataKey :: FeesSpec ,
634+ & Bytes :: from_slice ( & env, & bytes) ,
635+ ) ;
636+
637+ let mut storage = SorobanStorage :: new ( & env) ;
638+ storage
639+ . save_state ( & VaultState {
640+ total_assets : 1_000 ,
641+ total_shares : 1_000 ,
642+ idle_assets : 0 ,
643+ external_assets : 1_000 ,
644+ fee_anchor : FeeAccrualAnchor :: new (
645+ i128:: MAX as u128 + 1 ,
646+ templar_vault_kernel:: TimestampNs ( 123 ) ,
647+ ) ,
648+ ..Default :: default ( )
649+ } )
650+ . expect ( "save state" ) ;
651+
652+ assert_eq ! (
653+ proxy. view( owner. clone( ) , 0 , 0 ) ,
654+ Err ( templar_soroban_runtime:: ContractError :: ConversionOverflow )
655+ ) ;
656+
657+ storage
658+ . save_state ( & VaultState {
659+ total_assets : 1_000 ,
660+ total_shares : 1_000 ,
661+ idle_assets : 0 ,
662+ external_assets : 1_000 ,
663+ fee_anchor : FeeAccrualAnchor :: new ( 900 , templar_vault_kernel:: TimestampNs ( 123 ) ) ,
664+ ..Default :: default ( )
665+ } )
666+ . expect ( "save state" ) ;
667+
668+ let fee_info = proxy. view ( owner, 0 , 0 ) . unwrap ( ) . 0 . 3 ;
669+ assert_eq ! ( fee_info. 0 , 900 ) ;
670+ assert_eq ! ( fee_info. 1 , 123 ) ;
671+ assert_eq ! ( fee_info. 2 , ( Wad :: one( ) / 10 ) . as_u128_trunc( ) as i128 ) ;
672+ assert_eq ! ( fee_info. 3 , ( Wad :: one( ) / 5 ) . as_u128_trunc( ) as i128 ) ;
673+ assert_eq ! ( fee_info. 4 , ( Wad :: one( ) / 20 ) . as_u128_trunc( ) as i128 ) ;
674+ } ) ;
675+ }
676+
677+ #[ rstest]
678+ fn soroban_contract_proxy_view_max_deposit_and_mint_respect_opposite_headroom (
679+ soroban_contract_fixture : SorobanContractFixture ,
680+ ) {
681+ let env = soroban_contract_fixture. env ;
682+ let contract_id = soroban_contract_fixture. contract_id ;
683+ let proxy = VaultProxy :: new ( & env) ;
684+
685+ env. as_contract ( & contract_id, || {
686+ let mut storage = SorobanStorage :: new ( & env) ;
687+ storage
688+ . save_state ( & VaultState {
689+ total_assets : 2 ,
690+ total_shares : 1 ,
691+ idle_assets : 2 ,
692+ ..Default :: default ( )
693+ } )
694+ . expect ( "save state" ) ;
695+
696+ assert_eq ! ( proxy. max_deposit( ) . unwrap( ) , i128 :: MAX ) ;
697+ assert_eq ! ( proxy. max_mint( ) . unwrap( ) , i128 :: MAX ) ;
698+
699+ storage
700+ . save_state ( & VaultState {
701+ total_assets : 1 ,
702+ total_shares : 2 ,
703+ idle_assets : 1 ,
704+ ..Default :: default ( )
705+ } )
706+ . expect ( "save state" ) ;
707+
708+ let expected_max_deposit = ( ( ( i128:: MAX as u128 ) * 2 ) / 3 ) as i128 ;
709+ assert_eq ! ( proxy. max_deposit( ) . unwrap( ) , expected_max_deposit) ;
710+ assert_eq ! ( proxy. max_mint( ) . unwrap( ) , i128 :: MAX ) ;
711+ } ) ;
712+ }
713+
714+ #[ rstest]
715+ fn soroban_contract_fee_aware_preview_fails_on_supply_overflow (
716+ soroban_contract_fixture : SorobanContractFixture ,
717+ ) {
718+ let env = soroban_contract_fixture. env ;
719+ let contract_id = soroban_contract_fixture. contract_id ;
720+ let proxy = VaultProxy :: new ( & env) ;
721+
722+ env. ledger ( ) . set ( LedgerInfo {
723+ timestamp : 100 ,
724+ protocol_version : 25 ,
725+ ..Default :: default ( )
726+ } ) ;
727+
728+ env. as_contract ( & contract_id, || {
729+ let fees = FeesSpec :: new (
730+ FeeSlot :: new ( Wad :: zero ( ) , Address ( [ 1u8 ; 32 ] ) ) ,
731+ FeeSlot :: new ( Wad :: one ( ) , Address ( [ 2u8 ; 32 ] ) ) ,
732+ None ,
733+ ) ;
734+ let mut bytes = Vec :: with_capacity ( 97 ) ;
735+ bytes. extend_from_slice ( & fees. performance . fee_wad . as_u128_trunc ( ) . to_le_bytes ( ) ) ;
736+ bytes. extend_from_slice ( fees. performance . recipient . as_bytes ( ) ) ;
737+ bytes. extend_from_slice ( & fees. management . fee_wad . as_u128_trunc ( ) . to_le_bytes ( ) ) ;
738+ bytes. extend_from_slice ( fees. management . recipient . as_bytes ( ) ) ;
739+ bytes. push ( 0 ) ;
740+ env. storage ( ) . instance ( ) . set (
741+ & templar_soroban_runtime:: contract:: VaultDataKey :: FeesSpec ,
742+ & Bytes :: from_slice ( & env, & bytes) ,
743+ ) ;
744+
745+ let mut storage = SorobanStorage :: new ( & env) ;
746+ storage
747+ . save_state ( & VaultState {
748+ total_assets : u128:: MAX ,
749+ total_shares : u128:: MAX ,
750+ idle_assets : u128:: MAX ,
751+ fee_anchor : FeeAccrualAnchor :: new ( 1 , templar_vault_kernel:: TimestampNs ( 1 ) ) ,
752+ ..Default :: default ( )
753+ } )
754+ . expect ( "save state" ) ;
755+
756+ assert_eq ! (
757+ proxy. preview_deposit( 1 ) ,
758+ Err ( templar_soroban_runtime:: ContractError :: ConversionOverflow )
759+ ) ;
760+ } ) ;
761+ }
762+
590763#[ rstest]
591764fn soroban_contract_refresh_fees_command_updates_anchor ( ) {
592765 let env = Env :: default ( ) ;
0 commit comments