Skip to content

Instantly share code, notes, and snippets.

@androolloyd
Last active July 5, 2023 17:38
Show Gist options
  • Save androolloyd/555534b6d29759c0963c1dea1a5097db to your computer and use it in GitHub Desktop.
Save androolloyd/555534b6d29759c0963c1dea1a5097db to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.6.8;
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// Deposit contract interface
interface IDepositContract {
/// @notice A processed deposit event.
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
/// @notice Submit a Phase 0 DepositData object.
/// @param pubkey A BLS12-381 public key.
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
/// @param signature A BLS12-381 signature.
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
/// Used as a protection against malformed input.
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
/// @notice Query the current deposit count.
/// @return The deposit count encoded as a little endian 64-bit number.
function get_deposit_count() external view returns (bytes memory);
}
contract BatchDeposit {
using SafeMath for uint256;
address constant depositContract = address(0x00000000219ab540356cbb839cbe05303d7705fa);
uint32 constant PUBKEY_LENGTH = 48;
uint32 constant SIGNATURE_LENGTH = 96;
uint32 constant CREDENTIALS_LENGTH = 32;
uint8 constant MAX_VALIDATORS = 100;
uint constant DEPOSIT_AMOUNT = 32 ether;
/**
* @dev Performs a batch deposit
*/
function batchDeposit(
bytes calldata pubkeys,
bytes calldata withdrawal_credentials,
bytes calldata signatures,
bytes32[] calldata deposit_data_roots
)
external payable
{
// sanity checks
require(msg.value >= DEPOSIT_AMOUNT, "BatchDeposit: Amount is too low");
require(pubkeys.length >= PUBKEY_LENGTH, "BatchDeposit: You should deposit at least one validator");
require(pubkeys.length.mod(PUBKEY_LENGTH) == 0, "BatchDeposit: Invalid pubkey length");
require(pubkeys.length <= PUBKEY_LENGTH * MAX_VALIDATORS, "BatchDeposit: You can deposit max 100 validators at a time");
require(signatures.length >= SIGNATURE_LENGTH, "DepositContract: Invalid signature length");
require(signatures.length.mod(SIGNATURE_LENGTH) == 0, "BatchDeposit: Invalid signature length");
require(withdrawal_credentials.length == CREDENTIALS_LENGTH, "BatchDeposit: Invalid withdrawal_credentials length");
uint32 pubkeyCount = uint32(pubkeys.length.div(PUBKEY_LENGTH));
require(
pubkeyCount == signatures.length.div(SIGNATURE_LENGTH) && pubkeyCount == deposit_data_roots.length,
"BatchDeposit: Data counts don't match"
);
uint256 expectedAmount = DEPOSIT_AMOUNT.mul(pubkeyCount);
require(msg.value == expectedAmount, "BatchDeposit: Amount is not aligned with pubkeys number");
for (uint32 i = 0; i < pubkeyCount; ++i) {
bytes memory pubkey = bytes(pubkeys[i*PUBKEY_LENGTH:(i+1)*PUBKEY_LENGTH]);
bytes memory signature = bytes(signatures[i*SIGNATURE_LENGTH:(i+1)*SIGNATURE_LENGTH]);
IDepositContract(depositContract).deposit{value: DEPOSIT_AMOUNT}(
pubkey,
withdrawal_credentials,
signature,
deposit_data_roots[i]
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment