Skip to content

feat : new position managers#1054

Open
Kogaroshi wants to merge 48 commits intodevfrom
feat/new-position-managers
Open

feat : new position managers#1054
Kogaroshi wants to merge 48 commits intodevfrom
feat/new-position-managers

Conversation

@Kogaroshi
Copy link
Contributor

@Kogaroshi Kogaroshi commented Dec 8, 2025

New Position Managers :

  • SupplyRepayPositionManager : allows any user to supply and/or repay on behalf of any other user that activated this PositionManager, with no other permissions or approval required.
  • AllowancePositionManager : allows an user to give withdrawal allowance on their position, as well as credit delegation (all allowance/delegation as given for a specific Spoke and ReserveId pair for each spender), allowing the spender to withdraw from the user position or borrow on the position based on the given allowances/delegations. This PositionManager is also inheriting ERC721, allowing to EIP712-typed intent to grant the allowance/delegation to spenders.
  • PositionConfigPositionManager : allows an user to grand permissions to other users to call setUsingAsCollateral, updateUserRiskPremiumO & updateUserDynamicConfig on their behalf

@github-actions
Copy link

github-actions bot commented Dec 8, 2025

🌈 Test Results
No files changed, compilation skipped

Ran 2 tests for tests/unit/Spoke/Spoke.MultipleHub.t.sol:SpokeMultipleHubTest
[PASS] test_borrow_secondHub() (gas: 888898)
[PASS] test_borrow_thirdHub() (gas: 733063)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 48.23ms (3.42ms CPU time)

Ran 4 tests for tests/unit/Spoke/Liquidations/Spoke.LiquidationCall.Dust.t.sol:SpokeLiquidationCallDustTest
[PASS] test_collateralDust_min_debtToTarget() (gas: 17694308)
[PASS] test_debtToCover_exceeds_collateralValue() (gas: 17682480)
[PASS] test_dustColl_allowed() (gas: 17542911)
[PASS] test_dustDebt_allowed() (gas: 17673713)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 70.64ms (27.86ms CPU time)

Ran 14 tests for tests/gas/Spoke.Operations.gas.t.sol:SpokeOperations_Gas_Tests
[PASS] test_borrow() (gas: 1325330)
[PASS] test_liquidation_full() (gas: 10830690)
[PASS] test_liquidation_partial() (gas: 10830107)
[PASS] test_liquidation_receiveShares_full() (gas: 10813113)
[PASS] test_liquidation_receiveShares_partial() (gas: 10812532)
[PASS] test_liquidation_reportDeficit_full() (gas: 10809983)
[PASS] test_multicall_ops() (gas: 1404026)
[PASS] test_repay() (gas: 868274)
[PASS] test_setUserPositionManagersWithSig() (gas: 307064)
[PASS] test_supply() (gas: 538976)
[PASS] test_updateRiskPremium() (gas: 1315181)
[PASS] test_updateUserDynamicConfig() (gas: 587314)
[PASS] test_usingAsCollateral() (gas: 1470243)
[PASS] test_withdraw() (gas: 1946935)
Suite result: ok. 14 passed; 0 failed; 0 skipped; finished in 82.78ms (36.19ms CPU time)

Ran 14 tests for tests/gas/Spoke.Operations.gas.t.sol:SpokeOperations_ZeroRiskPremium_Gas_Tests
[PASS] test_borrow() (gas: 1091844)
[PASS] test_liquidation_full() (gas: 10695984)
[PASS] test_liquidation_partial() (gas: 10695401)
[PASS] test_liquidation_receiveShares_full() (gas: 10678407)
[PASS] test_liquidation_receiveShares_partial() (gas: 10677826)
[PASS] test_liquidation_reportDeficit_full() (gas: 10731904)
[PASS] test_multicall_ops() (gas: 1321429)
[PASS] test_repay() (gas: 777435)
[PASS] test_setUserPositionManagersWithSig() (gas: 311518)
[PASS] test_supply() (gas: 541657)
[PASS] test_updateRiskPremium() (gas: 943029)
[PASS] test_updateUserDynamicConfig() (gas: 591768)
[PASS] test_usingAsCollateral() (gas: 1088778)
[PASS] test_withdraw() (gas: 1587437)
Suite result: ok. 14 passed; 0 failed; 0 skipped; finished in 76.91ms (30.97ms CPU time)

Ran 4 tests for tests/unit/Spoke/Spoke.PermitReserve.t.sol:SpokePermitReserveTest
[PASS] test_permitReserve() (gas: 88227)
[PASS] test_permitReserve_forwards_correct_call() (gas: 35554)
[PASS] test_permitReserve_ignores_permit_reverts() (gas: 24390)
[PASS] test_permitReserve_revertsWith_ReserveNotListedIn() (gas: 22927)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 24.63ms (1.13ms CPU time)

Ran 13 tests for tests/unit/Spoke/Liquidations/Spoke.LiquidationCall.Scenarios.t.sol:SpokeLiquidationCallScenariosTest
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRefreshPremium() (gas: 25862789)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRemove() (gas: 25734318)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubReportDeficit() (gas: 25845285)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRestore() (gas: 25809746)
[PASS] test_liquidationCall_scenario1() (gas: 3865875)
[PASS] test_liquidationCall_scenario2() (gas: 3875279)
[PASS] test_liquidationCall_scenario3() (gas: 3336336)
[PASS] test_liquidationCall_scenario4() (gas: 27399985)
[PASS] test_liquidationCall_scenario5() (gas: 3477050)
[PASS] test_liquidationCall_scenario6() (gas: 2314499)
[PASS] test_liquidationCall_scenario7() (gas: 3065042)
[PASS] test_scenario_halted_asset() (gas: 26493881)
[PASS] test_scenario_halted_asset_with_deficit() (gas: 26338043)
Suite result: ok. 13 passed; 0 failed; 0 skipped; finished in 169.51ms (137.62ms CPU time)

Ran 12 tests for tests/unit/position-managers/SignatureGateway/SignatureGateway.t.sol:SignatureGatewayTest
[PASS] test_borrowWithSig() (gas: 791342)
[PASS] test_multicall() (gas: 383684)
[PASS] test_renouncePositionManagerRole() (gas: 27577)
[PASS] test_renouncePositionManagerRole_revertsWith_OnlyOwner() (gas: 18112)
[PASS] test_repayWithSig() (gas: 798010)
[PASS] test_setSelfAsUserPositionManagerWithSig() (gas: 309796)
[PASS] test_setUsingAsCollateralWithSig() (gas: 611011)
[PASS] test_supplyWithSig() (gas: 583947)
[PASS] test_updateUserDynamicConfigWithSig() (gas: 319713)
[PASS] test_updateUserRiskPremiumWithSig() (gas: 1007811)
[PASS] test_useNonce_monotonic(bytes32) (runs: 5000, μ: 13346, ~: 13346)
[PASS] test_withdrawWithSig() (gas: 584399)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 556.73ms (519.38ms CPU time)

Ran 3 tests for tests/unit/Spoke/Spoke.Access.t.sol:SpokeAccessTest
[PASS] testAccess_change_authority() (gas: 534235)
[PASS] testAccess_hub_functions_callable_by_spokes() (gas: 569443)
[PASS] testAccess_spoke_admin_config_access() (gas: 511517)
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 27.16ms (3.07ms CPU time)

Ran 10 tests for tests/unit/Spoke/Spoke.PositionManager.t.sol:SpokePositionManagerTest
[PASS] test_onlyPositionManager_on_borrow() (gas: 538159)
[PASS] test_onlyPositionManager_on_repay() (gas: 563162)
[PASS] test_onlyPositionManager_on_supply() (gas: 291492)
[PASS] test_onlyPositionManager_on_updateUserDynamicConfig() (gas: 1286437)
[PASS] test_onlyPositionManager_on_updateUserRiskPremium() (gas: 1523842)
[PASS] test_onlyPositionManager_on_usingAsCollateral() (gas: 144153)
[PASS] test_onlyPositionManager_on_withdraw() (gas: 320528)
[PASS] test_renouncePositionManagerRole() (gas: 20246)
[PASS] test_renouncePositionManagerRole_noop_from_disabled() (gas: 21860)
[PASS] test_setApprovalForPositionManager(bytes32) (runs: 5000, μ: 18089, ~: 18089)
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 444.95ms (420.15ms CPU time)

Ran 20 tests for tests/unit/AaveOracle.t.sol:AaveOracleTest
[PASS] test_DECIMALS() (gas: 8326)
[PASS] test_constructor() (gas: 18428)
[PASS] test_description() (gas: 12039)
[PASS] test_fuzz_constructor(uint8) (runs: 5000, μ: 19888, ~: 20214)
Logs:
  Bound result 1

[PASS] test_getReservePrice() (gas: 48776)
[PASS] test_getReservePrice_revertsWith_InvalidPrice() (gas: 48047)
[PASS] test_getReservePrice_revertsWith_InvalidSource() (gas: 10898)
[PASS] test_getReservePrices() (gas: 80715)
[PASS] test_getReservePrices_revertsWith_InvalidSource() (gas: 50930)
[PASS] test_getReserveSource() (gas: 48946)
[PASS] test_setReserveSource() (gas: 45988)
[PASS] test_setReserveSource_revertsWith_InvalidPrice() (gas: 102779)
[PASS] test_setReserveSource_revertsWith_InvalidSource() (gas: 17228)
[PASS] test_setReserveSource_revertsWith_InvalidSourceDecimals() (gas: 17065)
[PASS] test_setReserveSource_revertsWith_OnlySpoke() (gas: 13021)
[PASS] test_setReserveSource_revertsWith_OracleMismatch() (gas: 5010072)
[PASS] test_setSpoke() (gas: 5037974)
[PASS] test_setSpoke_revertsWith_InvalidAddress() (gas: 10870)
[PASS] test_setSpoke_revertsWith_OnlyDeployer(address) (runs: 5000, μ: 13397, ~: 13397)
[PASS] test_setSpoke_revertsWith_SpokeAlreadySet() (gas: 15080)
Suite result: ok. 20 passed; 0 failed; 0 skipped; finished in 1.12s (1.11s CPU time)

Ran 7 tests for tests/unit/Spoke/Spoke.Repay.EdgeCases.t.sol:SpokeRepayEdgeCaseTest
[PASS] test_fuzz_repay_effect_on_ex_rates(uint256,uint256) (runs: 5000, μ: 700748, ~: 700658)
Logs:
  Bound result 100000000000000000000000000000
  Bound result 11645004
  Bound result 40775633259982327842294181619

[PASS] test_repay_less_than_share() (gas: 599720)
[PASS] test_repay_only_base_debt_interest() (gas: 765698)
[PASS] test_repay_only_base_debt_no_premium() (gas: 643832)
[PASS] test_repay_supply_ex_rate_decr() (gas: 1466321)
[PASS] test_repay_supply_ex_rate_decr_skip_time() (gas: 1462937)
[PASS] test_repay_zero_shares_nonzero_premium_debt() (gas: 763610)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 16.61s (16.58s CPU time)

Ran 17 tests for tests/unit/AccessManagerEnumerable.t.sol:AccessManagerEnumerableTest
[PASS] test_getRoleMembers_fuzz(uint256,uint256) (runs: 5000, μ: 1981546, ~: 1980367)
Logs:
  Bound result 9
  Bound result 10

[PASS] test_getRoleTargetSelectors_fuzz(uint256,uint256) (runs: 5000, μ: 1528888, ~: 1527966)
Logs:
  Bound result 9
  Bound result 10

[PASS] test_grantRole() (gas: 315886)
[PASS] test_grantRole_fuzz(uint64,uint256) (runs: 5000, μ: 914118, ~: 919753)
Logs:
  Bound result 8

[PASS] test_revokeRole() (gas: 323138)
[PASS] test_setRoleAdmin_fuzz_trackAdminRoles_multipleRoles_multipleAdmins(uint256) (runs: 5000, μ: 2148216, ~: 2244247)
Logs:
  Bound result 12

[PASS] test_setRoleAdmin_fuzz_trackRolesAndTrackAdminRoles_multipleRoles(uint256) (runs: 5000, μ: 1961985, ~: 1899754)
Logs:
  Bound result 12

[PASS] test_setRoleAdmin_trackAdminOfRoles() (gas: 606131)
[PASS] test_setRoleAdmin_trackAdminOfRoles_changeAdminRole() (gas: 577217)
[PASS] test_setRoleAdmin_trackAdminRoles() (gas: 602311)
[PASS] test_setRoleAdmin_trackRolesAndTrackAdminRoles() (gas: 378311)
[PASS] test_setRoleGuardian_trackRoles() (gas: 263932)
[PASS] test_setTargetFunctionRole() (gas: 414440)
[PASS] test_setTargetFunctionRole_multipleTargets() (gas: 1103911)
[PASS] test_setTargetFunctionRole_removeTarget() (gas: 894967)
[PASS] test_setTargetFunctionRole_skipAddToAdminRole() (gas: 30940)
[PASS] test_setTargetFunctionRole_withReplace() (gas: 549104)
Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 22.46s (22.46s CPU time)

Ran 39 tests for tests/unit/position-managers/AllowancePositionManager.t.sol:AllowancePositionManagerTest
[PASS] test_DOMAIN_SEPARATOR() (gas: 5731)
[PASS] test_approveWithdrawWithSig_fuzz(address,uint256,uint256) (runs: 5000, μ: 193194, ~: 192668)
Logs:
  Bound result 4
  Bound result 511

[PASS] test_approveWithdrawWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 156985, ~: 156707)
[PASS] test_approveWithdrawWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 39635)
[PASS] test_approveWithdrawWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 37534)
[PASS] test_approveWithdrawWithSig_revertsWith_SpokeNotRegistered() (gas: 173107)
[PASS] test_approveWithdraw_fuzz(address,uint256,uint256) (runs: 5000, μ: 63804, ~: 63580)
Logs:
  Bound result 4
  Bound result 511

[PASS] test_approveWithdraw_revertsWith_SpokeNotRegistered() (gas: 17556)
[PASS] test_borrowOnBehalfOf() (gas: 586494)
Logs:
  Bound result 5000000000000000000
  Bound result 5000000000000000000

[PASS] test_borrowOnBehalfOf_fuzz(uint256,uint256) (runs: 5000, μ: 606960, ~: 607100)
Logs:
  Bound result 68691281934999
  Bound result 618221537415092

[PASS] test_borrowOnBehalfOf_fuzz_noAllowanceDecrease(uint256) (runs: 5000, μ: 605312, ~: 605070)
Logs:
  Bound result 3124043968137

[PASS] test_borrowOnBehalfOf_revertsWith_InsufficientCreditDelegation(uint256) (runs: 5000, μ: 307563, ~: 307764)
Logs:
  Bound result 3124043968137

[PASS] test_borrowOnBehalfOf_revertsWith_ReserveNotListed() (gas: 59237)
[PASS] test_borrowOnBehalfOf_revertsWith_SpokeNotRegistered() (gas: 17554)
[PASS] test_creditDelegationWithSig_fuzz(address,uint256,uint256) (runs: 5000, μ: 193136, ~: 192610)
Logs:
  Bound result 4
  Bound result 511

[PASS] test_creditDelegationWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 156943, ~: 156665)
[PASS] test_creditDelegationWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 39636)
[PASS] test_creditDelegationWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 37549)
[PASS] test_creditDelegationWithSig_revertsWith_SpokeNotRegistered() (gas: 173131)
[PASS] test_creditDelegation_fuzz(address,uint256,uint256) (runs: 5000, μ: 63835, ~: 63611)
Logs:
  Bound result 4
  Bound result 511

[PASS] test_creditDelegation_revertsWith_SpokeNotRegistered() (gas: 17555)
[PASS] test_creditDelegation_typeHash() (gas: 9822)
[PASS] test_eip712Domain() (gas: 10899)
[PASS] test_multicall() (gas: 80631)
[PASS] test_renounceCreditDelegation_fuzz(uint256) (runs: 5000, μ: 51600, ~: 51395)
Logs:
  Bound result 3124043968137

[PASS] test_renounceCreditDelegation_noop_alreadyRenounced() (gas: 45667)
[PASS] test_renounceCreditDelegation_revertsWith_SpokeNotRegistered() (gas: 17525)
[PASS] test_renounceWithdrawAllowance_fuzz(uint256) (runs: 5000, μ: 51626, ~: 51421)
Logs:
  Bound result 3124043968137

[PASS] test_renounceWithdrawAllowance_noop_alreadyRenounced() (gas: 45583)
[PASS] test_renounceWithdrawAllowance_revertsWith_SpokeNotRegistered() (gas: 17499)
[PASS] test_withdrawOnBehalfOf() (gas: 314862)
Logs:
  Bound result 100000000000000000000

[PASS] test_withdrawOnBehalfOf_fuzz(uint256) (runs: 5000, μ: 314705, ~: 314934)
Logs:
  Bound result 3124043968137

[PASS] test_withdrawOnBehalfOf_fuzz_allBalance(uint256) (runs: 5000, μ: 272325, ~: 272120)
Logs:
  Bound result 3124043968137

[PASS] test_withdrawOnBehalfOf_fuzz_allBalanceWithInterest(uint256,uint256) (runs: 5000, μ: 749769, ~: 749708)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_withdrawOnBehalfOf_fuzz_allBalance_noAllowanceDecreased(uint256) (runs: 5000, μ: 270061, ~: 269856)
Logs:
  Bound result 3124043968137

[PASS] test_withdrawOnBehalfOf_revertsWith_InsufficientWithdrawAllowance(uint256) (runs: 5000, μ: 191402, ~: 191603)
Logs:
  Bound result 3124043968137

[PASS] test_withdrawOnBehalfOf_revertsWith_ReserveNotListed() (gas: 59323)
[PASS] test_withdrawOnBehalfOf_revertsWith_SpokeNotRegistered() (gas: 17548)
[PASS] test_withdrawPermit_typeHash() (gas: 9882)
Suite result: ok. 39 passed; 0 failed; 0 skipped; finished in 81.74s (81.71s CPU time)

Ran 23 tests for tests/unit/AssetInterestRateStrategy.t.sol:AssetInterestRateStrategyTest
[PASS] test_calculateInterestRate_AtKinkPoint() (gas: 24326)
Logs:
  Bound result 2000
  Bound result 778565440757296803935461404101

[PASS] test_calculateInterestRate_AtMaxUtilization() (gas: 24621)
Logs:
  Bound result 10000
  Bound result 778565440757296803935461404101

[PASS] test_calculateInterestRate_LeftToKinkPoint(uint256) (runs: 5000, μ: 24190, ~: 24330)
Logs:
  Bound result 137
  Bound result 252173843969976304268974536488

[PASS] test_calculateInterestRate_RightToKinkPoint(uint256) (runs: 5000, μ: 25300, ~: 25351)
Logs:
  Bound result 8137
  Bound result 252173843969976304268974536488

[PASS] test_calculateInterestRate_ZeroDebtZeroLiquidity() (gas: 18771)
Logs:
  Bound result 0

[PASS] test_calculateInterestRate_fuzz_ZeroDebt(uint256) (runs: 5000, μ: 19071, ~: 18822)
Logs:
  Bound result 3124043968137

[PASS] test_calculateInterestRate_revertsWith_InterestRateDataNotSet() (gas: 11225)
[PASS] test_deploy_revertsWith_InvalidAddress() (gas: 3746)
[PASS] test_getBaseVariableBorrowRate() (gas: 14812)
[PASS] test_getInterestRateData() (gas: 19290)
[PASS] test_getMaxVariableBorrowRate() (gas: 15258)
[PASS] test_getOptimalUsageRatio() (gas: 14705)
[PASS] test_getVariableRateSlope1() (gas: 14791)
[PASS] test_getVariableRateSlope2() (gas: 14746)
[PASS] test_maxBorrowRate() (gas: 8312)
[PASS] test_maxOptimalRatio() (gas: 8312)
[PASS] test_minOptimalRatio() (gas: 8321)
[PASS] test_setInterestRateData() (gas: 68999)
[PASS] test_setInterestRateData_revertsWith_InvalidMaxRate() (gas: 41819)
[PASS] test_setInterestRateData_revertsWith_InvalidOptimalUsageRatio() (gas: 42380)
[PASS] test_setInterestRateData_revertsWith_InvalidRateData() (gas: 35269)
[PASS] test_setInterestRateData_revertsWith_OnlyHub() (gas: 23502)
[PASS] test_setInterestRateData_revertsWith_Slope2MustBeGteSlope1() (gas: 37658)
Suite result: ok. 23 passed; 0 failed; 0 skipped; finished in 1.13s (1.12s CPU time)

Ran 5 tests for tests/unit/position-managers/libraries/ConfigPermissions.t.sol:ConfigPermissionsTests
[PASS] test_constants() (gas: 12030)
[PASS] test_setCanSetUsingAsCollateral_fuzz(uint8,bool) (runs: 5000, μ: 10548, ~: 10536)
[PASS] test_setCanUpdateUserDynamicConfig_fuzz(uint8,bool) (runs: 5000, μ: 10594, ~: 10582)
[PASS] test_setCanUpdateUserRiskPremium_fuzz(uint8,bool) (runs: 5000, μ: 10582, ~: 10570)
[PASS] test_setFullPermissions_fuzz(uint8,bool) (runs: 5000, μ: 13385, ~: 13375)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 759.31ms (759.00ms CPU time)

Ran 12 tests for tests/unit/position-managers/libraries/EIP712Hash.t.sol:EIP712HashTest
[PASS] test_constants() (gas: 15865)
[PASS] test_hash_borrow_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6828, ~: 6828)
[PASS] test_hash_creditDelegation_fuzz((address,uint256,address,address,uint256,uint256,uint256)) (runs: 5000, μ: 5065, ~: 5065)
[PASS] test_hash_positionManagerUpdate_fuzz((address,bool)) (runs: 5000, μ: 6193, ~: 6193)
[PASS] test_hash_repay_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6850, ~: 6850)
[PASS] test_hash_setUserPositionManagers_fuzz((address,(address,bool)[],uint256,uint256)) (runs: 5000, μ: 453532, ~: 449238)
[PASS] test_hash_setUsingAsCollateral_fuzz((address,uint256,bool,address,uint256,uint256)) (runs: 5000, μ: 7328, ~: 7328)
[PASS] test_hash_supply_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6827, ~: 6827)
[PASS] test_hash_updateUserDynamicConfig_fuzz((address,address,uint256,uint256)) (runs: 5000, μ: 6450, ~: 6450)
[PASS] test_hash_updateUserRiskPremium_fuzz((address,address,uint256,uint256)) (runs: 5000, μ: 6449, ~: 6449)
[PASS] test_hash_withdrawPermit_fuzz((address,uint256,address,address,uint256,uint256,uint256)) (runs: 5000, μ: 5020, ~: 5020)
[PASS] test_hash_withdraw_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6850, ~: 6850)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 22.46s (22.46s CPU time)

Ran 1 test for tests/unit/Spoke/Spoke.AccrueInterest.Scenario.t.sol:SpokeAccrueInterestScenarioTest
[PASS] test_accrueInterest_fuzz_RPBorrowAndSkipTime_twoActions((uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256),uint40) (runs: 5000, μ: 5553214, ~: 5406847)
Logs:
  Bound result 416966230219621053938274952927
  Bound result 0
  Bound result 255831241153786906
  Bound result 41191294854201435881
  Bound result 124281697546210950467266407747
  Bound result 33522909571553656
  Bound result 481970306954558904
  Bound result 12412
  Bound result 6
  Bound result 124281697546210950467266407747
  Bound result 33522909571553656
  Bound result 481970306954558904
  Bound result 12412

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 137.46s (137.43s CPU time)

Ran 3 tests for tests/unit/misc/ExtSload.t.sol:ExtSloadTest
[PASS] test_extSload(bytes32) (runs: 5000, μ: 9767, ~: 9767)
[PASS] test_extSloads(uint256) (runs: 5000, μ: 952759, ~: 923386)
Logs:
  Bound result 812

[PASS] test_extSloads(uint256,bytes) (runs: 5000, μ: 1011196, ~: 968552)
Logs:
  Bound result 362

Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 59.92s (59.92s CPU time)

Ran 5 tests for tests/gas/Gateways.Operations.gas.t.sol:NativeTokenGateway_Gas_Tests
[PASS] test_borrowNative() (gas: 918063)
[PASS] test_repayNative() (gas: 989367)
[PASS] test_supplyAndCollateralNative() (gas: 305189)
[PASS] test_supplyNative() (gas: 286676)
[PASS] test_withdrawNative() (gas: 508888)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 50.13ms (4.43ms CPU time)

Ran 8 tests for tests/gas/Gateways.Operations.gas.t.sol:SignatureGateway_Gas_Tests
[PASS] test_borrowWithSig() (gas: 745327)
[PASS] test_repayWithSig() (gas: 980093)
[PASS] test_setSelfAsUserPositionManagerWithSig() (gas: 209555)
[PASS] test_setUsingAsCollateralWithSig() (gas: 289396)
[PASS] test_supplyWithSig() (gas: 460914)
[PASS] test_updateUserDynamicConfigWithSig() (gas: 145282)
[PASS] test_updateUserRiskPremiumWithSig() (gas: 143125)
[PASS] test_withdrawWithSig() (gas: 409953)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 52.84ms (8.01ms CPU time)

Ran 6 tests for tests/unit/Hub/Hub.Access.t.sol:HubAccessTest
[PASS] test_change_authority() (gas: 206690)
[PASS] test_change_role_responsibility() (gas: 121258)
[PASS] test_hub_access_manager_exposure() (gas: 13439)
[PASS] test_hub_admin_access() (gas: 1350388)
[PASS] test_migrate_role_responsibility() (gas: 708988)
[PASS] test_setInterestRateData_access() (gas: 102667)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 28.02ms (4.01ms CPU time)

