Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions .github/workflows/mainnet-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@ on:

jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Codespell
uses: codespell-project/actions-codespell@v2.0
with:
path: mainnet-contracts
check_hidden: true
check_filenames: true
skip: "pnpm-lock.yaml"
name: Check for spelling errors
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Codespell
uses: codespell-project/actions-codespell@v2.0
with:
path: mainnet-contracts
check_hidden: true
check_filenames: true
skip: "pnpm-lock.yaml"

tests:
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.12.1

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
cache: "yarn"
cache-dependency-path: yarn.lock
node-version: 20

Expand Down Expand Up @@ -69,7 +69,7 @@ jobs:
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "forge fmt"
file_pattern: '*.sol'
file_pattern: "*.sol"

- name: List selectors
working-directory: mainnet-contracts
Expand All @@ -81,11 +81,11 @@ jobs:
steps:
- name: Cancel previous runs
uses: styfle/cancel-workflow-action@0.12.1

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: 'yarn'
cache: "yarn"
cache-dependency-path: yarn.lock
node-version: 20

Expand All @@ -106,6 +106,7 @@ jobs:
env:
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}
HOLESKY_RPC_URL: ${{ secrets.HOLESKY_RPC_URL }}
HOODI_RPC_URL: ${{ secrets.HOODI_RPC_URL }}

- name: "Upload coverage report to Codecov"
uses: "codecov/codecov-action@v4"
Expand Down
1 change: 1 addition & 0 deletions l2-contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ bracket_spacing = true
[rpc_endpoints]
mainnet="${ETH_RPC_URL}"
holesky="${HOLESKY_RPC_URL}"
hoodi="${HOODI_RPC_URL}"

[invariant]
fail_on_revert=false
Expand Down
23 changes: 13 additions & 10 deletions mainnet-contracts/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# <h1 align="center"> Puffer Vault </h1>

[![Website][Website-badge]][Website] [![Docs][docs-badge]][docs]
[![Discord][discord-badge]][discord] [![X][X-badge]][X] [![Foundry][foundry-badge]][foundry]
[![Discord][discord-badge]][discord] [![X][X-badge]][X] [![Foundry][foundry-badge]][foundry]

[Website-badge]: https://img.shields.io/badge/WEBSITE-8A2BE2
[Website]: https://www.puffer.fi
Expand All @@ -15,8 +16,8 @@
[foundry]: https://getfoundry.sh
[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg

## Overview

## Overview
Stakers can deposit ETH and mint the [pufETH nLRT](https://docs.puffer.fi/protocol/nlrt#pufeth) via the PufferVault contract, which serves as a redeemable receipt for their restaked ETH. If sufficient exit liquidity is available, stakers can reclaim their ETH from the PufferVault. Over time, the redeemable amount is expected to increase from [validator tickets](https://docs.puffer.fi/protocol/validator-tickets) and restaking rewards.

In [contrast with conventional liquid staking tokens (LSTs)](https://docs.puffer.fi/protocol/nlrt#what-is-an-lst), pufETH can provide strictly more rewards for its holders. Not only does pufETH encompass PoS rewards and restaking rewards, but its value can accelerate quickly due to validator ticket sales. Furthermore, the PoS rewards for stakers are decoupled from the protocol validators' performance.
Expand All @@ -27,17 +28,18 @@ pufETH is implemented as a reward-bearing ERC20 token, following [ERC4626](https

Read more about pufETH and native Liquid Restaking Tokens (nLRTs) in the [Puffer Docs](https://docs.puffer.fi/protocol/nlrt#pufeth) website.


## How pufETH Works

Stakers deposit ETH to the PufferVault contract to mint the pufETH nLRT. At the protocol's inception, pufETH's conversion rate is one-to-one, but is expected to increase over time. Assuming the protocol performs well, i.e., accrues more rewards than penalties, the amount of ETH reedamable for pufETH will increase.

### Calculating the Conversion Rate

The conversion rate can be calculated simply as:

```
conversion rate = (deposits + rewards - penalties) / pufETH supply
```

Where:

- deposits and pufETH supply increase proportionally as stakers deposit ETH to mint pufETH, leaving the conversion rate unaffected.
Expand All @@ -46,18 +48,17 @@ Where:

- penalties accrue if validators are slashed on PoS for more than their 1 ETH collateral, which is [disincentivized behavior](https://docs.puffer.fi/protocol/validator-tickets#why--noop-incentives) and mitigated through [anti-slashing technology](https://docs.puffer.fi/technology/secure-signer). Penalties can also accrue if the restaking operator is slashed running AVSs, which is why Puffer is [restricting restaking operator participation](https://docs.puffer.fi/protocol/puffer-modules#restricting-reops) during its nascent stages.



## Contract addresses

- PufferVault (pufETH token): `0xD9A442856C234a39a81a089C06451EBAa4306a72`
- PufferDepositor: `0x4aA799C5dfc01ee7d790e3bf1a7C2257CE1DcefF`
- AccessManager: `0x8c1686069474410E6243425f4a10177a94EBEE11`
- Timelock: `0x3C28B7c7Ba1A1f55c9Ce66b263B33B204f2126eA`

For more detailed information on the contract deployments (Mainnet, Holesky, etc) and the ABIs, please check the [Deployments and ACL](https://github.com/PufferFinance/Deployments-and-ACL/blob/main/docs/deployments/) repository.

For more detailed information on the contract deployments (Mainnet, Holesky, Hoodi, etc) and the ABIs, please check the [Deployments and ACL](https://github.com/PufferFinance/Deployments-and-ACL/blob/main/docs/deployments/) repository.

## Audits

- BlockSec: [v1](./audits/BlockSec-pufETH-v1.pdf), [v2](https://github.com/PufferFinance/PufferPool/blob/polish-docs/docs/audits/Blocksec_audit_April2024.pdf)
- SlowMist: [v1](./audits/SlowMist-pufETH-v1.pdf), v2
- Quantstamp: [v1](./audits/Quantstamp-pufETH-v1.pdf)
Expand All @@ -66,21 +67,23 @@ For more detailed information on the contract deployments (Mainnet, Holesky, etc
- Nethermind: [v2](https://github.com/NethermindEth/PublicAuditReports/blob/main/NM0202-FINAL_PUFFER.pdf)
- Creed: [v2](https://github.com/PufferFinance/PufferPool/blob/polish-docs/docs/audits/Creed_Puffer_Finance_Audit_April2024.pdf)


# Tests

<strong>Make sure you have access to a valid archive node RPC for ETH Mainnet (e.g. Infura)</strong>

Installing dependencies and running tests can be executed running:

```
ETH_RPC_URL=https://mainnet.infura.io/v3/YOUR_KEY forge test -vvvv
```

# Echidna

To install Echidna, see the instructions [here](https://github.com/crytic/echidna). To use Echidna, run the following command from the project's root:

```bash
forge install crytic/properties --no-commit
echidna . --contract EchidnaPufferVaultV2 --config src/echidna/config.yaml
```
For more information see the properties [README](https://github.com/crytic/properties/tree/main).

For more information see the properties [README](https://github.com/crytic/properties/tree/main).
1 change: 1 addition & 0 deletions mainnet-contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ bracket_spacing = true
[rpc_endpoints]
mainnet="${ETH_RPC_URL}"
holesky="${HOLESKY_RPC_URL}"
hoodi="${HOODI_RPC_URL}"
sepolia="${SEPOLIA_RPC_URL}"
opsepolia ="${OP_SEPOLIA_RPC_URL}"

Expand Down
4 changes: 4 additions & 0 deletions mainnet-contracts/script/BaseScript.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ abstract contract BaseScript is Script {
return (block.chainid == 17000);
}

function isHoodi() internal view returns (bool) {
return (block.chainid == 560048);
}

function isAnvil() internal view returns (bool) {
return (block.chainid == 31337);
}
Expand Down
2 changes: 1 addition & 1 deletion mainnet-contracts/script/DeployEverything.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { MockAeraVault } from "test/mocks/MockAeraVault.sol";
* @author Puffer Finance
* @notice Deploys pufETH (upgrade it in test environment), Guardians, Oracle, Puffer, and sets up the access control
* @dev Example on how to run the script
* forge script script/DeployEverything.s.sol:DeployEverything --rpc-url=$RPC_URL --sig 'run(address[] calldata, uint256)' "[$DEV_WALLET]" 1 --broadcast
* forge script script/DeployEverything.s.sol:DeployEverything --rpc-url=$RPC_URL --sig 'run(address[] calldata, uint256, address)' "[$DEV_WALLET]" 1 $DEV_WALLET --broadcast
*/
contract DeployEverything is BaseScript {
address DAO;
Expand Down
6 changes: 6 additions & 0 deletions mainnet-contracts/script/DeployPufETH.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ contract DeployPufETH is BaseScript {
lidoWithdrawalQueue = ILidoWithdrawalQueue(0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50);
stETHStrategy = IStrategy(0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3);
eigenStrategyManager = IEigenLayer(0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6);
} else if (isHoodi()) {
stETH = IStETH(address(0x3508A952176b3c15387C97BE809eaffB1982176a));
weth = new WETH9();
lidoWithdrawalQueue = ILidoWithdrawalQueue(0xfe56573178f1bcdf53F01A6E9977670dcBBD9186);
stETHStrategy = IStrategy(0xF8a1a66130D614c7360e868576D5E59203475FE0);
eigenStrategyManager = IEigenLayer(0xeE45e76ddbEDdA2918b8C7E3035cd37Eab3b5D41);
} else {
stETH = IStETH(address(new stETHMock()));
weth = new WETH9();
Expand Down
19 changes: 17 additions & 2 deletions mainnet-contracts/script/DeployPuffer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,24 @@ contract DeployPuffer is BaseScript {
eigenSlasher = address(new EigenAllocationManagerMock());
treasury = address(1);
operationsMultisig = address(2);
} else {
} else if (isHolesky()) {
// Holesky https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#current-testnet-deployment
eigenPodManager = 0x30770d7E3e71112d7A6b7259542D1f680a70e315;
delegationManager = 0xA44151489861Fe9e3055d95adC98FbD462B948e7;
eigenSlasher = 0xcAe751b75833ef09627549868A04E32679386e7C;
treasury = 0x61A44645326846F9b5d9c6f91AD27C3aD28EA390;
rewardsCoordinator = 0xAcc1fb458a1317E886dB376Fc8141540537E68fE;
operationsMultisig = 0xDDDeAfB492752FC64220ddB3E7C9f1d5CcCdFdF0;
} else if (isHoodi()) {
// Hoodi https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#current-deployment-contracts
eigenPodManager = 0xcd1442415Fc5C29Aa848A49d2e232720BE07976c;
delegationManager = 0x867837a9722C512e0862d8c2E15b8bE220E8b87d;
eigenSlasher = 0xcAe751b75833ef09627549868A04E32679386e7C; // @todo Confirm EigenSlasher address
treasury = 0x61A44645326846F9b5d9c6f91AD27C3aD28EA390;
rewardsCoordinator = 0x29e8572678e0c272350aa0b4B8f304E47EBcd5e7;
operationsMultisig = 0xDDDeAfB492752FC64220ddB3E7C9f1d5CcCdFdF0;
} else {
revert("Deployment not configured for this chain");
}

operationsCoordinator = new OperationsCoordinator(PufferOracleV2(oracle), address(accessManager), 500); // 500 BPS = 5%
Expand Down Expand Up @@ -230,10 +240,15 @@ contract DeployPuffer is BaseScript {
}

// Holesky
if (block.chainid == 17000) {
if (isHolesky()) {
return 0x4242424242424242424242424242424242424242;
}

// Hoodi
if (isHoodi()) {
return 0x00000000219ab540356cBB839Cbe05303d7705Fa;
}

// Tests / local chain
if (isAnvil()) {
return address(new BeaconMock());
Expand Down
6 changes: 3 additions & 3 deletions mainnet-contracts/script/DeployRestakingOperator.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract DeployRestakingOperator is DeployerHelper {

RestakingOperator restakingOperatorImplementation = new RestakingOperator({
delegationManager: IDelegationManager(_getEigenDelegationManager()),
allocationManager: IAllocationManager(_getEigenSlasher()),
allocationManager: IAllocationManager(_getAllocationManager()),
moduleManager: PufferModuleManager(payable(_getPufferModuleManager())),
rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()),
restakingOperatorController: _getRestakingOperatorController()
Expand All @@ -49,7 +49,7 @@ contract DeployRestakingOperator is DeployerHelper {

RestakingOperator restakingOperatorImplementation = new RestakingOperator({
delegationManager: IDelegationManager(_getEigenDelegationManager()),
allocationManager: IAllocationManager(_getEigenSlasher()),
allocationManager: IAllocationManager(_getAllocationManager()),
moduleManager: PufferModuleManager(payable(_getPufferModuleManager())),
rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()),
restakingOperatorController: _getRestakingOperatorController()
Expand All @@ -69,7 +69,7 @@ contract DeployRestakingOperator is DeployerHelper {

RestakingOperator restakingOperatorImplementation = new RestakingOperator({
delegationManager: IDelegationManager(_getEigenDelegationManager()),
allocationManager: IAllocationManager(_getEigenSlasher()),
allocationManager: IAllocationManager(_getAllocationManager()),
moduleManager: PufferModuleManager(payable(_getPufferModuleManager())),
rewardsCoordinator: IRewardsCoordinator(_getRewardsCoordinator()),
restakingOperatorController: restakingOperatorController
Expand Down
Loading