Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/* ───────────── Official Deposit interface ───────────── */
interface IDepositContract {
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);
}
/* ───────────── Minimal upgradeable bases ───────────── */
abstract contract Initializable {
bool private _initialized;
bool private _initializing;
modifier initializer() {
require(!_initialized || _initializing, "already initialized");
bool top = !_initializing;
if (top) {_initializing = true; _initialized = true;}
_;
if (top) {_initializing = false;}
}
uint256[50] private __gapInitializable;
}
abstract contract OwnableUpgradeable is Initializable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function __Ownable_init(address initialOwner) internal initializer {
require(initialOwner != address(0), "zero owner");
_owner = initialOwner;
emit OwnershipTransferred(address(0), initialOwner);
}
function owner() public view returns (address) { return _owner; }
modifier onlyOwner() { require(_owner == msg.sender, "not owner"); _; }
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "zero owner");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
function renounceOwnership() public view onlyOwner { revert("renounce disabled"); }
uint256[49] private __gapOwnable;
}
abstract contract PausableUpgradeable is Initializable {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
function __Pausable_init() internal initializer { _paused = false; }
function paused() public view returns (bool) { return _paused; }
modifier whenNotPaused() { require(!_paused, "paused"); _; }
modifier whenPaused() { require(_paused, "not paused"); _; }
function _pause() internal whenNotPaused { _paused = true; emit Paused(msg.sender); }
function _unpause() internal whenPaused { _paused = false; emit Unpaused(msg.sender); }
uint256[49] private __gapPausable;
}
/* ───────────── BatchDeposit (proxy-friendly, full features) ───────────── */
contract BatchDeposit is Initializable, OwnableUpgradeable, PausableUpgradeable {
/* constants */
uint256 public constant PUBKEY_LENGTH = 48;
uint256 public constant SIGNATURE_LENGTH = 96;
uint256 public constant CREDENTIALS_LENGTH = 32;
uint256 public constant DEPOSIT_AMOUNT = 32 ether;
uint256 public constant MAX_VALIDATORS_SOFT = 100; // 与旧版一致
/* storage */
address public depositContract;
/* events */
event Withdrawn(address indexed payee, uint256 weiAmount);
event DepositContractUpdated(address indexed oldAddr, address indexed newAddr);
/* init (for proxy) */
function initialize(address depositContractAddr, address initialOwner) external initializer {
require(depositContractAddr != address(0), "bad deposit addr");
depositContract = depositContractAddr;
__Ownable_init(initialOwner);
__Pausable_init();
}
/* admin */
function pause() external onlyOwner { _pause(); }
function unpause() external onlyOwner { _unpause(); }
/// The underlying official contract address can only be updated when paused, to prevent misoperations
function setDepositContract(address newAddr) external onlyOwner whenPaused {
require(newAddr != address(0), "zero addr");
emit DepositContractUpdated(depositContract, newAddr);
depositContract = newAddr;
}
function withdraw(address payable to) external onlyOwner {
require(to != address(0), "zero to");
uint256 amount = address(this).balance;
emit Withdrawn(to, amount);
(bool ok, ) = to.call{value: amount}("");
require(ok, "withdraw failed");
}
function batchDeposit(
bytes calldata pubkeys, // count * 48
bytes calldata withdrawal_credentials, // 32
bytes calldata signatures, // count * 96
bytes32[] calldata deposit_data_roots // count
) external payable whenNotPaused {
uint256 count = deposit_data_roots.length;
require(count > 0 && count <= MAX_VALIDATORS_SOFT, "bad count");
require(pubkeys.length == count * PUBKEY_LENGTH, "bad pubkeys len");
require(signatures.length == count * SIGNATURE_LENGTH, "bad sigs len");
require(withdrawal_credentials.length == CREDENTIALS_LENGTH, "bad cred len");
uint256 expected = DEPOSIT_AMOUNT * count;
require(msg.value == expected, "bad value");
require(msg.value % 1_000_000_000 == 0, "not gwei aligned");
address depositAddr = depositContract;
for (uint256 i = 0; i < count; ) {
bytes memory pk = _slice(pubkeys, i * PUBKEY_LENGTH, PUBKEY_LENGTH);
bytes memory sig = _slice(signatures, i * SIGNATURE_LENGTH, SIGNATURE_LENGTH);
IDepositContract(depositAddr).deposit{value: DEPOSIT_AMOUNT}(
pk,
withdrawal_credentials,
sig,
deposit_data_roots[i]
);
unchecked { ++i; }
}
}
/// Optional: custom amount per validator (for future/L2)
function batchDepositCustom(
bytes calldata pubkeys, // count * 48
bytes calldata withdrawal_credentials, // 32
bytes calldata signatures, // count * 96
bytes32[] calldata deposit_data_roots, // count
uint256 amountPerValidator
) external payable whenNotPaused {
uint256 count = deposit_data_roots.length;
require(count > 0 && count <= MAX_VALIDATORS_SOFT, "bad count");
require(pubkeys.length == count * PUBKEY_LENGTH, "bad pubkeys len");
require(signatures.length == count * SIGNATURE_LENGTH, "bad sigs len");
require(withdrawal_credentials.length == CREDENTIALS_LENGTH, "bad cred len");
require(amountPerValidator > 0, "bad per-amount");
require(msg.value == amountPerValidator * count, "bad value");
require(msg.value % 1_000_000_000 == 0, "not gwei aligned");
address depositAddr = depositContract;
for (uint256 i = 0; i < count; ) {
bytes memory pk = _slice(pubkeys, i * PUBKEY_LENGTH, PUBKEY_LENGTH);
bytes memory sig = _slice(signatures, i * SIGNATURE_LENGTH, SIGNATURE_LENGTH);
IDepositContract(depositAddr).deposit{value: amountPerValidator}(
pk,
withdrawal_credentials,
sig,
deposit_data_roots[i]
);
unchecked { ++i; }
}
}
/* utils: calldata -> memory slice */
function _slice(bytes calldata data, uint256 start, uint256 len) private pure returns (bytes memory out) {
out = new bytes(len);
assembly {
let outPtr := add(out, 0x20)
calldatacopy(outPtr, add(data.offset, start), len)
}
}
/* storage gap (for future upgrades) */
uint256[48] private __gap;
}
Submitted on: 2025-10-21 12:18:19
Comments
Log in to comment.
No comments yet.