Skip to content

Comments

feat: Fee minter contract to periodically call mintFeeShares#1216

Open
CheyenneAtapour wants to merge 5 commits intomainfrom
feat/fee-minter
Open

feat: Fee minter contract to periodically call mintFeeShares#1216
CheyenneAtapour wants to merge 5 commits intomainfrom
feat/fee-minter

Conversation

@CheyenneAtapour
Copy link
Contributor

Adds a fee minter contract which will enforce specific conditions for calling mintFeeShares() on the hub, and allow for mintFeeShares() to be called consistently

@CheyenneAtapour CheyenneAtapour changed the base branch from dev to main February 17, 2026 02:01
@github-actions
Copy link

github-actions bot commented Feb 17, 2026

Forge Build Sizes

Contract Runtime Size (B) Initcode Size (B) Runtime Margin (B) Initcode Margin (B)
SpokeConfigurator ↓4% (-503) 11,825 ↓4% (-503) 12,021 ↑4% (+503) 12,751 ↑1% (+503) 37,131
FeeSharesMinterBase 3,632 3,905 20,944 45,247
🔕 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,886 18,704 7,690 30,448
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/position-manager/libraries/EIP712Hash.sol) 441 493 24,135 48,659
EIP712Hash (src/spoke/libraries/EIP712Hash.sol) 441 493 24,135 48,659
EIP712Hash.spoke 491 521 24,085 48,631
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
GatewayBaseWrapper 2,400 2,675 22,176 46,477
Hashes 44 94 24,532 49,058
Hub 23,510 23,707 1,066 25,445
HubConfigurator 13,833 14,029 10,743 35,123
JsonBindings 17,744 17,796 6,832 31,356
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,440 12,492 12,136 36,660
LiquidationLogic.spoke 9,829 9,861 14,747 39,291
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
NativeTokenGateway 6,187 6,604 18,389 42,548
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
SignatureGateway 11,485 12,024 13,091 37,128
SlotDerivation 44 94 24,532 49,058
SlotDerivation.hub 16 44 24,560 49,108
SlotDerivation.spoke 16 44 24,560 49,108
SpokeInstance 24,291 25,098 285 24,054
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
TokenizationSpokeInstance 13,497 14,796 11,079 34,356
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 Feb 17, 2026

🌈 Test Results
No files changed, compilation skipped

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersHaltedTest
[PASS] test_maxDeposit_returnsZero() (gas: 23325)
[PASS] test_maxMint_returnsZero() (gas: 39383)
[PASS] test_maxRedeem_returnsZero() (gas: 41573)
[PASS] test_maxWithdraw_returnsZero() (gas: 41475)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 39.53ms (502.65µs CPU time)

Ran 4 tests for tests/unit/Spoke/Liquidations/Spoke.LiquidationCall.Dust.t.sol:SpokeLiquidationCallDustTest
[PASS] test_collateralDust_min_debtToTarget() (gas: 17750446)
[PASS] test_debtToCover_exceeds_collateralValue() (gas: 17738563)
[PASS] test_dustColl_allowed() (gas: 17598961)
[PASS] test_dustDebt_allowed() (gas: 17729829)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 51.65ms (21.80ms CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersLiquidityGreaterThanBalanceTest
[PASS] test_maxRedeem_returnsSharesOfBalance() (gas: 55737)
[PASS] test_maxWithdraw_returnsBalance() (gas: 55446)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 24.90ms (384.09µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersLiquidityLessThanBalanceTest
[PASS] test_maxRedeem_returnsSharesOfLiquidity() (gas: 55704)
[PASS] test_maxWithdraw_returnsLiquidity() (gas: 55445)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 24.80ms (397.99µs CPU time)

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersNotActiveAndHaltedTest
[PASS] test_maxDeposit_returnsZero() (gas: 23311)
[PASS] test_maxMint_returnsZero() (gas: 39369)
[PASS] test_maxRedeem_returnsZero() (gas: 41559)
[PASS] test_maxWithdraw_returnsZero() (gas: 41461)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.29ms (492.35µs CPU time)

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersNotActiveTest
[PASS] test_maxDeposit_returnsZero() (gas: 23311)
[PASS] test_maxMint_returnsZero() (gas: 39369)
[PASS] test_maxRedeem_returnsZero() (gas: 41559)
[PASS] test_maxWithdraw_returnsZero() (gas: 41461)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.13ms (480.97µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersOwnerZeroSharesTest
[PASS] test_maxRedeem_returnsZero() (gas: 42977)
[PASS] test_maxWithdraw_returnsZero() (gas: 42890)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 24.72ms (269.67µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersZeroLiquidityTest
[PASS] test_maxRedeem_returnsZero() (gas: 42977)
[PASS] test_maxWithdraw_returnsZero() (gas: 42912)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.04ms (275.21µs CPU time)

Ran 13 tests for tests/unit/Spoke/Liquidations/Spoke.LiquidationCall.Scenarios.t.sol:SpokeLiquidationCallScenariosTest
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRefreshPremium() (gas: 25951869)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRemove() (gas: 25823524)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubReportDeficit() (gas: 25934643)
[PASS] test_liquidationCall_revertsWith_ReentrancyGuardReentrantCall_hubRestore() (gas: 25898930)
[PASS] test_liquidationCall_scenario1() (gas: 3855259)
[PASS] test_liquidationCall_scenario2() (gas: 3863585)
[PASS] test_liquidationCall_scenario3() (gas: 3332566)
[PASS] test_liquidationCall_scenario4() (gas: 27491077)
[PASS] test_liquidationCall_scenario5() (gas: 3470220)
[PASS] test_liquidationCall_scenario6() (gas: 2305391)
[PASS] test_liquidationCall_scenario7() (gas: 3049621)
[PASS] test_scenario_halted_asset() (gas: 26576257)
[PASS] test_scenario_halted_asset_with_deficit() (gas: 26419386)
Suite result: ok. 13 passed; 0 failed; 0 skipped; finished in 165.49ms (135.11ms CPU time)

Ran 9 tests for tests/gas/TokenizationSpoke.Operations.gas.t.sol:TokenizationSpokeOperations_Gas_Tests
[PASS] test_deposit() (gas: 124100)
[PASS] test_depositWithSig() (gas: 209891)
[PASS] test_mint() (gas: 152102)
[PASS] test_mintWithSig() (gas: 239936)
[PASS] test_permit() (gas: 118522)
[PASS] test_redeem() (gas: 838771)
[PASS] test_redeemWithSig() (gas: 320341)
[PASS] test_withdraw() (gas: 804181)
[PASS] test_withdrawWithSig() (gas: 321171)
Suite result: ok. 9 passed; 0 failed; 0 skipped; finished in 53.49ms (7.01ms 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, μ: 19885, ~: 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: 5030210)
[PASS] test_setSpoke() (gas: 5058111)
[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.16s (1.15s CPU time)

Ran 10 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Permit.t.sol:TokenizationSpokePermitTest
[PASS] test_nonces_uses_permit_nonce_key_namespace(bytes32) (runs: 5000, μ: 17679, ~: 17679)
[PASS] test_permit() (gas: 189761)
[PASS] test_permit_revertsWith_InvalidAddress_dueTo_ZeroAddressOwner() (gas: 51015)
[PASS] test_permit_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 28541)
[PASS] test_permit_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 51490)
[PASS] test_permit_revertsWith_InvalidSignature_dueTo_invalid_nonce_at_arbitrary_namespace(bytes32) (runs: 5000, μ: 55264, ~: 55264)
[PASS] test_permit_revertsWith_InvalidSignature_dueTo_invalid_nonce_at_permit_key_namespace(bytes32) (runs: 5000, μ: 57097, ~: 57097)
[PASS] test_renounceAllowance() (gas: 35296)
[PASS] test_renounceAllowance_noop() (gas: 24125)
[PASS] test_usePermitNonce(bytes32) (runs: 5000, μ: 18166, ~: 18166)
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 7.14s (7.12s CPU time)

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Reverts.InsufficientAllowance.t.sol:TokenizationSpokeInsufficientAllowanceTest
[PASS] test_depositWithSig_revertsWith_ERC20InsufficientAllowance() (gas: 155503)
[PASS] test_deposit_revertsWith_ERC20InsufficientAllowance() (gas: 66648)
[PASS] test_mintWithSig_revertsWith_ERC20InsufficientAllowance() (gas: 166264)
[PASS] test_mint_revertsWith_ERC20InsufficientAllowance() (gas: 71178)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 28.17ms (2.59ms CPU time)

Ran 6 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Upgradeable.t.sol:TokenizationSpokeUpgradeableTest
[PASS] test_implementation_constructor_fuzz(uint64) (runs: 5000, μ: 2807532, ~: 2807532)
[PASS] test_proxy_constructor_fuzz(uint64) (runs: 5000, μ: 2824610, ~: 2824602)
Logs:
  Bound result 649

[PASS] test_proxy_constructor_fuzz_revertsWith_InvalidInitialization(uint64) (runs: 5000, μ: 5604148, ~: 5604140)
Logs:
  Bound result 649

[PASS] test_proxy_constructor_revertsWith_InvalidInitialization_ZeroRevision() (gas: 2804292)
[PASS] test_proxy_reinitialization_fuzz(uint64) (runs: 5000, μ: 5614707, ~: 5614690)
Logs:
  Bound result 649

[PASS] test_proxy_reinitialization_revertsWith_CallerNotProxyAdmin() (gas: 5592997)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 13.45s (13.43s CPU time)

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

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

[PASS] test_grantRole() (gas: 315853)
[PASS] test_grantRole_fuzz(uint64,uint256) (runs: 5000, μ: 914134, ~: 919819)
Logs:
  Bound result 8

[PASS] test_renounceRole() (gas: 320971)
[PASS] test_renounceRole_shouldNotTrack() (gas: 24510)
[PASS] test_revokeRole() (gas: 323142)
[PASS] test_revokeRole_shouldNotTrack() (gas: 33091)
[PASS] test_setRoleAdmin_fuzz_trackAdminRoles_multipleRoles_multipleAdmins(uint256) (runs: 5000, μ: 2146376, ~: 2243730)
Logs:
  Bound result 12

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

[PASS] test_setRoleAdmin_trackAdminOfRoles() (gas: 605988)
[PASS] test_setRoleAdmin_trackAdminOfRoles_changeAdminRole() (gas: 576953)
[PASS] test_setRoleAdmin_trackAdminRoles() (gas: 602234)
[PASS] test_setRoleAdmin_trackRolesAndTrackAdminRoles() (gas: 378157)
[PASS] test_setRoleGuardian_trackRoles() (gas: 263878)
[PASS] test_setTargetFunctionRole() (gas: 414440)
[PASS] test_setTargetFunctionRole_multipleTargets() (gas: 1103888)
[PASS] test_setTargetFunctionRole_removeTarget() (gas: 894968)
[PASS] test_setTargetFunctionRole_skipAddToAdminRole() (gas: 30951)
[PASS] test_setTargetFunctionRole_withReplace() (gas: 548994)
Suite result: ok. 20 passed; 0 failed; 0 skipped; finished in 22.38s (22.38s 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, μ: 24186, ~: 24330)
Logs:
  Bound result 137
  Bound result 252173843969976304268974536488

[PASS] test_calculateInterestRate_RightToKinkPoint(uint256) (runs: 5000, μ: 25303, ~: 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, μ: 19076, ~: 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.14s (1.13s CPU time)

Ran 14 tests for tests/unit/misc/EIP712Hash.t.sol:EIP712HashTest
[PASS] test_constants() (gas: 18335)
[PASS] test_hash_borrow_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6689, ~: 6689)
[PASS] test_hash_positionManagerUpdate_fuzz((address,bool)) (runs: 5000, μ: 5361, ~: 5361)
[PASS] test_hash_repay_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6734, ~: 6734)
[PASS] test_hash_setUserPositionManagers_fuzz((address,(address,bool)[],uint256,uint256)) (runs: 5000, μ: 278419, ~: 277841)
[PASS] test_hash_setUsingAsCollateral_fuzz((address,uint256,bool,address,uint256,uint256)) (runs: 5000, μ: 7148, ~: 7148)
[PASS] test_hash_supply_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6755, ~: 6755)
[PASS] test_hash_tokenizedDeposit_fuzz((address,uint256,address,uint256,uint256)) (runs: 5000, μ: 6561, ~: 6561)
[PASS] test_hash_tokenizedMint_fuzz((address,uint256,address,uint256,uint256)) (runs: 5000, μ: 6538, ~: 6538)
[PASS] test_hash_tokenizedRedeem_fuzz((address,uint256,address,uint256,uint256)) (runs: 5000, μ: 6561, ~: 6561)
[PASS] test_hash_tokenizedWithdraw_fuzz((address,uint256,address,uint256,uint256)) (runs: 5000, μ: 6582, ~: 6582)
[PASS] test_hash_updateUserDynamicConfig_fuzz((address,address,uint256,uint256)) (runs: 5000, μ: 6349, ~: 6349)
[PASS] test_hash_updateUserRiskPremium_fuzz((address,address,uint256,uint256)) (runs: 5000, μ: 6348, ~: 6348)
[PASS] test_hash_withdraw_fuzz((address,uint256,uint256,address,uint256,uint256)) (runs: 5000, μ: 6711, ~: 6711)
Suite result: ok. 14 passed; 0 failed; 0 skipped; finished in 19.72s (19.72s CPU time)

Ran 12 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.WithSig.Reverts.InvalidSignature.t.sol:TokenizationSpokeWithSigInvalidSignatureTest
[PASS] test_depositWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 168645, ~: 171080)
[PASS] test_depositWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 29560)
[PASS] test_depositWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 27829)
[PASS] test_mintWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 168665, ~: 171100)
[PASS] test_mintWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 29515)
[PASS] test_mintWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 27793)
[PASS] test_redeemWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 168667, ~: 171102)
[PASS] test_redeemWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 29519)
[PASS] test_redeemWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 27819)
[PASS] test_withdrawWithSig_revertsWith_InvalidAccountNonce(bytes32) (runs: 5000, μ: 168664, ~: 171099)
[PASS] test_withdrawWithSig_revertsWith_InvalidSignature_dueTo_ExpiredDeadline() (gas: 29558)
[PASS] test_withdrawWithSig_revertsWith_InvalidSignature_dueTo_InvalidSigner() (gas: 27838)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 26.81s (26.79s CPU time)

Ran 5 tests for tests/unit/Spoke/Spoke.Withdraw.Scenario.t.sol:SpokeWithdrawScenarioTest
[PASS] test_withdraw_fuzz_all_liquidity_with_interest_multi_user((uint256,uint256,uint256,uint256,uint256[2],uint256)) (runs: 5000, μ: 943313, ~: 963064)
Logs:
  Bound result 0
  Bound result 11086671006371704
  Bound result 785592789150925985448020464673
  Bound result 670649063
  Bound result 740106
  Bound result 286710214268612704140077643609
  Bound result 94623

[PASS] test_withdraw_fuzz_partial_full_with_interest(uint256,uint256,uint256,uint40) (runs: 5000, μ: 712860, ~: 712886)
Logs:
  Bound result 22801872835208
  Bound result 2231316470926
  Bound result 17831863075646
  Bound result 385856

[PASS] test_withdraw_round_trip_deposit_withdraw(uint256,uint256,address,uint256) (runs: 5000, μ: 412866, ~: 412853)
Logs:
  Bound result 0
  Bound result 1
  Bound result 292444651076564202344860280473

[PASS] test_withdraw_round_trip_withdraw_deposit(uint256,uint256,uint256,address,uint256) (runs: 5000, μ: 433666, ~: 432963)
Logs:
  Bound result 3
  Bound result 1125000000000000000000
  Bound result 5625
  Bound result 1500000000000000000000000000

[PASS] test_withdraw_underwater_reserve_not_collateral() (gas: 813384)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 59.39s (59.35s CPU time)

Ran 6 tests for tests/unit/Spoke/Spoke.Withdraw.Validation.t.sol:SpokeWithdrawValidationTest
[PASS] test_withdraw_fuzz_revertsWith_InsufficientLiquidity_with_debt(uint256,uint256,uint256,uint256,uint256) (runs: 5000, μ: 430327, ~: 430437)
Logs:
  Bound result 0
  Bound result 140331615000681815353
  Bound result 4
  Bound result 60664
  Bound result 553896834

[PASS] test_withdraw_fuzz_revertsWith_InsufficientSupply_zero_supplied(uint256) (runs: 5000, μ: 55104, ~: 54812)
Logs:
  Bound result 3124043968137

[PASS] test_withdraw_revertsWith_InsufficientLiquidity_with_debt() (gas: 426855)
[PASS] test_withdraw_revertsWith_InvalidAmount_zero_supplied() (gas: 51432)
[PASS] test_withdraw_revertsWith_ReserveNotListed() (gas: 22408)
[PASS] test_withdraw_revertsWith_ReservePaused() (gas: 62929)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.81s (5.78s CPU time)

Ran 5 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.WithSig.t.sol:TokenizationSpokeWithSigTest
[PASS] test_depositWithSig(bytes32) (runs: 5000, μ: 349294, ~: 351730)
[PASS] test_mintWithSig(bytes32) (runs: 5000, μ: 348631, ~: 351067)
[PASS] test_redeemWithSig(bytes32) (runs: 5000, μ: 330393, ~: 332195)
[PASS] test_useNonce_monotonic(bytes32) (runs: 5000, μ: 17155, ~: 17155)
[PASS] test_withdrawWithSig(bytes32) (runs: 5000, μ: 331337, ~: 333120)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 48.81s (48.78s CPU time)

Ran 8 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.t.sol:TokenizationSpokeTest
[PASS] test_deposit() (gas: 234699)
[PASS] test_deposit_receiverDifferentFromCaller() (gas: 222970)
[PASS] test_mint() (gas: 234051)
[PASS] test_mint_receiverDifferentFromCaller() (gas: 222349)
[PASS] test_redeem() (gas: 220093)
[PASS] test_redeem_ownerDifferentFromCaller() (gas: 252513)
[PASS] test_withdraw() (gas: 216427)
[PASS] test_withdraw_ownerDifferentFromCaller() (gas: 248832)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 35.82ms (10.93ms 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, μ: 954599, ~: 930302)
Logs:
  Bound result 812

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

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

Ran 12 tests for tests/unit/Hub/FeeSharesMinterBase.t.sol:FeeSharesMinterBaseTest
[PASS] test_execute_largeScalePrecision() (gas: 59337)
[PASS] test_execute_revertsWith_MinShareNotMet() (gas: 177043)
[PASS] test_execute_revertsWith_PercentThresholdNotMet() (gas: 238378)
[PASS] test_execute_revertsWith_TimeIntervalNotMet() (gas: 290943)
[PASS] test_execute_success() (gas: 330345)
[PASS] test_fuzz_execute(uint256,uint256,uint256,uint256,uint16) (runs: 5000, μ: 272385, ~: 266794)
Logs:
  Bound result 19150094060736361193
  Bound result 3872
  Bound result 6563
  Bound result 27820800
  Bound result 5825

[PASS] test_fuzz_setConfig_revertsWith_InvalidConfig_FeePercent(uint16) (runs: 5000, μ: 21643, ~: 21858)
Logs:
  Bound result 10002

[PASS] test_fuzz_setConfig_revertsWith_InvalidConfig_TimeInterval(uint256) (runs: 5000, μ: 21214, ~: 21026)
Logs:
  Bound result 3124043968137

[PASS] test_fuzz_setConfig_success(uint256,uint16) (runs: 5000, μ: 69575, ~: 71500)
Logs:
  Bound result 6060813
  Bound result 0

[PASS] test_rescueToken() (gas: 663281)
[PASS] test_setConfig_revertsWith_OwnableUnauthorized() (gas: 18003)
[PASS] test_transferOwnership_2Step() (gas: 35785)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 3.62s (3.59s CPU time)

Ran 8 tests for tests/unit/misc/GatewayBase.t.sol:GatewayBaseTest
[PASS] test_constructor() (gas: 17151)
[PASS] test_registerSpoke_fuzz(address) (runs: 5000, μ: 41629, ~: 41629)
[PASS] test_registerSpoke_revertsWith_InvalidAddress() (gas: 13119)
[PASS] test_registerSpoke_revertsWith_OwnableUnauthorizedAccount() (gas: 13791)
[PASS] test_registerSpoke_unregister() (gas: 36076)
[PASS] test_renouncePositionManagerRole() (gas: 65293)
[PASS] test_renouncePositionManagerRole_revertsWith_InvalidAddress() (gas: 74220)
[PASS] test_renouncePositionManagerRole_revertsWith_OwnableUnauthorizedAccount() (gas: 74439)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 387.65ms (362.91ms CPU time)

Ran 5 tests for tests/gas/Gateways.Operations.gas.t.sol:NativeTokenGateway_Gas_Tests
[PASS] test_borrowNative() (gas: 917897)
[PASS] test_repayNative() (gas: 989322)
[PASS] test_supplyAndCollateralNative() (gas: 305177)
[PASS] test_supplyNative() (gas: 286660)
[PASS] test_withdrawNative() (gas: 508744)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 48.25ms (4.16ms CPU time)

Ran 8 tests for tests/gas/Gateways.Operations.gas.t.sol:SignatureGateway_Gas_Tests
[PASS] test_borrowWithSig() (gas: 745226)
[PASS] test_repayWithSig() (gas: 953430)
[PASS] test_setSelfAsUserPositionManagerWithSig() (gas: 209285)
[PASS] test_setUsingAsCollateralWithSig() (gas: 289338)
[PASS] test_supplyWithSig() (gas: 434257)
[PASS] test_updateUserDynamicConfigWithSig() (gas: 145235)
[PASS] test_updateUserRiskPremiumWithSig() (gas: 143188)
[PASS] test_withdrawWithSig() (gas: 409909)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 51.94ms (7.85ms CPU time)

Ran 6 tests for tests/unit/Hub/Hub.Access.t.sol:HubAccessTest
[PASS] test_change_authority() (gas: 206679)
[PASS] test_change_role_responsibility() (gas: 121247)
[PASS] test_hub_access_manager_exposure() (gas: 13439)
[PASS] test_hub_admin_access() (gas: 1350377)
[PASS] test_migrate_role_responsibility() (gas: 708944)
[PASS] test_setInterestRateData_access() (gas: 102656)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 27.98ms (3.94ms CPU time)

Ran 14 tests for tests/unit/Spoke/Spoke.Withdraw.t.sol:SpokeWithdrawTest
[PASS] test_fuzz_withdraw_effect_on_ex_rates(uint256,uint256) (runs: 5000, μ: 730628, ~: 732565)
Logs:
  Bound result 100
  Bound result 68691281934999

[PASS] test_withdraw_all_liquidity() (gas: 238623)
[PASS] test_withdraw_all_liquidity_with_interest_no_premium() (gas: 798185)
[PASS] test_withdraw_all_liquidity_with_interest_with_premium() (gas: 805716)
[PASS] test_withdraw_fuzz_all_elapsed_with_interest(uint256,uint256,uint40) (runs: 5000, μ: 660646, ~: 660564)
Logs:
  Bound result 999999999999999999999999999997
  Bound result 499999999999999999999999999997
  Bound result 7880

[PASS] test_withdraw_fuzz_all_greater_than_supplied(uint256) (runs: 5000, μ: 241648, ~: 241441)
Logs:
  Bound result 3124043968137

[PASS] test_withdraw_fuzz_all_liquidity_with_interest_no_premium((uint256,uint256,uint256,uint256,uint256)) (runs: 5000, μ: 871216, ~: 871703)
Logs:
  Bound result 0
  Bound result 2
  Bound result 1
  Bound result 2
  Bound result 286517826

[PASS] test_withdraw_fuzz_all_liquidity_with_interest_with_premium((uint256,uint256,uint256,uint256,uint256)) (runs: 5000, μ: 875683, ~: 875739)
Logs:
  Bound result 0
  Bound result 2
  Bound result 1
  Bound result 2
  Bound result 286517826

[PASS] test_withdraw_fuzz_all_with_interest(uint256,uint256) (runs: 5000, μ: 673803, ~: 673746)
Logs:
  Bound result 68691281934999
  Bound result 100

[PASS] test_withdraw_fuzz_suppliedAmount(uint256) (runs: 5000, μ: 243240, ~: 243034)
Logs:
  Bound result 3124043968137

[PASS] test_withdraw_max_greater_than_supplied() (gas: 222068)
[PASS] test_withdraw_revertsWith_ReentrancyGuardReentrantCall_hubRefreshPremium() (gas: 654522)
[PASS] test_withdraw_revertsWith_ReentrancyGuardReentrantCall_hubRemove() (gas: 402082)
[PASS] test_withdraw_same_block() (gas: 243752)
Suite result: ok. 14 passed; 0 failed; 0 skipped; finished in 73.99s (73.96s CPU time)

Ran 16 tests for tests/unit/SpokeConfigurator.GranularAccessControl.t.sol:SpokeConfiguratorGranularAccessControlTest
[PASS] test_fuzz_unauthorized_cannotCall_liquidationConfigManagerMethods(address) (runs: 5000, μ: 117059, ~: 117059)
[PASS] test_fuzz_unauthorized_cannotCall_positionManagerAdminMethods(address) (runs: 5000, μ: 39761, ~: 39761)
[PASS] test_fuzz_unauthorized_cannotCall_reserveManagerMethods(address) (runs: 5000, μ: 458283, ~: 458283)
[PASS] test_liquidationConfigManager_canCall_updateLiquidationConfig() (gas: 62481)
[PASS] test_liquidationConfigManager_canCall_updateLiquidationTargetHealthFactor() (gas: 62044)
[PASS] test_liquidationConfigManager_cannotCall_anyPositionManagerAdminMethod() (gas: 38976)
[PASS] test_liquidationConfigManager_cannotCall_anyReserveMethod() (gas: 460745)
[PASS] test_positionManagerAdmin_canCall_updatePositionManager() (gas: 75206)
[PASS] test_positionManagerAdmin_cannotCall_anyLiquidationConfigMethod() (gas: 117021)
[PASS] test_positionManagerAdmin_cannotCall_anyReserveMethod() (gas: 460504)
[PASS] test_reserveManager_canCall_freezeAllReserves() (gas: 156806)
[PASS] test_reserveManager_canCall_pauseAllReserves() (gas: 156791)
[PASS] test_reserveManager_canCall_updateFrozen() (gas: 65173)
[PASS] test_reserveManager_canCall_updatePaused() (gas: 65119)
[PASS] test_reserveManager_cannotCall_anyLiquidationConfigMethod() (gas: 116976)
[PASS] test_reserveManager_cannotCall_anyPositionManagerAdminMethod() (gas: 38975)
Suite result: ok. 16 passed; 0 failed; 0 skipped; finished in 6.03s (6.00s CPU time)

Ran 48 tests for tests/unit/SpokeConfigurator.t.sol:SpokeConfiguratorTest
[PASS] test_addCollateralFactor() (gas: 125650)
[PASS] test_addCollateralFactor_revertsWith_AccessManagedUnauthorized() (gas: 29137)
[PASS] test_addDynamicReserveConfig() (gas: 105394)
[PASS] test_addDynamicReserveConfig_revertsWith_AccessManagedUnauthorized() (gas: 29330)
[PASS] test_addLiquidationBonus_revertsWith_AccessManagedUnauthorized() (gas: 29058)
[PASS] test_addLiquidationFee() (gas: 125616)
[PASS] test_addLiquidationFee_revertsWith_AccessManagedUnauthorized() (gas: 29081)
[PASS] test_addMaxLiquidationBonus() (gas: 125643)
[PASS] test_addReserve() (gas: 424450)
[PASS] test_addReserve_revertsWith_AccessManagedUnauthorized() (gas: 30001)
[PASS] test_freezeAllReserves() (gas: 202557)
[PASS] test_freezeAllReserves_revertsWith_AccessManagedUnauthorized() (gas: 26901)
[PASS] test_freezeReserve() (gas: 72297)
[PASS] test_freezeReserve_revertsWith_AccessManagedUnauthorized() (gas: 28937)
[PASS] test_pauseAllReserves() (gas: 202437)
[PASS] test_pauseAllReserves_revertsWith_AccessManagedUnauthorized() (gas: 26812)
[PASS] test_pauseReserve() (gas: 72246)
[PASS] test_pauseReserve_revertsWith_AccessManagedUnauthorized() (gas: 28958)
[PASS] test_updateBorrowable() (gas: 102791)
[PASS] test_updateBorrowable_revertsWith_AccessManagedUnauthorized() (gas: 29105)
[PASS] test_updateCollateralFactor() (gas: 82763)
[PASS] test_updateCollateralFactor_revertsWith_AccessManagedUnauthorized() (gas: 29202)
[PASS] test_updateCollateralRisk() (gas: 76983)
[PASS] test_updateCollateralRisk_revertsWith_AccessManagedUnauthorized() (gas: 29010)
[PASS] test_updateDynamicReserveConfig() (gas: 201232)
[PASS] test_updateDynamicReserveConfig_revertsWith_AccessManagedUnauthorized() (gas: 29413)
[PASS] test_updateFrozen() (gas: 105579)
[PASS] test_updateFrozen_revertsWith_AccessManagedUnauthorized() (gas: 29038)
[PASS] test_updateHealthFactorForMaxBonus() (gas: 68166)
[PASS] test_updateHealthFactorForMaxBonus_revertsWith_AccessManagedUnauthorized() (gas: 26869)
[PASS] test_updateLiquidationBonusFactor() (gas: 71081)
[PASS] test_updateLiquidationBonusFactor_revertsWith_AccessManagedUnauthorized() (gas: 26848)
[PASS] test_updateLiquidationConfig() (gas: 65758)
[PASS] test_updateLiquidationConfig_revertsWith_AccessManagedUnauthorized() (gas: 27131)
[PASS] test_updateLiquidationFee() (gas: 82758)
[PASS] test_updateLiquidationFee_revertsWith_AccessManagedUnauthorized() (gas: 29177)
[PASS] test_updateLiquidationTargetHealthFactor() (gas: 71022)
[PASS] test_updateLiquidationTargetHealthFactor_revertsWith_AccessManagedUnauthorized() (gas: 26891)
[PASS] test_updateMaxLiquidationBonus() (gas: 82782)
[PASS] test_updateMaxLiquidationBonus_revertsWith_AccessManagedUnauthorized() (gas: 29136)
[PASS] test_updatePaused() (gas: 105494)
[PASS] test_updatePaused_revertsWith_AccessManagedUnauthorized() (gas: 29059)
[PASS] test_updatePositionManager() (gas: 74349)
[PASS] test_updatePositionManager_revertsWith_AccessManagedUnauthorized() (gas: 27000)
[PASS] test_updateReceiveSharesEnabled() (gas: 102910)
[PASS] test_updateReceiveSharesEnabled_revertsWith_AccessManagedUnauthorized() (gas: 29103)
[PASS] test_updateReservePriceSource() (gas: 280226)
[PASS] test_updateReservePriceSource_revertsWith_AccessManagedUnauthorized() (gas: 29056)
Suite result: ok. 48 passed; 0 failed; 0 skipped; finished in 43.03ms (14.27ms CPU time)

Ran 6 tests for tests/unit/libraries/SpokeUtils.t.sol:SpokeUtilsTest
[PASS] test_fuzz_toValue(uint256,uint256,uint256) (runs: 5000, μ: 15789, ~: 15844)
Logs:
  Bound result 615514462186775432459
  Bound result 9
  Bound result 9140094126966428

[PASS] test_get() (gas: 180696)
[PASS] test_get_revertsWith_ReserveNotListed() (gas: 167195)
[PASS] test_toValue() (gas: 8806)
[PASS] test_toValue_revertsWith_ArithmeticOverflow() (gas: 8959)
[PASS] test_toValue_revertsWith_ArithmeticUnderflow() (gas: 8734)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 429.97ms (405.44ms CPU time)

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Config.t.sol:TokenizationSpokeConfigTest
[PASS] test_configuration() (gas: 22899)
[PASS] test_constructor_asset_correctly_set() (gas: 42793)
[PASS] test_constructor_reverts_when_invalid_setup() (gas: 12222)
[PASS] test_setUp() (gas: 69892)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.90ms (1.00ms CPU time)

Ran 7 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Constants.t.sol:TokenizationSpokeConstantsTest
[PASS] test_DOMAIN_SEPARATOR() (gas: 14481)
[PASS] test_deposit_typeHash() (gas: 15183)
[PASS] test_eip712Domain() (gas: 19690)
[PASS] test_mint_typeHash() (gas: 15232)
[PASS] test_permit_typeHash() (gas: 15126)
[PASS] test_redeem_typeHash() (gas: 15126)
[PASS] test_withdraw_typeHash() (gas: 15129)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 26.56ms (1.93ms CPU time)

Ran 4 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.DepositWithPermit.t.sol:TokenizationSpokeDepositWithPermitTest
[PASS] test_depositWithPermit() (gas: 238120)
[PASS] test_depositWithPermit_forwards_correct_call() (gas: 194314)
[PASS] test_depositWithPermit_ignores_permit_reverts() (gas: 183502)
[PASS] test_depositWithPermit_works_with_existing_allowance() (gas: 203611)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 27.12ms (2.84ms CPU time)

Ran 17 tests for tests/unit/Spoke/TreasurySpoke.t.sol:TreasurySpokeTest
[PASS] test_borrow_revertsWith_UnsupportedAction() (gas: 9654)
[PASS] test_deploy_reverts_on_invalid_params() (gas: 4182)
[PASS] test_getters() (gas: 740264)
[PASS] test_initial_state() (gas: 167956)
[PASS] test_liquidationCall_revertsWith_UnsupportedAction() (gas: 10474)
[PASS] test_repay_revertsWith_UnsupportedAction() (gas: 9656)
[PASS] test_supply(uint256) (runs: 5000, μ: 124847, ~: 124633)
Logs:
  Bound result 3124043968137

[PASS] test_supply_revertsWith_Unauthorized(address) (runs: 5000, μ: 16116, ~: 16116)
[PASS] test_transfer_fuzz(address,uint256,uint256) (runs: 5000, μ: 100112, ~: 101182)
Logs:
  Bound result 641425215323277642603241435599205566
  Bound result 511

[PASS] test_transfer_revertsWith_ERC20InsufficientBalance(uint256) (runs: 5000, μ: 604011, ~: 604011)
[PASS] test_transfer_revertsWith_Unauthorized(address) (runs: 5000, μ: 14829, ~: 14829)
[PASS] test_withdraw_fuzz_amount_feesOnly(uint256) (runs: 5000, μ: 786058, ~: 785765)
Logs:
  Bound result 3124043968137

[PASS] test_withdraw_fuzz_amount_interestAndFees(uint256) (runs: 5000, μ: 773028, ~: 772859)
Logs:
  Bound result 3124043968137

[PASS] test_withdraw_fuzz_amount_interestOnly(uint256) (runs: 5000, μ: 806149, ~: 805851)
Logs:
  Bound result 399452744467735387087512641102

[PASS] test_withdraw_fuzz_maxLiquidityFee(uint256,uint256,uint256) (runs: 5000, μ: 851310, ~: 851420)
Logs:
  Bound result 4
  Bound result 218470873395738003579119570309
  Bound result 173721804

[PASS] test_withdraw_maxLiquidityFee() (gas: 852027)
Logs:
  Bound result 2
  Bound result 1000000000000000000000
  Bound result 29376000

[PASS] test_withdraw_revertsWith_Unauthorized(address) (runs: 5000, μ: 16479, ~: 16479)
Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 52.38s (52.35s CPU time)

Ran 8 tests for tests/unit/UnitPriceFeed.t.sol:UnitPriceFeedTest
[PASS] testDECIMALS() (gas: 8305)
[PASS] test_constructor_revertsWith_Uint8Overflow() (gas: 4674)
[PASS] test_description() (gas: 11865)
[PASS] test_fuzz_latestRoundData(uint80) (runs: 5000, μ: 11105, ~: 11105)
[PASS] test_fuzz_latestRoundData_DifferentDecimals(uint8) (runs: 5000, μ: 14418, ~: 14578)
Logs:
  Bound result 1

[PASS] test_getRoundData() (gas: 12402)
[PASS] test_getRoundData_futureRound() (gas: 12325)
[PASS] test_version() (gas: 8261)
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 1.01s (999.78ms CPU time)

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

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

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

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

[PASS] test_add_fuzz_revertsWith_InvalidShares_due_to_index(uint256,uint256,uint256) (runs: 5000, μ: 220657, ~: 220858)
Logs:
  Bound result 232555700122303543015100534065
  Bound result 3122928001
  Bound result 2

