Skip to content
87 changes: 51 additions & 36 deletions script/operations/steth-management/ClaimStEthWithdrawals.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,73 @@ import "forge-std/Script.sol";
import "forge-std/console2.sol";
import {Deployed} from "../../deploys/Deployed.s.sol";
import {EtherFiRestaker} from "../../../src/EtherFiRestaker.sol";
// import {ILidoWithdrawalQueue} from "../../../src/interfaces/ILiquifier.sol";
import {ILidoWithdrawalQueue} from "../../../src/interfaces/ILiquifier.sol";

interface ILidoWithdrawalQueue {
function getLastFinalizedRequestId() external view returns (uint256);
function getLastCheckpointIndex() external view returns (uint256);
function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex) external view returns (uint256[] memory hintIds);
}

// forge script script/operations/steth-claim-withdrawals/ClaimStEthWithdrawals.s.sol --fork-url $MAINNET_RPC_URL -vvvv
// forge script script/operations/steth-management/ClaimStEthWithdrawals.s.sol --fork-url $MAINNET_RPC_URL -vvvv

contract ClaimStEthWithdrawals is Script, Deployed {

EtherFiRestaker constant etherFiRestaker = EtherFiRestaker(payable(ETHERFI_RESTAKER));

function run() external {
uint256 startId = 113785; // Set this to the first request you want to claim
uint256 endId = 113863; // Set this to the last request you want to claim

ILidoWithdrawalQueue lidoWithdrawalQueue = ILidoWithdrawalQueue(address(etherFiRestaker.lidoWithdrawalQueue()));
console2.log("LidoWithdrawalQueue:", address(lidoWithdrawalQueue));
console2.log("EtherFiRestaker: ", address(etherFiRestaker));
console2.log("LidoWithdrawalQueue: ", address(lidoWithdrawalQueue));

// 1. Fetch all withdrawal request IDs owned by the restaker
uint256[] memory allRequestIds = lidoWithdrawalQueue.getWithdrawalRequests(address(etherFiRestaker));
console2.log("Total pending requests for restaker:", allRequestIds.length);

if (allRequestIds.length == 0) {
console2.log("No pending withdrawal requests found. Nothing to claim.");
return;
}

// Cap endId to the last finalized request
uint256 lastFinalizedId = ILidoWithdrawalQueue(address(lidoWithdrawalQueue)).getLastFinalizedRequestId();
console2.log("Last finalized request ID:", lastFinalizedId);
// 2. Get statuses for all requests
ILidoWithdrawalQueue.WithdrawalRequestStatus[] memory statuses =
lidoWithdrawalQueue.getWithdrawalStatus(allRequestIds);

if (endId > lastFinalizedId) {
console2.log("WARNING: endId", endId, "exceeds last finalized ID, capping to", lastFinalizedId);
endId = lastFinalizedId;
// 3. Filter to finalized & unclaimed requests
uint256 claimableCount = 0;
for (uint256 i = 0; i < statuses.length; i++) {
if (statuses[i].isFinalized && !statuses[i].isClaimed) {
claimableCount++;
}
}
require(startId <= endId, "No finalized requests in range");

uint256 count = endId - startId + 1;
console2.log("Claiming", count, "requests:", startId);
console2.log(" to", endId);
console2.log("Claimable (finalized & unclaimed):", claimableCount);

if (claimableCount == 0) {
console2.log("No claimable requests at this time. Nothing to claim.");
return;
}

uint256[] memory requestIds = new uint256[](claimableCount);
uint256 idx = 0;
Comment thread
pankajjagtapp marked this conversation as resolved.
for (uint256 i = 0; i < allRequestIds.length; i++) {
if (statuses[i].isFinalized && !statuses[i].isClaimed) {
requestIds[idx] = allRequestIds[i];
idx++;
}
}

uint256[] memory requestIds = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
requestIds[i] = startId + i;
console2.log("Claiming request IDs:");
for (uint256 i = 0; i < requestIds.length; i++) {
console2.log(" ", requestIds[i]);
}

// Get checkpoint hints
uint256 lastCheckpointIndex = ILidoWithdrawalQueue(address(lidoWithdrawalQueue)).getLastCheckpointIndex();
// 4. Get checkpoint hints for the claimable requests
uint256 lastCheckpointIndex = lidoWithdrawalQueue.getLastCheckpointIndex();
console2.log("Last checkpoint index:", lastCheckpointIndex);

uint256[] memory hints = ILidoWithdrawalQueue(address(lidoWithdrawalQueue)).findCheckpointHints(requestIds, 1, lastCheckpointIndex);
uint256[] memory hints = lidoWithdrawalQueue.findCheckpointHints(requestIds, 1, lastCheckpointIndex);

console2.log("Hints found for", hints.length, "requests");
console2.log("Hints found for", hints.length, "requests:");
for (uint256 i = 0; i < hints.length; i++) {
console2.log(" requestId:", requestIds[i], "hint:", hints[i]);
}

// Encode the calldata
// 5. Encode calldata for multisig / safe submission
bytes memory callData = abi.encodeWithSelector(
EtherFiRestaker.stEthClaimWithdrawals.selector,
requestIds,
Expand All @@ -68,16 +83,16 @@ contract ClaimStEthWithdrawals is Script, Deployed {
console2.log("Target:", address(etherFiRestaker));
console2.logBytes(callData);

// Simulate the transaction on fork as operating admin
// 6. Simulate on fork as operating admin
console2.log("");
console2.log("=== Simulating on fork ===");
uint256 liquidityPoolbalanceBefore = LIQUIDITY_POOL.balance;
console2.log("LiquidityPool balance before:", uint256(liquidityPoolbalanceBefore) / 1e18);
uint256 lpBalanceBefore = LIQUIDITY_POOL.balance;
console2.log("LiquidityPool balance before:", lpBalanceBefore / 1e18);
vm.prank(ETHERFI_OPERATING_ADMIN);
etherFiRestaker.stEthClaimWithdrawals(requestIds, hints);
uint256 liquidityPoolbalanceAfter = LIQUIDITY_POOL.balance;
console2.log("LiquidityPool balance after:", uint256(liquidityPoolbalanceAfter) / 1e18);
console2.log("ETH claimed:", (liquidityPoolbalanceAfter - liquidityPoolbalanceBefore) / 1e18);
uint256 lpBalanceAfter = LIQUIDITY_POOL.balance;
console2.log("LiquidityPool balance after:", lpBalanceAfter / 1e18);
console2.log("ETH claimed:", (lpBalanceAfter - lpBalanceBefore) / 1e18);
console2.log("Simulation successful");
}
}
Loading