Platform: Code4rena
Start Date: 30/05/2023
Pot Size: $300,500 USDC
Total HM: 79
Participants: 101
Period: about 1 month
Judge: Trust
Total Solo HM: 36
Id: 242
League: ETH
Rank: 67/101
Findings: 1
Award: $95.38
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xTheC0der
Also found by: Atree, BLOS, BPZ, Fulum, KupiaSec, SpicyMeatball, bin2chen, jasonxiale, lsaudit, minhquanym, xuwinnie, zzzitron
95.3782 USDC - $95.38
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L72 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L45 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/erc-4626/ERC4626MultiToken.sol#L66-L69 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/erc-4626/ERC4626MultiToken.sol#L77-L80 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L110-L118
When the function UlyssesToken.removeAsset()
is called, it is possible that the wrong assetId is assigned to an asset. As a result it is possible that:
A duplicate asset may be added.
When a duplicate asset would be added, the contract might hold less assets than it should hold, potentially resulting in insolvency or other issues.
An asset may not be able to be removed.
A wrong asset may be removed when UlyssesToken.removeAsset()
is called next time.
The wrong assetId may be returned upon the UlyssesToken.assetId()
view function.
The ERC4626MultiToken.assets
and ERC4626MultiToken.weights
arrays are crucial to determine which assets will be used. Based on ERC4626MultiToken.assets
and ERC4626MultiToken.weights
the UlyssesToken
(which inherits ERC4626MultiToken
) will pull and push the assets around, therefore when these arrays contain wrong information, the UlyssesToken
will receive and give wrong assets. Therefore this issue is of high severity.
When the function UlyssesToken.removeAsset()
is called, the assetIndex
is assigned to assetId[lastAsset]
which is wrong:
This means that potentially a wrong assetId gets assigned to an asset.
Example for how a duplicate asset can be added:
There are 3 assets: A, B, C in the ERC4626MultiToken.assets
array
Now UlyssesToken.removeAsset()
is called with the address of asset A.
The assetIndex of asset A is determined which is 0 (line 62 UlyssesToken.sol).
The assetId of C is set to 0 (line 72 UlyssesToken.sol).
C is then assigned to assets[0]
on line 73 in UlyssesToken.sol.
Now due to the fact that asset C has an assetId of 0, you can add asset C again via UlyssesToken.addAsset()
as a duplicate. This will not revert, because the assetId of asset C is 0:
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/UlyssesToken.sol#L45
Also, asset C can't be removed, because when calling UlyssesToken.removeAsset()
, because C's assetId is 0, and the calculation on line 62 will revert due to an underflow.
Example for how a wrong asset might get removed accidentally:
There are 3 assets: A, B, C in the ERC4626MultiToken.assets
array
Now UlyssesToken.removeAsset()
is called with the address of asset B.
The assetIndex of asset B is determined which is 1 (line 62 UlyssesToken.sol).
The assetId of C is set to 1 (line 72 UlyssesToken.sol).
C is then assigned to assets[1]
on line 73 in UlyssesToken.sol.
Now there is A and C in the assets array, but C has a wrong assetId.
Now if you want to remove asset C by calling again UlyssesToken.removeAsset()
, the assetId of C is 1. Therefore assetIndex
for C is calculated to be 0 on line 62. Then on line 73 A is overwritten and removed with the asset C due to the wrong index. So basically you wanted to remove asset C, but asset A got removed.
Here are a few examples where the ERC4626MultiToken.assets
and ERC4626MultiToken.weights
arrays are used and where it is crucial that they are correct:
In these functions ERC4626MultiToken.assets
information is used to receive and send out assets, which is why it is crucial that the ERC4626MultiToken.assets
array has the correct information to avoid severe issues like transferring the wrong assets.
In the case that the ERC4626MultiToken.assets
array contains duplicate assets, the UlyssesToken.updateAssetBalances()
will transfer to match the last weight of the duplicated asset. For example, asset A is duplicated, and A's first weight is 50, and second weight is 10, then the UlyssesToken will hold only A with the weight of 10. Therefore when a user tries to withdraw, UlyssesToken might be in the position of lack of funds.
Here is a POC that proves that an asset can be added multiple times:
https://gist.github.com/zzzitron/8b7c0a2c6a6a76f247eceefd3c8e462f
Manual review
Consider adjusting UlyssesToken.removeAsset()
in the following way in order to avoid assigning a wrong assetId:
// UlyssesToken // removeAsset 72 assetId[lastAsset] = assetId[asset];
ERC4626
#0 - c4-judge
2023-07-09T16:28:17Z
trust1995 marked the issue as primary issue
#1 - c4-judge
2023-07-09T16:28:23Z
trust1995 marked the issue as satisfactory
#2 - c4-judge
2023-07-11T17:20:43Z
trust1995 changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-07-11T17:20:51Z
trust1995 changed the severity to 3 (High Risk)
#4 - c4-sponsor
2023-07-12T17:28:00Z
0xLightt marked the issue as sponsor confirmed
#5 - c4-judge
2023-07-25T13:06:22Z
trust1995 marked issue #275 as primary and marked this issue as a duplicate of 275