[PASS] test_add_fuzz_single_asset(uint256,address,uint256) (runs: 5000, μ: 342376, ~: 342397)
Logs:
  Bound result 4
  Bound result 684

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

[PASS] test_add_multi_add_minimal_shares() (gas: 316011)
[PASS] test_add_revertsWith_AmountDowncastOverflow() (gas: 357696)
[PASS] test_add_revertsWith_InsufficientTransferred() (gas: 64504)
[PASS] test_add_revertsWith_InvalidAmount() (gas: 13631)
[PASS] test_add_revertsWith_InvalidShares() (gas: 220197)
[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: 330246)
Logs:
  Bound result 2
  Bound result 100000000000000000000

[PASS] test_add_with_increased_index() (gas: 298042)
[PASS] test_add_with_increased_index_with_premium() (gas: 674207)
Suite result: ok. 19 passed; 0 failed; 0 skipped; finished in 43.47s (43.45s CPU time)

Ran 12 tests for tests/unit/libraries/UserPositionDebt.t.sol:UserPositionUtilsTest
[PASS] test_applyPremiumDelta() (gas: 26101)
[PASS] test_calculatePremiumDelta() (gas: 17592)
[PASS] test_calculatePremiumRay() (gas: 19352)
[PASS] test_calculateRestoreAmount() (gas: 24204)
[PASS] test_fuzz_applyPremiumDelta((int256,int256,uint256)) (runs: 5000, μ: 34087, ~: 34184)
Logs:
  Bound result 215016815803108445248527167096
  Bound result 331073717946348281691194968263

[PASS] test_fuzz_calculatePremiumDelta((uint256,uint256,int256,uint256,uint256,uint256,uint256)) (runs: 5000, μ: 48848, ~: 49023)
Logs:
  Bound result 99000000050384421173548891413
  Bound result 267064735
  Bound result 15166708452425474209087810086592205507
  Bound result 310273141488122044938099248275
  Bound result 16212485736382855360724910945
  Bound result 99999
  Bound result 11272700326030427879754413608164415047

[PASS] test_fuzz_calculatePremiumRay(uint256,int256,uint256) (runs: 5000, μ: 33200, ~: 33324)
Logs:
  Bound result 99000000000000000000136025644
  Bound result 412200000000000000000000000000
  Bound result -1

[PASS] test_fuzz_calculateRestoreAmount(uint256,uint256,int256,uint256,uint256) (runs: 5000, μ: 42326, ~: 42301)
Logs:
  Bound result 69295509040982496176622344798
  Bound result 700000000000000000
  Bound result 45949006410929164602672518618644340988520946369
  Bound result 12361
  Bound result 1000001000000000000

[PASS] test_fuzz_getUserDebt_DrawnIndex(uint256,uint256,int256,uint256) (runs: 5000, μ: 39434, ~: 39396)
Logs:
  Bound result 99000000000000000000000001801
  Bound result 720000000000000000
  Bound result -40000000000000000000
  Bound result 249264818036790566556173706948

[PASS] test_fuzz_getUserDebt_HubAndAssetId(uint256,uint256,int256,uint256) (runs: 5000, μ: 45394, ~: 45356)
Logs:
  Bound result 99000000000000000000000001801
  Bound result 720000000000000000
  Bound result -40000000000000000000
  Bound result 249264818036790566556173706948

[PASS] test_getUserDebt_DrawnIndex() (gas: 23419)
[PASS] test_getUserDebt_HubAndAssetId() (gas: 30124)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 4.22s (4.22s CPU time)

Ran 12 tests for tests/unit/libraries/UserPositionUtils.t.sol:UserPositionUtilsTest
[PASS] test_applyPremiumDelta() (gas: 26101)
[PASS] test_calculatePremiumDelta() (gas: 17592)
[PASS] test_calculatePremiumRay() (gas: 19352)
[PASS] test_calculateRestoreAmount() (gas: 24204)
[PASS] test_fuzz_applyPremiumDelta((int256,int256,uint256)) (runs: 5000, μ: 34087, ~: 34184)
Logs:
  Bound result 215016815803108445248527167096
  Bound result 331073717946348281691194968263

[PASS] test_fuzz_calculatePremiumDelta((uint256,uint256,int256,uint256,uint256,uint256,uint256)) (runs: 5000, μ: 48848, ~: 49023)
Logs:
  Bound result 99000000050384421173548891413
  Bound result 267064735
  Bound result 15166708452425474209087810086592205507
  Bound result 310273141488122044938099248275
  Bound result 16212485736382855360724910945
  Bound result 99999
  Bound result 11272700326030427879754413608164415047

[PASS] test_fuzz_calculatePremiumRay(uint256,int256,uint256) (runs: 5000, μ: 33200, ~: 33324)
Logs:
  Bound result 99000000000000000000136025644
  Bound result 412200000000000000000000000000
  Bound result -1

[PASS] test_fuzz_calculateRestoreAmount(uint256,uint256,int256,uint256,uint256) (runs: 5000, μ: 42326, ~: 42301)
Logs:
  Bound result 69295509040982496176622344798
  Bound result 700000000000000000
  Bound result 45949006410929164602672518618644340988520946369
  Bound result 12361
  Bound result 1000001000000000000

[PASS] test_fuzz_getUserDebt_DrawnIndex(uint256,uint256,int256,uint256) (runs: 5000, μ: 39434, ~: 39396)
Logs:
  Bound result 99000000000000000000000001801
  Bound result 720000000000000000
  Bound result -40000000000000000000
  Bound result 249264818036790566556173706948

[PASS] test_fuzz_getUserDebt_HubAndAssetId(uint256,uint256,int256,uint256) (runs: 5000, μ: 45394, ~: 45356)
Logs:
  Bound result 99000000000000000000000001801
  Bound result 720000000000000000
  Bound result -40000000000000000000
  Bound result 249264818036790566556173706948

[PASS] test_getUserDebt_DrawnIndex() (gas: 23419)
[PASS] test_getUserDebt_HubAndAssetId() (gas: 30124)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 4.23s (4.23s CPU time)

Ran 17 tests for tests/unit/WadRayMath.t.sol:WadRayMathDifferentialTests
[PASS] test_bpsToRay_fuzz(uint256) (runs: 5000, μ: 11773, ~: 11826)
[PASS] test_bpsToWad_fuzz(uint256) (runs: 5000, μ: 11769, ~: 11813)
[PASS] test_constants() (gas: 12879)
[PASS] test_fromRayUp_fuzz(uint256) (runs: 5000, μ: 11046, ~: 11072)
[PASS] test_fromWadDown_fuzz(uint256) (runs: 5000, μ: 9378, ~: 9378)
[PASS] test_fuzz_rayDiv(uint256,uint256) (runs: 5000, μ: 12432, ~: 12807)
[PASS] test_fuzz_rayMul(uint256,uint256) (runs: 5000, μ: 11627, ~: 12155)
[PASS] test_fuzz_wadDiv(uint256,uint256) (runs: 5000, μ: 12613, ~: 12918)
[PASS] test_fuzz_wadMul(uint256,uint256) (runs: 5000, μ: 11586, ~: 12134)
[PASS] test_rayDiv() (gas: 39659)
[PASS] test_rayMul() (gas: 28767)
[PASS] test_roundRayUp_fuzz(uint256) (runs: 5000, μ: 12756, ~: 12909)
[PASS] test_roundRayUp_overflow() (gas: 14831)
[PASS] test_toRay_fuzz(uint256) (runs: 5000, μ: 11685, ~: 12264)
[PASS] test_toWad_fuzz(uint256) (runs: 5000, μ: 11834, ~: 12262)
[PASS] test_wadDiv() (gas: 40065)
[PASS] test_wadMul() (gas: 28408)
Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 2.14s (2.14s CPU time)

Ran 14 tests for tests/gas/Spoke.Operations.gas.t.sol:SpokeOperations_ZeroRiskPremium_Gas_Tests
[PASS] test_borrow() (gas: 1091646)
[PASS] test_liquidation_full() (gas: 10729492)
[PASS] test_liquidation_partial() (gas: 10728909)
[PASS] test_liquidation_receiveShares_full() (gas: 10711926)
[PASS] test_liquidation_receiveShares_partial() (gas: 10711345)
[PASS] test_liquidation_reportDeficit_full() (gas: 10766785)
[PASS] test_multicall_ops() (gas: 1325233)
[PASS] test_repay() (gas: 777435)
[PASS] test_setUserPositionManagersWithSig() (gas: 311072)
[PASS] test_supply() (gas: 541723)
[PASS] test_updateRiskPremium() (gas: 942831)
[PASS] test_updateUserDynamicConfig() (gas: 592076)
[PASS] test_usingAsCollateral() (gas: 1088624)
[PASS] test_withdraw() (gas: 1587151)
Suite result: ok. 14 passed; 0 failed; 0 skipped; finished in 83.52ms (31.35ms 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: 22949)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 25.95ms (1.11ms CPU time)

