Skip to content

Commit

Permalink
Eigen implementation (#53)
Browse files Browse the repository at this point in the history
* replace magic number with commission denominator variable

* handle case where bond is already withdrawn

* use signed int math where applicable

* do not send eth if amount is 0

* fix math issues

* move withdraw logic to withdrawFromProtocol function

* remove unnecessary code from withdrawFromProtocol function

* implement execution and avs reward tests

* run forge fmt

* cleanup execution and avs reward cases

* create mock eigenpodproxy and quick withdraw test

* test bond withdrawn fallback case

* only the pod proxy owner can opt into other avs than pufferAVS

* fix test for avs rewards to use podproxyowner

* run forge fmt

* implement verifyAndWithdraw

* use send eth transfer method more similar to pool

* use new eigenpod interface which exposes validator status

* added stub for withdraw slashed eth function

* implement initiate withdrawal

* Deploy PufferPool to Ephemery

* initial upload of create pod account and register script

* move pool contract creation to separate function

* parse json file as input to script

* ignore broadcast folder

* output created eigenpodproxy address to file

* Add scripts

* Add a DeployEverything script that deploys the everything...

* add pod provision eth script using new base script

* add additional scripts for simulation

---------

Co-authored-by: Benjamin <[email protected]>
Co-authored-by: JasonVranek <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2023
1 parent 6ef4e1d commit 2562517
Show file tree
Hide file tree
Showing 31 changed files with 3,662 additions and 126 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
PRIVATE_KEY=deployer private key
PK=deployer private key
EIGEN_POD_MANAGER=eigen pod manager address
EIGEN_SLASHER=eigen slasher address
MAINNET=mainnet rpc url
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
cache/
out/
broadcast/
simulation/
.env

235 changes: 235 additions & 0 deletions broadcast/DeployPuffer.s.sol/39438086/run-1690979192.json

Large diffs are not rendered by default.

595 changes: 595 additions & 0 deletions broadcast/DeployPuffer.s.sol/39438086/run-1690979211.json

Large diffs are not rendered by default.

595 changes: 595 additions & 0 deletions broadcast/DeployPuffer.s.sol/39438086/run-latest.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions deployments/39438086-deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"EigenPodProxyImplementation": "0xe30ff99574496ff6d964a15721afc842a56a7df1",
"PoolImplementation": "0x334e282fac9fd244e8dd6b68a74ea7ecd7b8028d",
"EigenPodProxyBeacon": "0xdabf735a41926cee8139ff37491f56ad33ea7d64",
"PufferPool": "0x00cefcd3125e6060a841308330329be418f8356e"
}
16 changes: 13 additions & 3 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@ runs = 10_000
# Run them in development manually by commenting `no_match_path` line and running "forge test --mc ETHTransferTest -vvvv"
# don't forget to turn on the optimizer :)
no_match_path = "test/gas-tests/*.sol"

optimizer = false
optimizer_runs = 10_000
fs_permissions = [{ access = "read-write", path = "./"}]
gas_reports=[
"PufferPool",
"EigenPodProxy",
"PufferAVSRegistry"
]
auto_detect_solc = false
cbor_metadata = false
bytecode_hash = "none"
optimizer = true
optimizer_runs = 200
solc = "0.8.20"
evm_version = "shanghai"

[fmt]
line_length = 120
int_types = "long"
tab_width = 4
quote_style = "double"
bracket_spacing = true

[rpc_endpoints]
Expand Down
36 changes: 36 additions & 0 deletions scripts/BaseScript.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

import { Script } from "forge-std/Script.sol";
import { Strings } from "openzeppelin/utils/Strings.sol";
import { IPufferPool } from "puffer/interface/IPufferPool.sol";
import { stdJson } from "forge-std/StdJson.sol";

/**
* @title Base Script
* @author Puffer finance
*/
abstract contract BaseScript is Script {
/**
* @dev Deployer private key is in `PK` env variable
*/
uint256 _deployerPrivateKey = vm.envUint("PK");
address internal _broadcaster = vm.addr(_deployerPrivateKey);

/**
* @dev Reads the deployment file
*/
string internal _deploymentFilePath = string(abi.encodePacked("deployments/", Strings.toString(block.chainid), "-deployment.json"));
string internal _deploymentData = vm.readFile(_deploymentFilePath);

/**
* @dev PufferPool deployed on block.chainId
*/
IPufferPool internal _pufferPool = IPufferPool(stdJson.readAddress(_deploymentData, ".PufferPool"));

modifier broadcast() {
vm.startBroadcast(_deployerPrivateKey);
_;
vm.stopBroadcast();
}
}
117 changes: 117 additions & 0 deletions scripts/CreatePodAccountAndRegisterValidatorKey.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

