文件 1 的 2:eth2-batch-deposit.sol
pragma solidity 0.8.9;
import {IDepositContract} from "./eth2-official-deposit-contract.sol";
interface IBatchDeposit {
function batchDeposit(uint validUntil, bytes calldata args) external payable;
}
contract BatchDeposit is IBatchDeposit {
IDepositContract public immutable depositContract;
uint constant private pubkeyLength = 48;
uint constant private withdrawalCredentialsLength = 32;
uint constant private signatureLength = 96;
uint constant private depositDataRootLength = 32;
uint constant private depositArgsLength =
pubkeyLength +
withdrawalCredentialsLength +
signatureLength +
depositDataRootLength;
constructor(IDepositContract _depositContract) {
require(address(_depositContract) != address(0),
"Please specify the correct deposit contract");
depositContract = _depositContract;
}
function batchDeposit(uint validUntil, bytes calldata args) external payable {
require(
block.timestamp < validUntil,
"Transaction submitted after agreed upon deadline");
require(
args.length % depositArgsLength == 0,
"Input data length must be multiple of depositArgsLength"
);
uint count = args.length / depositArgsLength;
require(msg.value % 32 ether == 0, "msg.value must be multiple of 32 ETH");
require(msg.value / 32 ether == count, "msg.value must be 32 ETH * count");
uint withdrawalCredentialsStart;
uint signatureStart;
uint depositDataRootStart;
for (uint pubkeyStart = 0; pubkeyStart < args.length; ) {
unchecked
{
withdrawalCredentialsStart = pubkeyStart + pubkeyLength;
signatureStart = withdrawalCredentialsStart + withdrawalCredentialsLength;
depositDataRootStart = signatureStart + signatureLength;
}
uint depositDataRootEnd;
unchecked { depositDataRootEnd = depositDataRootStart + depositDataRootLength; }
bytes32 depositDataRoot = bytes32(args[depositDataRootStart : depositDataRootEnd]);
depositContract.deposit{value: 32 ether}(
args[pubkeyStart : withdrawalCredentialsStart],
args[withdrawalCredentialsStart : signatureStart],
args[signatureStart : depositDataRootStart],
depositDataRoot
);
pubkeyStart = depositDataRootEnd;
}
}
}
文件 2 的 2:eth2-official-deposit-contract.sol
pragma solidity 0.8.9;
interface IDepositContract {
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
function get_deposit_root() external view returns (bytes32);
function get_deposit_count() external view returns (bytes memory);
}
interface ERC165 {
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
}
contract DepositContract is IDepositContract, ERC165 {
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
uint256 deposit_count;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
constructor() {
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
}
function get_deposit_root() override external view returns (bytes32) {
bytes32 node;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1)
node = sha256(abi.encodePacked(branch[height], node));
else
node = sha256(abi.encodePacked(node, zero_hashes[height]));
size /= 2;
}
return sha256(abi.encodePacked(
node,
to_little_endian_64(uint64(deposit_count)),
bytes24(0)
));
}
function get_deposit_count() override external view returns (bytes memory) {
return to_little_endian_64(uint64(deposit_count));
}
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) override external payable {
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
require(signature.length == 96, "DepositContract: invalid signature length");
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
uint deposit_amount = msg.value / 1 gwei;
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
emit DepositEvent(
pubkey,
withdrawal_credentials,
amount,
signature,
to_little_endian_64(uint64(deposit_count))
);
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)))
));
bytes32 node = sha256(abi.encodePacked(
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
));
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
deposit_count += 1;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1) {
branch[height] = node;
return;
}
node = sha256(abi.encodePacked(branch[height], node));
size /= 2;
}
assert(false);
}
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
}
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}
}
{
"compilationTarget": {
"contracts/eth2-batch-deposit.sol": "BatchDeposit"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200000
},
"remappings": []
}