Key hierarchy for Staking accounts

At the present moment, Staking as per ERC900 allows accounts to stake into their own staking account or another account, being this account the full owner of the stake. Every other contract that wants to check the stake of an account that is interacting with the contract (e.g. to check voting power), will get the stake of the account. This account can also unstake which will result in the Staking contract sending the tokens to the account (unless stake is locked).

The issue with this mono-account pattern is that when the owner of the stake wants to interact with other contracts, she must do it with the same key that would get the tokens in case whenever they are unstaked. Therefore this account key must be very well secured if enough funds are at stake.

A clear case for this is getting voting rights as a result of staking a certain amount of tokens. The staking action must be done from the account holding the tokens as the tokens need to move, but from that point forward, having to use the same key every time she wants to vote is a deterrent to voting as the security risk of using this key may not be worth it or this key may take some time to get access to.

We need to provide a way for performing actions as the beneficiary of these staked funds, without requiring to use a cold key that could get access to the funds for every action.

Below are 3 different options to how this could be solved, ordered by complexity and better UX (Note that all 3 options could be used in parallel depending on user preference):

Option 1: add a ‘hot key’/actor key to staking accounts

Similar to how the Casper FFG contract (rest in peace) allows to add a separate ‘validation’ and ‘withdraw’ account. The ‘validator’ account being the hot key that signs casper votes and the ‘withdraw’ account the one where the ether will be sent if either the ‘validator’ or ‘withdraw’ key start the unstaking process. If the hot key is compromised, the validator can be grieved but the attacker could never steal funds from the validator.

In the context of ERC900, we could add an ‘actor’ address (hot key) to every staking account, which would be the account that could use the benefits of being staked in the contract. stakeFor would return the tokens for the actor address. A separate method could be added to get the tokens a certain ultimate beneficiary (cold key) owns. Both the hot and cold key could start an unstake similarly to Casper, but the tokens would always go to the cold key.

This would allow someone to set a ‘hot key’ for their stake that doesn’t require the same security properties and use that key for voting or whatever it can be done with the stake.

The only downside of this option is that is less elegant than all the others and would need changing the ERC900 standard (could be done in a backwards compatible way, and if actor == cold key it would behave absolutely equal to right now), but it would be by far the best and most transparent user experience.

Option 2: staking accounts as proxy contracts

Rather than using a normal account, potential stakers could create a proxy contract that could forward calls depending on some arbitrary logic, such as the distinction between a hot and a cold key. The cold key would need to send the tokens to this contract and from that point forward all interactions with the Staking contract would need to be done through the proxy.

This is a slightly more elegant solution that would allow to keep ERC900 as it is and allow for implementing any authorization logic, but would come at the expense of every staker that wants this feature to deploy a Proxy contract. Also all transactions would need to be proxied, which would be a UX problem to be solved (account abstraction would be great for this usecase). Another benefit is that this proxy could be reused for any Ethereum activity a user does.

A quick prototype of how such scheme could be implemented, with a similar hot/cold key auth can be checked here:

Option 3: personal DAOs with arbitrary logic

Similar to option 2, but slightly more complex would be for the user to have a Personal DAO where their funds are stored. This would allow maximum compostability of arbitrary authentication logic, but there would still be some UX issues to be figured out.


Option 1 seems to be the most straightforward approach, but the staking contract designer would probably have to follow a similar architecture as the Casper contract with stakers being identified in the contract by an always incrementing index (and state for the staker would be associated with the index) as opposed to by an account address. This architecture would make modifying hot key and cold key addresses a lot easier.

I like option 2 because if a staking contract is already deployed, developers can avoid upgrading the staking contract (which might not be possible if its state is not already being maintained by a delegate proxy) and instead users can opt in to this new workflow by deploying the account proxy contract whenever they want. From the staking contract’s point of view it doesn’t matter if the caller is an EOA or a contract. The situation where this gets more complicated is if the staking contract also needs to validate signatures since a contract can’t generate signatures. In this case, the account proxy could implement a ERC1271 signature validation function which the staking contract could use to validate signatures.

Some additions to the account proxy that might be helpful:

  • Authorizing multiple hot keys. Depending on the nature of the staking contract that you are interacting with, you might want to use one hot key for one type of action and another hot key for another type of action.
  • Batch forward multiple function calls using something like AragonOS’s CallScript mechanism. An example where this could benefit users: suppose a staking contract is upgradeable and uses an ERC20 token. Staking N tokens would usually require at least 2 transactions: an ERC20 approval for N tokens followed by a function call in the staking contract that transfers the N tokens from the user to the contract. Since the staking contract is upgradeable, users might not want to approve a very large amount - while this would eliminate the need for future ERC20 approval transactions, since the staking contract is upgradeable, the user might be wary of the implementation of the staking contract unexpectedly changing. If the user is interacting with the staking contract through an account proxy, she can use the account proxy as a wallet and transfer a large amount of tokens to the account proxy. Then, the user could use the account proxy to batch forward an ERC20 approval for N tokens and the function call into the staking contract. The result is a single atomic transaction that completes the staking process for N tokens.