Skip to content

Commit 3e790c1

Browse files
authored
automated withdrawals from the staking strategy and validators (#2685)
* Allow withdraws from the staking strategy by the Registrator for automation * Added signMessage HH task for Etherscan ownership verification * Deployed latest strategy contract to Hoodi * Added autoValidatorWithdrawals Hardhat task * Fixed unit tests * Changed execute option to dryrun * Created reusable totalPartialWithdrawals function Fixed summing of partial withdrawal amount as its in Gwei
1 parent 480333f commit 3e790c1

14 files changed

Lines changed: 525 additions & 119 deletions

contracts/contracts/strategies/NativeStaking/CompoundingStakingSSVStrategy.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ contract CompoundingStakingSSVStrategy is
116116
address _recipient,
117117
address _asset,
118118
uint256 _amount
119-
) external override onlyVault nonReentrant {
119+
) external override nonReentrant {
120120
require(_asset == WETH, "Unsupported asset");
121+
require(
122+
msg.sender == vaultAddress || msg.sender == validatorRegistrator,
123+
"Caller not Vault or Registrator"
124+
);
121125

122126
_withdraw(_recipient, _amount, address(this).balance);
123127
}
@@ -128,7 +132,7 @@ contract CompoundingStakingSSVStrategy is
128132
uint256 _ethBalance
129133
) internal {
130134
require(_withdrawAmount > 0, "Must withdraw something");
131-
require(_recipient != address(0), "Must specify recipient");
135+
require(_recipient == vaultAddress, "Recipient not Vault");
132136

133137
// Convert any ETH from validator partial withdrawals, exits
134138
// or execution rewards to WETH and do the necessary accounting.

contracts/contracts/strategies/NativeStaking/CompoundingValidatorManager.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ abstract contract CompoundingValidatorManager is Governable, Pausable {
5555
/// @notice The address of the beacon chain deposit contract
5656
address internal immutable BEACON_CHAIN_DEPOSIT_CONTRACT;
5757
/// @notice The address of the SSV Network contract used to interface with
58-
address public immutable SSV_NETWORK;
58+
address internal immutable SSV_NETWORK;
5959
/// @notice Address of the OETH Vault proxy contract
6060
address internal immutable VAULT_ADDRESS;
6161
/// @notice Address of the Beacon Proofs contract that verifies beacon chain data
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { upgradeCompoundingStakingSSVStrategy } = require("../deployActions");
2+
3+
const mainExport = async () => {
4+
await upgradeCompoundingStakingSSVStrategy();
5+
6+
console.log("Running 031 deployment done");
7+
return true;
8+
};
9+
10+
mainExport.id = "031_upgrade_staking_contract";
11+
mainExport.tags = [];
12+
mainExport.dependencies = [];
13+
mainExport.skip = () => false;
14+
15+
module.exports = mainExport;

contracts/deployments/hoodi/.migrations.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@
2828
"027_upgrade_staking_contract": 1757382067,
2929
"028_deploy_new_staking_contracts": 1757935264,
3030
"029_upgrade_staking_contract": 1758151852,
31-
"030_upgrade_staking_contract": 1758515961
31+
"030_upgrade_staking_contract": 1758515961,
32+
"031_upgrade_staking_contract": 1760330143
3233
}

contracts/deployments/hoodi/CompoundingStakingSSVStrategy.json

Lines changed: 69 additions & 98 deletions
Large diffs are not rendered by default.

contracts/deployments/hoodi/solcInputs/6009afe519e1f288a46249ae425a2ffb.json

Lines changed: 105 additions & 0 deletions
Large diffs are not rendered by default.

contracts/hardhat.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const SONIC_DEPLOYER = MAINNET_DEPLOYER;
7373
const SONIC_ADMIN = "0xAdDEA7933Db7d83855786EB43a238111C69B00b6";
7474
// 2/8 multi-sig that controls fund allocations. Aka "Guardian".
7575
const SONIC_STRATEGIST = "0x63cdd3072F25664eeC6FAEFf6dAeB668Ea4de94a";
76+
const MAINNET_RELAYER = "0x4b91827516f79d6F6a1F292eD99671663b09169a";
7677

7778
const MULTICHAIN_STRATEGIST = "0x4FF1b9D9ba8558F5EAfCec096318eA0d8b541971";
7879

@@ -201,6 +202,13 @@ const localEnvStrategist =
201202
: MULTICHAIN_STRATEGIST
202203
: 0;
203204

205+
const localEnvRegistrator =
206+
process.env.FORK === "true"
207+
? isHoodiFork
208+
? HOODI_RELAYER
209+
: MAINNET_RELAYER
210+
: 1; // signer at index 2
211+
204212
module.exports = {
205213
solidity: {
206214
version: "0.8.28",
@@ -407,6 +415,12 @@ module.exports = {
407415
multichainStrategistAddr: {
408416
default: MULTICHAIN_STRATEGIST,
409417
},
418+
registratorAddr: {
419+
default: 2,
420+
localhost: localEnvRegistrator,
421+
mainnet: MAINNET_RELAYER,
422+
hoodi: HOODI_RELAYER,
423+
},
410424
},
411425
contractSizer: {
412426
alphaSort: true,

contracts/tasks/crypto.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,18 @@ const concatKDF = (secret, s1, keyLen) => {
178178
return hashSum.slice(0, keyLen);
179179
};
180180

181+
const signMessage = async ({ signer, message }) => {
182+
console.log(`Message: ${message}`);
183+
console.log(`Signer: ${await signer.getAddress()}`);
184+
185+
const hash = await signer.signMessage(message);
186+
console.log(`Hash: ${hash}`);
187+
};
188+
181189
module.exports = {
182190
genECDHKey,
183191
decryptValidatorKey,
184192
decryptValidatorKeyWithMasterKey,
185193
decryptValidatorKeyFromStorage,
194+
signMessage,
186195
};

contracts/tasks/tasks.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
genECDHKey,
1212
decryptValidatorKey,
1313
decryptValidatorKeyWithMasterKey,
14+
signMessage,
1415
} = require("./crypto");
1516
const { advanceBlocks } = require("./block");
1617
const {
@@ -110,6 +111,7 @@ const {
110111
stakeValidator,
111112
withdrawValidator,
112113
removeValidator,
114+
autoValidatorWithdrawals,
113115
setRegistrator,
114116
} = require("./validatorCompound");
115117
const { tenderlySync, tenderlyUpload } = require("./tenderly");
@@ -1651,6 +1653,20 @@ task("masterDecrypt").setAction(async (_, __, runSuper) => {
16511653
return runSuper();
16521654
});
16531655

1656+
subtask(
1657+
"signMessage",
1658+
"Sign a message using a Elliptic-curve Diffie–Hellman (ECDH) private key"
1659+
)
1660+
.addParam("message", "Message to be signed", undefined, types.string)
1661+
.setAction(async (taskArgs) => {
1662+
const signer = await getSigner();
1663+
1664+
await signMessage({ ...taskArgs, signer });
1665+
});
1666+
task("signMessage").setAction(async (_, __, runSuper) => {
1667+
return runSuper();
1668+
});
1669+
16541670
// Defender
16551671
subtask(
16561672
"setActionVars",
@@ -2121,6 +2137,30 @@ task("removeValidator").setAction(async (_, __, runSuper) => {
21212137
return runSuper();
21222138
});
21232139

2140+
subtask(
2141+
"autoValidatorWithdrawals",
2142+
"Automatically withdraws funds from a validator"
2143+
)
2144+
.addParam(
2145+
"buffer",
2146+
"Withdrawal buffer in basis points. 100 = 1%",
2147+
100,
2148+
types.int
2149+
)
2150+
.addParam(
2151+
"dryrun",
2152+
"Do not send any txs to the staking strategy contract",
2153+
false,
2154+
types.boolean
2155+
)
2156+
.setAction(async (taskArgs) => {
2157+
const signer = await getSigner();
2158+
await autoValidatorWithdrawals({ ...taskArgs, signer });
2159+
});
2160+
task("autoValidatorWithdrawals").setAction(async (_, __, runSuper) => {
2161+
return runSuper();
2162+
});
2163+
21242164
subtask(
21252165
"stakeValidatorUuid",
21262166
"Converts WETH to ETH and deposits to a validator from the Compounding Staking Strategy"

0 commit comments

Comments
 (0)