Clear minipool queue contract

When someone deposits ETH into the deposit pool, up to 2 minipools get their 16 ETH assigned.

It can happen that there is a queue of 100 minipools and the deposit pool is full yet the minipool queue is not moving until small amounts are deposited, new minipools are created or someone volunteers to pay for gas to assign deposits.

Contract

This simple contract can clear the minipool queue in one transaction.

It’s quite basic and should only be used when there’s enough ETH in the deposit pool because it will waste gas if you try to assign deposits to more minipools than there’s ETH in the deposit pool.

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.14;

interface RocketStorageInterface {
  function getAddress(bytes32 _key) external view returns (address);
}

interface RocketDepositPoolInterface {
  function assignDeposits() external;
}

interface RocketMinipoolQueueInterface {
  function getTotalLength() external view returns (uint256);
  function getLength(MinipoolDeposit _depositType) external view returns (uint256);
}

interface RocketDAOProtocolSettingsDepositInterface {
  function getMaximumDepositAssignments() external view returns (uint256);
}

enum MinipoolDeposit {
  None,
  Full,
  Half,
  Empty
}

contract RocketDepositPoolQueue {
  RocketStorageInterface rocketStorage;

  constructor(RocketStorageInterface rocketStorageAddress) {
    rocketStorage = RocketStorageInterface(rocketStorageAddress);
  }

  function getQueueLength() public view returns (uint256) {
    RocketMinipoolQueueInterface queue = RocketMinipoolQueueInterface(getContractAddress("rocketMinipoolQueue"));
    return queue.getTotalLength();
  }

  function getHalfQueueLength() public view returns (uint256) {
    RocketMinipoolQueueInterface queue = RocketMinipoolQueueInterface(getContractAddress("rocketMinipoolQueue"));
    return queue.getLength(MinipoolDeposit.Half);
  }

  function getAssignmentCount() public view returns (uint256) {
    RocketDAOProtocolSettingsDepositInterface settings = RocketDAOProtocolSettingsDepositInterface(getContractAddress("rocketDAOProtocolSettingsDeposit"));
    return settings.getMaximumDepositAssignments();
  }

  function clearQueue() public {
    clearQueueUpTo(getQueueLength());
  }

  function clearHalfQueue() public {
    clearQueueUpTo(getHalfQueueLength());
  }

  function clearQueueUpTo(uint256 minipoolCount) public {
    RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
    uint256 step = getAssignmentCount();
    for (uint256 i = 0; i < minipoolCount; i += step) {
      depositPool.assignDeposits();
    }
  }

  function getContractAddress(string memory contractName) private view returns (address) {
    return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", contractName)));
  }
}

Gas usage in my unit tests:

Minipools Gas Cost at 30 gwei
2 minipools 360k 0.0108 ETH
10 minipools 1.4m 0.042 ETH
98 minipools 13m 0.39 ETH

Usage

It’s deployed to Ethereum mainnet https://etherscan.io/address/0xd95c1b65255eb69303c0159c656976389f8da225#writeContract

Connect your MetaMask and enter how many 16 ETH deposits you’d like to assign to minipools by entering the number for clearQueueUpTo. Make sure there’s enough ETH in the deposit pool or some of the gas will be spent for nothing.

The worst that can happen is the transaction reverts and you’ve wasted the max transaction fee that you chose in MetaMask.

Example

I was finally able to test this (low gas and enough ETH in the deposit pool):

5 Likes