Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas saving when claiming Lido withdrawals #50

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/contracts/LidoARM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ contract LidoARM is Initializable, AbstractARM {
* Reference: https://docs.lido.fi/contracts/withdrawal-queue-erc721/
* Note: There is a 1k amount limit. Caller should split large withdrawals in chunks of less or equal to 1k each.)
*/
function requestLidoWithdrawals(uint256[] memory amounts)
function requestLidoWithdrawals(uint256[] calldata amounts)
external
onlyOperatorOrOwner
returns (uint256[] memory requestIds)
Expand All @@ -92,13 +92,14 @@ contract LidoARM is Initializable, AbstractARM {
/**
* @notice Claim the ETH owed from the redemption requests and convert it to WETH.
* Before calling this method, caller should check on the request NFTs to ensure the withdrawal was processed.
* @param requestIds The request IDs of the withdrawal requests.
* @param hintIds The hint IDs of the withdrawal requests.
* Call `findCheckpointHints` on the Lido withdrawal queue contract to get the hint IDs.
*/
function claimLidoWithdrawals(uint256[] memory requestIds) external {
function claimLidoWithdrawals(uint256[] calldata requestIds, uint256[] calldata hintIds) external {
uint256 etherBefore = address(this).balance;

// Claim the NFTs for ETH.
uint256 lastIndex = lidoWithdrawalQueue.getLastCheckpointIndex();
uint256[] memory hintIds = lidoWithdrawalQueue.findCheckpointHints(requestIds, 1, lastIndex);
lidoWithdrawalQueue.claimWithdrawals(requestIds, hintIds);

uint256 etherAfter = address(this).balance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ contract Fork_Concrete_LidoARM_RequestLidoWithdrawals_Test_ is Fork_Shared_Test_
emit LidoARM.ClaimLidoWithdrawals(emptyList);

// Main call
lidoARM.claimLidoWithdrawals(new uint256[](0));
lidoARM.claimLidoWithdrawals(emptyList, emptyList);

assertEq(address(lidoARM).balance, 0);
assertEq(lidoARM.lidoWithdrawalQueueAmount(), 0);
Expand All @@ -72,12 +72,15 @@ contract Fork_Concrete_LidoARM_RequestLidoWithdrawals_Test_ is Fork_Shared_Test_
uint256[] memory requests = new uint256[](1);
requests[0] = stETHWithdrawal.getLastRequestId();

uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requests, 1, lastIndex);

// Expected events
vm.expectEmit({emitter: address(lidoARM)});
emit LidoARM.ClaimLidoWithdrawals(requests);

// Main call
lidoARM.claimLidoWithdrawals(requests);
lidoARM.claimLidoWithdrawals(requests, hintIds);

// Assertions after
assertEq(lidoARM.lidoWithdrawalQueueAmount(), 0);
Expand All @@ -100,12 +103,15 @@ contract Fork_Concrete_LidoARM_RequestLidoWithdrawals_Test_ is Fork_Shared_Test_
requests[0] = stETHWithdrawal.getLastRequestId() - 1;
requests[1] = stETHWithdrawal.getLastRequestId();

uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requests, 1, lastIndex);

// Expected events
vm.expectEmit({emitter: address(lidoARM)});
emit LidoARM.ClaimLidoWithdrawals(requests);

// Main call
lidoARM.claimLidoWithdrawals(requests);
lidoARM.claimLidoWithdrawals(requests, hintIds);

// Assertions after
assertEq(lidoARM.lidoWithdrawalQueueAmount(), 0);
Expand Down
12 changes: 9 additions & 3 deletions test/fork/LidoFixedPriceMultiLpARM/Deposit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,10 @@ contract Fork_Concrete_LidoARM_Deposit_Test_ is Fork_Shared_Test_ {
_mockFunctionClaimWithdrawOnLidoARM(DEFAULT_AMOUNT);

// 4. Operator claim withdrawal on lido
uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requests, 1, lastIndex);
lidoARM.totalAssets();
lidoARM.claimLidoWithdrawals(requests);
lidoARM.claimLidoWithdrawals(requests, hintIds);

// 5. User burn shares
(, uint256 receivedAssets) = lidoARM.requestRedeem(shares);
Expand Down Expand Up @@ -532,7 +534,9 @@ contract Fork_Concrete_LidoARM_Deposit_Test_ is Fork_Shared_Test_ {
uint256[] memory requests = new uint256[](1);
requests[0] = requestId;
// 4. Operator claim the withdrawal on lido
lidoARM.claimLidoWithdrawals(requests);
uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requests, 1, lastIndex);
lidoARM.claimLidoWithdrawals(requests, hintIds);
// 5. User burn shares
(, uint256 receivedAssets) = lidoARM.requestRedeem(shares);

Expand Down Expand Up @@ -592,7 +596,9 @@ contract Fork_Concrete_LidoARM_Deposit_Test_ is Fork_Shared_Test_ {
requests[0] = requestId;

// 4. Operator claim the withdrawal on lido
lidoARM.claimLidoWithdrawals(requests);
uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requests, 1, lastIndex);
lidoARM.claimLidoWithdrawals(requests, hintIds);

// 5. User burn shares
(, uint256 receivedAssets) = lidoARM.requestRedeem(shares);
Expand Down
19 changes: 19 additions & 0 deletions test/fork/utils/MockCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ contract MockLidoWithdraw {
function claimWithdrawals(uint256[] memory, uint256[] memory) external {
ethSender.sendETH(lidoARM);
}

/// @notice Mock the call to the Lido contract's `getLastCheckpointIndex` function.
function getLastCheckpointIndex() public pure returns (uint256) {
// hardcoded as this is not used by the Lido ARM
return 300;
}

/// @notice Mock the call to the Lido contract's `findCheckpointHints` function.
function findCheckpointHints(uint256[] calldata _requestIds, uint256, uint256)
external
pure
returns (uint256[] memory hintIds)
{
hintIds = new uint256[](_requestIds.length);
for (uint256 i = 0; i < _requestIds.length; ++i) {
// hardcoded as this is not used by the Lido ARM
hintIds[i] = 300;
}
}
}

contract ETHSender {
Expand Down
2 changes: 1 addition & 1 deletion test/invariants/BasicInvariants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ contract Invariant_Basic_Test_ is Invariant_Base_Test_ {
swapHandler = new SwapHandler(address(lidoARM), address(weth), address(steth), swaps);
ownerHandler =
new OwnerHandler(address(lidoARM), address(weth), address(steth), MIN_BUY_T1, MAX_SELL_T1, MAX_FEES);
llmHandler = new LLMHandler(address(lidoARM), address(steth));
llmHandler = new LLMHandler(address(lidoARM), address(steth), address(lidoWithdraw));
donationHandler = new DonationHandler(address(lidoARM), address(weth), address(steth));

lpHandler.setSelectorWeight(lpHandler.deposit.selector, 5_000); // 50%
Expand Down
15 changes: 11 additions & 4 deletions test/invariants/handlers/LLMHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import {BaseHandler} from "./BaseHandler.sol";
// Contracts
import {IERC20} from "contracts/Interfaces.sol";
import {LidoARM} from "contracts/LidoARM.sol";
import {IStETHWithdrawal} from "contracts/Interfaces.sol";

/// @notice LidoLiquidityManager Handler contract
/// @dev This contract is used to handle all functionnalities that are related to the Lido Liquidity Manager.
/// @dev This contract is used to handle all functionalities that are related to the Lido Liquidity Manager.
contract LLMHandler is BaseHandler {
////////////////////////////////////////////////////
/// --- CONSTANTS && IMMUTABLES
////////////////////////////////////////////////////
IERC20 public immutable steth;
LidoARM public immutable arm;
address public immutable owner;
IStETHWithdrawal public stETHWithdrawal;
uint256 public constant MAX_AMOUNT = 1_000 ether;

////////////////////////////////////////////////////
Expand All @@ -36,9 +38,10 @@ contract LLMHandler is BaseHandler {
////////////////////////////////////////////////////
/// --- CONSTRUCTOR
////////////////////////////////////////////////////
constructor(address _arm, address _steth) {
constructor(address _arm, address _steth, address _lidoWithdrawalQueue) {
arm = LidoARM(payable(_arm));
owner = arm.owner();
stETHWithdrawal = IStETHWithdrawal(_lidoWithdrawalQueue);
steth = IERC20(_steth);
}

Expand Down Expand Up @@ -111,7 +114,9 @@ contract LLMHandler is BaseHandler {
vm.startPrank(owner);

// Claim stETH withdrawal for WETH
arm.claimLidoWithdrawals(requestIds_);
uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requestIds_, 1, lastIndex);
arm.claimLidoWithdrawals(requestIds_, hintIds);

// Stop Prank
vm.stopPrank();
Expand All @@ -137,7 +142,9 @@ contract LLMHandler is BaseHandler {
vm.startPrank(owner);

// Claim stETH withdrawal for WETH
arm.claimLidoWithdrawals(requestIds);
uint256 lastIndex = stETHWithdrawal.getLastCheckpointIndex();
uint256[] memory hintIds = stETHWithdrawal.findCheckpointHints(requestIds, 1, lastIndex);
arm.claimLidoWithdrawals(requestIds, hintIds);

// Stop Prank
vm.stopPrank();
Expand Down