Ran 10 tests for tests/unit/Spoke/Spoke.PositionManager.t.sol:SpokePositionManagerTest
[PASS] test_onlyPositionManager_on_borrow() (gas: 538423)
[PASS] test_onlyPositionManager_on_repay() (gas: 563668)
[PASS] test_onlyPositionManager_on_supply() (gas: 291767)
[PASS] test_onlyPositionManager_on_updateUserDynamicConfig() (gas: 1284339)
[PASS] test_onlyPositionManager_on_updateUserRiskPremium() (gas: 1520970)
[PASS] test_onlyPositionManager_on_usingAsCollateral() (gas: 144395)
[PASS] test_onlyPositionManager_on_withdraw() (gas: 320748)
[PASS] test_renouncePositionManagerRole() (gas: 20268)
[PASS] test_renouncePositionManagerRole_noop_from_disabled() (gas: 21904)
[PASS] test_setApprovalForPositionManager(bytes32) (runs: 5000, μ: 18111, ~: 18111)
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 401.37ms (376.70ms 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, μ: 701031, ~: 700944)
Logs:
  Bound result 97612385398899630614842
  Bound result 138010191
  Bound result 94575821765687438291648

[PASS] test_repay_less_than_share() (gas: 599973)
[PASS] test_repay_only_base_debt_interest() (gas: 766149)
[PASS] test_repay_only_base_debt_no_premium() (gas: 644239)
[PASS] test_repay_supply_ex_rate_decr() (gas: 1460027)
[PASS] test_repay_supply_ex_rate_decr_skip_time() (gas: 1456643)
[PASS] test_repay_zero_shares_nonzero_premium_debt() (gas: 764072)
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 14.99s (14.96s 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, μ: 45291, ~: 44992)
Logs:
  Bound result 7

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

[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 239

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

[PASS] test_addAsset_fuzz_reverts_InvalidIrData(address,uint8,address,address) (runs: 5000, μ: 82463575545, ~: 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 0

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

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

[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: 828431)
[PASS] test_hub_max_riskPremium() (gas: 8610)
[PASS] test_isUnderlyingListed() (gas: 1178402)
[PASS] test_updateAssetConfig_NewFeeReceiver_noFees() (gas: 716089)
[PASS] test_updateAssetConfig_NewFeeReceiver_revertsWith_SpokeNotActive_noFees() (gas: 613421)
[PASS] test_updateAssetConfig_UseExistingSpokeAndListedAsFeeReceiver_revertsWith_SpokeAlreadyListed() (gas: 70708)
[PASS] test_updateAssetConfig_fuzz(uint256,(address,uint16,address,address)) (runs: 5000, μ: 269272, ~: 269598)
Logs:
  Bound result 3
  Bound result 3

[PASS] test_updateAssetConfig_fuzz_FromZeroLiquidityFee(uint256,uint16) (runs: 5000, μ: 819000, ~: 818822)
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, μ: 721404, ~: 721226)
Logs:
  Bound result 3
  Bound result 1
  Bound result 3
  Bound result 1

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

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

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

[PASS] test_updateAssetConfig_fuzz_Scenario(uint256) (runs: 5000, μ: 700486, ~: 700539)
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, μ: 40144, ~: 40042)
Logs:
  Bound result 3
  Bound result 3

[PASS] test_updateAssetConfig_fuzz_revertsWith_InvalidReinvestmentController() (gas: 469753)
[PASS] test_updateAssetConfig_fuzz_revertsWith_calculateInterestRateReverts(uint256,(address,uint16,address,address)) (runs: 5000, μ: 198867, ~: 199295)
Logs:
  Bound result 0
  Bound result 1

[PASS] test_updateAssetConfig_fuzz_revertsWith_setInterestRateDataReverts(uint256,(address,uint16,address,address)) (runs: 5000, μ: 95913, ~: 96293)
Logs:
  Bound result 2
  Bound result 111

[PASS] test_updateAssetConfig_oldFeeReceiver_flags() (gas: 880112)
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, μ: 59225, ~: 59284)
Logs:
  Bound result 0

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

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

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

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

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

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

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

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

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

[PASS] test_draw_fuzz_revertsWith_InvalidAddress(uint256) (runs: 5000, μ: 16138, ~: 16138)
[PASS] test_draw_revertsWith_DrawCapExceeded_due_to_deficit() (gas: 267210)
[PASS] test_draw_revertsWith_InsufficientLiquidity() (gas: 28289)
[PASS] test_draw_revertsWith_InsufficientLiquidity_due_to_draw() (gas: 165836)
[PASS] test_draw_revertsWith_InsufficientLiquidity_due_to_remove() (gas: 129018)
[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 22.64s (22.60s CPU time)

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

Ran 6 tests for tests/unit/Hub/Hub.MintFeeShares.t.sol:HubMintFeeSharesTest
[PASS] test_mintFeeShares() (gas: 308656)
[PASS] test_mintFeeShares_noFees() (gas: 364545)
[PASS] test_mintFeeShares_noShares() (gas: 289788)
[PASS] test_mintFeeShares_revertsWith_AccessManagedUnauthorized() (gas: 24139)
[PASS] test_mintFeeShares_revertsWith_AssetNotListed() (gas: 27553)
[PASS] test_mintFeeShares_revertsWith_SpokeNotActive() (gas: 239515)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 28.24ms (3.85ms CPU time)

Ran 9 tests for tests/gas/Hub.Operations.gas.t.sol:HubOperations_Gas_Tests
[PASS] test_add() (gas: 270108)
[PASS] test_deficit() (gas: 1304823)
[PASS] test_draw() (gas: 418553)
[PASS] test_mintFeeShares() (gas: 499923)
[PASS] test_payFee_transferShares() (gas: 931377)
[PASS] test_refreshPremium() (gas: 634389)
[PASS] test_remove() (gas: 310664)
[PASS] test_restore() (gas: 877812)
[PASS] test_restore_with_transfer() (gas: 878477)
Suite result: ok. 9 passed; 0 failed; 0 skipped; finished in 50.11ms (7.08ms CPU time)

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

[PASS] test_payFee_fuzz_with_interest(uint256,uint256,uint256) (runs: 5000, μ: 697776, ~: 698055)
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: 135493)
[PASS] test_payFee_revertsWith_underflow_added_shares_exceeded_with_interest() (gas: 636899)
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 24.94s (24.91s CPU time)

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

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

[PASS] test_reclaim_multipleSweepsAndReclaims() (gas: 725320)
[PASS] test_reclaim_revertsWith_AssetNotListed() (gas: 13093)
[PASS] test_reclaim_revertsWith_InsufficientTransferred() (gas: 438036)
[PASS] test_reclaim_revertsWith_InsufficientTransferred_noSwept() (gas: 102540)
[PASS] test_reclaim_revertsWith_InvalidAmount_zero() (gas: 92710)
[PASS] test_reclaim_revertsWith_OnlyReinvestmentController(address) (runs: 5000, μ: 93593, ~: 93593)
[PASS] test_reclaim_revertsWith_OnlyReinvestmentController_init() (gas: 40516)
[PASS] test_reclaim_revertsWith_underflow_exceedsSwept_afterSweep() (gas: 601387)
Suite result: ok. 11 passed; 0 failed; 0 skipped; finished in 14.75s (14.73s CPU time)

Ran 26 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.ERC4626Compliance.t.sol:TokenizationSpokeERC4626ComplianceTest
[PASS] test_RT_deposit_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 654142, ~: 592582)
Logs:
  Bound result 271

[PASS] test_RT_deposit_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 654523, ~: 593033)
Logs:
  Bound result 271

[PASS] test_RT_mint_redeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 659609, ~: 596969)
Logs:
  Bound result 1766720146007376024554344

[PASS] test_RT_mint_withdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 659942, ~: 597385)
Logs:
  Bound result 1766720146007376024554344

[PASS] test_RT_redeem_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 654113, ~: 592966)
Logs:
  Bound result 985159085698179735671318074554
  Bound result 59206793166169007

[PASS] test_RT_redeem_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 654729, ~: 592780)
Logs:
  Bound result 4288

[PASS] test_RT_withdraw_deposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 659534, ~: 597779)
Logs:
  Bound result 985159085698179735671318074554
  Bound result 86753177770073525

[PASS] test_RT_withdraw_mint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 659930, ~: 597560)
Logs:
  Bound result 80891216523206226866729058589

