Git Product home page Git Product logo

pufeth's People

Contributors

0xluckyluke avatar bxmmm1 avatar cheyenneatapour avatar miles-six avatar shayanb avatar technovision99 avatar walidofnow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pufeth's Issues

Allow for Unit tests for pufETH

Refactor the codebase so that we can have unit tests because right now everything is hardcoded for the mainnet fork tests

[TOB-PUF-3] stETH rebasing creates arbitrage opportunities

stETH rebasing creates arbitrage opportunities

Severity: Low
Difficulty: Medium
Type: Timing
Target: src/PufferVaultV2.sol

Description

The PufferVaultV2 accepts deposits of stETH, wETH, and regular ETH. When computing the total assets deposited in the vault, the totalAssets function is used. It computes the total deposited amount of stETH, wETH, and ETH, including any amounts deposited in Eigenlayer and Lido. However the function computes the deposited amount of stETH using stETH.balanceOf(). Because stETH is a rebasing token, this creates an arbitrage opportunity. By depositing right before an stETH rebase and withdrawing right after, an opportunistic actor can get extra wETH.

Exploit Scenario

Before the Lido oracle updates the price of stETH, Eve, a malicious user, deposits wETH into the PufferV2Vault. After the oracle update, Eve burns her shares to receive even more wETH than she deposited.

Recommendations

Short term, store the total shares of the Puffer vault rather than the balance directly. Alternatively consider using wstETH instead.

Long term, keep up to date with the documentation regarding any external dependencies.

Add ETH, USDC, USDT swap support

  • Upgradeable smart contract
  • Interacts with Sushiswap's backend to find the best swap route from token X to stETH
  • It should allow users to swap any ERC20 to stETH
  • Depending on the amount of stETH received from the swap, we will mint user pufETH
  • Contract must support Permit / normal approval

Swap on front end using sushiswap endpoint to query best path

pufETH Pre-Audit Requests

  • separate the erc20 logic from the EigenLayer / Lido / PufferPool logic
  • make upgradeable
  • add docs for running tests
  • adds docs for what is being tested

pufETH : rPufETH conversion rate math

At the end of Chapter 1, we will:

  1. withdraw ETH from Lido
  2. deposit ETH into PufferPool

During this transition, the reserve currency of pufETH will switch from stETH to rPufETH. However, the circulating pufETH will not change. We need to track the conversion rate between pufETH to rPufETH for future deposits into the PufETH contract.

[TOB-PUF-2] setWithdrawalLimit does not reset assetsWithdrawnToday

setWithdrawalLimit does not reset assetsWithdrawnToday

Severity: Medium
Difficulty: Medium
Type: Data Validation
Target: src/PufferVaultV2.sol

Description

The PufferVaultV2 has a withdrawal limit that can be configured by the DAO. This withdrawal limit is designed to ensure that there are at most a maximum number of outflows per day from the vault. When updating the withdrawal limit, the setWithdrawalLimit function is used. Crucially, however, this function does not reset the assetsWithdrawnToday state variable. As a result, if the withdrawal limit is set to be lower than assetsWithdrawnToday, the getRemainingAssetsDailyWithdrawalLimit function will always return 0 instead of dailyAssetsWithdrawalLimit. As a result, the withdraw and redeem functions will revert unless called with an amount of 0.

function getRemainingAssetsDailyWithdrawalLimit() public view virtual returns (uint256) {
VaultStorage storage $ = _getPufferVaultStorage();
uint96 dailyAssetsWithdrawalLimit = $.dailyAssetsWithdrawalLimit;
uint96 assetsWithdrawnToday = $.assetsWithdrawnToday;
if (dailyAssetsWithdrawalLimit < assetsWithdrawnToday) {
return 0;
}

function withdraw(uint256 assets, address receiver, address owner)
public
virtual
override
restricted
returns (uint256)
{
uint256 maxAssets = maxWithdraw(owner);
if (assets > maxAssets) {
revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
}

Exploit Scenario

The current amount of withdrawn assets is 90 ether. Alice, the admin of the PufferVaultV2, changes the withdrawal limit to 80 ether. Bob, a staker, is unable to call redeem and get back his wETH in exchange for his shares.

Recommendations

Short term, make sure to reset the assetsWithdrawnToday variable when changing the withdrawal limit.

Long term, improve unit testing to uncover edge cases and ensure intended behavior throughout the system.

[TOB-PUF 5] Validator ticket purchase can revert due to strict equality

Validator ticket purchase can revert due to strict equality

Severity: Informational
Difficulty: Medium
Type: Data Validation
Target: src/ValidatorTicket.sol

Description

Validator tickets are used by participants in the Puffer Protocol as collateral in order to become validators. When buying a validator ticket, the following strict comparison is used:

https://github.com/PufferFinance/PufferPool/blob/3684282e45e0e777e7fe6e9e2c005560a6579eda/src/ValidatorTicket.sol#L82-L90
In the event of a misestimation or user error, validator ticket purchases can revert, causing a user to have to waste gas.

Exploit Scenario

Bob, a user, wants to purchase validator tickets but mistakenly sends a bit more ether than the price of 1 validator ticket. His transaction reverts and he wastes some gas.

Recommendations

Short term, try to accept a range of deposits and refund users the remaining value unused to purchase validator tickets.

Long term, improve unit testing to uncover ways in which transactions can revert, and determine their impact.

[TOB-PUF-6] updateDailyWithdrawals does not emit an event

updateDailyWithdrawals does not emit an event

Severity: Low
Difficulty: Low
Type: Auditing and Logging
Target: src/PufferVaultV2.sol

Description

The Puffer Vault V2's updateDailyWithdrawals function does not emit an event despite making a state changing operation. As a result of this, components that handle events offchain may not react to any updates of the daily withdrawal limit.

function _updateDailyWithdrawals(uint256 withdrawalAmount) internal virtual {
VaultStorage storage $ = _getPufferVaultStorage();
// Check if it's a new day to reset the withdrawal count
if ($.lastWithdrawalDay < block.timestamp / 1 days) {
$.lastWithdrawalDay = uint64(block.timestamp / 1 days);
$.assetsWithdrawnToday = 0;
}
$.assetsWithdrawnToday += uint96(withdrawalAmount);
}

Recommendations

Short term, make the updateDailyWithdrawals function emit an event.

Long term, ensure all state-changing operations emit events appropriately.

Add logic for wstETH deposits

The user should be able to deposit wstETH via Permit.
We redeem stETH by unwrappin wstETH, and deposit stETH to our pool minting pufETH to our user.

Problem with points

Hey! My username on puffer is korol_pryanikov, I wanted to point out an issue with points.

Basically, you can earn free points by swapping puffeth for steth , deposit steth again and cycle up the process to get more points and eth number on the dashboard.

I think it is worth fixing to erase the unfairness .

Sincerely yours,
M

[TOB-PUF-4] Vault withdrawals and redemptions can revert

Vault withdrawals and redemptions can revert

Severity: Medium
Difficulty: Low
Type: Timing
Target: src/PufferVaultV2.sol

Description

Because collateral from the vault is used to provision validators, there can be periods where there is a shortage of collateral in the vault. As a result, withdrawals and redemptions will revert.

The PufferVaultV2 contract's withdrawal and redemption functions both call the _wrapETH() function. This function calculates the wETH balance of the contract, and attempts to call wETH.deposit(). However, there may not be enough ether in the contract due to the transferETH() function. This function transfers ether out of the vault to fund validators, and is expected to be called frequently. As a result, unless the vault has sufficient wETH or ETH deposits, the _wrapETH() function will revert and so will withdrawals/redemptions.

/**
* @notice Wraps the vault's ETH balance to WETH.
* @dev Used to provide WETH liquidity
*/
function _wrapETH(uint256 assets) internal virtual {
uint256 wethBalance = _WETH.balanceOf(address(this));
if (wethBalance < assets) {
_WETH.deposit{ value: assets - wethBalance }();
}
}

/**
* @notice Transfers ETH to a specified address.
* @dev Restricted to PufferProtocol smart contract
* @dev It is used to transfer ETH to PufferModules to fund Puffer validators.
* @param to The address of the PufferModule to transfer ETH to
* @param ethAmount The amount of ETH to transfer
*/
function transferETH(address to, uint256 ethAmount) external restricted {
// Our Vault holds ETH & WETH
// If we don't have enough ETH for the transfer, unwrap WETH
uint256 ethBalance = address(this).balance;
if (ethBalance < ethAmount) {
// Reverts if no WETH to unwrap
_WETH.withdraw(ethAmount - ethBalance);
}
// slither-disable-next-line arbitrary-send-eth
(bool success,) = to.call{ value: ethAmount }("");
if (!success) {
revert ETHTransferFailed();
}
emit TransferredETH(to, ethAmount);
}

Exploit Scenario

Bob, a user, deposits stETH into the Vault and receives shares in return. After a while, the Vault has been rapidly funding validators and as a result does not have enough ether or wETH to allow Bob to withdraw his stETH. As a result the call to wETH.deposit() will fail and Bob's tokens are stuck in the contract.

Recommendations

Short term, try to wrap stETH to ETH to always ensure that the Vault will have enough wETH to service withdrawals and redemptions.

Long term, improve unit testing to uncover edge cases and ensure intended behavior in the system. Try to have tests replicate on-chain conditions as much as possible.

[Critical] Lido withdrawal queue accounting bug

In the current version we are doing lido withdrawal atomically (mainnet integration test), but in real-world scenario we need to account for the withdrawals that are pending.
The code currently doesn't care about that

[TOB-PUF-1] An attacker can DOS withdrawals

An attacker can DOS withdrawals

Severity: High
Difficulty: Medium
Type:Denial of Service
Target: src/PufferVaultV2.sol

Description

The Puffer Vault is a 4626 compliant vault implementation that allows users to deposit ETH, wETH, and stETH in exchange for rewards. Currently, the vault has a daily withdrawal limit that restricts the amount that can be withdrawn from the vault.
However, an attacker can take a flashloan, deposit their flashloaned tokens into the protocol, and then immediately withdraw their deposit. This exhausts the withdrawal limit and as a result users will be unable to withdraw their tokens.

uint256 maxAssets = maxWithdraw(owner);
if (assets > maxAssets) {
revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
}

/**
* @notice Calculates the maximum amount of assets (WETH) that can be withdrawn by the `owner`.
* @dev This function considers both the remaining daily withdrawal limit and the `owner`'s balance.
* See {IERC4626-maxWithdraw}
* @param owner The address of the owner for which the maximum withdrawal amount is calculated.
* @return maxAssets The maximum amount of assets that can be withdrawn by the `owner`.
*/
function maxWithdraw(address owner) public view virtual override returns (uint256 maxAssets) {
uint256 remainingAssets = getRemainingAssetsDailyWithdrawalLimit();
uint256 maxUserAssets = previewRedeem(balanceOf(owner));
return remainingAssets < maxUserAssets ? remainingAssets : maxUserAssets;
}

function getRemainingAssetsDailyWithdrawalLimit() public view virtual returns (uint256) {
VaultStorage storage $ = _getPufferVaultStorage();
uint96 dailyAssetsWithdrawalLimit = $.dailyAssetsWithdrawalLimit;
uint96 assetsWithdrawnToday = $.assetsWithdrawnToday;
if (dailyAssetsWithdrawalLimit < assetsWithdrawnToday) {
return 0;
}
// If we are in a new day, return the full daily limit
if ($.lastWithdrawalDay < block.timestamp / 1 days) {
return dailyAssetsWithdrawalLimit;
}
return dailyAssetsWithdrawalLimit - assetsWithdrawnToday;
}

Exploit Scenario

Bob, a user of the Puffer Protocol, wants to burn some of his vault shares and reclaim his wETH. Eve, an attacker, sees his transaction in the mempool. She then proceeds to flashloan 100 ether and immediately deposit and withdraw in the same transaction. Bob's withdrawal transaction reverts because the withdrawal limit is exhausted.

Recommendations

Short term, validate that users have held their deposits for a minimum amount of time in order to prevent an attacker from exhausting the withdrawal limit with a flashloan.

Long term, ensure that the protocol is robust against transaction reordering or atomic deposits/withdrawals.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.