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

mainnet native staking fork tests #2037

Merged
merged 36 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9a6fcfb
manuallyFixAccounting now uses delta values and only callable by the …
naddison36 Apr 26, 2024
389a260
Added pauseOnFail param to internal _doAccounting
naddison36 Apr 29, 2024
c12f770
Merge remote-tracking branch 'origin/sparrowDom/nativeStaking' into n…
naddison36 Apr 29, 2024
a332c98
ran prettier
naddison36 Apr 29, 2024
e8201dc
Added Defender Relayer for validator registrator
naddison36 Apr 29, 2024
76c703f
Removed now redundant IWETH9 import
naddison36 Apr 29, 2024
f91e078
Merge remote-tracking branch 'origin/sparrowDom/nativeStaking' into n…
naddison36 Apr 29, 2024
51e1748
Merge remote-tracking branch 'origin/sparrowDom/nativeStaking' into n…
naddison36 Apr 29, 2024
8bf8f7d
moved more logic into native staking fixture
naddison36 Apr 29, 2024
86e7d29
Removed unused imports
naddison36 May 1, 2024
ba3fe44
fix native staking unit tests
naddison36 May 1, 2024
1ed5a17
Fail accounting if activeDepositedValidators < fullyWithdrawnValidators
naddison36 May 1, 2024
fe85d60
Updated the OETH value flows
naddison36 May 2, 2024
5333508
Added governable Hardhat tasks
naddison36 May 2, 2024
97c0cc7
deconstruct params for Hardhat tasks
naddison36 May 2, 2024
1498267
WIP Hardhat tasks for validator registration
naddison36 May 2, 2024
0ece8b9
Added depositSSV HH task
naddison36 May 3, 2024
ce11531
Updated OETH contract dependency diagram
naddison36 May 3, 2024
146465b
Update to diagrams
naddison36 May 3, 2024
aee4382
mini fixes
sparrowDom May 3, 2024
274fbed
fix bug and minor test improvement
sparrowDom May 3, 2024
91894c2
update yarn fulie
sparrowDom May 3, 2024
b5b2f56
unify the holesky and the mainnet fork tests
sparrowDom May 3, 2024
0249e53
prettier
sparrowDom May 3, 2024
9f6bbf2
re-deploy holesky native staking strategy (#2046)
sparrowDom May 3, 2024
f89f245
test updates
sparrowDom May 3, 2024
22a31d7
also re-deploy the harvester
sparrowDom May 3, 2024
476947f
upgrade harvester as well
sparrowDom May 3, 2024
75b267a
fix test
sparrowDom May 5, 2024
d12957a
fix upgrade script and correct the bug in deploy actions
sparrowDom May 5, 2024
97f1fcd
Merge branch 'sparrowDom/nativeStaking' into nicka/native-staking-for…
sparrowDom May 6, 2024
b99edec
Merge branch 'sparrowDom/nativeStaking' into nicka/native-staking-for…
sparrowDom May 6, 2024
2969a4f
Deployed new Native Staking strategy including the proxy
naddison36 May 6, 2024
2632e40
Added Hardhat tasks for generic strategy functions
naddison36 May 6, 2024
ed3d232
Merge remote-tracking branch 'origin/nicka/native-staking-fork-tests'…
naddison36 May 6, 2024
eeed0c8
remove nativeStakingSSVStrategyProxy from js addresses file
naddison36 May 7, 2024
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
4 changes: 3 additions & 1 deletion contracts/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ module.exports = {
getNamedAccounts: "readable",
hre: "readable",
},
rules: {},
rules: {
"no-constant-condition": ["error", { checkLoops: false }],
},
};
25 changes: 20 additions & 5 deletions contracts/contracts/harvest/BaseHarvester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,23 @@ abstract contract BaseHarvester is Governable {
address _rewardTo,
IOracle _priceProvider
) internal virtual {
uint256 balance = IERC20(_swapToken).balanceOf(address(this));

// No need to swap if the reward token is the base token. eg USDT or WETH.
// There is also no limit on the transfer. Everything in the harvester will be transferred
// to the Dripper regardless of the liquidationLimit config.
if (_swapToken == baseTokenAddress) {
IERC20(_swapToken).safeTransfer(rewardProceedsAddress, balance);
// currently not paying the farmer any rewards as there is no swap
emit RewardProceedsTransferred(
baseTokenAddress,
address(0),
balance,
0
);
return;
}

RewardTokenConfig memory tokenConfig = rewardTokenConfigs[_swapToken];

/* This will trigger a return when reward token configuration has not yet been set
Expand All @@ -487,8 +504,6 @@ abstract contract BaseHarvester is Governable {
return;
}

uint256 balance = IERC20(_swapToken).balanceOf(address(this));

if (balance == 0) {
return;
}
Expand Down Expand Up @@ -548,14 +563,14 @@ abstract contract BaseHarvester is Governable {
tokenConfig.harvestRewardBps,
1e4
);
uint256 protcolYield = baseTokenBalance - farmerFee;
uint256 protocolYield = baseTokenBalance - farmerFee;

baseToken.safeTransfer(rewardProceedsAddress, protcolYield);
baseToken.safeTransfer(rewardProceedsAddress, protocolYield);
baseToken.safeTransfer(_rewardTo, farmerFee);
emit RewardProceedsTransferred(
baseTokenAddress,
_rewardTo,
protcolYield,
protocolYield,
farmerFee
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { InitializableAbstractStrategy } from "../../utils/InitializableAbstractStrategy.sol";
import { ISSVNetwork, Cluster } from "../../interfaces/ISSVNetwork.sol";
import { IWETH9 } from "../../interfaces/IWETH9.sol";
import { FeeAccumulator } from "./FeeAccumulator.sol";
import { ValidatorAccountant } from "./ValidatorAccountant.sol";
import { Cluster } from "../../interfaces/ISSVNetwork.sol";

struct ValidatorStakeData {
bytes pubkey;
Expand Down
4 changes: 4 additions & 0 deletions contracts/contracts/strategies/NativeStaking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

## Fee Accumulator

### Hierarchy

![Fee Accumulator Hierarchy](../../../docs/FeeAccumulatorHierarchy.svg)

### Squashed

![Fee Accumulator Squashed](../../../docs/FeeAccumulatorSquashed.svg)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol";
import { ValidatorRegistrator } from "./ValidatorRegistrator.sol";
import { IWETH9 } from "../../interfaces/IWETH9.sol";

Expand Down Expand Up @@ -108,12 +107,7 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
returns (bool accountingValid)
{
if (address(this).balance < consensusRewards) {
// pause if not already
if (pauseOnFail) {
_pause();
}
// fail the accounting
return false;
return _failAccounting(pauseOnFail);
}

// Calculate all the new ETH that has been swept to the contract since the last accounting
Expand All @@ -123,6 +117,9 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
// send the ETH that is from fully withdrawn validators to the Vault
if (newSweptETH >= MAX_STAKE) {
uint256 fullyWithdrawnValidators = newSweptETH / MAX_STAKE;
if (activeDepositedValidators < fullyWithdrawnValidators) {
return _failAccounting(pauseOnFail);
}
activeDepositedValidators -= fullyWithdrawnValidators;

uint256 wethToVault = MAX_STAKE * fullyWithdrawnValidators;
Expand Down Expand Up @@ -164,13 +161,21 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
}
// Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.
else {
// pause if not already
if (pauseOnFail) {
_pause();
}
// fail the accounting
accountingValid = false;
return _failAccounting(pauseOnFail);
}
}

/// @dev pause any further accounting if required and return false
function _failAccounting(bool pauseOnFail)
Copy link
Member

Choose a reason for hiding this comment

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

nice cleanup

internal
returns (bool accountingValid)
{
// pause if not already
if (pauseOnFail) {
_pause();
}
// fail the accounting
accountingValid = false;
}

/// @notice Allow the Strategist to fix the accounting of this strategy and unpause.
Expand Down
34 changes: 26 additions & 8 deletions contracts/deploy/deployActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,26 @@ const deployOUSDHarvester = async (ousdDripper) => {
return dHarvesterProxy;
};

const upgradeOETHHarvester = async () => {
const assetAddresses = await getAssetAddresses(deployments);
const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy");
const cOETHHarvesterProxy = await ethers.getContract("OETHHarvesterProxy");

const dOETHHarvester = await deployWithConfirmation("OETHHarvester", [
cOETHVaultProxy.address,
assetAddresses.WETH,
]);

await withConfirmation(cOETHHarvesterProxy.upgradeTo(dOETHHarvester.address));

log("Upgraded OETHHarvesterProxy");
return cOETHHarvesterProxy;
};

const deployOETHHarvester = async (oethDripper) => {
const assetAddresses = await getAssetAddresses(deployments);
const { governorAddr } = await getNamedAccounts();
const sGovernor = await ethers.provider.getSigner(governorAddr);
const cVaultProxy = await ethers.getContract("VaultProxy");
const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy");

const dOETHHarvesterProxy = await deployWithConfirmation(
Expand All @@ -568,11 +583,13 @@ const deployOETHHarvester = async (oethDripper) => {
);

await withConfirmation(
cOETHHarvesterProxy["initialize(address,address,bytes)"](
dOETHHarvester.address,
governorAddr,
[]
)
// prettier-ignore
cOETHHarvesterProxy
.connect(sGovernor)["initialize(address,address,bytes)"](
dOETHHarvester.address,
governorAddr,
[]
)
);

log("Initialized OETHHarvesterProxy");
Expand All @@ -581,11 +598,11 @@ const deployOETHHarvester = async (oethDripper) => {
cOETHHarvester
.connect(sGovernor)
.setRewardProceedsAddress(
isMainnet || isHolesky ? oethDripper.address : cVaultProxy.address
isMainnet || isHolesky ? oethDripper.address : cOETHVaultProxy.address
)
);

return dOETHHarvesterProxy;
return cOETHHarvester;
};

/**
Expand Down Expand Up @@ -1509,6 +1526,7 @@ module.exports = {
deployHarvesters,
deployOETHHarvester,
deployOUSDHarvester,
upgradeOETHHarvester,
configureVault,
configureOETHVault,
configureStrategies,
Expand Down
41 changes: 41 additions & 0 deletions contracts/deploy/holesky/004_upgrade_strategy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const {
upgradeNativeStakingSSVStrategy,
upgradeOETHHarvester,
} = require("../deployActions");
const { withConfirmation } = require("../../utils/deploy");

const mainExport = async () => {
console.log("Running 004 deployment on Holesky...");

console.log("Upgrading native staking strategy");
await upgradeNativeStakingSSVStrategy();

console.log("deploying harvester");
const cOETHDripperProxy = await ethers.getContract("OETHDripperProxy");
const cOETHHarvester = await upgradeOETHHarvester(cOETHDripperProxy.address);

const strategyProxy = await ethers.getContract(
"NativeStakingSSVStrategyProxy"
);
const cStrategy = await ethers.getContractAt(
"NativeStakingSSVStrategy",
strategyProxy.address
);

console.log("configuring harvester and the strategy");
await withConfirmation(
cOETHHarvester.setSupportedStrategy(strategyProxy.address, true)
);

await withConfirmation(cStrategy.setHarvesterAddress(cOETHHarvester.address));

console.log("Running 004 deployment done");
return true;
};

mainExport.id = "004_upgrade_strategy";
mainExport.tags = [];
mainExport.dependencies = [];
mainExport.skip = () => false;

module.exports = mainExport;
61 changes: 61 additions & 0 deletions contracts/deploy/holesky/005_new_harvester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const { parseEther } = require("ethers/lib/utils");

const { deployNativeStakingSSVStrategy } = require("../deployActions");
const { withConfirmation } = require("../../utils/deploy");
const { resolveContract } = require("../../utils/resolvers");

const mainExport = async () => {
console.log("Running 005 deployment on Holesky...");

console.log("Deploying a new Native Staking strategy and proxy");

console.log("Deploying Native Staking");
const nativeStakingSSVStrategy = await deployNativeStakingSSVStrategy();

const { governorAddr } = await getNamedAccounts();
const sGovernor = await ethers.provider.getSigner(governorAddr);

const cOETHHarvester = await resolveContract(
"OETHHarvesterProxy",
"OETHHarvester"
);
const cVault = await resolveContract("OETHVaultProxy", "VaultAdmin");

await withConfirmation(
nativeStakingSSVStrategy
.connect(sGovernor)
.setHarvesterAddress(cOETHHarvester.address)
);

console.log("configuring harvester and the strategy");
await withConfirmation(
cOETHHarvester
.connect(sGovernor)
.setSupportedStrategy(nativeStakingSSVStrategy.address, true)
);

await withConfirmation(
cVault.connect(sGovernor).approveStrategy(nativeStakingSSVStrategy.address)
);

await withConfirmation(
nativeStakingSSVStrategy.connect(sGovernor).setRegistrator(governorAddr)
);

const fuseStartBn = parseEther("21.6");
const fuseEndBn = parseEther("25.6");

await nativeStakingSSVStrategy
.connect(sGovernor)
.setFuseInterval(fuseStartBn, fuseEndBn);

console.log("Running 005 deployment done");
return true;
};

mainExport.id = "005_deploy_new_harvester";
mainExport.tags = [];
mainExport.dependencies = [];
mainExport.skip = () => false;

module.exports = mainExport;
24 changes: 24 additions & 0 deletions contracts/deploy/mainnet/091_native_ssv_staking.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,23 @@ module.exports = deploymentWithGovernanceProposal(
// 7. Safe approve SSV token spending
await cStrategy.connect(sDeployer).safeApproveAllTokens();

// 8. Deploy Harvester
const cOETHHarvesterProxy = await ethers.getContract("OETHHarvesterProxy");
Copy link
Member

Choose a reason for hiding this comment

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

thanks for catching this

await deployWithConfirmation("OETHHarvester", [
cVaultProxy.address,
addresses.mainnet.WETH,
]);
const dOETHHarvesterImpl = await ethers.getContract("OETHHarvester");

console.log(
"Native Staking SSV Strategy address: ",
cStrategyProxy.address
);
console.log("Fee accumulator address: ", cFeeAccumulator.address);
console.log(
"New OETHHarvester implementation address: ",
dOETHHarvesterImpl.address
);

// Governance Actions
// ----------------
Expand Down Expand Up @@ -152,6 +164,18 @@ module.exports = deploymentWithGovernanceProposal(
ethers.utils.parseEther("25.6"),
],
},
// 5. set validator registrator
{
contract: cStrategy,
signature: "setRegistrator(address)",
args: [addresses.mainnet.validatorRegistrator],
},
// 6. Upgrade the OETH Harvester
{
contract: cOETHHarvesterProxy,
signature: "upgradeTo(address)",
args: [dOETHHarvesterImpl.address],
},
],
};
}
Expand Down
4 changes: 3 additions & 1 deletion contracts/deployments/holesky/.migrations.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"001_core": 1714168010,
"002_upgrade_strategy": 1714233842,
"003_deposit_to_native_strategy": 1714307581
"003_deposit_to_native_strategy": 1714307581,
"004_upgrade_strategy": 1714944723,
"005_deploy_new_harvester": 1714998707
}
Loading
Loading