[PASS] test_asset((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 574318, ~: 518744)
[PASS] test_convertToAssets((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 583504, ~: 527075)
Logs:
  Bound result 975112556096400868711721621910

[PASS] test_convertToShares((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 583613, ~: 527173)
Logs:
  Bound result 975112556096400868711721621910

[PASS] test_deposit((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 5000, μ: 637494, ~: 577476)
Logs:
  Bound result 690435204109038304881744787953
  Bound result 290303187240314838229224087916

[PASS] test_maxDeposit((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 577373, ~: 521470)
[PASS] test_maxMint((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 577446, ~: 521535)
[PASS] test_maxRedeem((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 582410, ~: 525964)
[PASS] test_maxWithdraw((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 582296, ~: 525862)
[PASS] test_mint((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 5000, μ: 642967, ~: 581660)
Logs:
  Bound result 651027443056690691671633205593
  Bound result 366447878590348026683001402631

[PASS] test_previewDeposit((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 633346, ~: 573780)
Logs:
  Bound result 271

[PASS] test_previewMint((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 638389, ~: 577679)
Logs:
  Bound result 1766720146007376024554344

[PASS] test_previewRedeem((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 637273, ~: 576465)
Logs:
  Bound result 1083

[PASS] test_previewWithdraw((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 643049, ~: 581643)
Logs:
  Bound result 40574654480022913966937685770

[PASS] test_redeem((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 5000, μ: 644097, ~: 581675)
Logs:
  Bound result 820282019728792003956564819965
  Bound result 3577979161833

[PASS] test_redeem_zero_allowance((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 593960, ~: 537557)
Logs:
  Bound result 1083

[PASS] test_totalAssets((address[4],uint256[4],uint256[4],int256)) (runs: 5000, μ: 577797, ~: 521848)
[PASS] test_withdraw((address[4],uint256[4],uint256[4],int256),uint256,uint256) (runs: 5000, μ: 649377, ~: 586431)
Logs:
  Bound result 51

[PASS] test_withdraw_zero_allowance((address[4],uint256[4],uint256[4],int256),uint256) (runs: 5000, μ: 599318, ~: 542346)
Logs:
  Bound result 40574654480022913966937685770

Suite result: ok. 26 passed; 0 failed; 0 skipped; finished in 197.41s (197.39s CPU time)

Ran 16 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.Edge.t.sol:TokenizationSpokeEdgeTest
[PASS] test_depositZero_revertsWith_InvalidAmount() (gas: 48685)
[PASS] test_deposit_revertsWith_ERC20InsufficientAllowance_noApproval() (gas: 55989)
[PASS] test_mintZero_revertsWith_InvalidAmount() (gas: 48332)
[PASS] test_mint_revertsWith_ERC20InsufficientAllowance_noApproval() (gas: 61568)
[PASS] test_multipleMintDepositRedeemWithdraw() (gas: 595766)
[PASS] test_redeemZero_revertsWith_InvalidAmount() (gas: 191353)
[PASS] test_redeem_revertsWith_ERC20InsufficientAllowance_callerNotOwner() (gas: 191569)
[PASS] test_redeem_revertsWith_ERC20InsufficientBalance_noShares() (gas: 37311)
[PASS] test_redeem_revertsWith_ERC20InsufficientBalance_on_InsufficientShares() (gas: 187551)
[PASS] test_singleDepositWithdraw() (gas: 227041)
[PASS] test_singleMintRedeem() (gas: 231090)
[PASS] test_vaultInteractionsForSomeoneElse() (gas: 311568)
[PASS] test_withdrawZero_revertsWith_InvalidAmount() (gas: 191851)
[PASS] test_withdraw_revertsWith_ERC20InsufficientAllowance_callerNotOwner() (gas: 197576)
[PASS] test_withdraw_revertsWith_ERC20InsufficientBalance() (gas: 195176)
[PASS] test_withdraw_revertsWith_ERC20InsufficientBalance_noBalance() (gas: 43354)
Suite result: ok. 16 passed; 0 failed; 0 skipped; finished in 32.87ms (7.25ms CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersAddCapExactlyReachedTest
[PASS] test_maxDeposit_returnsZero() (gas: 41194)
[PASS] test_maxMint_returnsZero() (gas: 45260)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.86ms (274.05µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersAddCapMaxTest
[PASS] test_maxDeposit_returnsMaxUint() (gas: 23335)
[PASS] test_maxMint_returnsMaxUint() (gas: 23506)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 24.96ms (235.15µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersAddCapVariableEmptyTest
[PASS] test_maxDeposit_returnsCapTimesUnits() (gas: 44721)
[PASS] test_maxMint_returnsSharesOfCap() (gas: 56909)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.62ms (352.70µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersAddCapVariablePartialTest
[PASS] test_maxDeposit_returnsRemaining() (gas: 49351)
[PASS] test_maxMint_returnsSharesOfRemaining() (gas: 61475)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 30.70ms (398.00µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersAddCapZeroTest
[PASS] test_maxDeposit_returnsZero() (gas: 41194)
[PASS] test_maxMint_returnsZero() (gas: 45282)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 24.82ms (284.52µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersCapExceededByYieldTest
[PASS] test_maxDeposit_returnsZero() (gas: 41205)
[PASS] test_maxMint_returnsZero() (gas: 45271)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 26.31ms (282.14µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersExactBoundaryAfterYieldTest
[PASS] test_maxDeposit_exactBoundary_succeeds() (gas: 137781)
[PASS] test_maxMint_exactBoundary_succeeds() (gas: 146334)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.97ms (675.61µs CPU time)

Ran 2 tests for tests/unit/TokenizationSpoke/TokenizationSpoke.MaxGetters.t.sol:TokenizationSpokeMaxGettersExactBoundaryLimitedLiquidityTest
[PASS] test_maxRedeem_exactBoundary_limitedLiquidity_succeeds() (gas: 104190)
[PASS] test_maxWithdraw_exactBoundary_limitedLiquidity_succeeds() (gas: 99769)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 25.44ms (489.67µs CPU time)

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

[PASS] test_refreshPremium_fuzz_withAccrual(uint256,uint256,uint256,uint256) (runs: 5000, μ: 465839, ~: 475158)
Logs:
  Bound result 3423
  Bound result 103
  Bound result 7500000000000000
  Bound result 19346

[PASS] test_refreshPremium_haltedSpokesAllowed() (gas: 121430)
[PASS] test_refreshPremium_maxRiskPremiumThreshold() (gas: 895057)
[PASS] test_refreshPremium_negativeDeltas(uint256) (runs: 5000, μ: 458473, ~: 459023)
Logs:
  Bound result 3124043968137

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

[PASS] test_refreshPremium_revertsWith_InvalidPremiumChange_NonZeroRestoredPremiumRay() (gas: 850467)
[PASS] test_refreshPremium_revertsWith_InvalidPremiumChange_RiskPremiumThresholdExceeded_DecreasingPremium() (gas: 871779)
[PASS] test_refreshPremium_revertsWith_SpokeNotActive() (gas: 58921)
[PASS] test_refreshPremium_riskPremiumThreshold() (gas: 917235)
[PASS] test_refreshPremium_spokePremiumUpdateIsContained() (gas: 707532)
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 19.93s (19.90s CPU time)

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

[PASS] test_remove_all_with_interest() (gas: 361850)
[PASS] test_remove_fuzz(uint256,uint256) (runs: 5000, μ: 204533, ~: 204587)
Logs:
  Bound result 4
  Bound result 100

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

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

[PASS] test_remove_fuzz_multi_spoke_with_interest(uint256,uint256,uint256,uint256) (runs: 5000, μ: 412569, ~: 413053)
Logs:
  Bound result 48663493632045201941551042395
  Bound result 1957
  Bound result 7500000000000000
  Bound result 18672

[PASS] test_remove_revertsWith...*[Comment body truncated]*

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

♻️ Forge Gas Snapshots

🔕 Unchanged
Path Value
snapshots/Hub.Operations.json
add 86,692
add: with transfer 107,989
draw 104,148
eliminateDeficit: full 72,567
eliminateDeficit: partial 82,172
mintFeeShares 82,741
payFee 70,805
refreshPremium 70,362
remove: full 75,596
remove: partial 80,734
reportDeficit 111,882
restore: full 76,552
restore: full - with transfer 169,161
restore: partial 85,262
restore: partial - with transfer 143,242
transferShares 69,619
snapshots/NativeTokenGateway.Operations.json
borrowNative 228,557
repayNative 166,460
supplyAsCollateralNative 160,122
supplyNative 135,753
withdrawNative: full 125,548
withdrawNative: partial 136,735
snapshots/SignatureGateway.Operations.json
borrowWithSig 213,790
repayWithSig 186,732
setSelfAsUserPositionManagerWithSig 75,118
setUsingAsCollateralWithSig 85,387
supplyWithSig 151,985
updateUserDynamicConfigWithSig 63,120
updateUserRiskPremiumWithSig 62,090
withdrawWithSig 130,803
snapshots/Spoke.Getters.json
getUserAccountData: supplies: 0, borrows: 0 13,014
getUserAccountData: supplies: 1, borrows: 0 49,426
getUserAccountData: supplies: 2, borrows: 0 81,102
getUserAccountData: supplies: 2, borrows: 1 101,454
getUserAccountData: supplies: 2, borrows: 2 120,714
snapshots/Spoke.Operations.ZeroRiskPremium.json
borrow: first 190,296
borrow: second action, same reserve 170,162
liquidationCall (receiveShares): full 303,081
liquidationCall (receiveShares): partial 302,499
liquidationCall (reportDeficit): full 367,565
liquidationCall: full 320,706
liquidationCall: partial 320,124
permitReserve + repay (multicall) 164,565
permitReserve + supply (multicall) 146,745
permitReserve + supply + enable collateral (multicall) 161,196
repay: full 123,903
repay: partial 128,861
setUserPositionManagersWithSig: disable 46,772
setUserPositionManagersWithSig: enable 68,684
supply + enable collateral (multicall) 141,398
supply: 0 borrows, collateral disabled 122,835
supply: 0 borrows, collateral enabled 105,806
supply: second action, same reserve 105,735
updateUserDynamicConfig: 1 collateral 74,545
updateUserDynamicConfig: 2 collaterals 89,413
updateUserRiskPremium: 1 borrow 95,657
updateUserRiskPremium: 2 borrows 105,337
usingAsCollateral: 0 borrows, enable 59,616
usingAsCollateral: 1 borrow, disable 105,701
usingAsCollateral: 1 borrow, enable 42,504
usingAsCollateral: 2 borrows, disable 127,250
usingAsCollateral: 2 borrows, enable 42,516
withdraw: 0 borrows, full 127,944
withdraw: 0 borrows, partial 132,840
withdraw: 1 borrow, partial 159,894
withdraw: 2 borrows, partial 174,452
withdraw: non collateral 105,891
snapshots/Spoke.Operations.json
borrow: first 259,220
borrow: second action, same reserve 202,086
liquidationCall (receiveShares): full 335,114
liquidationCall (receiveShares): partial 334,532
liquidationCall (reportDeficit): full 359,765
liquidationCall: full 352,739
liquidationCall: partial 352,157
permitReserve + repay (multicall) 162,036
permitReserve + supply (multicall) 146,745
permitReserve + supply + enable collateral (multicall) 161,196
repay: full 117,982
repay: partial 137,340
setUserPositionManagersWithSig: disable 46,772
setUserPositionManagersWithSig: enable 68,684
supply + enable collateral (multicall) 141,398
supply: 0 borrows, collateral disabled 122,835
supply: 0 borrows, collateral enabled 105,806
supply: second action, same reserve 105,735
updateUserDynamicConfig: 1 collateral 74,545
updateUserDynamicConfig: 2 collaterals 89,413
updateUserRiskPremium: 1 borrow 149,005
updateUserRiskPremium: 2 borrows 199,256
usingAsCollateral: 0 borrows, enable 59,616
usingAsCollateral: 1 borrow, disable 159,046
usingAsCollateral: 1 borrow, enable 42,504
usingAsCollateral: 2 borrows, disable 229,165
usingAsCollateral: 2 borrows, enable 42,516
withdraw: 0 borrows, full 127,944
withdraw: 0 borrows, partial 132,840
withdraw: 1 borrow, partial 210,737
withdraw: 2 borrows, partial 256,902
withdraw: non collateral 105,891
snapshots/TokenizationSpoke.Operations.json
deposit 113,234
depositWithSig 124,138
mint 112,915
mintWithSig 123,782
permit 62,766
redeem: on behalf, full 90,886
redeem: on behalf, partial 113,607
redeem: self, full 88,874
redeem: self, partial 108,074
redeemWithSig 123,456
withdraw: on behalf, full 91,302
withdraw: on behalf, partial 114,127
withdraw: self, full 89,394
withdraw: self, partial 108,594
withdrawWithSig 123,987

Comment on lines 12 to 15
struct MintConfig {
uint256 minTimeInterval;
uint256 minUnrealizedFeePercent; // 1e4 = 100% (basis points)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

could be packed tighter since minUnrealizedFeePercent is in BPS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

minTimeInterval is still uint256 tho, so still taking 2 slots there.

/// @param assetId The identifier of the asset.
/// @param config The new configuration.
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
_configs[hub][assetId] = config;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to add input checks there, tho it's owner gated, a missconfig can be troublesome

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, tho I think 0 is fine for both configs. I can surely check the minUnrealizedFeePercent is under 100%, but then unsure how to bound the time interval. I was thinking maybe 1 year is fine? Likely we won't go over that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment on lines 41 to 43
if (!_checkExecute(hub, assetId)) {
revert ConditionsNotMet();
}
Copy link
Member

Choose a reason for hiding this comment

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

require style

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

/// @param hub The address of the hub.
/// @param assetId The identifier of the asset.
/// @return True if conditions are met, false otherwise.
function _checkExecute(address hub, uint256 assetId) internal view 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.

Suggested change
function _checkExecute(address hub, uint256 assetId) internal view returns (bool) {
function _checkExecute(address hub, uint256 assetId) internal virtual view returns (bool) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

/// @title FeeSharesMinterBase
/// @author Aave Labs
/// @notice Contract to mint fee shares on the Hub when specific conditions are met.
contract FeeSharesMinterBase is Ownable {
Copy link
Member

Choose a reason for hiding this comment

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

call this a steward maybe

Copy link
Contributor Author

Choose a reason for hiding this comment

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

idk about Steward, but still open to suggestions about names

pragma solidity ^0.8.0;

import {IHub} from 'src/hub/interfaces/IHub.sol';
import {Ownable} from 'src/dependencies/openzeppelin/Ownable.sol';
Copy link
Member

Choose a reason for hiding this comment

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

always use Ownable2Step pls

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@CheyenneAtapour CheyenneAtapour marked this pull request as ready for review February 17, 2026 20:14
Copilot AI review requested due to automatic review settings February 17, 2026 20:14
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request introduces a fee minter contract (FeeSharesMinterBase) that enables automated, condition-based calling of the mintFeeShares() function on Hub contracts. The contract allows an owner to configure specific conditions (minimum time interval and minimum unrealized fee percentage) that must be met before fee shares can be minted, providing a controlled and consistent mechanism for fee share minting through automation/keeper systems.

Changes:

  • Added FeeSharesMinterBase.sol contract with configurable conditions for automated fee minting
  • Added comprehensive test suite covering success cases, access control, and edge cases
  • Implemented checks for time intervals, fee percentage thresholds, and minimum share requirements

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
src/hub/FeeSharesMinterBase.sol New contract implementing configurable automation logic for calling mintFeeShares with time-based and threshold-based conditions
tests/unit/Hub/FeeSharesMinterBase.t.sol Comprehensive test suite covering access control, execution conditions, edge cases, and precision scenarios

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -0,0 +1,96 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The contract uses pragma solidity ^0.8.0 (caret version), but other implementation contracts in the codebase (Hub, HubConfigurator, AssetInterestRateStrategy) use exact version 0.8.28. Consider changing to pragma solidity 0.8.28 for consistency with the codebase conventions.

Suggested change
pragma solidity ^0.8.0;
pragma solidity 0.8.28;

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: UNLICENSED
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The file is missing the copyright header that is present in other test files. Add the copyright comment after the SPDX license identifier, following the pattern used in other test files in the codebase.

Suggested change
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2024 The Protocol Authors

Copilot uses AI. Check for mistakes.
Comment on lines 40 to 46
function execute(address hub, uint256 assetId) external {
if (!_checkExecute(hub, assetId)) {
revert ConditionsNotMet();
}

lastMintTime[hub][assetId] = block.timestamp;
IHub(hub).mintFeeShares(assetId);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The execute and setConfig functions do not validate that the hub address is non-zero. Consider adding a check to ensure hub != address(0) to prevent accidental misconfiguration.

Copilot uses AI. Check for mistakes.
/// @param hub The address of the hub.
/// @param assetId The identifier of the asset.
/// @param config The new configuration.
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The setConfig function does not validate that the hub address is non-zero. Consider adding a check to ensure hub != address(0) to prevent accidental misconfiguration.

Suggested change
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
require(hub != address(0), "INVALID_HUB");

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +36
vm.prank(ADMIN);
minter.setConfig(address(hub1), daiAssetId, config);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The setConfig function emits a ConfigUpdated event but the tests do not verify that this event is emitted correctly. Consider adding an event emission test to ensure the event is emitted with the correct parameters.

Copilot uses AI. Check for mistakes.

assertTrue(minter.checkExecute(address(hub1), daiAssetId), 'Should be executable');

minter.execute(address(hub1), daiAssetId);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

There is no test coverage for the scenario where the minter contract does not have the HUB_ADMIN_ROLE when calling execute. This would cause mintFeeShares to revert with an access control error. Consider adding a test to verify this behavior and ensure proper error handling.

Copilot uses AI. Check for mistakes.
minter.setConfig(address(hub1), daiAssetId, config);

// Generate fees
// Add 1000 DAI, borrow 100 DAI
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The comment states "borrow 100 DAI" but the actual drawAmount is 900e18 (900 DAI). Please update the comment to accurately reflect the actual draw amount.

Suggested change
// Add 1000 DAI, borrow 100 DAI
// Add 1000 DAI, borrow 900 DAI

Copilot uses AI. Check for mistakes.
/// @param hub The address of the hub.
/// @param assetId The identifier of the asset.
/// @param config The new configuration.
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The setConfig function does not validate the minUnrealizedFeePercent value. Consider adding a check to ensure minUnrealizedFeePercent <= 10000 (100% in basis points) to prevent configuration errors where the threshold is set higher than 100%.

Suggested change
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
require(config.minUnrealizedFeePercent <= 10000, "INVALID_MIN_UNREALIZED_FEE_PERCENT");

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings February 17, 2026 20:56
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +50 to +55
function execute(address hub, uint256 assetId) external {
require(_checkExecute(hub, assetId), ConditionsNotMet());

lastMintTime[hub][assetId] = block.timestamp;
IHub(hub).mintFeeShares(assetId);
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The execute function does not emit an event when fee shares are successfully minted. This makes it difficult to track when the minter contract triggers fee minting versus other callers. Consider adding an event that captures the hub address, assetId, and the address that triggered the execution to improve observability and monitoring of the automated minting process.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +45
function setConfig(address hub, uint256 assetId, MintConfig memory config) external onlyOwner {
require(
config.minUnrealizedFeePercent <= MAX_BPS && config.minTimeInterval <= MAX_TIME_INTERVAL,
InvalidConfig()
);
_configs[hub][assetId] = config;
emit ConfigUpdated(hub, assetId, config);
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The setConfig and execute functions do not validate that the hub address is non-zero. While setting a zero address won't break the contract immediately, it could lead to issues when trying to execute. Consider adding a require statement to validate hub != address(0) to fail fast and provide clearer error messages.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants