Since the Shapella upgrade on April 12th, 2023, we’ve been observing a significant uptick in Ethereum staking, with an increasing number of validators entering the space.
To understand the challenges that come with it, let's first consider the mechanics of a staking transaction on Ethereum:
Typically, such a deposit transaction consumes around 60 thousand gas, at a gas price of 40 gwei, that’s 0.0024 ETH in gas per validator, which is approximately $4 at today’s price and a day's worth of staking rewards for a single validator.
As the deposits happen in separate transactions, the cost will grow linearly, depositing for N validators would mean paying ~ N * 0.0024 ETH, which is quite cost-intensive and inefficient.
💡 Did you know? In Ethereum's Proof-of-Stake model, gas fees are primarily used to compensate for the compute time of executing transactions and smart contracts, with a significant portion of these fees being burned to reduce the overall supply of ETH. The amount of gas for a given action is fixed, but the price of a gas unit fluctuates based on demand.
To counter the inefficiency of individual staking transactions, the concept of a batch deposit smart contract was introduced. It can take as input N deposit data and N * 32 ETH as value, and perform multiple deposits to the official deposit transaction in the same transaction, thus reducing the cost of performing multiple validator deposits.
Executing all the deposits in a single transaction benefits from the storage of the official deposit contract being “warm” after the first deposit, which makes for significantly cheaper read and write after the first deposit.
However, there's a catch — you can't make unlimited deposits in one go due to the block gas limit (currently between 15 million and 30 million). The more gas-efficient your batch deposit contract, the more validators you can accommodate in a single transaction.
Some contracts set a maximum deposit limit, while others leave it to the user's discretion.
Kiln’s EVM team investigated multiple ways to optimise the processing of batch deposits in order to consume less gas. We implemented the contract in 4 different languages (Solidity, Vyper, Yul, Huff), sometimes multiple times with different implementations to have the data needed to make a choice.
Overall the Vyper implementation combines the readability of Python with remarkable runtime efficiency.
In this article, we'll delve into the specifics of this implementation.
Vyper emerged as a champion, which is a Python-like programming language designed specifically for the EVM. Projects like Curve, Yearn and even Uniswap V1 have already adopted Vyper for its simplicity and readability. The new batch deposit contract leverages Vyper's syntax and design principles, making it easy for many people to understand.
This was a critical point in our choice. While Yul and Huff offered slightly more efficiency, their complexity and incompatibility with Etherscan verification were significant drawbacks; only a handful of people are able to understand and check the behavior of those contracts.
We want to encourage the users to check the contracts they interact with. Vyper’s clear and concise syntax, resembling Python, can be understood by most.
Vyper is a language with a lot of constraints, explicitly designed to avoid common vulnerabilities and pitfalls associated with Ethereum smart contract development. This inherent safety ensures the contract is secure by design.
Our Vyper implementation doesn’t trade off runtime efficiency for readability. We have optimized calldata encoding and iteration processes to ensure competitive gas efficiency. The meticulous design of the contract code, combined with optimal execution, ensures minimal gas usage without compromising performance. Find a brief overview of the efficiency features below.
Calldata is the data passed to a smart contract when calling a contract on the blockchain. It is stored on the blockchain.
The Ethereum yellow paper sets a cost of 4 gas for every byte set to zero in calldata, and 16 gas for every non-zero byte. That means for the same amount of computation using less calldata will be cheaper.
By concatenating the inputs into bytes instead of using bytes we reduce the amount of empty calldata, thus reducing gas costs. This can be done because all the elements in the calldata arrays are of known sizes. The deposit_data_roots is kept as bytes32 in order to use the in operator, which leads to leaner bytecode and is idiomatic Python.
The contract code has been meticulously designed to execute as efficiently as possible. Using the IR (intermediate representation) and the compiled bytecode, we tuned the implementation to ensure we have the simplest runtime execution by trying different ways of iterating through the deposits and slicing the calldata.
We also noticed a quirk of Vyper; the compiler needs bounds on every list in the code, but the bigger you allow the list to be, the more overhead you will have, even if you only use a small capacity of the list.
To provide the best gas efficiency to small batches, the contract contains two endpoints depending on the number of validators you want to deposit.
To validate the efficiency claims of the new batch deposit contract, extensive benchmarking has been conducted. The benchmarks reveal that this implementation is as efficient as the best existing solutions while offering superior readability.
The benchmarks are all done by running a foundry script on a mainnet fork, depositing the same keys at the same block to get the most accurate comparison.
Vyper is among the simplest implementations, but it is far from the worst performer. Quite the opposite, it performs similarly to the sophisticated solidity versions while being easier to interact with and more flexible.
Compared to doing one deposit per transaction, the gas cost per validator staked with the Vyper contract is lowered by 30% for batches of 3 or more, 40% for batches of more than 5 and over 50% for batches of more than 25 validators.
The new Batch Deposit Contract is also future-proofed. It has been designed with the foresight that Ethereum's validator ecosystem may require larger deposits in the future (EIP-7251), possibly going up to 2,048 ETH per validator. To accommodate this potential change, the contract includes functions for batch depositing with custom values.
These functions allow stakers to deposit variable amounts, ensuring that the contract remains adaptable and responsive to the evolving needs of the network.
The contract is deployed on mainnet and verified on Etherscan at the following address: 0x576834cB068e677db4aFF6ca245c7bde16C3867e
Also deployed to testnets:
The code of the contract was reviewed by Spearbit. You can find the code & review report in the GitHub repository: https://github.com/kilnfi/vyper_batch_deposit
No security issues were found during the review.
Our new implementation of the batch deposit contract for Ethereum validators in Vyper offers a compelling combination of readability, efficiency, and future-proofing. The use of Vyper makes it understandable by as wide an audience as possible, while its optimized calldata encoding and contract code ensure competitive performance. Benchmarks confirm its competitive efficiency compared to existing solutions, making it an attractive choice for Ethereum validators batch deposits.
Many thanks to the Vyper team and contributors for building and maintaining the compiler and ecosystem upon which we build. Go give them a star or sponsor them on Github: https://github.com/vyperlang/vyper
Watch out for the next article about the ultra-high-efficiency but highly cryptic Huff versions!
Kiln is the leading enterprise-grade staking platform, enabling institutional customers to stake assets, and to whitelabel staking functionality into their offering. Kiln runs validators on all major PoS blockchains, with over $2.2 billion crypto assets being programmatically staked, and running over 3% of the Ethereum network on a multi-cloud, multi-region infrastructure. Kiln also provides a validator-agnostic suite of products for fully automated deployment of validators, and reporting and commission management, enabling custodians, wallets, and exchanges to streamline staking operations across providers. Kiln is also SOC2 Type 2 certified.