Guides
Manage Deposits

Managing Deposits

This guide covers topics around creating and managing staking deposit positions in the UniStaker contract. It is intended for developers and integrators. It is not intended for everyday users, who would be expected to interact with the project via a client.

Overview

In the UniStaker system, staking positions are managed on a per-deposit basis. A given deposit is owned by the account that creates it, and can be managed and modified independent of other deposits, including ones that might be owned by the same account.

Each deposit has a unique DepositIdentifier, i.e. an integer that uniquely identifies a single deposit position.

The data of a deposit is defined by the following struct:

 struct Deposit {
    uint256 balance;
    address owner;
    address delegatee;
    address beneficiary;
  }

In addition to the deposit's balance and owner, which are self explanatory, each deposit includes the following parameters which warrant some explanation:

  • delegatee - The governance delegate who receives the voting weight for this deposit.
  • beneficiary - The address that accrues staking rewards earned by this deposit.

Creating a Deposit

In the UniStaker.sol contract, a user stakes UNI tokens by calling one of the stake family of methods:

function stake(uint256 _amount, address _delegatee)
    external
    returns (DepositIdentifier _depositId);
 
function stake(uint256 _amount, address _delegatee, address _beneficiary)
    external
    returns (DepositIdentifier _depositId);
 
function permitAndStake(
    uint256 _amount,
    address _delegatee,
    address _beneficiary,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external returns (DepositIdentifier _depositId);
 
function stakeOnBehalf(
    uint256 _amount,
    address _delegatee,
    address _beneficiary,
    address _depositor,
    bytes memory _signature
  ) external returns (DepositIdentifier _depositId);

The DepositId returned by this method will be used to reference this deposit during future interactions with the UniStaker contract.

Each method also emits the following event:

event StakeDeposited(
    address owner, DepositIdentifier indexed depositId, uint256 amount, uint256 depositBalance
  );

The various stake methods include the following assumptions about the caller and about token approvals:

  • The stake/2 and stake/3 methods will make the caller the owner of the new deposit. The owner account must pre-approve the UniStaker contract to spend at least the amount of UNI being deposited.
  • The permitAndStake method also makes the caller the owner of the new deposit. Rather than require a pre-approval, this method takes the signature parameters associated with the UNI permit (opens in a new tab) method and forwards them ahead to create the approval. This is a convenience method that allows staking with one transaction instead of two.
  • The stakeOnBehalf method will make the _depositor the owner of the new deposit. It can be submitted by any account, such as a relayer, as long as a valid signature from the _depositor authorizes the deposit. The _depositor must pre-approve the UniStaker contract to spend at least the amount of UNI being deposited.

Modifying a Deposit

To modify a deposit, you must have the unique DepositIdentifier that was returned by whichever stake method was used to create it originally. The identifier is also emitted in the StakeDeposited event, as documented above. Once you have the DepositIdentifier, the account that owns it may perform the following actions.

Adding UNI

Adding UNI to a staking position is done through the stakeMore family of methods:

  function stakeMore(DepositIdentifier _depositId, uint256 _amount) external;
 
  function permitAndStakeMore(
    DepositIdentifier _depositId,
    uint256 _amount,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external;
 
  function stakeMoreOnBehalf(
    DepositIdentifier _depositId,
    uint256 _amount,
    address _depositor,
    bytes memory _signature
  ) external;

The stakeMore family of methods follow a pattern similar to the stake methods as it relates to ownership and token approvals:

  • The stakeMore method must be called by the owner of the deposit, and the owner must have pre-approved the UniStaker contract for the _amount.
  • The permitAndStakeMore method must be called by the owner of the deposit, and the owner must provide a permit signature which will be forwarded to UNI to approve the _amount
  • The stakeMoreOnBehalf method may be called by anyone, but the _depositor must be the owner of the deposit, and must provide a valid signature.

Changing the Governance Delegatee

Updating the governance delegatee, that is, the account that can vote in Uniswap Governance on behalf of the token weight that is staked as part of this deposit, can be done with the alterDelegatee and the alterDelegateeOnBehalf methods. As the name suggests, the latter may be called by a relayer, as long as valid signature is provided by the deposit owner.

Changing the Reward Beneficiary

Updating the reward beneficiary, that is, the account that earns rewards on behalf of the UNI that is staked in this deposit, can be done with the alterBeneficiary and alterBeneficiaryOnBehalf methods. As the name suggests, the latter may be called by a relayer, as long as valid signature is provided by the deposit owner.

Rewards stop accruing the old beneficiary, and begin accruing to the new beneficiary, as soon as the transaction that changes it completes. Note that rewards which have already accrued are not impacted by calling this method. The old beneficiary is still eligible to claim those rewards at any point in the future.

Withdrawing a Deposit

Withdrawing UNI from the UniStaker contract can be done with the withdraw and withdrawOnBehalf methods. As the name suggests, the latter may be called by a relayer, as long as valid signature is provided by the deposit owner. The UNI that is withdrawn is transferred to the deposit owner account.

Batching Stake Operations

The UniStaker contract supports multicall (opens in a new tab), allowing users to batch multiple calls to the contract's methods in a single atomic transaction. This can be useful if, for example, a depositor wanted to add to their staking position and update the beneficiary address at the same time.