@@ -1791,3 +1791,144 @@ fn test_set_loan_manager_requires_admin() {
17911791 . try_set_loan_manager( & token_id, & reporter)
17921792 . is_err( ) ) ;
17931793}
1794+
1795+ // ── Issue #9: DepositorCount correctness ─────────────────────────────────────
1796+ // Note: TotalDeposits principal-tracking is already covered by #36's
1797+ // test_total_deposits_tracks_principal_after_yield_then_redeem.
1798+ // These tests focus on the DepositorCount hardening (checked_sub underflow
1799+ // guard) and cap enforcement after yield-bearing withdrawals.
1800+
1801+ #[ test]
1802+ fn test_cap_enforced_after_yield_bearing_withdrawal ( ) {
1803+ // Two providers each deposit 1000 (cap = 2000). After yield arrives,
1804+ // A fully withdraws. TotalDeposits must correctly reflect only B's
1805+ // remaining principal (1000), leaving exactly 1000 room under the cap.
1806+ let env = Env :: default ( ) ;
1807+ env. mock_all_auths ( ) ;
1808+
1809+ let token_admin = Address :: generate ( & env) ;
1810+ let ( token_id, stellar_asset_client, _) = create_token_contract ( & env, & token_admin) ;
1811+
1812+ let pool_id = env. register ( LendingPool , ( ) ) ;
1813+ let pool_client = LendingPoolClient :: new ( & env, & pool_id) ;
1814+ pool_client. initialize ( & token_admin) ;
1815+ pool_client. set_withdrawal_cooldown ( & 0 ) ;
1816+ pool_client. set_max_pool_size ( & token_id, & 2_000 ) ;
1817+
1818+ let provider_a = Address :: generate ( & env) ;
1819+ let provider_b = Address :: generate ( & env) ;
1820+ let provider_c = Address :: generate ( & env) ;
1821+ stellar_asset_client. mint ( & provider_a, & 1_000 ) ;
1822+ stellar_asset_client. mint ( & provider_b, & 1_000 ) ;
1823+ stellar_asset_client. mint ( & provider_c, & 1_000 ) ;
1824+
1825+ pool_client. deposit ( & provider_a, & token_id, & 1_000 ) ;
1826+ pool_client. deposit ( & provider_b, & token_id, & 1_000 ) ;
1827+ assert_eq ! ( pool_client. get_total_deposits( & token_id) , 2_000 ) ;
1828+
1829+ // 1000 tokens of yield arrive; record_yield updates TotalManagedAssets.
1830+ stellar_asset_client. mint ( & pool_id, & 1_000 ) ;
1831+ pool_client. record_yield ( & token_id, & 1_000 ) ;
1832+ // managed = 3000, total_shares = 2000.
1833+
1834+ // A redeems all 1000 shares → gets 1500 assets (principal + yield).
1835+ // TotalDeposits must become 1000 (B's principal only).
1836+ pool_client. withdraw ( & provider_a, & token_id, & 1_000 ) ;
1837+ assert_eq ! ( pool_client. get_total_deposits( & token_id) , 1_000 ) ;
1838+
1839+ // Cap = 2000, TotalDeposits = 1000 → room = 1000.
1840+ // Depositing 1001 must be rejected.
1841+ let res = pool_client. try_deposit ( & provider_c, & token_id, & 1_001 ) ;
1842+ assert_eq ! ( res, Err ( Ok ( crate :: PoolError :: PoolSizeExceeded ) ) ) ;
1843+
1844+ // Depositing exactly 1000 fills the remaining cap room and must succeed.
1845+ pool_client. deposit ( & provider_c, & token_id, & 1_000 ) ;
1846+ assert_eq ! ( pool_client. get_total_deposits( & token_id) , 2_000 ) ;
1847+ }
1848+
1849+ #[ test]
1850+ fn test_depositor_count_across_deposit_partial_full_withdraw_redeposit ( ) {
1851+ // DepositorCount must stay consistent across every state transition:
1852+ // first deposit (+1), partial withdraw (unchanged), full withdraw (-1),
1853+ // and re-deposit from zero shares (+1 again).
1854+ let env = Env :: default ( ) ;
1855+ env. mock_all_auths ( ) ;
1856+
1857+ let token_admin = Address :: generate ( & env) ;
1858+ let ( token_id, stellar_asset_client, _) = create_token_contract ( & env, & token_admin) ;
1859+
1860+ let pool_id = env. register ( LendingPool , ( ) ) ;
1861+ let pool_client = LendingPoolClient :: new ( & env, & pool_id) ;
1862+ pool_client. initialize ( & token_admin) ;
1863+ pool_client. set_withdrawal_cooldown ( & 0 ) ;
1864+
1865+ let provider_a = Address :: generate ( & env) ;
1866+ let provider_b = Address :: generate ( & env) ;
1867+ stellar_asset_client. mint ( & provider_a, & 2_000 ) ;
1868+ stellar_asset_client. mint ( & provider_b, & 2_000 ) ;
1869+
1870+ // Two deposits → count = 2.
1871+ pool_client. deposit ( & provider_a, & token_id, & 2_000 ) ;
1872+ pool_client. deposit ( & provider_b, & token_id, & 2_000 ) ;
1873+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 2 ) ;
1874+
1875+ // Partial withdrawal by A (remaining > 0) → count unchanged.
1876+ pool_client. withdraw ( & provider_a, & token_id, & 1_000 ) ;
1877+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 2 ) ;
1878+
1879+ // Full withdrawal by A (remaining == 0) → count decrements.
1880+ pool_client. withdraw ( & provider_a, & token_id, & 1_000 ) ;
1881+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 1 ) ;
1882+
1883+ // A re-deposits (existing_shares == 0, key was removed) → count increments.
1884+ pool_client. deposit ( & provider_a, & token_id, & 1_000 ) ;
1885+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 2 ) ;
1886+
1887+ // Full withdrawal by B → count decrements.
1888+ pool_client. withdraw ( & provider_b, & token_id, & 2_000 ) ;
1889+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 1 ) ;
1890+
1891+ // Full withdrawal by A → count reaches 0.
1892+ pool_client. withdraw ( & provider_a, & token_id, & 1_000 ) ;
1893+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 0 ) ;
1894+ }
1895+
1896+ #[ test]
1897+ fn test_total_deposits_zero_after_all_shares_redeemed_with_yield ( ) {
1898+ // When all providers fully withdraw after yield has accumulated,
1899+ // TotalDeposits and total_shares must both reach zero.
1900+ let env = Env :: default ( ) ;
1901+ env. mock_all_auths ( ) ;
1902+
1903+ let token_admin = Address :: generate ( & env) ;
1904+ let ( token_id, stellar_asset_client, _) = create_token_contract ( & env, & token_admin) ;
1905+
1906+ let pool_id = env. register ( LendingPool , ( ) ) ;
1907+ let pool_client = LendingPoolClient :: new ( & env, & pool_id) ;
1908+ pool_client. initialize ( & token_admin) ;
1909+ pool_client. set_withdrawal_cooldown ( & 0 ) ;
1910+
1911+ let provider_a = Address :: generate ( & env) ;
1912+ let provider_b = Address :: generate ( & env) ;
1913+ stellar_asset_client. mint ( & provider_a, & 1_000 ) ;
1914+ stellar_asset_client. mint ( & provider_b, & 1_000 ) ;
1915+
1916+ pool_client. deposit ( & provider_a, & token_id, & 1_000 ) ; // 1000 shares
1917+ pool_client. deposit ( & provider_b, & token_id, & 1_000 ) ; // 1000 shares
1918+ // managed = 2000, total_shares = 2000, TotalDeposits = 2000.
1919+
1920+ // 1000 tokens of yield arrive; record_yield updates TotalManagedAssets.
1921+ stellar_asset_client. mint ( & pool_id, & 1_000 ) ;
1922+ pool_client. record_yield ( & token_id, & 1_000 ) ;
1923+ // managed = 3000, total_shares = 2000.
1924+
1925+ // A redeems 1000 shares → 1500 assets. TotalDeposits → 1000.
1926+ pool_client. withdraw ( & provider_a, & token_id, & 1_000 ) ;
1927+ assert_eq ! ( pool_client. get_total_deposits( & token_id) , 1_000 ) ;
1928+
1929+ // B redeems 1000 shares → 1500 assets. TotalDeposits → 0.
1930+ pool_client. withdraw ( & provider_b, & token_id, & 1_000 ) ;
1931+ assert_eq ! ( pool_client. get_total_deposits( & token_id) , 0 ) ;
1932+ assert_eq ! ( pool_client. get_total_shares( & token_id) , 0 ) ;
1933+ assert_eq ! ( pool_client. get_pool_stats( & token_id) . depositor_count, 0 ) ;
1934+ }
0 commit comments