import {Script} from "forge-std/Script.sol";
import {NewBaseScript} from "scripts/NewBaseScript.s.sol";
import {SafeProxyFactory} from "safe-contracts/proxies/SafeProxyFactory.sol";
import {Safe} from "safe-contracts/Safe.sol";
import {IEigenPodProxy} from "puffer/interface/IEigenPodProxy.sol";
import {IPufferPool} from "puffer/interface/IPufferPool.sol";
import {BeaconProxy} from "openzeppelin/proxy/beacon/BeaconProxy.sol";
import {UpgradeableBeacon} from "openzeppelin/proxy/beacon/UpgradeableBeacon.sol";
import {DeployBeacon} from "scripts/DeployBeacon.s.sol";
import {EigenPodProxy} from "puffer/EigenPodProxy.sol";
import {PufferPool} from "puffer/PufferPool.sol";
import {Test} from "forge-std/Test.sol";
import {DeploySafe} from "scripts/DeploySafe.s.sol";
import {DeployPufferPool} from "scripts/DeployPufferPool.s.sol";
import {Strings} from "openzeppelin/utils/Strings.sol";
import {CustomJSONBuilder} from "scripts/DeployPuffer.s.sol";
import "forge-std/console.sol";
import "forge-std/StdJson.sol";

using stdJson for string;

// Commandline argument will give path to json file for params, and public key, needed in vm.startBroadcast()
// Example script call (Assumes `PK` environment variable is set to eth private key):
// forge script ./CreatePodAccountAndRegisterValidatorKey.s.sol:CreatePodAndRegisterKey ~/puffer/PufferPool/simulation/ephemery-sim-1/validator-1 --sig 'run(string)' --rpc-url 'https://otter.bordel.wtf/erigon' --broadcast
contract CreatePodAndRegisterKey is NewBaseScript {
function _parseRegistrationData(
string memory json
)
internal
returns (
IPufferPool pool,
address[] memory podAccountOwners,
address podRewardsRecipient,
uint256 podAccountThreshold,
IPufferPool.ValidatorKeyData memory data
)
{
// Parse out necessary fields
address poolAddress = abi.decode(
vm.parseJson(json, ".poolContract"),
(address)
);

pool = IPufferPool(poolAddress);

(podAccountOwners) = abi.decode(
vm.parseJson(json, ".podAccountOwners"),
(address[])
);

(podRewardsRecipient) = abi.decode(
vm.parseJson(json, ".podRewardsRecipient"),
(address)
);

podAccountThreshold = vm.parseJsonUint(json, ".podAccountThreshold");

data.blsPubKey = vm.parseJsonBytes(json, ".blsPubKey");

data.signature = vm.parseJsonBytes(json, ".signature");

data.depositDataRoot = vm.parseJsonBytes32(json, ".depositDataRoot");

// For now, don't read blsEncPrivKeyShares from Json, just hardcode empty array (TODO)
bytes[] memory blsEncPrivKeyShares;
data.blsEncPrivKeyShares = blsEncPrivKeyShares;

data.blsPubKeyShares = vm.parseJsonBytesArray(json, ".blsPubKeyShares");

data.blockNumber = vm.parseJsonUint(json, ".blockNumber");

// Ignore raveEvidence for now (TODO)
data.raveEvidence = bytes("");
}

function run(string calldata jsonDir) external broadcast {
string memory pathToJson = string.concat(jsonDir, "/inputs.json");

// Read in Json file
string memory json = vm.readFile(pathToJson);

(
IPufferPool pool,
address[] memory podAccountOwners,
address podRewardsRecipient,
uint256 podAccountThreshold,
IPufferPool.ValidatorKeyData memory data
) = _parseRegistrationData(json);

// TODO: Add logic to determine which bond amount to use based on above parsed parameters
// Hardcoded bond amount and podRewardsRecipient
uint256 bondAmount = 16 ether;

Safe podAccount;
IEigenPodProxy eigenPodProxy;

console.log(address(pool));
console.log(podRewardsRecipient);

(podAccount, eigenPodProxy) = pool
.createPodAccountAndRegisterValidatorKey{value: bondAmount}({
podAccountOwners: podAccountOwners,
podAccountThreshold: podAccountThreshold,
data: data,
podRewardsRecipient: podRewardsRecipient
});

// Write the EigenPodProxy address to be easily consumed by calling bash script
vm.writeFile(
string.concat(jsonDir, "/EigenPodProxy-address"),
Strings.toHexString(address(eigenPodProxy))
);
}
}
40 changes: 40 additions & 0 deletions scripts/CreatePodAndRegisterKey.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

import { BaseScript } from "scripts/BaseScript.s.sol";
import { IPufferPool } from "puffer/interface/IPufferPool.sol";

/**
* @title Create Eigen Pod Proxy and register validator key script
* @author Puffer finance
* @notice Calls the `createPodAccountAndRegisterValidatorKey` function on PufferPool
* @dev Example on how to run the script
*
* forge script scripts/CreatePodAndRegisterKey.s.sol:CreatePodAndRegisterKey --rpc-url=$EPHEMERY_RPC_URL --broadcast --sig "run(bytes)" -vvvv 0xa091f34f8e90ce7eb0f2ca31a3f12e98dbbdffcae36da273d2fe701b3b14d83a492a4704c0ac4a550308faf0eac6384e
*/
contract CreatePodAndRegisterKey is BaseScript {
/**
* @param pubKey Is the validator pubKey
*/
function run(bytes calldata pubKey) external broadcast {

address[] memory owners = new address[](1);
owners[0] = _broadcaster;

// Use empty object
IPufferPool.ValidatorKeyData memory validatorData = IPufferPool.ValidatorKeyData({
blsPubKey: pubKey,
signature: new bytes(0),
depositDataRoot: bytes32(""),
blsEncPrivKeyShares: new bytes[](0),
blsPubKeyShares: new bytes[](0),
blockNumber: block.number,
raveEvidence: new bytes(0)
});

// Hardcoded bond amount and podRewardsRecipient
uint256 bondAmount = 16 ether;

_pufferPool.createPodAccountAndRegisterValidatorKey{value: bondAmount}({podAccountOwners: owners, podAccountThreshold: owners.length, data: validatorData, podRewardsRecipient: _broadcaster});
}
}
12 changes: 8 additions & 4 deletions scripts/DeployBeacon.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
pragma solidity >=0.8.0 <0.9.0;

import { Script } from "forge-std/Script.sol";
import {UpgradeableBeacon} from "openzeppelin/proxy/beacon/UpgradeableBeacon.sol";
import { UpgradeableBeacon } from "openzeppelin/proxy/beacon/UpgradeableBeacon.sol";
import { EigenPodProxy } from "puffer/EigenPodProxy.sol";
import { IEigenPodManager } from "eigenlayer/interfaces/IEigenPodManager.sol";
import {EigenPodManagerMock} from "eigenlayer-test/mocks/EigenPodManagerMock.sol";
import { EigenPodManagerMock } from "eigenlayer-test/mocks/EigenPodManagerMock.sol";
import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol";
import { SlasherMock } from "test/mocks/SlasherMock.sol";
import { IStrategyManager } from "eigenlayer/interfaces/IStrategyManager.sol";
import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol";

