maxMint()
in CollateralTracker.sol
is not compliant with EIP-4626
#229
Labels
bug
Something isn't working
downgraded by judge
Judge downgraded the risk level of this issue
duplicate-501
grade-b
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
🤖_61_group
AI based duplicate group recommendation
satisfactory
satisfies C4 submission criteria; eligible for awards
Lines of code
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L477-L480
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L444-L448
Vulnerability details
Impact
The very similar issue was reported as Medium during the recent Code4rena contest:
https://github.com/code-423n4/2024-03-pooltogether-findings/issues/335
.That contest reported that
maxDeposit()
might return a value greater than can be deposited, violating EIP-4626. The current issue is:maxMint()
might return a value greater than can be minted, violating EIP-4626.According to provided docs -
CollateralTracker.sol
is anERC4626 vault where token liquidity from passive Panoptic Liquidity Providers (PLPs) and collateral for option positions are deposited
.However, the current implementation does not comply with EIP-4626. Since the compliance requirement was straightforwardly mentioned as a requirement in the docs - I've evaluated this issue as Medium.
This may cause unexpected behavior due to being non compliant with EIP-4626. Other protocols that integrate with contract may incorrectly assume that it's EIP-4626 compliant - especially that documentation states that it's ERC-4626. EIP-4626 purpose is to create a robust and consistent implementation patterns for vaults. Any deviation from this standard will broke the composability and may lead to fund loss. While protocol's implements a vault and describes it as ERC-4626, it should fully conform to EIP-4626 standard.
Proof of Concept
According to EIP-4626, function
maxMint
returnsmaximum amount of shares that can be minted from the Vault for the receiver, through a mint call.
.Moreover, according to EIP-4626:
Let's take a look at the current implementation of these functions.
File: CollateralTracker.sol
Function
mint()
verifies the assets amount against the hardcoded valuetype(uint104).max
. It does not, however, utilize (call)maxMint()
at all.This is incorrect, because, according to EIP-4626, function
maxMint()
is responsible for returning the maximum amount of shares that can be minted from the Vault.File: CollateralTracker.sol
As we clearly see in the implementation of
maxMint()
, it might return different value thantype(uint104).max
.To demonstrated that indeed those values might be different, we've prepared a simple PoC which can be used in Remix-IDE.
The code had been slightly modified, to demonstrate the current settings in which
maxMint()
will return a value greater than can be minted (e.g.COMMISSION_FEE
has been set to50
,totalAssets()
is set to 1000000 and so on).After deploying above contract, we can see, that function
maxMint()
returns20181502093185741715370399289567
.This indicated, that we should be able to call
mint()
with any value equal or lower than20181502093185741715370399289567
.Let's try to call
mint(20181502093185741715370300000000)
.20181502093185741715370300000000
is significantly lower than20181502093185741715370399289567
, thus it should be possible to callmint()
with that parameter. However, callingmint(20181502093185741715370300000000)
will revert. This means that contract violates EIP-4626 standard -maxMint()
returned a value greater than can be minted.Let's examine what really happened.
maxMint()
returns20181502093185741715370399289567
. Thus it should be possible to mint max20181502093185741715370399289567
shares.20181502093185741715370300000000
shares (20181502093185741715370300000000 < 20181502093185741715370399289567
) thus function should not revert.mint(20181502093185741715370300000000)
however reverts withDepositTooLarge()
error.This simply demonstrated that
maxMint()
might return a value greater than can be minted, violating EIP-4626.Tools Used
Manual code review
Recommended Mitigation Steps
Utilize
maxMint()
directly in themint()
function. E.g., please take a look at an example how it's done in different functions:Function
withdraw()
straightforwardly checks ifassets > maxWithdraw(owner)
.Function
redeem()
straightforwardly checks ifshares > maxRedeem(owner)
.The same should be done for function
mint()
:Assessed type
ERC4626
The text was updated successfully, but these errors were encountered: