githubEdit

Pre-Create a DV with an OVM

Assign pre-created validators to customers on demand

A customer opting into staking at an unknown time, is a key trigger for enterprise staking deployments. There are two primary ways these ad-hoc demands are programatically fulfilled, using smart contracts or using/generating the private keys.

This guide will focus on the former, managing validators using Obol smart contracts, and their role based access control. This approach requires less coordination for multi-operator setups, and is simpler than creating or interacting with private key material on the fly by a remote trigger.

This guide will demonstrate the key steps in preparing a DV cluster for this type of scenario. A blank OVM will be created and assigned validator keys, along with a splitter contract for distributing rewards. Adjust the number of OVMs, splitters, and their key counts for your use case. Administratorship of the OVMs and splitters will be given to a private key that will sit in a secure back end API server, and when a customer triggers an allocation of an OVM, the API server private key will make the necessary updates to an unallocated OVM, and then revoke its control over the smart contracts, leaving them ready for the customer's deposit.

The Hoodi testnet will be used for all examples.

circle-exclamation

Pre-requisites

To keep the cast examples neat, we'll declare the key addresses upfront here, and refer to them as environment variables in each cast command.

Fee Splitting

A key decision when it comes to preparing a Distributed Validator is how Node Operators and Service providers can be non-custodially compensated for their services. Obol Validator Managers are built to leverage Splits.orgarrow-up-right split contracts. For this demo, split contracts will be pre-created with the unallocated OVMs, and edited for customers as they appear. You may want to consider smoothing MEV across your customers using a pair of nested splitters. This is described in more detail at the end of the guide.

Contract Deployment

A safe and convenient way to deploy an OVM contract is through the existing contract factoryarrow-up-right. A splitter contract can be deployed in a similar fashion.

After you have deployed an Obol Validator Manager contract, let's save its address and an example customer address as environment variables to make the rest of the cast demo easier.

Create the DV Cluster

At this point, you can prepare a DV cluster pointed at these OVMs and split contracts. Use the charon create cluster ... --publish command if you are controlling the validator keys centrally, or charon create dkg ... ---publish if you have a group of operators taking part in the cluster. Comma separate the --withdrawal-addresses and --fee-recipient-addresses flags with your created OVMs and Pull Splits. Once you complete the key creation, you can load these artifacts into your nodes and get the cluster online and ready for deposits. At this point the last remaining action will be with the API key, which will change the ownership of an OVM to make it ready for deposits.

Assigning the Contracts to Customers

When a capital allocator (customer) is onboarding, the pre-created contracts can be assigned to that entity. The principal beneficiary address is updated to the entity's preferred address, permissions are allocated to the customer and backend's addresses as needed, and then ownership of the OVMs are transferred or burned.

Transferring Ownership

circle-exclamation

The last step before the OVM is ready for activation is to transfer the ownership of the OVM away from the backend, to either the customer, or an extremely well secured administrative multi-sig wallet like a SAFEarrow-up-right that can intervene to update key values in future if needed. Consider that the owner of an OVM has custodial control over it.

Handling Deposits

The capital allocator can now deposit the validators that point to this withdrawal address. The validator keys are held by the provisioned DV cluster operators and the deposit data was created during cluster creation.

This step would normally be through a wallet and web interface. This example using raw private keys is for demo purposes only.

circle-info

To accurately differentiate reward from principal in an OVM, the OVM contract needs to be invoked during the deposit call. Each OVM has a deposit() function exactly matching and wrapping the official deposit smart contract, and should be used for that purpose.

If a deposit is made not through the OVM, the OVM can be updated with the setAmountOfPrincipalStake() method by the owner or an address with the SET_BENEFICIARY_ROLE.

The validator(s) will enter the activation queue and the amountOfPrincipalStake value on the contract will track how much of the balance is considered the principal (owed to the beneficiary). The EL and CL rewards from any targeting validators will be sent to the OVM contract and Pull Split.

Withdrawing Validator Balance

Compounding validators (0x02 type) can have part of their principal withdrawn from active stake, or be fully exited, via the same withdraw() call. Specifying a nonzero value for amounts will initiate a partial withdrawal, while 0 will fully exit the validator. You cannot specify an amount that will leave the validator with less than 32 ether in active stake remaining.

circle-exclamation

Reward Distribution and Splitters

When withdrawals requested eventually exit the beacon chain, they appear on the OVM contract, and should be distributed to the rewardRecipient or principalRecipient (depending on if they amount to above or below the principalThreshold of 16 eth). Calling distributeFunds() will push the Ether to the correct address. Split contracts as principal or reward addresses will also need to be distributed from for the funds to land in their ultimate recipients addresses.

Appendix: MEV Smoothing

If you setup validators where every customer gets their own fee recipient address (and underlying splitter), they will each get proposals rarely (approximately twice per year for a 32 ETH validator). Due to MEV being unequally distributed, only a small number of proposals in the year contain most of the MEV. This means that most of your users will get the median amount of Ether as MEV rather than the average, and may notice a lower APR versus setups that pool and distribute their variable rewards across their users. It may be beneficial for you to instead smooth the MEV being accrued through block proposals across all depositors in the cluster. This can be achieved through two nested split contracts as follows:

  • First create an editable PullSplitarrow-up-right we'll refer to as the Child Split. The owner of this split should be the $BACKEND_API_ADDRESS.

  • Next create a second PullSplit we'll refer to as the Parent Split. It can be immutable if preferred. It should send the majority of its inflow to the Child Split, and some amount to a set of addresses that receive operating fees for the cluster.

  • Set the parent split as the --fee-recipient-address for all validators in the cluster. This means all proposal rewards for the cluster will go to this address.

  • When a customer makes a deposit, use the $BACKEND_API_PRIVATE_KEY to update the Child Split to proportionally reflect the eth provided by all customers to the cluster.

  • As proposals by the validators earn tips and MEV, this collects on the Split Contracts. Distributing these rewards sends the ether to the fee recipients and customers.

Last updated

Was this helpful?