LogoLogo
  • Introduction
    • Get started
  • Kiln Products
    • Overview
    • 🧱Validators
      • ⛓️Protocols
        • Algorand (ALGO)
        • Aptos (APT)
        • Avalanche (AVAX)
        • Babylon (BABY)
        • Berachain(BERA/BGT)
        • Cardano (ADA)
        • Celestia (TIA)
        • CoreChain (CORE)
        • Cosmos (ATOM)
        • Cronos (CRO)
        • DyDx (DYDX)
        • Ethereum (ETH)
        • Eigenlayer (EIGEN)
        • Fetch.ai (FET)
        • Flare (FLR)
        • Harmony (ONE)
        • Injective (INJ)
        • Iota (IOTA)
        • Kava (KAVA)
        • Kusama (KSM)
        • Mantra (OM)
        • MultiversX (EGLD)
        • Near (NEAR)
        • Oasis (ROSE)
        • Osmosis (OSMO)
        • Peaq (PEAQ)
        • Polkadot (DOT)
        • Polygon (POL - ex MATIC)
        • Sei (SEI)
        • Solana (SOL)
        • Stacks (STX)
        • Starknet (STRK)
        • Story(IP)
        • Sui (SUI)
        • Symbiotic (SYM)
        • Tezos (XTZ)
        • The Open Network (TON)
        • Tron (TRX)
        • Zetachain (ZETA)
    • 🎆Connect
      • Accessing Kiln Connect
      • API specs
      • Postman Demo
      • SDK
        • Getting started
        • How to find Kiln's validators
        • Upgrade guide from v2 to v3
      • Tutorials
        • SOL - Tag a stake
        • DYDX - How to bridge your rewards
      • Kiln Connect FAQs
    • 🚀Dashboard
      • Architecture
      • Staking
        • Babylon (BABY)
        • Cardano (ADA)
        • Celestia (TIA)
        • Cosmos (ATOM)
        • Cronos (CRO)
        • DYDX (DYDX)
        • Ethereum (ETH)
          • Native EigenLayer Restaking
          • Deposit into compounding validators
          • Convert skimming validators to compounding validators
        • Fetch.ai (FET)
        • Injective (INJ)
        • Kava (KAVA)
        • Kusama (KSM)
        • Mantra (OM)
        • Near (NEAR)
        • Osmosis (OSMO)
        • Polkadot (DOT)
        • Polygon (POL)
        • Solana (SOL)
        • Tezos (XTZ)
        • The Open Network (TON)
        • TRON (TRX)
        • ZetaChain (ZETA)
      • Reporting
        • Cardano (ADA)
        • Celestia (TIA)
        • Cosmos (ATOM)
        • Cronos (CRO)
        • DYDX (DYDX)
        • Ethereum (ETH)
        • Fetch.ai (FET)
        • Injective (INJ)
        • Kava (KAVA)
        • Kusama (KSM)
        • Mantra (OM)
        • Near (NEAR)
        • Osmosis (OSMO)
        • Polkadot (DOT)
        • Polygon (POL)
        • Solana (SOL)
        • Tezos (XTZ)
        • The Open Network (TON)
        • Tron (TRX)
        • ZetaChain (ZETA)
      • Unstaking
        • Cardano (ADA)
        • Celestia (TIA)
        • Cosmos (ATOM)
        • Cronos (CRO)
        • DYDX (DYDX)
        • Ethereum (ETH)
          • Native EigenLayer Withdraw Flow
          • Partial withdraw liquidity
        • Fetch.ai (FET)
        • Injective (INJ)
        • Kava (KAVA)
        • Kusama (KSM)
        • Mantra (OM)
        • Near (NEAR)
        • Osmosis (OSMO)
        • Polkadot (DOT)
        • Polygon (POL)
        • Solana (SOL)
          • Deactivate
          • Merge
          • Split
          • Withdraw
        • Tezos (XTZ)
        • Tron (TRX)
        • The Open Network (TON)
        • ZetaChain (ZETA)
      • Import stakes
      • Move stakes
      • Invite a user to your organization
      • Troubleshooting
      • 🎓Tutorials
        • Cosmos Chains - Auto-compound a stake
      • Dashboard FAQs
    • 📥dApp
      • ETH staking
      • EigenLayer
        • Operator delegation
        • Native restaking
        • Liquid restaking
      • 🔒Security
      • Kiln dApp FAQ
    • ⛓️Onchain
      • 🙋‍♀️Dedicated Validators
        • Key concepts
          • Deposit Flow
          • Rewards Management
          • Unstaking
        • How to integrate
          • 1. Contract Interactions
          • 2. Administration
      • 🤽‍♂️Pooled Staking
        • Key concepts
          • Staking Positions
          • Oracle Reports
          • Exit & Withdrawal
        • How to integrate
          • 🐎Getting Started
          • 🏗️Architecture overview
          • 😊User experience
          • 📃Integration contract
          • ⛑️Contract governance
          • ℹ️Contract ABI
          • 📥Using TheGraph
          • 🥩Staking Interactions
            • Staking
            • Rewards
            • Unstaking & Withdrawals
      • 🎡Tokenized Validators
      • 🎩Node Operators: how to integrate
        • Dedicated Validators (v1)
          • Manage Validators
        • Pooled Staking
          • 1. Using TheGraph for Setup
          • 2. Deploying vPool
          • 3. Key Provisioning in vFactory
          • 4. Oracle Daemon
          • 5. Depositing via Integration Contracts
          • 6. Exit Daemon
          • 7. Deploy Integration Contract (optional)
      • 🔓Security
        • Staking Risks
        • Audits & Bug Bounties
        • Business Continuity
        • Source code
    • 💰DeFi
      • 🦋Morpho via Kiln DeFi
      • 💻How to integrate?
        • Smart contract interactions
        • Governance
        • Reporting data
        • Administration
        • Supported DeFi Protocols
        • Additional Rewards Management
      • 🔓Security
        • Risks
        • Audits & Bug Bounty
        • Pause
        • Source code
        • Compliance Features
      • Kiln DeFi FAQ
    • 🪄Widget
      • 🧑‍🎨Customize - Widget Editor
      • ☄️Integrate - No/Low Code
    • 🛠️More Tools
      • ETH Batch Deposit Contract
      • Safe CD
  • Integrations
    • 🔭Plan your integration
    • 🔌Integrate Kiln products
    • 📱Wallets
      • Stake from Ledger Live
        • Direct Staking
        • Using Kiln Widget
          • DeFi Lending in Ledger Live
        • Using Kiln dApp
          • Dedicated Validators
          • Pooled Staking
          • EigenLayer Restaking
      • Stake from Safe{Wallet}
        • How to stake ETH on Safe & FAQ
        • DeFi Lending in Safe{Wallet}
        • Using Kiln dApp
      • Stake from Squads
    • 🏢Custody platforms
      • Stake from Anchorage
      • Stake from Bitgo
      • Stake from Coinbase Custody/Prime
      • Stake from Copper
      • Stake from Cordial Systems
      • Stake from DFNS
      • Stake from Finoa
      • Stake from Fireblocks
      • Stake from Fordefi
      • Stake from Hex Safe
      • Stake from Ledger Enterprise
      • Stake from Metamask Institutional
      • Stake from Rakkar
      • Stake from Utila
  • Working with Kiln
    • ☑️Staking & DeFi Track Record
    • ✅Security & Insurances
    • ☎️Support
      • FAQs
        • Enzyme FAQ
        • EigenLayer FAQ
        • Kiln x Safe{Wallet} FAQ
        • Ledger Live FAQ
Powered by GitBook
On this page
  • V2
  • Contract instances
  • Batch Deposit
  • ABI:
  • Source Files
  • Security
  • Performance
  • V1 [DEPRECATED]
  • Contract instances
  • Batch Deposit
  • Source Files
  • Performance
  • Security
  • FAQs

Was this helpful?

Export as PDF
  1. Kiln Products
  2. More Tools

ETH Batch Deposit Contract

The Kiln Ethereum Batch Deposit Contract is a minimal implementation of deposit transaction batching. The goal is to deposit N validations keys in one transaction with N * 32 ETH as value.

PreviousMore ToolsNextSafe CD

Last updated 7 months ago

Was this helpful?

V2

Contract instances

The Batch Deposit Contract is available and verified at:

  • mainnet:

  • testnet (Holesky):

Batch Deposit

You can interact with the batchDeposit function of the contract to deposit multiple validator keys deposit data, with the multiple of 32 ETH as transaction value.

We recommend you use it to send N deposit data with N * 32 ETH where N < 512.

  • if <= 64 validators:

    call the batchDeposit(bytes publicKeys, bytes withdrawalCreds, bytes signatures, bytes32[] dataRoots) function

  • if > 64 validators, up to 512 validators

    call the bigBatchDeposit(bytes publicKeys, bytes withdrawalCreds, bytes signatures, bytes32[] dataRoots) function

publicKeys is the concatenation of the public keys of the validators

withdrawalCreds is the concatenation of the withdrawal credentials of the validators

signatures is the concatenation of the signatures of the validator deposits

dataRoots is a list of the data roots for the validator deposits

ABI:

[{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"depositAdd","type":"address"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"batchDeposit","inputs":[{"name":"publicKeys","type":"bytes"},{"name":"withdrawalCreds","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"dataRoots","type":"bytes32[]"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"batchDepositCustom","inputs":[{"name":"publicKeys","type":"bytes"},{"name":"withdrawalCreds","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"dataRoots","type":"bytes32[]"},{"name":"amountPerValidator","type":"uint256"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"bigBatchDeposit","inputs":[{"name":"publicKeys","type":"bytes"},{"name":"withdrawalCreds","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"dataRoots","type":"bytes32[]"}],"outputs":[]},{"stateMutability":"payable","type":"function","name":"bigBatchDepositCustom","inputs":[{"name":"publicKeys","type":"bytes"},{"name":"withdrawalCreds","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"dataRoots","type":"bytes32[]"},{"name":"amountPerValidator","type":"uint256"}],"outputs":[]}]

Source Files

Security

Performance

Here is a model of gas costs evolution based on a mainnet fork:

Batch Deposit 5
   Gas: 186 730 (37 346 / validator)
Batch Deposit 10
   Gas: 323 494 (32 349 / validator)
Batch Deposit 20
   Gas: 619 505 (30 975 / validator)
Batch Deposit 30
   Gas: 888 375 (29 612 / validator)
Batch Deposit 50
   Gas: 1 462 756 (29 255 / validator)
Batch Deposit 100
   Gas: 2 892 896 (28 929 / validator)
Batch Deposit 200
   Gas: 5 710 927 (28 554 / validator)
Batch Deposit 300
   Gas: 8 523 984 (28 413 / validator)
Batch Deposit 400
   Gas: 11 340 350 (28 350 / validator)

The V2 is capped at 512 validators per transaction

V1 [DEPRECATED]

Contract instances

The Batch Deposit Contract is available at:

    • NOTE: Goerli will no longer be supported by Kiln from November 9th 2023.

Batch Deposit

You can interact with the batchDeposit function of the contract to deposit multiple validator keys deposit data, with the multiple of 32 ETH as transaction value.

We recommend you use it to send N deposit data with N * 32 ETH where N < 150.

function batchDeposit(
        bytes[] calldata pubkeys,
        bytes[] calldata withdrawal_credentials,
        bytes[] calldata signatures,
        bytes32[] calldata deposit_data_roots
    ) external payable

ABI:

{
    "inputs": [
      {
        "internalType": "bytes[]",
        "name": "pubkeys",
        "type": "bytes[]"
      },
      {
        "internalType": "bytes[]",
        "name": "withdrawal_credentials",
        "type": "bytes[]"
      },
      {
        "internalType": "bytes[]",
        "name": "signatures",
        "type": "bytes[]"
      },
      {
        "internalType": "bytes32[]",
        "name": "deposit_data_roots",
        "type": "bytes32[]"
      }
    ],
    "name": "batchDeposit",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function",
    "payable": true
  }

Source Files

The smart contract is relatively straightforward and does not maintain a state of the deposited keys. It simply exposes a function batchDeposit(bytes[] calldata pubkeys,bytes[] calldata withdrawal_credentials, bytes[] calldata signatures, bytes32[] calldata deposit_data_roots) external payable one can call with N deposit data and N *32 ETH as tx value.

The Contract sources are available here:

Performance

Here is a model of gas costs evolution based on a mainnet fork:

Batch Deposit 7
   Gas: 221233
Batch Deposit 8
   Gas: 251485
Batch Deposit 9
   Gas: 279802
Batch Deposit 10
   Gas: 312046
Batch Deposit 100
   Gas: 3049083
Batch Deposit 200
   Gas: 6103163
Batch Deposit 300
   Gas: 9233661
Batch Deposit 400
   Gas: 12419018
Batch Deposit 500
   Gas: 15697211

We recommend to not batch deposit more than 150 keys to avoid transaction with too much gas and low chance to get included by the network.

Security

FAQs

What is the interaction between the Kiln "batch deposit" contract and the ETH staking contract?

What it then does is perform n deposits on the official deposit contract with 32 ETH each atomically within the same transaction, thus saving a significant amount of gas compared to the standard approach.

Please note:

  1. The batch deposit contract never holds any funds

  2. if a number different than a multiple of 32 ETH is sent or there are not enough parameters to cover it'll return the ETH back to the user

Is there a "transfer" of funds between the two contracts?

Every time a batch is sent to the deposit contract this contract does internal transactions to send these funds to the Beacon chain contract. All this is done in the same transaction, atomically, and in the same block so it never holds any funds. See the previous answer for more details.

Do the staking rewards ever "pass through" the Kiln batch deposit contract?

Never, it's just for depositing the 32 ETH + the parameters in one call to the official contract. Once this is done the rewards go directly to the withdrawal credentials of the validator (your end-user wallet).

Are there any "private" functions of the batch deposit contract that only Kiln can alter or owns?

No, there are no private functions. The contract is not upgradeable so it cannot be altered. The only callable functions are public batch deposit functions. The full source code of both the legacy version and the new one are public and can be found above.

What are the areas that are vulnerable to smart contract's proper operations?

As with any software deployed, the key risks for smart contracts are operational and technical:

  • Network attacks / security risks

Network-wide issues, such as attacks, bugs can affect the staking process. Similarly, platforms can be hacked.

  • Faults in code

Smart contract systems may be deployed with errors or faults in the code that mean the program doesn’t function in the way intended.

These risks can be mitigated by strong build checks at the outset, testnet and mainnet phasing, auditing and monitoring of logic and code execution. Kiln’s batch deposit contracts have been audited and battle-tested in production for years.

What are the relevant risk mitigations?

Kiln performs:

  • Strong build checks before deployment of code

  • Operates testnet and mainnet phasing

  • Regular code audits, and at least one organizational audit a year as part of SOC2 certifications.

  • Penetration tests

  • Bug bounty programs

If anything happens to Kiln, will client asset be affected?

No, Kiln has both a disaster recovery and business continuity policy in place. It provides the customer with pre-signed validator exit messages meaning the customer can unilaterally unstake its Ethereum validators at any moment (without Kiln’s involvement).

Source code is available .

Spearbit audited the Batch Deposit Contract in Sep 2023, report available . No security issue was found during the audit.

testnet (Holesky):

testnet (Goerli):

mainnet:

Batch Deposit Contract is based on Stake Fish batch deposit contract which . Some of Kiln partners have also internally audited the Batch Deposit Contract.

The requires sending 32 ETH + the parameters to stake: pubkeys, withdrawal credentials, signatures and deposit_data_roots. This official contract requires 1 transaction per deposit, so to stake n validators requires doing n transactions. The Kiln batch deposit contract receives n*32 ETH and an array of length n of the staking parameters for deposits (the same parameters as the official contrat: pubkeys, withdrawal credentials, signatures and deposit_data_roots).

For more details on Kiln’s security measure, please visit

🛠️
0x576834cB068e677db4aFF6ca245c7bde16C3867e
0x0866af1D55bb1e9c2f63b1977926276F8d51b806
here
here
0x008f55a61d3b96a0bdab16f3b68f6c40e3ed6fda
0x5FaDfdb7eFffd3B4AA03f0F29d9200Cf5F191F31
0x9b8c989FF27e948F55B53Bb19B3cC1947852E394
has been audited
official Beacon chain deposit contract
https://security.kiln.fi
https://github.com/kilnfi/deposit-batch-contract/blob/main/contracts/BatchDeposit.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";

import "./IDeposit.sol";

// Based on Stakefish and Staked.us contracts.
//
// We don't need fees, but we want truffle & unit tests.
contract BatchDeposit {
    using Address for address payable;
    using SafeMath for uint256;

    uint256 public constant kDepositAmount = 32 ether;
    IDeposit private depositContract_;

    event LogDepositLeftover(address to, uint256 amount);
    event LogDepositSent(bytes pubkey, bytes withdrawal);

    // We pass the address of a contract here because this will change
    // from one environment to another. On mainnet and testnet we use
    // the official addresses:
    //
    // - testnet: 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b (https://goerli.etherscan.io/address/0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b)
    // - mainnet: 0x00000000219ab540356cbb839cbe05303d7705fa (https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa)
    //
    // The migration script handles this.
    constructor (address deposit_contract_address) {
	depositContract_ = IDeposit(deposit_contract_address);
    }

    function batchDeposit(
        bytes[] calldata pubkeys,
        bytes[] calldata withdrawal_credentials,
        bytes[] calldata signatures,
        bytes32[] calldata deposit_data_roots
    ) external payable {
        require(
            pubkeys.length == withdrawal_credentials.length &&
            pubkeys.length == signatures.length &&
            pubkeys.length == deposit_data_roots.length,
            "#BatchDeposit batchDeposit(): All parameter array's must have the same length."
        );
        require(
            pubkeys.length > 0,
            "#BatchDeposit batchDeposit(): All parameter array's must have a length greater than zero."
        );
        require(
            msg.value >= kDepositAmount.mul(pubkeys.length),
            "#BatchDeposit batchDeposit(): Ether deposited needs to be at least: 32 * (parameter `pubkeys[]` length)."
        );

        uint256 deposited = 0;

        for (uint256 i = 0; i < pubkeys.length; i++) {
            depositContract_.deposit{value: kDepositAmount}(
                pubkeys[i],
                withdrawal_credentials[i],
                signatures[i],
                deposit_data_roots[i]
            );
	    emit LogDepositSent(pubkeys[i], withdrawal_credentials[i]);
            deposited = deposited.add(kDepositAmount);
        }

        assert(deposited == kDepositAmount.mul(pubkeys.length));

        uint256 ethToReturn = msg.value.sub(deposited);
        if (ethToReturn > 0) {
          emit LogDepositLeftover(msg.sender, ethToReturn);
	  Address.sendValue(payable(msg.sender), ethToReturn);
        }
    }
}