Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The Operator Pool requires offchain information to update pooling metrics like Operator Pool value and exchange rate, to take into account rewards generated by the pool validators.
This data is called an oracle report and is submitted by whitelisted Oracles to the Operator Pool smart contract, once every 24 hours. If all the Oracle quorum agrees on the same report, this one is processed by the Operator pool.
This means that user position values, and exit requests are processed on a 24 hours basis if oracles generate valid reports.
An oracle report consists of data like total ETH staked, total ETH currently in the protocol Exit Queue, total validators staked, total validators exited and strategy inputs to tell the pool to either accelerate user exits time or increase new validator deposits.
Using this data, Operator pool will perform multiple state transitions to:
deposit new validators
answer the exit demand in the exit queue
compute relevant staking fees
update the Operator pool exchange rate to take into account new rewards
exit validators if needed to match the user exit demand in the pool exit queue
You can find the report data and all Operator Pool metrics on our dune dashboard.
User can request to exit their staked position anytime.
It’s important to note that all staked positions are 100% collateralized, with ETH primarily held in active validators. Any remaining ETH may be in the operator pool, awaiting deposit into new validators.
Exiting a validator to access the underlying 32 ETH is not instantaneous; it requires the validator to go through the protocol's exit process and enter an exit queue. Consequently, a user exiting their staked position involves a two-step process:
Requesting exit: Users first signal their intent to exit by interacting with the integration contract, which locks their receipt tokens and places them in the exit queue of the operator pool contract. Each pool has its own exit queue, and an integration contract can be linked to multiple operator pools.
Receiving an exit ticket: A ticket, representing the user's position in the exit queue, is minted as an NFT. This ticket reflects the user's place in the queue until their ETH are available to withdraw.
Under normal conditions, with the pool generating rewards and receiving new deposits, it generally takes about 1-4 days for exited ETH to become available for withdrawal. However, in cases where exit demand significantly exceeds the available ETH, the process may be delayed. This delay is influenced by the Ethereum validator exit and withdrawal queue, which typically takes several days but can extend significantly longer in extreme cases.
When the pooling contract receives ETH to fulfill exit requests, it creates a 'cask' that corresponds to the received ETH and the current exit queue demand.
The exit queue functions as a system where users receive a ticket to retrieve their ETH. This ETH is periodically allocated to the queue in the form of casks. The queue operates on a first-come, first-served basis, without discrimination or favoritism towards any user or scenario.
When casks arrive, users must still claim their ETH. This is due to Solidity's limitations on the number of actions that can be performed in a single transaction. Consequently, a cask may fulfill one or multiple tickets at once. The smart contract cannot iterate through all tickets in a single transaction due to the potentially high and variable number of tickets.
Tickets can have several statuses:
unfulfillable
: the ticket is waiting for a cask to arrive and fulfill its exit request.
partially fulfillable
: the ticket has one or several casks partially fulfilling its exit request, this means that the ticket can be claimed, but this action will result in getting an updated ticket with the remaining amount yet to be claimed.
fulfillable
: the ticket has one or several casks completely fulfilling its exit request. Claiming this ticket will retrieve the funds and burn the ticket.
In the example above, we can see that:
Tickets #0
and #1
are currently fulfillable
by cask #0
.
Ticket #2
is currently partially fulfillable
by cask #1
Ticket #3
is unfulfillable
.
Keep in mind that both of these queues can grow independently, and the goal of the pooling contract is to provide casks to make the cask queue grow and reach the same height as the ticket queue, making all ticket fulfillable
When a user stakes, their position proportionally increases in value as staking rewards are generated by the operator pool validators.
Users deploy ETH through the partner's integration smart contract, which then forwards the ETH to one or multiple operator pools, depending on the configuration. This approach offers flexibility in structuring staking offerings, such as:
A single node operator with a 15% end-user fee.
Three operators with a 10/40/50% allocation and a total 17% end-user fee.
During the staking transaction, the user receives a receipt token representing their staked position within the integration contract. The integration contract, in turn, holds a proportional stake in the Node Operator Pool, based on the amount of ETH deployed through it.
The user's receipt tokens can be either soulbound or transferable (e.g., cToken or aToken), depending on the partner's choice during contract setup. The value of staked positions is determined by the underlying value of the operator pool, with an internal exchange rate that updates daily to reflect validator rewards. This exchange rate is purely technical and used exclusively between the smart contracts.
Staking fees are automatically calculated by the smart contracts based on the rewards generated, ensuring a seamless experience for the end user, who only deals with net values.
Stake any amount of ETH to one or multiple node operators through a fully onchain solution.
When you stake natively on Ethereum there is no pooling mechanisms, you simply bound 32 ETH to a validation key and pilote the stake with the validation private key. This process is complex and very expensive for most users, 99.93% of Ethereum wallets having less than 32 ETH on their balance.
Kiln Onchain Pooled Staking enables partners and Operators to interconnect onchain very easily in order to propose custom ETH Pooled Staking to the partner's end users.
Easy to use - users deposit any amount of ETH and start earning rewards immediately
Transparency first - all fund of flows, from deposit, to rewards, unstaking or commission dispatching is full visible and auditable onchain
Don't trust, verify - anyone can verify the provisioned validators to be staked, pooling economics and flow of funds onchain
Integrate in a few hours - only a few contract interactions are required for the staking and unstaking flows, making it very convenient to integrate on your platform
Create a custom Liquid Staking Token or not - partners decide wether they want their users to receive a transferable ERC20 (cToken or aToken) to represent their staked pooled position
Use one or multiple operator - partner can choose to expose their users to one or multiple underlying node operator, in different proportions and can change this at anytime
A 2 transaction setup for partners - bring custom pooled staking to your platform in only a few hours
Earn more with auto-compounding - by default Kiln Onchain Pooled Staking auto-compounds rewards to make sure the pool deposit as much validators and thus earn as much rewards as possible
Integrating Kiln Onchain gives partners total control over the staking experience. This includes designing the user experience, integration with native workflows, end-user fees, and the selection of node operators.
This comprehensive guide equips you with all the essentials to initiate the integration process.
This page provides a high-level understanding and overview of the architecture of the smart contracts as it relates to pooled staking.
Before getting started, here are a few terms we commonly use in the architecture overview.
partner: a platform (e.g., wallet, web platform) offering staking services to their users by integrating with the Onchain platform.
operator: a node operator or validator infrastructure provider (e.g., Kiln or Coinbase Cloud) that owns and operates Ethereum validators for staking services on the Onchain platform.
operator pool: also known as a 'vPool', it is a set of smart contracts serving as the 'pooled staking engine,' with each one owned and operated by a node operator. One vPool can serve multiple partners.
integration contract: A smart contract owned and operated by the partner, serving as the primary integration point to the platform (end-user fees, commissions, node operator preferences and splits, and more!)
A smart contract owned and operated by the partner, serving as the primary integration point for their users to the platform. These contracts manage end-user fees, commissions, node operator preference and split, and can be paused to prevent deposits at the discretion of the partner.
Integration contracts can connect to multiple Operator Pools to enable validator set diversification. They can be configured to allocate the desired balance across each operator, allowing for configurations such as 50/50, 60/40, 80/20, etc., tailored to specific requirements.
Under the hood, when a user deploys their ETH, the integration contract forwards the ETH to the operator pool. Users are abstracted from the underlying technical complexity of the platform, receiving a ERC-20 token in their wallet when they stake ETH. This token, soulbound or transferable based on the partner's preference, represents their stake ownership and serves as the withdrawal credentials.
Partners have full control over the can customise the token and define the token name and symbol. For example, myETH (My Company ETH).
NOTE: the Operator Pool has no concept of tokens. The integration contracts are where tokens are issued and managed.
Think of Operator Pools as pooled staking engines, each owned by a node operator but entirely managed by smart contract logic. These sets of smart contracts handle all fundamental operations and lifecycle tasks of the pool, including deposits, validator activation and deactivation, reward distribution, exits, and management of staking fees.
Every Operator Pool can allow one or more integration contracts to connect and deposit ETH, allowing simple validator set diversification.
Rewards are generated by all of the validators supporting the pool, and are recycled back into the pool.
The value of the pool is recalculated daily via the receipt of oracle reports and the value of the operator pool will increase in value over time as rewards flow into the system.
This means that users will typically see the value of their staked position increase on daily basis.
An oracle report consists of data like total ETH staked, total ETH currently in the protocol Exit Queue, total validators staked, total validators exited and strategy inputs to tell the pool to either accelerate user exits time or increase new validator deposits.
Using this data, Operator pool will perform multiple state transitions to:
deposit new validators
answer the exit demand in the exit queue
compute operator commission
update the Operator pool shares exchange rate with ETH to take into account new rewards
exit validators if needed to match the user exit demand in the pool exit queue
Users can request to exit any amount of ETH up to the total value of their staked position. That is, they can request a full or partial exit.
Exiting, or withdrawing ETH, from the pool is a three staged process:
Request exit (requires user tx)
Wait for the request to be fulfilled
Withdraw the ETH (requires user tx)
Issue a request to withdraw an amount of ETH from the pool. After the transaction is confirmed and executed, an 'exit queue ticket' is issued, which is a soulbound NFT token representing the withdrawal request.
Exit requests are processed on a daily basis via the receipt of Oracle reports.
Fulfilling exit requests involves two scenarios::
ETH from deposits not yet staked and generated rewards.
The next oracle report on the pool (daily on mainnet) will process the exit.
ETH from validator exits
Where exit demand significantly surpasses available ETH, validator exits become necessary.
In this case, the validator must complete the full exit and withdrawal process, and then await the subsequent oracle report. This process generally takes between 2-10 days but may extend further, depending on the dynamics of the Ethereum exit queue.
Under normal conditions, with the pool earning rewards and incoming liquidity from deposits, it will take approximately 1-4 days for the user's exited ETH to become available for withdrawal.
Once fulfilled, the exited ETH is available to be withdrawn by the user.
When the ETH is available, the user will need to perform a 'claim transaction' during which the NFT will be burned in exchange for the ETH.
Before diving straight into integrating, think about the user experience and all the key workflows you should consider as part of your integration. Here is a non-exhaustive list to help plan your user experience.
Feature in 'Earn' dashboard or integrate into existing workflows.
Emphasize ETH staking as a new product for heightened awareness.
Clearly display the reward rate for informed user decision-making. It is not static.
Set a practical minimum (e.g. 0.1 or 0.05 ETH) to avoid the staking tx costing more than the stake itself.
Alert users if the cost of the transaction is high relative to the amount being staked
Clarify if users must read and accept your Terms and Conditions.
Consider displaying estimated rewards based on the current reward rate, as some partners do.
Provide users with an estimate of when their stake begins earning rewards.
Include a link to the 'staking request' transaction on Etherscan for transparency.
Display the user's staked ETH amount and current value
Consider showing historical reward data e.g. yesterday, last week, last month
Simplify 'stake more' or 'unstake' options for user convenience.
Enable users to request full or partial unstake.
Highlight that unstaking may take several days; the ETH is not immediately available.
For each exit request, show users an estimate
If unstaking too early, caution about potential network fee implications.
Include a link to the 'unstaking request' transaction on Etherscan for transparency.
Display 'unstake request' status and estimated time.
Alert the user to the fact that they have ETH to be claimed.
Include a link to the 'claim ETH' transaction on Etherscan for transparency.
Think about how you handle the integration contract being paused?
Stakers will not be able to stake (transactions will revert), so it's better to think about how to gracefully handle these situations before they happen.
Learn more about your integration contract along with the choice of receipt tokens available to you.
The integration contract will be deployed by Kiln, but then owned and operated by our partner. This provides partners with complete control over the user experience and the staking product being distributed to their end users.
Partners can do the following:
Users receive a receipt token representing their staked position. Partners must decide on the token type upfront (soulbound or liquid), as it cannot be set or changed later. Kiln deploys the correct integration contract based on this initial decision.
Partners can customise the below token attributes. Both attributes will be displayed on Etherscan and ERC20 compatible interfaces:
token symbol: e.g. cbETH, cdcETH, psETH
token name: e.g. Pooled Staked ETH, Coinbase Wallet ETH
Partners need to choose the token type they wish to include in the staking product being offered to their end users.
Below is a comparison and summary of token types, highlighting differences in integration approaches, capabilities, and end-user experiences.
NOTE: Commonly, start with Native20 (non-transferrable tokens). Consider an optional upgrade to Liquid20 later; connect with us for details on the upgrade path and associated considerations.
Stake any amount
✅
✅
✅
Commission taken onchain
✅
✅
✅
Multi commission recipients
✅
✅
✅
Users chooses pool
❌
❌
❌
Partner chooses pool (sets % distribution across pools)
✅
✅
✅
Transfers
❌
✅
✅
NOTES
aToken
cToken
contract expose the exact same view methods as Liquid20C
without the transfer
and approve
methods. It looks like a token, but cannot behave like one.
Liquid20A
an ERC20
rebase token.
the quantity of tokens held in the users wallet will increase each time rewards are distributed. Rebase tokens maintain a fixed exchange rate but the quantity of tokens issued increases to reflect earned rewards.
Liquid20C
standard ERC20
token
The quantity of tokens held in the users wallet do not change, however their value increases each time rewards are distributed.
admin
address
Admin wallet address
pools
address[]
List of pool addresses
poolFees
uint256[]
List of fee for each pool in basis points
poolPercentages
uint256[]
Share of new stake to go to each pool, in basis points, must add up to 10 000
commissionRecipients
address[]
List of address of commission beneficiaries
commissionDistribution
uint256[]
Share of each beneficiary, in basis points, must add up to 10 000
name
string
ERC-20 style display name
symbol
string
ERC-20 style display symbol
This page describes the roles needed to operate the integration contracts
The integration contract requires two essential admin roles, established during deployment but modifiable later.
These roles, assigned to unique addresses, are strongly recommended to be behind a multi-sig or MPC wallet for enhanced security and flexibility.
SYS_ADMIN
multi-sig or MPC wallet
pausing deposits
changing SYS_ADMIN
modifying end user fee (aka commission)
modifying commission split and recipients
modifying operator pools
modifying weighting across operator pools,
when connected to two or more pools
PROXY_ADMIN
multi-sig or MPC wallet
pausing and resume contract
changing PROXY_ADMIN
upgrading implementation contract
Used to stake any amount of ETH into the pool.
This can only be executed by calling a smart contract method:
Used to find the amount of shares for a given wallet from the integration contract
Using the smart contract view methods
Allows you to obtain the value (in ETH) of the users current staked position
For example, consider the scenario where a user staked 12 ETH several months ago. Every time they visit your app, you want to showcase the current value of the staked position accounting for the accrued value from staking rewards over time.
You can simply query the smart contract with the users wallet.
Using the smart contract view methods
Using TheGraph queries
TheGraph queries
The unstaking process is a three-stage process:
user requests an exit or unstake (user tx)
exit request is processed and ETH is sourced to pay for the exit
user claims exited ETH (user tx)
When user request an exit, they burn their shares (receipt tokens in their wallet) in exchange for one or exit queue tickets.
These tickets have a status based on wether or not they can be claimed (user is able to claim their ETH), which depends on the liquidity provided to the exit queues by the vPools.
You can find more details about tickets and casks here.
Used to request an exit (unstake) of any amount of ETH, up to the maximum user balance.
When initiating an exit, you must specify the quantity of integration tokens to exit. Subsequently, the integration contract will generate one or several tickets in the associated vPool exit queues.
This can only be executed by calling a smart contract method:
Used to retrieve all exit queue tickets for a user, providing the status of each ticket. This information enables you to inform the user about the availability of their exited ETH for claiming. Additionally, you have the option to permit users to claim a portion of their exit request if it is partially fulfilled, with the ability to claim the remainder once it becomes available.
Using The Graph
Query
Example results
Here you will retrieve all the different exit tickets of an account.
ticketId
is the id of the ticket, and also the id of the NFT representing this ticket
size
is the size of the ticket in vPool Shares (not ETH !)
maxExitable
is the maximum amount of eth that can be retrieved by the ticket. Shares locked inside tickets are not earning rewards anymore.
fulfillableAmount
is the amount of vPool shares that are currently fulfillable
fulfillableBy
is a list of casks that fulfill a ticket
exitQueue.address
is the address of the exit queue contract where the ticket is held
To retrieve the status of the ticket, it's pretty simple:
unfulfillable
when fulfillableAmount
is 0
partially fulfillable
when fulfillableAmount
< size
fulfillable
when fulfillableAmount
= size
Used to claim the ETH of a user who has requested to exit the pool.
You can use TheGraph query above to retrieve all the required information. In the case where we would want to claim all the tickets from the example query above (assuming we would have a new cask with id 1
that fulfills all the tickets), the transaction would look like
You must pass only one cask ID per ticket, if the ticket is matched by more than one cask, pass the lowest cask id and the code will automatically claim on all the casks matching the ticket
Used to retrieve the historical reward rate of the pool so that it can be displayed to your users in your native workflow.
The pool reward rate can only ever be based on a rolling historical average (we can never predict the future rate). The data returned includes the average of the last week, last month, last 3 months, last 6 months and 'all time'.
It is up to you to choose the which historical rolling average to use, but most partners currently use the last week.
TIP: you need to convert the results to a percentage (%). For example:
'All Time' Gross Reward Rate returns: 26478817328005117
Convert to %
26478817328005117 / 10^18 =
.02647 (2.647%)
Using The Graph
Query
Example results
Used to retrieve the rewards of the given user based on their staked position
TIP: the result is in wei, you need to convert it to ETH. For example:
allTimeRewards = 46478817328005117
Convert to ETH
46478817328005117 / 10^18 = 0.046478817
ETH
allTimeRewards
= ((sharesBalance * totalUnderlyingSupply) / totalSupply) - adjustedTotalDeposited
Using The Graph
Query
Example results
This page provides an overview of the various contract interactions when staking.
The examples below when calling smart contract functions (e.g. stake()) are done using cast,
a cli tool, only as an illustration of how the transactions or view calls should be performed.
All of the contract calls below are for: Liquid20C, Liquid20A & Native20 contracts
Integrating Kiln into native workflow will require a mix of smart contract calls and TheGraph queries:
Smart contract
Our smart contract ABI's can be found here and the contract address will be provided by Kiln once it is deployed.
The Graph
Use the credentials provided by Kiln if using the public instance, or your own credentials if you are hosting your own instance.
Get the historical reward rate of the pool (last week, month, 3 months, 6 months, All Time).
Stake any amount of ETH into the pool.
Get a list of all deposits / stakes made from a wallet
Current value of stake(s)
Get the current underlying value of a wallet's staked position. Includes original stake + accrued rewards.
Get the historical reward rate of the wallet's staked position.
Unstake
Initiate an exit (or unstake) of a wallet's full or partial staked position.
Unstake status
Get the current status of a wallet's exit request.
Withdraw unstaked ETH
Claim unstaked ETH once the exit request has been fulfilled.
TheGraph indexes data from the smart contracts to facilitate easier access. You can use it to understand what happens live during this guide.
To gain access to TheGraph, contact Kiln and you will receive credentials to Kiln's TheGraph instance. Feel free to also host the subgraph on your end, allowing you to run the queries locally. During this guide, we will often use TheGraph queries to verify that specific actions had the expected outcome.
The easiest way to interact with TheGraph is by visiting the web query builder.
To test it, you can try a simple query to retrieve all the deposits made by an address on an integration contract (Native20,Liquid20A or Liquid20C):
And if everything goes well, you should have a response looking like this if the wallet made some deposits.
You're now ready to follow the guide and run the queries when needed !