Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: low
Invalid

The upgradeable contracts don't follow the upgradeable pattern

Summary

Several contracts in the protocol are designed to be upgradeable. But these contracts don't follow the required upgradeable pattern.

Vulnerability Details

The UpgradeableProxy contract is designed to be a proxy of SystemConfig, PreMarkets, DeliveryPlace, CapitalPool and TokenManager contracts. These contracts are designed to be upgradeable using the Transparent Proxy pattern from OpenZeppelin. The problem is that the most of the contracts don't follow the upgradeable pattern.

The UpgradeableProxyContract uses a constructor. But the constructors are not called during proxy deployment, which leads to uninitialized contract states:

constructor(
address _logic,
address _admin,
address _tadleFactory,
bytes memory _data
) TransparentUpgradeableProxy(_logic, _admin, _data) {
tadleFactory = ITadleFactory(_tadleFactory);
}

The SystemConfig and TokenManager contracts have initialize function but this function has modifier onlyOwner. The initialize function should have initializer modifier even when the owner is trusted. That ensures the function can only be called once, preventing accidental reinitialization.

Also, all contracts that are upgradeable (UpgradeableProxy, SystemConfig, PreMarkets, DeliveryPlace, CapitalPool and TokenManager) should call in the constructor the _disableInitializers function to prevent the base contract from being initialized again. This is a best practice for upgradeable contracts to ensure that the contract cannot be reinitialized after deployment.

Impact

The use of constructors instead of initialize function in the upgradeable contracts leads to uninitialized contract states, because the constructors are not called during proxy deployment.
Also the lack of call to the _disableInitializers function can lead to re-initialization of the contracts and respectively to loss of funds.

Tools Used

Manual Review

Recommendations

Use initialize function instead of constructor and add a call to the _disableInitializers function in the upgradeable contracts. Also, add initializer modifier to ensure the function is only called once and to set the contract’s initialization state.

Updates

Lead Judging Commences

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

[invalid] finding-Rescuable-upgradeable-initializeOwner

Valid high severity, since `initializeOwner` is not called for proxy contracts and the constructor for each `Rescuable.sol` contract will not be invoked during proxy deployment, this leaves the `owner` for each proxy unitialized allowing potential to withdraw fund from other proxy contracts inheriting `Rescuable.sol` respectively.

Appeal created

0xnevi Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

[invalid] finding-Rescuable-upgradeable-initializeOwner

Valid high severity, since `initializeOwner` is not called for proxy contracts and the constructor for each `Rescuable.sol` contract will not be invoked during proxy deployment, this leaves the `owner` for each proxy unitialized allowing potential to withdraw fund from other proxy contracts inheriting `Rescuable.sol` respectively.

Support

FAQs

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