The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: low
Invalid

If TokenManager changes it will lead to desync between `LiquidationPool` and `LiquidationPoolManager`

Summary

When there is a changes of TokenManager, it will create an unsync value of TokenManager between LiquidationPool and LiquidationPoolManager

Vulnerability Details

First, SmartVaultManager is an upgradable, which can initialize these variables (according to SmartVaultManager.sol)

function initialize(uint256 _collateralRate, uint256 _feeRate, address _euros, address _protocol, address _liquidator, address _tokenManager, address _smartVaultDeployer, address _smartVaultIndex, address _nftMetadataGenerator)

one of it's parameter is tokenManager. TokenManager is not an upgradable contract, thus if exist any changes, the address need to be updated into the SmartVaultManager via initialize (upgrades)

now if we look at LiquidationPoolManager constructor

File: LiquidationPoolManager.sol
22: constructor(address _TST, address _EUROs, address _smartVaultManager, address _eurUsd, address payable _protocol, uint32 _poolFeePercentage) {
23: pool = address(new LiquidationPool(_TST, _EUROs, _eurUsd, ISmartVaultManager(_smartVaultManager).tokenManager()));
24: TST = _TST;
25: EUROs = _EUROs;
26: smartVaultManager = _smartVaultManager;
27: protocol = _protocol;
28: poolFeePercentage = _poolFeePercentage;
29: }

the tokenManager value is being used in constructor to create LiquidationPool.

meanwhile in LiquidationPool contract:

File: LiquidationPool.sol
16: address private immutable TST;
17: address private immutable EUROs;
18: address private immutable eurUsd;
...
24: address payable public manager;
...
31: constructor(address _TST, address _EUROs, address _eurUsd, address _tokenManager) {
32: TST = _TST;
33: EUROs = _EUROs;
34: eurUsd = _eurUsd;
35: tokenManager = _tokenManager;
36: manager = payable(msg.sender);
37: }

here we can see the manager is not immutable, means there is a possibility it will be updated, this also follows the SmartVaultManager where tokenManager can be changed. But inside LiquidationPool there is no function to update this tokenManager

Further more, unlike LiquidationPoolManager where in order to get the accepted token is via ITokenManager.Token[] memory tokens = ITokenManager(manager.tokenManager()).getAcceptedTokens();, in LiquidationPool it is using the static tokenManager variable ITokenManager.Token[] memory _tokens = ITokenManager(tokenManager).getAcceptedTokens();

This will resulting potential different of tokenManager instance between LiquidationPoolManager and LiquidationPool.

For example, if the newest tokenManager contract contains an updated patch or version, the LiquidationPool will still hold the old one.

I understand, the TokenManager.sol is not in scope, but the issue is not about it. The issue is on the LiquidationPool contract when there is changes of TokenManager address.

Impact

If a newer TokenManager contract contains crucial patches or updates, the LiquidationPool could continue to use the old, potentially less secure, version.

Tools Used

Manual analysis

Recommendations

Pass the smartVaultManager to LiquidationPool instance instead of passing the value of TokenManager, and use ITokenManager.Token[] memory tokens = ITokenManager(manager.tokenManager()).getAcceptedTokens(); for synced one.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

informational/invalid

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.