Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(shares): extract shares math to lib #94

Merged
merged 9 commits into from
Jul 11, 2023

Conversation

Rubilmax
Copy link
Collaborator

@Rubilmax Rubilmax commented Jul 10, 2023

src/Blue.sol Outdated Show resolved Hide resolved
@Rubilmax Rubilmax force-pushed the refactor/shares-math branch from b48e75c to 2d83d77 Compare July 10, 2023 14:20
@Rubilmax Rubilmax marked this pull request as ready for review July 10, 2023 14:21
@Rubilmax Rubilmax requested a review from Jean-Grimal July 10, 2023 14:21
Copy link
Contributor

@QGarchery QGarchery left a comment

Choose a reason for hiding this comment

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

I like this idea, it looks cleaner. Not sure about the dependencies though, this will have to be decided in another discussion

src/libraries/SharesMath.sol Outdated Show resolved Hide resolved
src/libraries/SharesMath.sol Outdated Show resolved Hide resolved
src/libraries/SharesMath.sol Show resolved Hide resolved
@Rubilmax Rubilmax requested a review from QGarchery July 11, 2023 07:16
library SharesMath {
using FixedPointMathLib for uint256;

uint256 internal constant VIRTUAL_SHARES = 10 ** 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we want to put 1 or 2 as exponent to make the attack very unprofitable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With exponent 0, the attack costs the attacker as much as they steal from the first deposit. I don't think we need to make it less profitable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

What are the drawbacks of increasing the offset?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems there's no drawback in increasing the offset, so I agree with the suggestion but even suggest to change it to 1e18.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not obvious what are the side effect of increasing this much though, is it clear for you?

Copy link
Collaborator Author

@Rubilmax Rubilmax Jul 11, 2023

Choose a reason for hiding this comment

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

Don't trust me then, run the maths! I may also have made a mistake (there's a high chance I did).

Copy link
Contributor

Choose a reason for hiding this comment

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

No worries I don't trust you 😛 don't have the time rn to do it, I was curious if you did

Copy link
Contributor

Choose a reason for hiding this comment

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

After some discussion I think it's ok to put a huge number as long as it's not creating overflows. 1e18 seems safe (you could easily deposit 100M worth of an asset with 18 decimals costing 0.01$) but we could use 1e9 to make sure it would work in any configuration.

src/libraries/SharesMath.sol Outdated Show resolved Hide resolved
Comment on lines +112 to 116
uint256 shares = amount.toSharesDown(totalSupply[id], totalSupplyShares[id]);
supplyShare[id][msg.sender] += shares;
totalSupplyShares[id] += shares;

totalSupply[id] += amount;
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like there is a vulnerability in this implementation.
Virtual shares are taken into account for the shares calculations (in the SharesMath lib), but are never added to totalSupplyShares.
So it seems that the first supplier still owns all the market shares, and can still perform the inflation attack.
Shouldn’t we add the virtual shares to totalSupplyShares at the initialization of the market (or if totalSupplyShares==0) ?
Maybe I’m wrong

Copy link
Contributor

Choose a reason for hiding this comment

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

Because what I understand from https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack is that the virtual shares are meant to catch part of the attacker’s donation (that aims to inflate the rate) and therefore make his attack unprofitable.

Copy link
Contributor

Choose a reason for hiding this comment

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

And same for totalSupply : if totalSupply[id] == 0, we sould add 1 to totalSupply[id]

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@QGarchery QGarchery Jul 11, 2023

Choose a reason for hiding this comment

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

In this implementation, when you do any computation with the shares, you take into account the virtual shares and the virtual assets (only 1 virtual asset actually). You don't add those virtual shares and assets to the mappings representing the actual values.

I think that this implementation works, because those virtual shares actually catch part of the attacker's donation. For example, by writing V as the virtual shares (V = $10^\delta$):

  1. The attacker supplies a0 assets.
    The added shares are toSharesDown(a0, 0, 0) = a0 * V
assets shares
a0 a0 * V
  1. The attacker transfers a1 assets.
assets shares
a0 + a1 a0 * V
  1. A user supplies u assets.
    The added shares are toSharesDown(u, a0 + a1, a0 * V) = u * (V + a0 * V) / (1 + a0 + a1) = V * u * (1 + a0) / (1 + a0 + a1), which is what is written in the openzeppelin page for the number of shares that the user gets

Keep in mind that this manipulation cannot be done on Morpho Blue, because it is not relying on the balance (so there is no way to modify the total assets simply by transferring tokens to the contract). Instead, the purpose of this PR is to ensure a high initial conversion rate

Copy link
Contributor

Choose a reason for hiding this comment

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

Discussed with @QGarchery I agree it's ok

Copy link
Contributor

@QGarchery QGarchery left a comment

Choose a reason for hiding this comment

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

Approving the changes about the shares math. We still need to decide about the math library used, and the actual computation of mulDiv.

I also think that we have a strong invariant with this PR: the ratio shares / assets is greater than the virtual shares. I'm adding it to the invariant list #96

src/libraries/SharesMath.sol Outdated Show resolved Hide resolved
@Rubilmax Rubilmax merged commit a2fbc6b into refactor/morpho-utils Jul 11, 2023
@Rubilmax Rubilmax deleted the refactor/shares-math branch July 11, 2023 16:27
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.

Share prices can be inflated Use wad-based operations in test suite Internalize share to amount logic
5 participants