Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ contract InitializeGovernedUpgradeabilityProxy is Governable {
bytes calldata _data
) public payable onlyGovernor {
require(_implementation() == address(0));
require(_logic != address(0), "Implementation not set");
assert(
IMPLEMENTATION_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
Expand Down
32 changes: 19 additions & 13 deletions contracts/contracts/utils/DepositContractUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,35 @@
pragma solidity ^0.8.0;

contract DepositContractUtils {

function calculateDepositDataRoot(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature
) public pure returns(bytes32 node){
uint deposit_amount = 32 ether / 1 gwei;
) public pure returns (bytes32 node) {
uint256 deposit_amount = 32 ether / 1 gwei;
bytes memory amount = to_little_endian_64(uint64(deposit_amount));

// Compute deposit data root (`DepositData` hash tree root)
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
bytes32 signature_root = sha256(abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
));
node = sha256(abi.encodePacked(
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
));
bytes32 signature_root = sha256(
abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
)
);
node = sha256(
abi.encodePacked(
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
)
);
}

function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
function to_little_endian_64(uint64 value)
internal
pure
returns (bytes memory ret)
{
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
Expand All @@ -36,5 +43,4 @@ contract DepositContractUtils {
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}

}
38 changes: 31 additions & 7 deletions contracts/deploy/mainnet/097_native_ssv_staking.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { deploymentWithGovernanceProposal } = require("../../utils/deploy");
const addresses = require("../../utils/addresses");
const { isFork } = require("../../test/helpers.js");
const { impersonateAndFund } = require("../../utils/signers");

module.exports = deploymentWithGovernanceProposal(
{
Expand Down Expand Up @@ -30,14 +32,10 @@ module.exports = deploymentWithGovernanceProposal(
// Deployer Actions
// ----------------

// 1. Deploy the new strategy proxy
const dStrategyProxy = await deployWithConfirmation(
// 1. Fetch the strategy proxy deployed by relayer
const cStrategyProxy = await ethers.getContract(
"NativeStakingSSVStrategyProxy"
);
const cStrategyProxy = await ethers.getContractAt(
"NativeStakingSSVStrategyProxy",
dStrategyProxy.address
);

// 2. Deploy the new fee accumulator proxy
const dFeeAccumulatorProxy = await deployWithConfirmation(
Expand All @@ -64,9 +62,10 @@ module.exports = deploymentWithGovernanceProposal(
"NativeStakingSSVStrategy",
dStrategyImpl.address
);

const cStrategy = await ethers.getContractAt(
"NativeStakingSSVStrategy",
dStrategyProxy.address
cStrategyProxy.address
);

// 3. Initialize Proxy with new implementation and strategy initialization
Expand All @@ -82,6 +81,31 @@ module.exports = deploymentWithGovernanceProposal(
]
);

if (isFork) {
const relayerSigner = await impersonateAndFund(
addresses.mainnet.validatorRegistrator,
"100"
);
await withConfirmation(
cStrategyProxy
.connect(relayerSigner)
.transferGovernance(deployerAddr, await getTxOpts())
);
} else {
/* Before kicking off the deploy script make sure the Defender relayer transfers the governance
* of the proxy to the deployer account that shall be deploying this script so it will be able
* to initialize the proxy contract
*
* Run the following to make it happen, and comment this error block out:
* yarn run hardhat transferGovernanceNativeStakingProxy --address 0xdeployerAddress --network mainnet
*/
new Error("Transfer governance not yet ran");
}

await withConfirmation(
cStrategyProxy.connect(sDeployer).claimGovernance(await getTxOpts())
);

// 4. Init the proxy to point at the implementation, set the governor, and call initialize
const proxyInitFunction = "initialize(address,address,bytes)";
await withConfirmation(
Expand Down
259 changes: 259 additions & 0 deletions contracts/deployments/mainnet/NativeStakingSSVStrategyProxy.json

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions contracts/tasks/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ const {
checkBalance,
} = require("./strategy");

// can not import from utils/deploy since that imports hardhat globally
const withConfirmation = async (deployOrTransactionPromise) => {
const hre = require("hardhat");

const result = await deployOrTransactionPromise;
const receipt = await hre.ethers.provider.waitForTransaction(
result.receipt ? result.receipt.transactionHash : result.hash,
3
);

result.receipt = receipt;
return result;
};

const log = require("../utils/logger")("tasks");

// Environment tasks.
Expand Down Expand Up @@ -885,6 +899,63 @@ task("depositSSV").setAction(async (_, __, runSuper) => {
return runSuper();
});

/**
* The native staking proxy needs to be deployed via the defender relayer because the SSV networkd
* grants the SSV rewards to the deployer of the contract. And we want the Defender Relayer to be
* the recipient
*/
subtask(
"deployNativeStakingProxy",
"Deploy the native staking proxy via the Defender Relayer"
).setAction(async () => {
const defenderSigner = await getDefenderSigner();

log("Deploy NativeStakingSSVStrategyProxy");
const nativeStakingProxyFactory = await ethers.getContractFactory(
"NativeStakingSSVStrategyProxy"
);
const contract = await nativeStakingProxyFactory
.connect(defenderSigner)
.deploy();
await contract.deployed();
log(`Address of deployed contract is: ${contract.address}`);
});
task("deployNativeStakingProxy").setAction(async (_, __, runSuper) => {
return runSuper();
});

/**
* Governance of the SSV strategy proxy needs to be transferred to the deployer address so that
* the deployer is able to initialize the proxy
*/
subtask(
"transferGovernanceNativeStakingProxy",
"Transfer governance of the proxy from the the Defender Relayer"
)
.addParam("address", "Address of the new governor", undefined, types.string)
.setAction(async (taskArgs) => {
const defenderSigner = await getDefenderSigner();

log("Tranfer governance of NativeStakingSSVStrategyProxy");

const nativeStakingProxyFactory = await ethers.getContract(
"NativeStakingSSVStrategyProxy"
);
await withConfirmation(
nativeStakingProxyFactory
.connect(defenderSigner)
.transferGovernance(taskArgs.address)
);
log(
`Governance of NativeStakingSSVStrategyProxy transferred to ${taskArgs.address}`
);
});
task("transferGovernanceNativeStakingProxy").setAction(
async (_, __, runSuper) => {
return runSuper();
}
);

// Defender
subtask(
"operateValidators",
Expand Down
1 change: 1 addition & 0 deletions contracts/utils/addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ addresses.mainnet.beaconChainDepositContract =
"0x00000000219ab540356cBB839Cbe05303d7705Fa";

// Native Staking Strategy
// Defender relayer
addresses.mainnet.validatorRegistrator =
"0x4b91827516f79d6F6a1F292eD99671663b09169a";

Expand Down
6 changes: 3 additions & 3 deletions contracts/utils/hardhat-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ const adjustTheForkBlockNumber = () => {
if (isForkTest) {
if (isArbForkTest) {
forkBlockNumber = process.env.ARBITRUM_BLOCK_NUMBER
? process.env.ARBITRUM_BLOCK_NUMBER
? Number(process.env.ARBITRUM_BLOCK_NUMBER)
: undefined;
} else if (isHoleskyForkTest) {
forkBlockNumber = process.env.HOLESKY_BLOCK_NUMBER
? process.env.HOLESKY_BLOCK_NUMBER
? Number(process.env.HOLESKY_BLOCK_NUMBER)
: undefined;
} else {
forkBlockNumber = process.env.BLOCK_NUMBER
? process.env.BLOCK_NUMBER
? Number(process.env.BLOCK_NUMBER)
: undefined;
}
}
Expand Down