/**
* @title DeployBeacon script
* @author Puffer finance
* @notice Dep;loyment of Beacon for EigenPodProxy
* @notice Deployment of Beacon for EigenPodProxy
*/
contract DeployBeacon is Script {
function run(bool useEigenPodManagerMock) external returns (EigenPodProxy, UpgradeableBeacon) {
Expand All @@ -22,7 +25,8 @@ contract DeployBeacon is Script {
eigenPodManager = 0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338;
}

EigenPodProxy eigenPodProxyImplementation = new EigenPodProxy(IEigenPodManager(eigenPodManager), ISlasher(address(0)));
ISlasher slasher = new SlasherMock(IStrategyManager(address(0)), IDelegationManager(address(0)));
EigenPodProxy eigenPodProxyImplementation = new EigenPodProxy(IEigenPodManager(eigenPodManager), slasher);

UpgradeableBeacon beacon = new UpgradeableBeacon(address(eigenPodProxyImplementation));

Expand Down
87 changes: 87 additions & 0 deletions scripts/DeployEverything.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0 <0.9.0;

import {Script} from "forge-std/Script.sol";
import {NewBaseScript} from "scripts/NewBaseScript.s.sol";
import {SafeProxyFactory} from "safe-contracts/proxies/SafeProxyFactory.sol";
import {Safe} from "safe-contracts/Safe.sol";
import {IEigenPodProxy} from "puffer/interface/IEigenPodProxy.sol";
import {IPufferPool} from "puffer/interface/IPufferPool.sol";
import {BeaconProxy} from "openzeppelin/proxy/beacon/BeaconProxy.sol";
import {UpgradeableBeacon} from "openzeppelin/proxy/beacon/UpgradeableBeacon.sol";
import {DeployBeacon} from "scripts/DeployBeacon.s.sol";
import {EigenPodProxy} from "puffer/EigenPodProxy.sol";
import {PufferPool} from "puffer/PufferPool.sol";
import {Test} from "forge-std/Test.sol";
import {DeploySafe} from "scripts/DeploySafe.s.sol";
import {DeployPufferPool} from "scripts/DeployPufferPool.s.sol";
import {Strings} from "openzeppelin/utils/Strings.sol";
import {CustomJSONBuilder} from "scripts/DeployPuffer.s.sol";
import {IEigenPodManager} from "eigenlayer/interfaces/IEigenPodManager.sol";
import {EigenPodManagerMock} from "eigenlayer-test/mocks/EigenPodManagerMock.sol";
import {ISlasher} from "eigenlayer/interfaces/ISlasher.sol";
import {SlasherMock} from "test/mocks/SlasherMock.sol";
import {IStrategyManager} from "eigenlayer/interfaces/IStrategyManager.sol";
import {IDelegationManager} from "eigenlayer/interfaces/IDelegationManager.sol";
import {ERC1967Proxy} from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";
import "forge-std/console.sol";
import "forge-std/StdJson.sol";

using stdJson for string;

// Commandline argument will give path to ephemery simulation dir
// Example script call (Assumes `PK` environment variable is set to eth private key):
// forge script ./DeployEverything.s.sol:DeployEverything ./simulation/ephemery-sim-2 --sig 'run(string)' --rpc-url 'https://otter.bordel.wtf/erigon' --broadcast
contract DeployEverything is NewBaseScript {
function run(string calldata simulationDir) external broadcast {
console.log("Running DeployEverything");

address eigenPodManager = address(new EigenPodManagerMock());
// if (!useEigenPodManagerMock) {
// eigenPodManager = 0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338;
// }

ISlasher slasher = new SlasherMock(
IStrategyManager(address(0)),
IDelegationManager(address(0))
);
EigenPodProxy eigenPodProxyImplementation = new EigenPodProxy(
IEigenPodManager(eigenPodManager),
slasher
);

UpgradeableBeacon beacon = new UpgradeableBeacon(
address(eigenPodProxyImplementation)
);
beacon.transferOwnership(_broadcaster);

// begin DeploySafe
SafeProxyFactory proxyFactory = new SafeProxyFactory();

Safe safeImplementation = new Safe();

// Deploys Puffer Pool implementation
PufferPool poolImpl = new PufferPool(address(beacon));
// Deploys Proxy contract
ERC1967Proxy proxy = new ERC1967Proxy(address(poolImpl), "");
// Casts Proxy to PufferPool
PufferPool pool = PufferPool(payable(address(proxy)));
// Initializes the Pool
address[] memory treasuryOwners = new address[](1);
treasuryOwners[0] = address(_broadcaster); // mock owner

pool.initialize({
safeProxyFactory: address(proxyFactory),
safeImplementation: address(safeImplementation),
treasuryOwners: treasuryOwners
});

// For test environment transfer ownership to Test contract
pool.transferOwnership(_broadcaster);

console.log(address(pool));

// Write the PufferPool address to be easily consumed by calling bash script
vm.writeFile(string.concat(simulationDir, "/PufferPool-address"), Strings.toHexString(address(pool)));
}
}
Loading

0 comments on commit 2562517

Please sign in to comment.