Ran 19 tests for tests/unit/Hub/Hub.Add.t.sol:HubAddTest
[PASS] test_add_AddCapReachedButNotExceeded_rounding() (gas: 667246)
[PASS] test_add_fuzz_AddCapReachedButNotExceeded(uint40) (runs: 5000, μ: 157894, ~: 157848)
Logs:
  Bound result 9

[PASS] test_add_fuzz_multi_asset_multi_spoke(uint256,uint256,uint256) (runs: 5000, μ: 332483, ~: 332644)
Logs:
  Bound result 3
  Bound result 218470873395738003579119570309
  Bound result 446067553769140138733721804

[PASS] test_add_fuzz_revertsWith_AddCapExceeded(uint40) (runs: 5000, μ: 112439, ~: 112393)
Logs:
  Bound result 9

[PASS] test_add_fuzz_revertsWith_AddCapExceeded_due_to_interest(uint40,uint256,uint256) (runs: 5000, μ: 267087, ~: 266951)
Logs:
  Bound result 1291
  Bound result 1071208440522043736492
  Bound result 173721804

[PASS] test_add_fuzz_revertsWith_InvalidShares_due_to_index(uint256,uint256,uint256) (runs: 5000, μ: 223922, ~: 224119)
Logs:
  Bound result 888155841543549236576985341915
  Bound result 1883605286
  Bound result 8

[PASS] test_add_fuzz_single_asset(uint256,address,uint256) (runs: 5000, μ: 342384, ~: 342404)
Logs:
  Bound result 0
  Bound result 703644743081881714272112120216

[PASS] test_add_fuzz_single_spoke_multi_add(uint256,uint256) (runs: 5000, μ: 805229, ~: 805253)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_add_multi_add_minimal_shares() (gas: 322520)
[PASS] test_add_revertsWith_AmountDowncastOverflow() (gas: 360306)
[PASS] test_add_revertsWith_InsufficientTransferred() (gas: 64504)
[PASS] test_add_revertsWith_InvalidAmount() (gas: 13631)
[PASS] test_add_revertsWith_InvalidShares() (gas: 223458)
[PASS] test_add_revertsWith_SharesDowncastOverflow() (gas: 224358)
[PASS] test_add_revertsWith_SpokeHalted() (gas: 99705)
[PASS] test_add_revertsWith_SpokeNotActive() (gas: 99741)
[PASS] test_add_single_asset() (gas: 330255)
Logs:
  Bound result 2
  Bound result 100000000000000000000

[PASS] test_add_with_increased_index() (gas: 301312)
[PASS] test_add_with_increased_index_with_premium() (gas: 680739)
Suite result: ok. 19 passed; 0 failed; 0 skipped; finished in 45.12s (45.09s CPU time)

Ran 7 tests for tests/unit/Spoke/Spoke.AccrueInterest.t.sol:SpokeAccrueInterestTest
[PASS] test_accrueInterest_NoActionTaken() (gas: 132992)
[PASS] test_accrueInterest_NoInterest_NoDebt(uint40) (runs: 5000, μ: 631884, ~: 631713)
Logs:
  Bound result 9

[PASS] test_accrueInterest_NoInterest_OnlySupply(uint40) (runs: 5000, μ: 250065, ~: 250063)
Logs:
  Bound result 9

[PASS] test_accrueInterest_TenPercentRp(uint256,uint40) (runs: 5000, μ: 579029, ~: 579471)
Logs:
  Bound result 68691281934999
  Bound result 0

[PASS] test_accrueInterest_fuzz_BorrowAmountAndSkipTime(uint256,uint40) (runs: 5000, μ: 538421, ~: 538786)
Logs:
  Bound result 68691281934999
  Bound result 0

[SKIP: pending rft] test_accrueInterest_fuzz_RPBorrowAndSkipTime((uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256),uint40) (runs: 0, μ: 0, ~: 0)
[PASS] test_accrueInterest_fuzz_RatesRPBorrowAndSkipTime((uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256),(uint96,uint96,uint96,uint96),uint40) (runs: 5000, μ: 4047180, ~: 4063662)
Logs:
  Bound result 854738000545542158836806989168
  Bound result 20000
  Bound result 348
  Bound result 1064470260
  Bound result 10530
  Bound result 1711
  Bound result 9500
  Bound result 8476
  Bound result 17230
  Bound result 20590
  Bound result 16375
  Bound result 13372
  Bound result 19416
  Bound result 10530
  Bound result 1711
  Bound result 9500
  Bound result 8476

Suite result: ok. 6 passed; 0 failed; 1 skipped; finished in 115.50s (115.47s CPU time)

Ran 5 tests for tests/unit/Spoke/Spoke.AccrueLiquidityFee.EdgeCases.t.sol:SpokeAccrueLiquidityFeeEdgeCasesTest
[PASS] test_accrueLiquidityFee_fuzz_maxLiquidityFee_with_premium(uint256,uint256,uint256,uint256) (runs: 5000, μ: 544374, ~: 544426)
Logs:
  Bound result 20330
  Bound result 480000000
  Bound result 2
  Bound result 1460

[PASS] test_accrueLiquidityFee_fuzz_maxLiquidityFee_with_premium_multiple_users(uint256,uint256,uint256,uint256,uint256) (runs: 5000, μ: 802769, ~: 802880)
Logs:
  Bound result 5478
  Bound result 27820800
  Bound result 3
  Bound result 4658
  Bound result 7861

[PASS] test_accrueLiquidityFee_maxLiquidityFee_multi_spoke() (gas: 645198830)
[PASS] test_accrueLiquidityFee_maxLiquidityFee_multi_user() (gas: 263612164)
[PASS] test_accrueLiquidityFee_maxLiquidityFee_with_premium() (gas: 544488)
Logs:
  Bound result 5000
  Bound result 34560000
  Bound result 2
  Bound result 500000000000000000000

Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 19.01s (18.98s CPU time)

Ran 7 tests for tests/unit/Spoke/Spoke.AccrueLiquidityFee.t.sol:SpokeAccrueLiquidityFeeTest
[PASS] test_accrueLiquidityFee() (gas: 862061)
[PASS] test_accrueLiquidityFee_NoActionTaken() (gas: 122076)
[PASS] test_accrueLiquidityFee_NoInterest_OnlySupply(uint40) (runs: 5000, μ: 245140, ~: 245104)
Logs:
  Bound result 9

[PASS] test_accrueLiquidityFee_exact() (gas: 867969)
[PASS] test_accrueLiquidityFee_fuzz_BorrowAmountAndSkipTime(uint256,uint40) (runs: 5000, μ: 938903, ~: 960676)
Logs:
  Bound result 68691281934999
  Bound result 0

[PASS] test_accrueLiquidityFee_maxLiquidityFee() (gas: 545723)
[PASS] test_accrueLiquidityFee_setUsingAsCollateral() (gas: 894143)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 19.43s (19.40s CPU time)

Ran 8 tests for tests/unit/Spoke/Spoke.Repay.Scenario.t.sol:SpokeRepayScenarioTest
[PASS] test_fuzz_repay_borrow_twice_repay_twice((uint256,uint256,uint256,uint40),(uint256,uint256,uint256,uint40)) (runs: 5000, μ: 1254099, ~: 1258981)
Logs:
  Bound result 9137
  Bound result 9642
  Bound result 650000000000000000
  Bound result 18446462598732840959
  Bound result 3208
  Bound result 8744

[PASS] test_fuzz_repay_multiple_users_repay_same_reserve((uint256,uint256,uint256,uint256,uint256,uint256,address),(uint256,uint256,uint256,uint256,uint256,uint256,address),(uint256,uint256,uint256,uint256,uint256,uint256,address),uint256) (runs: 5000, μ: 2023668, ~: 1997642)
Logs:
  Bound result 17767
  Bound result 34560000
  Bound result 50000000000000000000
  Bound result 6549
  Bound result 11
  Bound result 487600000000000000000
  Bound result 11565

[PASS] test_repay_fuzz_multiple_users_multiple_assets(((uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),address),((uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),address),((uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),address),uint40) (runs: 5000, μ: 6051752, ~: 6052023)
Logs:
  Bound result 1675
  Bound result 10000000000
  Bound result 1443
  Bound result 3477
  Bound result 1600000000000000000000000000
  Bound result 208
  Bound result 6896
  Bound result 184
  Bound result 17500
  Bound result 86400
  Bound result 32853269984665640
  Bound result 13252
  Bound result 1100
  Bound result 1800000000
  Bound result 75262166716795500045843381702999272335185641935269528743983274444808962179073
  Bound result 5699
  Bound result 19
  Bound result 553
  Bound result 40333869799735295
  Bound result 111
  Bound result 31536000
  Bound result 450552876409790643671482431940419874915447411150352389258589821042463539455
  Bound result 10404182470604544204553419764450835806550920082972447274895179599772779265404
  Bound result 19472
  Bound result 10311

[PASS] test_repay_fuzz_two_users_multiple_assets(((uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),address),((uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),address),uint40) (runs: 5000, μ: 4286203, ~: 4286443)
Logs:
  Bound result 2763
  Bound result 1176
  Bound result 125000000000000000
  Bound result 11332
  Bound result 300000000000000000000
  Bound result 7000
  Bound result 975000000000000000
  Bound result 14826
  Bound result 13052238805970149254
  Bound result 255
  Bound result 3669
  Bound result 8725
  Bound result 20441
  Bound result 1100000000000000000000000000
  Bound result 7921
  Bound result 100000000000000000
  Bound result 5361

[PASS] test_repay_partial_then_max() (gas: 689753)
[PASS] test_repay_round_trip_borrow_repay(uint256,uint256,uint40,address,uint256) (runs: 5000, μ: 871603, ~: 875524)
Logs:
  Bound result 4
  Bound result 102314711
  Bound result 170430
  Bound result 4454419372094181496958201405

[PASS] test_repay_round_trip_repay_borrow(uint256,uint256,uint256,uint40,address,uint256) (runs: 5000, μ: 953234, ~: 962438)
Logs:
  Bound result 2
  Bound result 324294819636191080161797193214
  Bound result 2
  Bound result 108124522261102920963076409181
  Bound result 26876781578198269320285200012

[PASS] test_repay_two_users_repay_same_reserve((uint256,uint256,uint256,uint256,uint256,uint256,address),(uint256,uint256,uint256,uint256,uint256,uint256,address),uint256) (runs: 5000, μ: 1464545, ~: 1493425)
Logs:
  Bound result 5401549876140600956580819487
  Bound result 65251468040295843421393
  Bound result 11263328023241632836759
  Bound result 7707153379656042561281651440489383780992440
  Bound result 681445185

Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 311.26s (311.23s CPU time)

Ran 2 tests for tests/unit/Spoke/Spoke.Repay.Validation.t.sol:SpokeRepayValidationTest
[PASS] test_repay_revertsWith_ReserveNotListed() (gas: 23184)
[PASS] test_repay_revertsWith_ReservePaused() (gas: 63682)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.05ms (407.72µs CPU time)

Ran 38 tests for tests/unit/Hub/Hub.Config.t.sol:HubConfigTest
[PASS] test_addAsset_fuzz(address,uint8,address) (runs: 5000, μ: 390345, ~: 390384)
Logs:
  Bound result 18

[PASS] test_addAsset_fuzz_revertsWith_InvalidAddress_feeReceiver(address,uint8,address) (runs: 5000, μ: 45290, ~: 44992)
Logs:
  Bound result 1

[PASS] test_addAsset_fuzz_revertsWith_InvalidAddress_irStrategy(address,uint8,address) (runs: 5000, μ: 45333, ~: 45035)
Logs:
  Bound result 1

[PASS] test_addAsset_fuzz_revertsWith_InvalidAddress_underlying(uint8,address,address) (runs: 5000, μ: 36719, ~: 36719)
[PASS] test_addAsset_fuzz_revertsWith_InvalidAssetDecimals(address,uint8,address,address) (runs: 5000, μ: 45944, ~: 45986)
Logs:
  Bound result 248

[PASS] test_addAsset_fuzz_revertsWith_InvalidAssetDecimals_tooLow(address,uint8,address,address) (runs: 5000, μ: 46022, ~: 46308)
Logs:
  Bound result 5

[PASS] test_addAsset_fuzz_reverts_InvalidIrData(address,uint8,address,address) (runs: 5000, μ: 85020584174, ~: 34963)
Logs:
  Bound result 11

[PASS] test_addAsset_revertsWith_BlockTimestampDowncastOverflow() (gas: 956634)
[PASS] test_addAsset_revertsWith_DrawnRateDowncastOverflow() (gas: 953770)
[PASS] test_addAsset_reverts_UnderlyingAlreadyListed() (gas: 48980)
[PASS] test_addSpoke_fuzz(uint256,(uint40,uint40,uint24,bool,bool)) (runs: 5000, μ: 126676, ~: 126726)
Logs:
  Bound result 1

[PASS] test_addSpoke_fuzz_revertsWith_AssetNotListed(uint256,(uint40,uint40,uint24,bool,bool)) (runs: 5000, μ: 35246, ~: 35235)
Logs:
  Bound result 79525911719320062826962259332051026080659682862848688525261240851864880112445

[PASS] test_addSpoke_fuzz_revertsWith_InvalidAddress_spoke(uint256,(uint40,uint40,uint24,bool,bool)) (runs: 5000, μ: 33929, ~: 33979)
Logs:
  Bound result 1

[PASS] test_addSpoke_revertsWith_SpokeAlreadyListed() (gas: 39773)
[PASS] test_getAssetId() (gas: 73160)
[PASS] test_getAssetId_fuzz_revertsWith_AssetNotListed(address) (runs: 5000, μ: 18561, ~: 18561)
[PASS] test_hub_deploy_reverts_on_InvalidConstructorInput() (gas: 828595)
[PASS] test_hub_max_riskPremium() (gas: 8610)
[PASS] test_isUnderlyingListed() (gas: 1178402)
[PASS] test_updateAssetConfig_NewFeeReceiver_noFees() (gas: 719381)
[PASS] test_updateAssetConfig_NewFeeReceiver_revertsWith_SpokeNotActive_noFees() (gas: 618740)
[PASS] test_updateAssetConfig_UseExistingSpokeAndListedAsFeeReceiver_revertsWith_SpokeAlreadyListed() (gas: 70708)
[PASS] test_updateAssetConfig_fuzz(uint256,(address,uint16,address,address)) (runs: 5000, μ: 269274, ~: 269609)
Logs:
  Bound result 1
  Bound result 5529

[PASS] test_updateAssetConfig_fuzz_FromZeroLiquidityFee(uint256,uint16) (runs: 5000, μ: 822257, ~: 822117)
Logs:
  Bound result 3
  Bound result 1
  Bound result 3
  Bound result 0
  Bound result 3
  Bound result 1

[PASS] test_updateAssetConfig_fuzz_LiquidityFee(uint256,uint16) (runs: 5000, μ: 724650, ~: 724510)
Logs:
  Bound result 3
  Bound result 1
  Bound result 3
  Bound result 1

[PASS] test_updateAssetConfig_fuzz_NewFeeReceiver(uint256) (runs: 5000, μ: 823646, ~: 823680)
Logs:
  Bound result 3
  Bound result 3
  Bound result 1000

[PASS] test_updateAssetConfig_fuzz_NewInterestRateStrategy(uint256) (runs: 5000, μ: 698246, ~: 698280)
Logs:
  Bound result 3

[PASS] test_updateAssetConfig_fuzz_ReuseFeeReceiver_revertsWith_SpokeAlreadyListed(uint256) (runs: 5000, μ: 875247, ~: 875281)
Logs:
  Bound result 3
  Bound result 3
  Bound result 3
  Bound result 1000

[PASS] test_updateAssetConfig_fuzz_Scenario(uint256) (runs: 5000, μ: 700552, ~: 700605)
Logs:
  Bound result 3
  Bound result 3
  Bound result 1000
  Bound result 3
  Bound result 1000
  Bound result 3
  Bound result 0
  Bound result 3
  Bound result 0
  Bound result 3
  Bound result 0
  Bound result 3
  Bound result 0

[PASS] test_updateAssetConfig_fuzz_revertsWith_InvalidInterestRateStrategy(uint256) (runs: 5000, μ: 61083, ~: 61136)
Logs:
  Bound result 3

[PASS] test_updateAssetConfig_fuzz_revertsWith_InvalidLiquidityFee(uint256,(address,uint16,address,address)) (runs: 5000, μ: 40147, ~: 40042)
Logs:
  Bound result 1
  Bound result 5529

[PASS] test_updateAssetConfig_fuzz_revertsWith_InvalidReinvestmentController() (gas: 485646)
[PASS] test_updateAssetConfig_fuzz_revertsWith_calculateInterestRateReverts(uint256,(address,uint16,address,address)) (runs: 5000, μ: 198811, ~: 199256)
Logs:
  Bound result 0
  Bound result 1270

[PASS] test_updateAssetConfig_fuzz_revertsWith_setInterestRateDataReverts(uint256,(address,uint16,address,address)) (runs: 5000, μ: 95896, ~: 96293)
Logs:
  Bound result 3
  Bound result 0

[PASS] test_updateAssetConfig_oldFeeReceiver_flags() (gas: 880156)
Logs:
  Bound result 1
  Bound result 500
  Bound result 3
  Bound result 1000
  Bound result 5
  Bound result 500
  Bound result 3
  Bound result 1000

[PASS] test_updateSpokeConfig_fuzz(uint256,(uint40,uint40,uint24,bool,bool)) (runs: 5000, μ: 59226, ~: 59284)
Logs:
  Bound result 1

[PASS] test_updateSpokeConfig_fuzz_revertsWith_SpokeNotListed(uint256,address,(uint40,uint40,uint24,bool,bool)) (runs: 5000, μ: 40524, ~: 40592)
Logs:
  Bound result 0

[PASS] test_updateSpokeConfig_revertsWith_AssetNotListed() (gas: 29639)
Suite result: ok. 38 passed; 0 failed; 0 skipped; finished in 96.73s (96.70s CPU time)

Ran 5 tests for tests/unit/Spoke/Spoke.Borrow.EdgeCases.t.sol:SpokeBorrowEdgeCasesTest
[PASS] test_borrow_fuzz_rounding_effect(uint256,uint256) (runs: 5000, μ: 1043058, ~: 1043162)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_borrow_fuzz_rounding_effect_inflated_ex_rate(uint256,uint256,uint256) (runs: 5000, μ: 1415887, ~: 1415972)
Logs:
  Bound result 742685671417786291732933
  Bound result 9000000000000000000000
  Bound result 832486860

[PASS] test_borrow_fuzz_rounding_effect_shares(uint256,uint256) (runs: 5000, μ: 1085243, ~: 1084988)
Logs:
  Bound result 68691281934999
  Bound result 832464101

[PASS] test_borrow_rounding_effect_multiple_actions() (gas: 1145613)
[PASS] test_borrow_rounding_effect_shares() (gas: 1084250)
Logs:
  Bound result 5000000000000000000
  Bound result 94608000

Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 58.35s (58.32s CPU time)

Ran 16 tests for tests/unit/Hub/Hub.Draw.t.sol:HubDrawTest
[PASS] test_draw_DifferentSpokes() (gas: 355531)
[PASS] test_draw_fuzz_IncreasedBorrowRate(uint256,uint256) (runs: 5000, μ: 696682, ~: 696813)
Logs:
  Bound result 3
  Bound result 100

[PASS] test_draw_fuzz_amounts_same_block(uint256,uint256) (runs: 5000, μ: 289113, ~: 289127)
Logs:
  Bound result 3
  Bound result 100

[PASS] test_draw_fuzz_revertsWith_DrawCapExceeded(uint40) (runs: 5000, μ: 82328, ~: 82284)
Logs:
  Bound result 9

[PASS] test_draw_fuzz_revertsWith_DrawCapExceeded_due_to_interest(uint40,uint256,uint256) (runs: 5000, μ: 287071, ~: 287313)
Logs:
  Bound result 1291
  Bound result 70309
  Bound result 173721804

[PASS] test_draw_fuzz_revertsWith_InsufficientLiquidity(uint256,uint256) (runs: 5000, μ: 34735, ~: 34512)
Logs:
  Bound result 3
  Bound result 100

[PASS] test_draw_fuzz_revertsWith_InsufficientLiquidity_due_to_draw(uint256) (runs: 5000, μ: 172784, ~: 172492)
Logs:
  Bound result 3124043968137

[PASS] test_draw_fuzz_revertsWith_InsufficientLiquidity_due_to_remove(uint256) (runs: 5000, μ: 134501, ~: 134296)
Logs:
  Bound result 3124043968137

[PASS] test_draw_fuzz_revertsWith_InvalidAddress(uint256) (runs: 5000, μ: 16138, ~: 16138)
[PASS] test_draw_revertsWith_DrawCapExceeded_due_to_deficit() (gas: 270488)
[PASS] test_draw_revertsWith_InsufficientLiquidity() (gas: 28289)
[PASS] test_draw_revertsWith_InsufficientLiquidity_due_to_draw() (gas: 169103)
[PASS] test_draw_revertsWith_InsufficientLiquidity_due_to_remove() (gas: 131632)
[PASS] test_draw_revertsWith_InvalidAmount() (gas: 16269)
[PASS] test_draw_revertsWith_SpokeHalted() (gas: 61452)
[PASS] test_draw_revertsWith_SpokeNotActive() (gas: 61391)
Suite result: ok. 16 passed; 0 failed; 0 skipped; finished in 23.58s (23.56s CPU time)

Ran 8 tests for tests/unit/Hub/Hub.EliminateDeficit.t.sol:HubEliminateDeficitTest
[PASS] test_eliminateDeficit(uint256) (runs: 5000, μ: 663609, ~: 663609)
[PASS] test_eliminateDeficit_fuzz_revertsWith_AccessManagedUnauthorized(address) (runs: 5000, μ: 32580, ~: 32580)
[PASS] test_eliminateDeficit_fuzz_revertsWith_ArithmeticUnderflow_CallerSpokeNoFunds(uint256) (runs: 5000, μ: 352008, ~: 352008)
[PASS] test_eliminateDeficit_revertsWith_InvalidAmount_ZeroAmountNoDeficit() (gas: 36025)
[PASS] test_eliminateDeficit_revertsWith_InvalidAmount_ZeroAmountWithDeficit() (gas: 348183)
[PASS] test_eliminateDeficit_revertsWith_InvalidAmount_on_UnregisteredCoveredSpoke() (gas: 36412)
[PASS] test_eliminateDeficit_revertsWith_SpokeNotActive_on_UnregisteredAsset() (gas: 384005)
[PASS] test_eliminateDeficit_revertsWith_callerSpokeNotActive() (gas: 159412)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 16.92s (16.88s CPU time)

Ran 6 tests for tests/unit/Hub/Hub.MintFeeShares.t.sol:HubMintFeeSharesTest
[PASS] test_mintFeeShares() (gas: 311928)
[PASS] test_mintFeeShares_noFees() (gas: 367828)
[PASS] test_mintFeeShares_noShares() (gas: 293092)
[PASS] test_mintFeeShares_revertsWith_AccessManagedUnauthorized() (gas: 24139)
[PASS] test_mintFeeShares_revertsWith_AssetNotListed() (gas: 27553)
[PASS] test_mintFeeShares_revertsWith_SpokeNotActive() (gas: 242774)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 28.00ms (4.12ms CPU time)

Ran 9 tests for tests/gas/Hub.Operations.gas.t.sol:HubOperations_Gas_Tests
[PASS] test_add() (gas: 270130)
[PASS] test_deficit() (gas: 1341928)
[PASS] test_draw() (gas: 418586)
[PASS] test_mintFeeShares() (gas: 499967)
[PASS] test_payFee_transferShares() (gas: 968580)
[PASS] test_refreshPremium() (gas: 634444)
[PASS] test_remove() (gas: 310697)
[PASS] test_restore() (gas: 877878)
[PASS] test_restore_with_transfer() (gas: 878543)
Suite result: ok. 9 passed; 0 failed; 0 skipped; finished in 51.11ms (7.21ms CPU time)

Ran 6 tests for tests/unit/Hub/Hub.PayFee.t.sol:HubPayFeeTest
[PASS] test_payFee_fuzz(uint256,uint256) (runs: 5000, μ: 703896, ~: 704052)
Logs:
  Bound result 68691281934999
  Bound result 0
  Bound result 100

[PASS] test_payFee_fuzz_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 704335, ~: 704600)
Logs:
  Bound result 615514462186775432459
  Bound result 10765498
  Bound result 571193127101173104469

[PASS] test_payFee_revertsWith_InvalidShares() (gas: 20356)
[PASS] test_payFee_revertsWith_SpokeNotActive() (gas: 61390)
[PASS] test_payFee_revertsWith_underflow_added_shares_exceeded() (gas: 138740)
[PASS] test_payFee_revertsWith_underflow_added_shares_exceeded_with_interest() (gas: 643431)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 25.97s (25.94s CPU time)

Ran 11 tests for tests/unit/Hub/Hub.Reclaim.t.sol:HubReclaimTest
[PASS] test_reclaim() (gas: 652774)
Logs:
  Bound result 1000000000000000000000
  Bound result 500000000000000000000
  Bound result 200000000000000000000

[PASS] test_reclaim_fullAmount() (gas: 635224)
[PASS] test_reclaim_fuzz(uint256,uint256,uint256) (runs: 5000, μ: 654501, ~: 653715)
Logs:
  Bound result 615514462186775432459
  Bound result 571193127101173104469
  Bound result 564283877115702805413

[PASS] test_reclaim_multipleSweepsAndReclaims() (gas: 743828)
[PASS] test_reclaim_revertsWith_AssetNotListed() (gas: 13093)
[PASS] test_reclaim_revertsWith_InsufficientTransferred() (gas: 456516)
[PASS] test_reclaim_revertsWith_InsufficientTransferred_noSwept() (gas: 102551)
[PASS] test_reclaim_revertsWith_InvalidAmount_zero() (gas: 92721)
[PASS] test_reclaim_revertsWith_OnlyReinvestmentController(address) (runs: 5000, μ: 93604, ~: 93604)
[PASS] test_reclaim_revertsWith_OnlyReinvestmentController_init() (gas: 40516)
[PASS] test_reclaim_revertsWith_underflow_exceedsSwept_afterSweep() (gas: 619868)
Suite result: ok. 11 passed; 0 failed; 0 skipped; finished in 15.37s (15.34s CPU time)

Ran 12 tests for tests/unit/Hub/Hub.RefreshPremium.t.sol:HubRefreshPremiumTest
[PASS] test_refreshPremium_emitsEvent() (gas: 254760)
[PASS] test_refreshPremium_fuzz_positiveDeltas(uint256,int256,int256) (runs: 5000, μ: 488442, ~: 493737)
Logs:
  Bound result 999999999910000000000000000001
  Bound result 1
  Bound result 5495

[PASS] test_refreshPremium_fuzz_withAccrual(uint256,uint256,uint256,uint256) (runs: 5000, μ: 465869, ~: 475213)
Logs:
  Bound result 4430
  Bound result 162
  Bound result 7500000000000000
  Bound result 12415

[PASS] test_refreshPremium_haltedSpokesAllowed() (gas: 121441)
[PASS] test_refreshPremium_maxRiskPremiumThreshold() (gas: 898439)
[PASS] test_refreshPremium_negativeDeltas(uint256) (runs: 5000, μ: 458528, ~: 459078)
Logs:
  Bound result 3124043968137

[PASS] test_refreshPremium_negativeDeltas_withAccrual(uint256) (runs: 5000, μ: 528475, ~: 528691)
Logs:
  Bound result 3124043968137

[PASS] test_refreshPremium_revertsWith_InvalidPremiumChange_NonZeroRestoredPremiumRay() (gas: 853847)
[PASS] test_refreshPremium_revertsWith_InvalidPremiumChange_RiskPremiumThresholdExceeded_DecreasingPremium() (gas: 875149)
[PASS] test_refreshPremium_revertsWith_SpokeNotActive() (gas: 58921)
[PASS] test_refreshPremium_riskPremiumThreshold() (gas: 920617)
[PASS] test_refreshPremium_spokePremiumUpdateIsContained() (gas: 707620)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 20.67s (20.65s CPU time)

Ran 15 tests for tests/unit/Hub/Hub.Remove.t.sol:HubRemoveTest
[PASS] test_remove() (gas: 208909)
Logs:
  Bound result 2
  Bound result 100000000000000000000

[PASS] test_remove_all_with_interest() (gas: 368332)
[PASS] test_remove_fuzz(uint256,uint256) (runs: 5000, μ: 207679, ~: 207615)
Logs:
  Bound result 4
  Bound result 100

[PASS] test_remove_fuzz_all_liquidity_with_interest(uint256,uint256) (runs: 5000, μ: 419528, ~: 419766)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_remove_fuzz_multi_spoke(uint256,uint256) (runs: 5000, μ: 288973, ~: 289074)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_remove_fuzz_multi_spoke_with_interest(uint256,uint256,uint256,uint256) (runs: 5000, μ: 422309, ~: 422798)
Logs:
  Bound result 13237
  Bound result 2594
  Bound result 3621
  Bound result 15781

[PASS] test_remove_revertsWith_InsufficientLiquidity() (gas: 158411)
[PASS] test_remove_revertsWith_InsufficientLiquidity_exceeding_added_amount() (gas: 147500)
[PASS] test_remove_revertsWith_InsufficientLiquidity_zero_added() (gas: 21376)
[PASS] test_remove_revertsWith_InvalidAddress() (gas: 16471)
[PASS] test_remove_revertsWith_InvalidAmount() (gas: 18628)
[PASS] test_remove_revertsWith_SpokeHalted() (gas: 61971)
[PASS] test_remove_revertsWith_SpokeNotActive() (gas: 61880)
[PASS] test_remove_revertsWith_underflow_exceeding_added_amount() (gas: 185386)
[PASS] test_remove_revertsWtih_underflow_one_extra_wei() (gas: 364720)
Suite result: ok. 15 passed; 0 failed; 0 skipped; finished in 31.07s (31.04s CPU time)

Ran 17 tests for tests/unit/Spoke/Spoke.Borrow.HealthFactor.t.sol:SpokeBorrowHealthFactorTest
[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_collateral_price_drop(uint256,uint256) (runs: 5000, μ: 910532, ~: 910506)
Logs:
  Bound result 100
  Bound result 68691281934999

[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_multiple_colls(uint256,uint256) (runs: 5000, μ: 856989, ~: 857432)
Logs:
  Bound result 77
  Bound result 3831

[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_multiple_colls_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 893571, ~: 893644)
Logs:
  Bound result 11500
  Bound result 7000
  Bound result 480000000

[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_multiple_debts(uint256,uint256) (runs: 5000, μ: 1097536, ~: 1097767)
Logs:
  Bound result 99999999999068691281935000
  Bound result 99999999999000000000000101

[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_multiple_debts_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 1136788, ~: 1136910)
Logs:
  Bound result 4970607736281711122510
  Bound result 98650676047895389388470996977
  Bound result 320435018

[PASS] test_borrow_fuzz_revertsWith_HealthFactorBelowThreshold_with_interest(uint256,uint256) (runs: 5000, μ: 673314, ~: 673483)
Logs:
  Bound result 563840599603444961
  Bound result 68122

[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold() (gas: 640860)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_collateral_price_drop_weth() (gas: 904756)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls() (gas: 851213)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls_collateral_price_drop_dai() (gas: 1124858)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls_collateral_price_drop_weth() (gas: 1124902)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls_with_interest() (gas: 888653)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_debts() (gas: 1090007)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_multiple_debts_with_interest() (gas: 1128575)
[PASS] test_borrow_revertsWith_HealthFactorBelowThreshold_with_interest() (gas: 668189)
[PASS] test_fuzz_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls_collateral_price_drop_dai(uint256,uint256,uint256) (runs: 5000, μ: 1129001, ~: 1129383)
Logs:
  Bound result 112500000076
  Bound result 7200
  Bound result 48

[PASS] test_fuzz_borrow_revertsWith_HealthFactorBelowThreshold_multiple_colls_collateral_price_drop_weth(uint256,uint256,uint256) (runs: 5000, μ: 1129474, ~: 1129405)
Logs:
  Bound result 112500000076
  Bound result 7200
  Bound result 48

Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 126.98s (126.96s CPU time)

Ran 7 tests for tests/unit/Hub/Hub.ReportDeficit.t.sol:HubReportDeficitTest
[PASS] test_reportDeficit_fuzz_revertsWith_SurplusDrawnDeficitReported(uint256) (runs: 5000, μ: 220032, ~: 220468)
Logs:
  Bound result 3124043968137

[PASS] test_reportDeficit_fuzz_revertsWith_SurplusPremiumRayDeficitReported(uint256) (runs: 5000, μ: 221057, ~: 221493)
Logs:
  Bound result 3124043968137

[PASS] test_reportDeficit_fuzz_with_premium(uint256,uint256,uint256,uint256) (runs: 5000, μ: 674566, ~: 675297)
Logs:
  Bound result 4640
  Bound result 12812
  Bound result 503
  Bound result 7500000000000000

[PASS] test_reportDeficit_halted() (gas: 265152)
[PASS] test_reportDeficit_revertsWith_InvalidAmount() (gas: 24893)
[PASS] test_reportDeficit_revertsWith_SpokeNotActive(address) (runs: 5000, μ: 33865, ~: 33865)
[PASS] test_reportDeficit_with_premium() (gas: 673657)
Logs:
  Bound result 10000000000
  Bound result 31536000
  Bound result 5000000000
  Bound result 0

Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 18.14s (18.11s CPU time)

Ran 3 tests for tests/unit/Hub/Hub.Rescue.t.sol:HubRescueTest
[PASS] test_cannot_rescue_liquidity_fee_reverts_with_InsufficientTransferred() (gas: 274449)
[PASS] test_rescue_fuzz_with_interest(uint256,uint256) (runs: 5000, μ: 514695, ~: 514649)
Logs:
  Bound result 335999999
  Bound result 8773

[PASS] test_rescue_scenario_fuzz(uint256) (runs: 5000, μ: 454221, ~: 454013)
Logs:
  Bound result 3124043968137

Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 17.83s (17.80s CPU time)

Ran 23 tests for tests/unit/Hub/Hub.Restore.t.sol:HubRestoreTest
[PASS] test_restore_full_amount_with_interest() (gas: 363934)
Logs:
  Bound result 1000000000000000000000
  Bound result 500000000000000000000
  Bound result 31536000

[PASS] test_restore_full_amount_with_interest_and_premium() (gas: 673926)
Logs:
  Bound result 100000000000000000000
  Bound result 50000000000000000000
  Bound result 31536000
  Bound result 1

[PASS] test_restore_fuzz_full_amount_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 365262, ~: 365352)
Logs:
  Bound result 615514462186775432459
  Bound result 571193127101173104469
  Bound result 173721804

[PASS] test_restore_fuzz_full_amount_with_interest_and_premium(uint256,uint256,uint256,uint256) (runs: 5000, μ: 664563, ~: 675987)
Logs:
  Bound result 5267
  Bound result 1365
  Bound result 480000000
  Bound result 180

[PASS] test_restore_fuzz_revertsWith_SurplusDrawnRestored_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 246558, ~: 247795)
Logs:
  Bound result 615514462186775432459
  Bound result 571193127101173104469
  Bound result 173721804

[PASS] test_restore_fuzz_revertsWith_SurplusDrawnRestored_with_interest_and_premium(uint256,uint256,uint256,uint256) (runs: 5000, μ: 638560, ~: 638813)
Logs:
  Bound result 5267
  Bound result 1365
  Bound result 480000000
  Bound result 180

[PASS] test_restore_one_share_delta_increase_revertsWith_InvalidPremiumChange() (gas: 214789)
[PASS] test_restore_partial_drawn() (gas: 325588)
[PASS] test_restore_partial_same_block() (gas: 325714)
[PASS] test_restore_premiumDeltas_twoWeiIncrease_realizedDelta() (gas: 235510)
[PASS] test_restore_revertsWith_InsufficientTransferred() (gas: 255301)
[PASS] test_restore_revertsWith_InvalidAmount_zero() (gas: 58002)
[PASS] test_restore_revertsWith_InvalidPremiumChange_premiumIncrease() (gas: 221754)
[PASS] test_restore_revertsWith_InvalidPremiumChange_premiumSharesIncrease() (gas: 221754)
[PASS] test_restore_revertsWith_SpokeHalted() (gas: 99191)
[PASS] test_restore_revertsWith_SpokeNotActive_whenPaused() (gas: 181319)
[PASS] test_restore_revertsWith_SurplusDrawnRestored() (gas: 364429)
[PASS] test_restore_revertsWith_SurplusDrawnRestored_with_interest() (gas: 246420)
Logs:
  Bound result 100000000000000000000
  Bound result 50000000000000000000
  Bound result 15768000

[PASS] test_restore_revertsWith_SurplusDrawnRestored_with_interest_and_premium() (gas: 636775)
Logs:
  Bound result 100000000000000000000
  Bound result 50000000000000000000
  Bound result 31536000
  Bound result 1

[PASS] test_restore_revertsWith_SurplusPremiumRayRestored() (gas: 527044)
[PASS] test_restore_revertsWith_underflow_offsetIncrease() (gas: 230611)
[PASS] test_restore_tooMuchDrawn_revertsWith_SurplusDrawnRestored() (gas: 217195)
[PASS] test_restore_when_asset_caps_reset() (gas: 438513)
Suite result: ok. 23 passed; 0 failed; 0 skipped; finished in 18.90s (18.87s CPU time)

Ran 1 test for tests/unit/Hub/Hub.Rounding.t.sol:HubRoundingTest
[PASS] test_sharePriceWithMultipleDonations() (gas: 664198343)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.98s (2.95s CPU time)

Ran 17 tests for tests/unit/Spoke/Spoke.Repay.t.sol:SpokeRepayTest
[PASS] test_fuzz_amounts_repay_only_premium(uint256,uint256,uint40) (runs: 5000, μ: 887078, ~: 890365)
Logs:
  Bound result 615514462186775432459
  Bound result 6348
  Bound result 363243867333215

[PASS] test_fuzz_repay_amounts_only_interest(uint256,uint256,uint40) (runs: 5000, μ: 888895, ~: 894145)
Logs:
  Bound result 615514462186775432459
  Bound result 6348
  Bound result 6773030186390597

[PASS] test_fuzz_repay_only_premium(uint256,uint40) (runs: 5000, μ: 758857, ~: 759047)
Logs:
  Bound result 68691281934999
  Bound result 1
  Bound result 1

[PASS] test_fuzz_repay_same_block_fuzz_amounts(uint256,uint256) (runs: 5000, μ: 899827, ~: 909815)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_fuzz_repay_x_y_shares(uint256,uint40) (runs: 5000, μ: 621731, ~: 621852)
Logs:
  Bound result 68691281934999
  Bound result 1

[PASS] test_repay() (gas: 704125)
[PASS] test_repay_all_with_accruals() (gas: 417074)
[PASS] test_repay_fuzz_amountsAndWait(uint256,uint256,uint40) (runs: 5000, μ: 935383, ~: 937307)
Logs:
  Bound result 615514462186775432459
  Bound result 571193127101173104469
  Bound result 6348

[PASS] test_repay_fuzz_amounts_base_debt(uint256,uint256,uint40) (runs: 5000, μ: 947976, ~: 961440)
Logs:
  Bound result 615514462186775432459
  Bound result 6348
  Bound result 12587671819954927

[PASS] test_repay_fuzz_amounts_base_debt_no_premium(uint256,uint256,uint40) (runs: 5000, μ: 823003, ~: 832183)
Logs:
  Bound result 615514462186775432459
  Bound result 6348
  Bound result 12587671819954927

[PASS] test_repay_fuzz_revertsWith_ERC20InsufficientBalance(uint256) (runs: 5000, μ: 553583, ~: 553716)
Logs:
  Bound result 3124043968137

[PASS] test_repay_max() (gas: 599974)
[PASS] test_repay_multiple_reserves_fuzz_amountsAndWait(uint256,uint256,uint256,uint256,uint256,uint40) (runs: 5000, μ: 3348719, ~: 3355168)
Logs:
  Bound result 248111466093868109584737499669
  Bound result 1
  Bound result 273983824922877658
  Bound result 28090053839266215334
  Bound result 5861
  Bound result 2947297

[PASS] test_repay_only_interest() (gas: 727830)
[PASS] test_repay_revertsWith_ERC20InsufficientAllowance() (gas: 555950)
[PASS] test_repay_revertsWith_ReentrancyGuardReentrantCall() (gas: 650763)
[PASS] test_repay_same_block() (gas: 680981)
Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 196.45s (196.42s CPU time)

Ran 5 tests for tests/unit/Spoke/Spoke.ReserveConfig.t.sol:SpokeReserveConfigTest
[PASS] test_borrow_fuzz_borrowable_paused_frozen_scenarios(bool,bool,bool) (runs: 5000, μ: 280467, ~: 254947)
[PASS] test_repay_fuzz_paused_scenarios(bool) (runs: 5000, μ: 552523, ~: 551163)
[PASS] test_setUsingAsCollateral_fuzz_paused_frozen_scenarios(bool) (runs: 5000, μ: 184057, ~: 184069)
[PASS] test_supply_paused_frozen_scenarios() (gas: 321719)
[PASS] test_withdraw_paused_scenarios() (gas: 294108)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 16.17s (16.15s CPU time)

Ran 4 tests for tests/unit/Hub/Hub.Skim.t.sol:HubSkimTest
[PASS] test_skimAdd_fuzz_donationAfterAdd(uint256,uint256,uint256) (runs: 5000, μ: 228239, ~: 228304)
Logs:
  Bound result 3
  Bound result 18470873395738003579119570309
  Bound result 446067553769140138733721804

[PASS] test_skimAdd_fuzz_donationBeforeAdd(uint256,uint256,uint256) (runs: 5000, μ: 228271, ~: 228336)
Logs:
  Bound result 3
  Bound result 18470873395738003579119570309
  Bound result 446067553769140138733721804

[PASS] test_skimAdd_fuzz_wrongSpokeTransfer(uint256,uint256,uint256) (runs: 5000, μ: 217358, ~: 217300)
Logs:
  Bound result 3
  Bound result 18132171100462486213502917929
  Bound result 446067553769140138733721804

[PASS] test_skimRestore_fuzz_liquidityDonation(uint256,uint256,uint256) (runs: 5000, μ: 272680, ~: 274039)
Logs:
  Bound result 3
  Bound result 18470873395738003579119570309
  Bound result 446067553769140138733721804

Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 28.78s (28.75s CPU time)

Ran 10 tests for tests/unit/Hub/Hub.SpokeConfig.t.sol:HubSpokeConfigTest
[PASS] test_add_active_halted_scenarios() (gas: 303691)
[PASS] test_draw_active_halted_scenarios() (gas: 304383)
[PASS] test_eliminateDeficit_active_halted_scenarios() (gas: 838931)
[PASS] test_mintFeeShares_active_halted_scenarios() (gas: 839923)
[PASS] test_payFeeShares_active_halted_scenarios() (gas: 369275)
[PASS] test_refreshPremium_active_halted_scenarios() (gas: 267102)
[PASS] test_remove_active_halted_scenarios() (gas: 319521)
[PASS] test_reportDeficit_active_halted_scenarios() (gas: 444886)
[PASS] test_restore_active_halted_scenarios() (gas: 355284)
[PASS] test_transferShares_fuzz_active_halted_scenarios(bool,bool,bool,bool) (runs: 5000, μ: 213528, ~: 213542)
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 4.10s (4.07s CPU time)

Ran 8 tests for tests/unit/Hub/Hub.Sweep.t.sol:HubSweepTest
[PASS] test_sweep() (gas: 483294)
Logs:
  Bound result 1000000000000000000000
  Bound result 1000000000000000000000

[PASS] test_sweep_does_not_impact_utilization(uint256,uint256) (runs: 5000, μ: 630334, ~: 631460)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_sweep_fuzz(uint256,uint256) (runs: 5000, μ: 483825, ~: 483816)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_sweep_revertsWith_AssetNotListed() (gas: 12581)
[PASS] test_sweep_revertsWith_InsufficientLiquidity() (gas: 219888)
[PASS] test_sweep_revertsWith_InvalidAmount() (gas: 103885)
[PASS] test_sweep_revertsWith_OnlyReinvestmentController(address) (runs: 5000, μ: 93946, ~: 93946)
[PASS] test_sweep_revertsWith_OnlyReinvestmentController_init() (gas: 40055)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 18.68s (18.66s CPU time)

Ran 7 tests for tests/unit/Hub/Hub.TransferShares.t.sol:HubTransferSharesTest
[PASS] test_transferShares() (gas: 191440)
Logs:
  Bound result 1000000000000000000000
  Bound result 1000000000000000000000

[PASS] test_transferShares_fuzz(uint256,uint256) (runs: 5000, μ: 194827, ~: 195023)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_transferShares_fuzz_revertsWith_underflow_spoke_added_shares_exceeded(uint256) (runs: 5000, μ: 151442, ~: 151149)
Logs:
  Bound result 3124043968137

[PASS] test_transferShares_revertsWith_AddCapExceeded() (gas: 195766)
[PASS] test_transferShares_revertsWith_SpokeHalted() (gas: 184920)
[PASS] test_transferShares_revertsWith_SpokeNotActive() (gas: 178184)
[PASS] test_transferShares_zeroShares_revertsWith_InvalidShares() (gas: 22595)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 7.89s (7.87s CPU time)

Ran 6 tests for tests/unit/Hub/HubAccrueInterest.t.sol:HubAccrueInterestTest
[PASS] test_accrueInterest_NoActionTaken() (gas: 42658)
[PASS] test_accrueInterest_NoInterest_NoDebt(uint40) (runs: 5000, μ: 391610, ~: 391510)
Logs:
  Bound result 9

[PASS] test_accrueInterest_NoInterest_OnlyAdd(uint40) (runs: 5000, μ: 206804, ~: 206740)
Logs:
  Bound result 9

[PASS] test_accrueInterest_fuzz_BorrowAmountAndElapsed(uint256,uint40) (runs: 5000, μ: 271307, ~: 271199)
Logs:
  Bound result 68691281934999
  Bound result 1

[PASS] test_accrueInterest_fuzz_BorrowAmountRateAndElapsed(uint256,uint256,uint40) (runs: 5000, μ: 392395, ~: 392137)
Logs:
  Bound result 615514462186775432459
  Bound result 27544
  Bound result 6348

[PASS] test_accrueInterest_fuzz_BorrowAndWait(uint40) (runs: 5000, μ: 270027, ~: 269927)
Logs:
  Bound result 9

Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 24.85s (24.82s CPU time)

Ran 13 tests for tests/unit/HubConfigurator.GranularAccessControl.t.sol:HubConfiguratorGranularAccessControlTest
[PASS] test_assetManager_canCall_deactivateAsset() (gas: 138311)
[PASS] test_assetManager_canCall_haltAsset() (gas: 138325)
[PASS] test_assetManager_canCall_resetAssetCaps() (gas: 138743)
[PASS] test_assetManager_canCall_updateLiquidityFee() (gas: 84268)
[PASS] test_assetManager_cannotCall_anySpokeManagerMethod() (gas: 353934)
[PASS] test_fuzz_unauthorized_cannotCall_assetManagerMethods(address) (runs: 5000, μ: 277895, ~: 277895)
[PASS] test_fuzz_unauthorized_cannotCall_spokeManagerMethods(address) (runs: 5000, μ: 352478, ~: 352478)
[PASS] test_spokeManager_canCall_addSpoke() (gas: 126721)
[PASS] test_spokeManager_canCall_resetSpokeCaps() (gas: 185463)
[PASS] test_spokeManager_canCall_updateSpokeActive() (gas: 66003)
[PASS] test_spokeManager_canCall_updateSpokeCaps() (gas: 66504)
[PASS] test_spokeManager_canCall_updateSpokeHalted() (gas: 65972)
[PASS] test_spokeManager_cannotCall_anyAssetManagerMethod() (gas: 278850)
Suite result: ok. 13 passed; 0 failed; 0 skipped; finished in 5.87s (5.84s CPU time)

Ran 60 tests for tests/unit/HubConfigurator.t.sol:HubConfiguratorTest
[PASS] test_addAsset_fuzz(bool,address,uint8,address,uint256,uint16,uint32,uint32,uint32) (runs: 5000, μ: 448787, ~: 449342)
Logs:
  Bound result 7
  Bound result 8551
  Bound result 9999
  Bound result 0
  Bound result 31874
  Bound result 42657

[PASS] test_addAsset_fuzz_revertsWith_AccessManagedUnauthorized(address) (runs: 5000, μ: 42331, ~: 42700)
[PASS] test_addAsset_fuzz_revertsWith_InvalidAssetDecimals(bool,address,uint8,address,uint256,address) (runs: 5000, μ: 65211, ~: 65188)
Logs:
  Bound result 21
  Bound result 0

[PASS] test_addAsset_revertsWith_InvalidAddress_irStrategy() (gas: 58255)
[PASS] test_addAsset_revertsWith_InvalidAddress_underlying() (gas: 58171)
[PASS] test_addAsset_revertsWith_InvalidLiquidityFee() (gas: 350377)
[PASS] test_addAsset_reverts_invalidIrData() (gas: 76503)
[PASS] test_addSpoke() (gas: 135409)
[PASS] test_addSpokeToAssets() (gas: 232696)
[PASS] test_addSpokeToAssets_revertsWith_AccessManagedUnauthorized() (gas: 28151)
[PASS] test_addSpokeToAssets_revertsWith_MismatchedConfigs() (gas: 35971)
[PASS] test_addSpoke_revertsWith_AccessManagedUnauthorized() (gas: 27817)
[PASS] test_deactivateAsset() (gas: 176526)
[PASS] test_deactivateAsset_revertsWith_AccessManagedUnauthorized() (gas: 29002)
[PASS] test_deactivateSpoke() (gas: 175401)
[PASS] test_deactivateSpoke_revertsWith_AccessManagedUnauthorized() (gas: 28996)
[PASS] test_haltAsset() (gas: 176560)
[PASS] test_haltAsset_revertsWith_AccessManagedUnauthorized() (gas: 28981)
[PASS] test_haltSpoke() (gas: 175422)
[PASS] test_haltSpoke_revertsWith_AccessManagedUnauthorized() (gas: 29004)
[PASS] test_resetAssetCaps() (gas: 250744)
[PASS] test_resetAssetCaps_revertsWith_AccessManagedUnauthorized() (gas: 28956)
[PASS] test_resetSpokeCaps() (gas: 268435)
[PASS] test_resetSpokeCaps_revertsWith_AccessManagedUnauthorized() (gas: 29046)
[PASS] test_updateFeeConfig_Scenario() (gas: 353206)
Logs:
  Bound result 0
  Bound result 1800
  Bound result 0
  Bound result 400
  Bound result 0
  Bound result 0

[PASS] test_updateFeeConfig_fuzz(uint256,uint16,address) (runs: 5000, μ: 195268, ~: 195561)
Logs:
  Bound result 2
  Bound result 0

[PASS] test_updateFeeConfig_fuzz_revertsWith_AccessManagedUnauthorized(address) (runs: 5000, μ: 28519, ~: 28519)
[PASS] test_updateFeeConfig_revertsWith_InvalidAddress_spoke() (gas: 59417)
[PASS] test_updateFeeConfig_revertsWith_InvalidLiquidityFee() (gas: 62593)
[PASS] test_updateFeeReceiver_Scenario() (gas: 227692)
[PASS] test_updateFeeReceiver_WithdrawFromOldSpoke() (gas: 429602)
[PASS] test_updateFeeReceiver_correctAccruals() (gas: 466574)
[PASS] test_updateFeeReceiver_fuzz(address) (runs: 5000, μ: 188869, ~: 188889)
[PASS] test_updateFeeReceiver_fuzz_revertsWith_AccessManagedUnauthorized(address) (runs: 5000, μ: 28203, ~: 28203)
[PASS] test_updateFeeReceiver_revertsWith_InvalidAddress_spoke() (gas: 64072)
[PASS] test_updateFeeReceiver_revertsWith_SpokeAlreadyListed() (gas: 89372)
[PASS] test_updateInterestRateData() (gas: 81916)
[PASS] test_updateInterestRateData_revertsWith_AccessManagedUnauthorized() (gas: 30162)
[PASS] test_updateInterestRateStrategy() (gas: 102777)
[PASS] test_updateInterestRateStrategy_fuzz_revertsWith_AccessManagedUnauthorized(address) (runs: 5000, μ: 39577, ~: 39577)
[PASS] test_updateInterestRateStrategy_revertsWith_InterestRateStrategyReverts() (gas: 85998)
[PASS] test_updateInterestRateStrategy_revertsWith_InvalidAddress_irStrategy() (gas: 75586)
[PASS] test_updateInterestRateStrategy_revertsWith_InvalidInterestRateStrategy() (gas: 76221)
[PASS] test_updateLiquidityFee_fuzz(uint256,uint16) (runs: 5000, μ: 100485, ~: 102101)
Logs:
  Bound result 3
  Bound result 0

[PASS] test_updateLiquidityFee_revertsWith_AccessManagedUnauthorized() (gas: 27626)
[PASS] test_updateLiquidityFee_revertsWith_InvalidLiquidityFee() (gas: 64212)
[PASS] test_updateReinves...*[Comment body truncated]*

@github-actions
Copy link

github-actions bot commented Dec 8, 2025

Forge Build Sizes

Contract Runtime Size (B) Initcode Size (B) Runtime Margin (B) Initcode Margin (B)
EIP712Hash (src/position-manager/libraries/EIP712Hash.sol) ↑26% (+114) 555 ↑23% (+114) 607 ↓0% (-114) 24,021 ↓0% (-114) 48,545
JsonBindings ↑22% (+2889) 15,742 ↑22% (+2889) 15,794 ↓25% (-2889) 8,834 ↓8% (-2889) 33,358
NativeTokenGateway ↑60% (+3725) 9,912 ↑60% (+3955) 10,559 ↓20% (-3725) 14,664 ↓9% (-3955) 38,593
SignatureGateway ↑4% (+430) 11,915 ↑4% (+429) 12,453 ↓3% (-430) 12,661 ↓1% (-429) 36,699
GatewayBaseWrapper 2,400 2,675 22,176 46,477
AllowancePositionManager 10,847 11,398 13,729 37,754
ConfigPermissionsMap 44 94 24,532 49,058
ConfigPermissionsWrapper 753 781 23,823 48,371
PositionConfigPositionManager 9,621 10,137 14,955 39,015
PositionManagerBaseWrapper 6,311 6,827 18,265 42,325
PositionManagerFalseFlags 6,084 6,600 18,492 42,552
SupplyRepayPositionManager 7,779 8,295 16,797 40,857
🔕 Unchanged
Contract Runtime Size (B) Initcode Size (B) Runtime Margin (B) Initcode Margin (B)
AaveOracle 2,834 3,488 21,742 45,664
AccessManager 12,985 14,210 11,591 34,942
AccessManagerEnumerable 16,881 18,699 7,695 30,453
Address 44 94 24,532 49,058
Arrays 44 94 24,532 49,058
Arrays.hub 16 44 24,560 49,108
Arrays.spoke 16 44 24,560 49,108
AssetInterestRateStrategy 2,704 2,889 21,872 46,263
AssetLogic 44 94 24,532 49,058
AssetLogic.hub 16 44 24,560 49,108
AuthorityUtils 44 94 24,532 49,058
AuthorityUtils.hub 16 44 24,560 49,108
AuthorityUtils.spoke 16 44 24,560 49,108
Bytes 44 94 24,532 49,058
Bytes.spoke 16 44 24,560 49,108
Comparators 44 94 24,532 49,058
Comparators.hub 16 44 24,560 49,108
Comparators.spoke 16 44 24,560 49,108
Constants 499 551 24,077 48,601
Create2Utils 134 184 24,442 48,968
DeployUtils 44 94 24,532 49,058
DeployWrapper 3,330 3,358 21,246 45,794
ECDSA 44 94 24,532 49,058
ECDSA.spoke 16 44 24,560 49,108
EIP712Hash (src/spoke/libraries/EIP712Hash.sol) 171 221 24,405 48,931
EIP712Hash.spoke 166 194 24,410 48,958
EIP712Types 44 94 24,532 49,058
ERC1967Proxy 135 891 24,441 48,261
ERC1967Utils 44 94 24,532 49,058
EnumerableSet 44 94 24,532 49,058
EnumerableSet.hub 16 44 24,560 49,108
Errors 44 94 24,532 49,058
ExtSloadWrapper 394 422 24,182 48,730
Hashes 44 94 24,532 49,058
Hub 23,547 23,744 1,029 25,408
HubConfigurator 13,833 14,029 10,743 35,123
KeyValueList 44 94 24,532 49,058
KeyValueList.spoke 16 44 24,560 49,108
KeyValueListWrapper 957 985 23,619 48,167
LibBit 44 94 24,532 49,058
LibBit.spoke 16 44 24,560 49,108
LiquidationLogic 12,318 12,370 12,258 36,782
LiquidationLogic.spoke 9,772 9,804 14,804 39,348
LiquidationLogicWrapper 18,567 18,741 6,009 30,411
LowLevelCall 44 94 24,532 49,058
Math 44 94 24,532 49,058
Math.hub 16 44 24,560 49,108
Math.spoke 16 44 24,560 49,108
MathUtils 44 94 24,532 49,058
MathUtils.hub 16 44 24,560 49,108
MathUtils.spoke 16 44 24,560 49,108
MockERC1271Wallet 828 962 23,748 48,190
MockERC20 2,540 3,006 22,036 46,146
MockNoncesKeyed 858 886 23,718 48,266
MockPriceFeed 737 1,395 23,839 47,757
MockReentrantCaller 882 1,083 23,694 48,069
MockSkimSpoke 1,116 1,275 23,460 47,877
NoncesKeyed 644 672 23,932 48,480
NoncesKeyed.spoke 387 413 24,189 48,739
Panic 44 94 24,532 49,058
Panic.hub 16 44 24,560 49,108
Panic.spoke 16 44 24,560 49,108
PercentageMath 44 94 24,532 49,058
PercentageMath.hub 16 44 24,560 49,108
PercentageMath.spoke 16 44 24,560 49,108
PercentageMathWrapper 632 660 23,944 48,492
PositionStatusMap 44 94 24,532 49,058
PositionStatusMap.spoke 16 44 24,560 49,108
PositionStatusMapWrapper 3,341 3,369 21,235 45,783
Premium 44 94 24,532 49,058
Premium.hub 16 44 24,560 49,108
Premium.spoke 16 44 24,560 49,108
ProxyAdmin 1,320 1,556 23,256 47,596
RescuableWrapper 908 1,042 23,668 48,110
ReserveFlagsMap 44 94 24,532 49,058
ReserveFlagsMap.spoke 16 44 24,560 49,108
ReserveFlagsMapWrapper 928 956 23,648 48,196
Roles 218 269 24,358 48,883
SafeCast 44 94 24,532 49,058
SafeCast.hub 16 44 24,560 49,108
SafeCast.spoke 16 44 24,560 49,108
SafeERC20 44 94 24,532 49,058
SafeERC20.hub 16 44 24,560 49,108
SafeERC20.spoke 16 44 24,560 49,108
SharesMath 44 94 24,532 49,058
SharesMath.hub 16 44 24,560 49,108
SignatureChecker 44 94 24,532 49,058
SignatureChecker.spoke 16 44 24,560 49,108
SlotDerivation 44 94 24,532 49,058
SlotDerivation.hub 16 44 24,560 49,108
SlotDerivation.spoke 16 44 24,560 49,108
SpokeConfigurator 12,328 12,524 12,248 36,628
SpokeInstance 24,193 25,000 383 24,152
SpokeUtils 96 146 24,480 49,006
SpokeUtils.spoke 71 99 24,505 49,053
SpokeUtilsWrapper 1,827 1,855 22,749 47,297
StorageSlot 44 94 24,532 49,058
StorageSlot.hub 16 44 24,560 49,108
StorageSlot.spoke 16 44 24,560 49,108
TestnetERC20 3,649 4,525 20,927 44,627
Time 44 94 24,532 49,058
TransientSlot 44 94 24,532 49,058
TransientSlot.spoke 16 44 24,560 49,108
TransparentUpgradeableProxy 1,419 4,078 23,157 45,074
TreasurySpoke 3,599 4,014 20,977 45,138
UnitPriceFeed 777 1,771 23,799 47,381
UserPositionUtils (src/spoke/libraries/UserPositionDebt.sol) 44 94 24,532 49,058
UserPositionUtils (src/spoke/libraries/UserPositionUtils.sol) 44 94 24,532 49,058
UserPositionUtils.spoke 16 44 24,560 49,108
UserPositionUtilsWrapper (tests/mocks/UserPositionDebtWrapper.sol) 3,263 3,291 21,313 45,861
UserPositionUtilsWrapper (tests/mocks/UserPositionUtilsWrapper.sol) 3,263 3,291 21,313 45,861
Utils 44 94 24,532 49,058
WETH9 2,148 2,614 22,428 46,538
WadRayMath 44 94 24,532 49,058
WadRayMath.hub 16 44 24,560 49,108
WadRayMath.spoke 16 44 24,560 49,108
WadRayMathWrapper 1,514 1,542 23,062 47,610

@github-actions
Copy link

github-actions bot commented Dec 8, 2025

♻️ Forge Gas Snapshots

Path Value
snapshots/AllowancePositionManager.Operations.json
approveWithdraw 49872
approveWithdrawWithSig 65714
borrowOnBehalfOf 307991
delegateCredit 49863
delegateCreditWithSig 65714
renounceCreditDelegation 28007
renounceWithdrawAllowance 27972
withdrawOnBehalfOf: full 120720
withdrawOnBehalfOf: partial 130699
snapshots/NativeTokenGateway.Operations.json
borrowNative ↑0% (+78) 228,690
repayNative ↑0% (+78) 166,483
supplyAsCollateralNative ↑0% (+78) 160,145
supplyNative ↑0% (+62) 135,789
withdrawNative: full ↑0% (+80) 125,620
withdrawNative: partial ↑0% (+101) 136,825
snapshots/PositionConfigPositionManager.Operations.json
renounceCanUpdateUserDynamicConfigPermission 27649
renounceCanUpdateUserRiskPremiumPermission 27671
renounceCanUpdateUsingAsCollateralPermission 27672
renounceGlobalPermission 27694
setCanUpdateUserDynamicConfigPermission 49773
setCanUpdateUserRiskPremiumPermission 49795
setCanUpdateUsingAsCollateralPermission 49795
setGlobalPermission 49795
setUsingAsCollateralOnBehalfOf 72520
updateUserDynamicConfigOnBehalfOf 50450
updateUserRiskPremiumOnBehalfOf 130306
snapshots/PositionManagerBase.Operations.json
setSelfAsUserPositionManagerWithSig 75313
snapshots/SignatureGateway.Operations.json
borrowWithSig ↑0% (+25) 213,870
repayWithSig ↑0% (+25) 186,702
setSelfAsUserPositionManagerWithSig ↑0% (+47) 75,432
setUsingAsCollateralWithSig ↑0% (+47) 85,412
supplyWithSig ↑0% (+37) 151,996
updateUserDynamicConfigWithSig ↑0% (+47) 63,145
updateUserRiskPremiumWithSig ↓0% (-41) 62,027
withdrawWithSig ↑0% (+38) 130,832
snapshots/SupplyRepayPositionManager.Operations.json
repayOnBehalfOf 167888
supplyOnBehalfOf 136916
🔕 Unchanged
Path Value
snapshots/Hub.Operations.json
add 86,703
add: with transfer 108,000
draw 104,159
eliminateDeficit: full 72,578
eliminateDeficit: partial 82,183
mintFeeShares 82,752
payFee 70,816
refreshPremium 70,373
remove: full 75,607
remove: partial 80,745
reportDeficit 111,893
restore: full 76,563
restore: full - with transfer 169,172
restore: partial 85,273
restore: partial - with transfer 143,253
transferShares 69,630
snapshots/Spoke.Getters.json
getUserAccountData: supplies: 0, borrows: 0 12,992
getUserAccountData: supplies: 1, borrows: 0 49,404
getUserAccountData: supplies: 2, borrows: 0 81,080
getUserAccountData: supplies: 2, borrows: 1 101,509
getUserAccountData: supplies: 2, borrows: 2 120,769
snapshots/Spoke.Operations.ZeroRiskPremium.json
borrow: first 190,362
borrow: second action, same reserve 170,228
liquidationCall (receiveShares): full 303,239
liquidationCall (receiveShares): partial 302,657
liquidationCall (reportDeficit): full 366,361
liquidationCall: full 320,875
liquidationCall: partial 320,293
permitReserve + repay (multicall) 164,532
permitReserve + supply (multicall) 146,712
permitReserve + supply + enable collateral (multicall) 161,141
repay: full 123,892
repay: partial 128,850
setUserPositionManagersWithSig: disable 47,039
setUserPositionManagersWithSig: enable 68,951
supply + enable collateral (multicall) 141,343
supply: 0 borrows, collateral disabled 122,824
supply: 0 borrows, collateral enabled 105,795
supply: second action, same reserve 105,724
updateUserDynamicConfig: 1 collateral 74,523
updateUserDynamicConfig: 2 collaterals 89,391
updateUserRiskPremium: 1 borrow 95,712
updateUserRiskPremium: 2 borrows 105,392
usingAsCollateral: 0 borrows, enable 59,594
usingAsCollateral: 1 borrow, disable 105,756
usingAsCollateral: 1 borrow, enable 42,482
usingAsCollateral: 2 borrows, disable 127,305
usingAsCollateral: 2 borrows, enable 42,494
withdraw: 0 borrows, full 127,955
withdraw: 0 borrows, partial 132,851
withdraw: 1 borrow, partial 159,982
withdraw: 2 borrows, partial 174,540
withdraw: non collateral 105,902
snapshots/Spoke.Operations.json
borrow: first 259,297
borrow: second action, same reserve 202,163
liquidationCall (receiveShares): full 335,283
liquidationCall (receiveShares): partial 334,701
liquidationCall (reportDeficit): full 360,978
liquidationCall: full 352,919
liquidationCall: partial 352,337
permitReserve + repay (multicall) 162,009
permitReserve + supply (multicall) 146,712
permitReserve + supply + enable collateral (multicall) 161,141
repay: full 117,971
repay: partial 137,329
setUserPositionManagersWithSig: disable 47,039
setUserPositionManagersWithSig: enable 68,951
supply + enable collateral (multicall) 141,343
supply: 0 borrows, collateral disabled 122,824
supply: 0 borrows, collateral enabled 105,795
supply: second action, same reserve 105,724
updateUserDynamicConfig: 1 collateral 74,523
updateUserDynamicConfig: 2 collaterals 89,391
updateUserRiskPremium: 1 borrow 149,071
updateUserRiskPremium: 2 borrows 199,333
usingAsCollateral: 0 borrows, enable 59,594
usingAsCollateral: 1 borrow, disable 159,112
usingAsCollateral: 1 borrow, enable 42,482
usingAsCollateral: 2 borrows, disable 229,242
usingAsCollateral: 2 borrows, enable 42,494
withdraw: 0 borrows, full 127,955
withdraw: 0 borrows, partial 132,851
withdraw: 1 borrow, partial 210,836
withdraw: 2 borrows, partial 257,012
withdraw: non collateral 105,902

IERC20 asset = _getReserveUnderlying(reserveId);

uint256 userTotalDebt = SPOKE.getUserTotalDebt(reserveId, onBehalfOf);
uint256 repayAmount = amount > userTotalDebt ? userTotalDebt : amount;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we care about the attack vector of borrowing more before they max repay kicks in? We d need to disallow max repay here.

Copy link
Contributor

@avniculae avniculae Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or check amount is not more than x% more than userTotalDebt? would it be an overkill?

or maybe take the following inputs (reserveId, amount, maxShares, onBehalfOf). we then revert if shares returned by spoke.repay are > maxShares. would this add too much friction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it brings a lot of complexity or would block some users behaviors rather than solving the potential issue.
Imo it's up to user decision to approve+pass a large/larger than needed repayAmount, which would signify they are ready to repay up to that inputed amount.
In here, it's not like we simply override the amount to repay all debt if the inputed amount is higher than the debt of onBehalfOf, so we already have the parameter limiting how much the user is ready to repay at maximum.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think blocking max_uint is a way to block unintentional max repayments, so using a fixed amount means user knows what is doing, while using max_uint is naive call by a noob user thus it's blocked to protect them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then just passing max_uint - 1 would serve the same usecase and skip the blocker, noob user might see it as "inputed the wrong value" and execute the same exact mistake with just - 1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(1 year later)
yes, max_uint - 1 would mean user knows what they are doing basicaly. Personally in favor of blocking max_uint only

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree^

IERC20 asset = _getReserveUnderlying(reserveId);
asset.safeTransferFrom(msg.sender, address(this), amount);
asset.forceApprove(SPOKE, amount);
return ISpoke(SPOKE).supply(reserveId, amount, onBehalfOf);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given that the spoke returns both shares and amounts, I'm wondering whether we should assert (with a require) that amount returned is equal to amount. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is just an extension of the Spoke method, so I think returning the amount & shares here should be sufficient so integrators can then verify the returned values.

}

/// @inheritdoc ICreditDelegationPositionManager
function approveCreditDelegationWithSig(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have something like PositionManagerWithAllowanceBase.sol, which implements the map and the 2 functions for approvals (with and without sig)? (to avoid duplication)

wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the signature part, I'd rather keep each unique typehash & type, rather than a common one used for both. As for the mapping, no real opinion there, the current duplication does not bother me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ofc, still unique typehash, but we can pass typehash to the base


function _isMulticallAllowed() internal pure virtual returns (bool);

function _isSpokeRegistryActive() internal pure virtual returns (bool);
Copy link
Member

@miguelmtzinf miguelmtzinf Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the usecase of this one?

);

/// @notice Approves a spender to withdraw assets from the specified reserve.
/// @dev If `amount` is the maximum `uint256`, the allowance is not updated on `withdrawOnBehalfOf`. This is semantically equivalent to an infinite approval.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @dev If `amount` is the maximum `uint256`, the allowance is not updated on `withdrawOnBehalfOf`. This is semantically equivalent to an infinite approval.
/// @dev Using `type(uint256).max` as the amount results in an infinite approval, so the allowance is never decreased.

) external;

