Skip to content
Merged
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
49 changes: 42 additions & 7 deletions src/js/tasks/etherfiQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,39 @@ const { logTxDetails } = require("../utils/txLogger");
const log = require("../utils/logger")("task:etherfiQueue");

const uri = "https://origin.squids.live/ops-squid/graphql";
const MAX_EETH_WITHDRAW_AMOUNT = parseUnits("1000");

const ETHERFI_WITHDRAWAL_NFT_ABI = [
"function isFinalized(uint256 requestId) view returns (bool)",
"function getRequest(uint256 requestId) view returns (tuple(uint96 amountOfEEth, uint96 shareOfEEth, bool isValid, uint32 feeGwei))",
];

const splitEtherFiWithdrawAmount = (
withdrawAmount,
maxAmount = MAX_EETH_WITHDRAW_AMOUNT,
) => {
if (maxAmount <= 0n) throw new Error("maxAmount must be greater than zero");

const requestAmounts = [];
let remainingAmount = withdrawAmount;
while (remainingAmount > 0n) {
const requestAmount =
remainingAmount > maxAmount ? maxAmount : remainingAmount;
requestAmounts.push(requestAmount);
remainingAmount -= requestAmount;
}
return requestAmounts;
};

const maxEtherFiWithdrawShares = async (baseContext, signer) => {
if (baseContext.version === "legacy" || baseContext.baseSymbol !== "WEETH") {
return MAX_EETH_WITHDRAW_AMOUNT;
}

const adapter = await adapterContract(baseContext.config.adapter, signer);
return adapter.convertToShares(MAX_EETH_WITHDRAW_AMOUNT);
};

const requestEtherFiWithdrawals = async (options) => {
const { signer, amount } = options;
const baseContext = await resolveArmBase(options);
Expand All @@ -32,14 +59,21 @@ const requestEtherFiWithdrawals = async (options) => {
: await baseWithdrawAmount(options);
if (!withdrawAmount || withdrawAmount === 0n) return;

log(`Requesting withdrawal for ${withdrawAmount} ${baseSymbol}...`);
const tx = await requestBaseAssetWithdrawal({
baseContext,
signer,
amount: withdrawAmount,
});
const maxWithdrawShares = await maxEtherFiWithdrawShares(baseContext, signer);
const requestAmounts = splitEtherFiWithdrawAmount(
withdrawAmount,
maxWithdrawShares,
);
for (const requestAmount of requestAmounts) {
log(`Requesting withdrawal for ${requestAmount} ${baseSymbol}...`);
const tx = await requestBaseAssetWithdrawal({
baseContext,
signer,
amount: requestAmount,
});

await logTxDetails(tx, "requestEtherFiWithdrawal");
await logTxDetails(tx, "requestEtherFiWithdrawal");
}
};

const claimEtherFiWithdrawals = async (options) => {
Expand Down Expand Up @@ -176,4 +210,5 @@ module.exports = {
claimEtherFiWithdrawals,
etherFiRequestStatuses,
selectClaimableEtherFiRequests,
splitEtherFiWithdrawAmount,
};
21 changes: 20 additions & 1 deletion test/js/etherfiQueue.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
const assert = require("assert");

const { AbiCoder, Contract, id } = require("ethers");
const { AbiCoder, Contract, id, parseUnits } = require("ethers");

const {
etherFiRequestStatuses,
selectClaimableEtherFiRequests,
splitEtherFiWithdrawAmount,
} = require("../../src/js/tasks/etherfiQueue");

const coder = AbiCoder.defaultAbiCoder();

const selector = (signature) => id(signature).slice(0, 10);

const run = async () => {
// Ether.fi rejects individual withdrawal requests above 1,000 eETH.
assert.deepStrictEqual(splitEtherFiWithdrawAmount(parseUnits("1000")), [
parseUnits("1000"),
]);
assert.deepStrictEqual(splitEtherFiWithdrawAmount(parseUnits("1971.5")), [
parseUnits("1000"),
parseUnits("971.5"),
]);
assert.deepStrictEqual(splitEtherFiWithdrawAmount(parseUnits("2000")), [
parseUnits("1000"),
parseUnits("1000"),
]);
// weETH uses the adapter-provided share amount equivalent to 1,000 eETH.
assert.deepStrictEqual(
splitEtherFiWithdrawAmount(parseUnits("1800"), parseUnits("950")),
[parseUnits("950"), parseUnits("850")],
);

// Pure filter: finalized AND still valid on-chain is the only claimable state.
{
const statuses = [
Expand Down
Loading