/// @notice Approves a credit delegation allowance for a spender.
/// @dev If `amount` is the maximum `uint256`, the allowance is not updated on `borrowOnBehalfOf`. This is semantically equivalent to an infinite approval.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @dev If `amount` is the maximum `uint256`, the allowance is not updated on `borrowOnBehalfOf`. This is semantically equivalent to an infinite approval.
/// @dev Using `type(uint256).max` as the amount results in an infinite approval, so the allowance is never decreased.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving this here is a little weird bc it contains spoke eip712hash tests as well, if you want to split it in two thats fine or im ok keeping as is also

positionManager.approveWithdrawWithSig(p, signature);
}

function test_approveWithdrawWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() public {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signature one's even tho they are i would move to a separate file like AllowancePosm.WithSig.t.sol to be cleaner

supplyAmount
);
vm.prank(bob);
(returnValues.shares, returnValues.amount) = positionManager.withdrawOnBehalfOf(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls also expect emit event of allownce going down from the posm (same for all tests)

assertTrue(permissions.canUpdateUserDynamicConfig);
}

function test_setGlobalPermission_fuzz(bool status) public {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does need to be a fuzz test given only two possible inputs pls spit this in two unit tests instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also would love to see one symbolic syntax test also ideally 🐱

Comment on lines +221 to +225
vm.prank(alice);
positionManager.setCanUpdateUserRiskPremiumPermission(address(spoke1), bob, true);
assertTrue(
positionManager.getConfigPermissions(address(spoke1), bob, alice).canUpdateUserRiskPremium
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe sign off in a helper to reduce duplications

Comment on lines +283 to +286
(uint192 nonceKey, uint64 nonce) = _unpackNonce(p.nonce);
unchecked {
++nonce;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls put this in a helper

calls[0] = abi.encodeCall(gateway.supplyWithSig, (p, signature));
calls[1] = abi.encodeCall(gateway.setUsingAsCollateralWithSig, (p2, signature2));

gateway.multicall(calls);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also check for return values, that is missing


function _approvePositionManager(address who) internal {
assertFalse(spoke1.isPositionManager(who, POSITION_MANAGER));
function _approvePositionManager(address user) internal {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its not necessary for user to be this arg thats why i kept the generic who, pls revert

Comment on lines -3089 to +3109
address who,
address user,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again not necessary for user to be this pls revert i wanna have this lmfao

Comment on lines 3112 to 3114
(uint192 nonceKey, uint64 nonce) = _unpackNonce(prevKeyNonce);
// prettier-ignore
unchecked { ++nonce; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reuse the helper you create here as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and theres a test somewhere also does this (search for unchecked in tests/)

Comment on lines +161 to +164

function _multicallEnabled() internal pure override returns (bool) {
return false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add natspec with rationale why pls


/// @title NativeTokenGateway
/// @author Aave Labs
/// @notice Gateway to interact with a spoke using the native coin of a chain.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coin was intentional here, most are not tokens bc they dont follow spec, its a common vocabulary i would revert this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wydm with don't follow the spec? token is not a standard. To me it's clear what "native token" is, it's the gas token of the chain.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coin = native currency
token = erc20

shorthand terminology from what i've seen

Comment on lines +257 to +259
if (currentAllowance != type(uint256).max) {
_withdrawAllowances[spoke][reserveId][owner][spender] = currentAllowance.uncheckedSub(amount);
}
Copy link
Member

@DhairyaSethi DhairyaSethi Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (currentAllowance != type(uint256).max) {
_withdrawAllowances[spoke][reserveId][owner][spender] = currentAllowance.uncheckedSub(amount);
}
if (currentAllowance != type(uint256).max) {
_updateWithdrawAllowance(spoke, reserveId, owner, spender, currentAllowance.uncheckedSub(amount));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isnt this what you had planned such that events are emitted on this storage update?

function _multicallEnabled() internal pure virtual returns (bool);

/// @dev Flag to enable the spoke registry, is set to false all Spokes are considered registered. Needs to be set by the inheriting contracts.
function _isSpokeRegistryActive() internal pure virtual returns (bool);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this is not needed bc consumer can just not use the modifier because this is explicitely default opt out feature anyway unlike multicall


/// @dev Verifies the specified spoke is registered.
function _isSpokeRegistered(address spoke) internal view returns (bool) {
return _registeredSpokes[spoke] || !_isSpokeRegistryActive();
Copy link
Member

@DhairyaSethi DhairyaSethi Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk if this way compiler will remove dead paths on compile time, i would do explicit IF condition here and everywhere else where we have immutable feature flag bools

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to swap the order also no? so we short-circuit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no short circuit. these are immutable booleans and ideally compiler will remove the false path from the bytecode entirely. i suggested these especially bc they will have zero run time cost, hence being no cost abstractions

function multicall(
bytes[] calldata data
) public override(Multicall, IMulticall) returns (bytes[] memory) {
require(_multicallEnabled(), UnsupportedAction());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah can you check ir output if compiler removes the dead paths this way on compile time otherwise explicit if and then revert is also fine (im ok not having require style here bc exceptionally its will be removed by compiler)


/// @inheritdoc IPositionManagerBase
function renouncePositionManagerRole(address spoke, address user) external onlyOwner {
require(user != address(0), InvalidAddress());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed imo

Comment on lines +79 to +81
) external onlyRegisteredSpoke(spoke) {
address underlying = _getReserveUnderlying(spoke, reserveId);
try
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so we ensure underlying cannot be zero bc spoke is registered but maybe we rm modifier to allow through anyspoke (doesnt really matter anyway) save us the sload and do underlying not zero instead

IntentConsumer,
Ownable2Step,
Rescuable,
Multicall
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add comment here its optional feature only is you set _enableMulticall to true

}

/// @inheritdoc IAllowancePositionManager
function creditDelegation(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is borrowAllowance in V3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants