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
29 changes: 21 additions & 8 deletions src/js/tasks/ethenaQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,27 @@ const legacyEthenaUnstakers = async (legacyArm) => {
// Fetches data for a list of addresses in PARALLEL (much faster)
const fetchUnstakerStates = async (signer, adapter, addresses) => {
const contract = new ethers.Contract(SUSDE_ADDRESS, SUSDE_ABI, signer);
// Pin all queue reads to one block. Reading each value at `latest` can mix
// state from a concurrent request or claim and produce an impossible FIFO
// snapshot.
const blockNumber = await signer.provider.getBlockNumber();
const blockTag = { blockTag: blockNumber };
const { timestamp: currentTimestamp } =
await signer.provider.getBlock("latest");
await signer.provider.getBlock(blockNumber);
let requestCount;
let maxUnstakers;

if (!addresses) {
[requestCount, maxUnstakers] = await Promise.all([
adapter.totalRequests(),
adapter.MAX_UNSTAKERS(),
adapter.totalRequests(blockTag),
adapter.MAX_UNSTAKERS(blockTag),
]);
const unstakerCount = Number(
requestCount < maxUnstakers ? requestCount : maxUnstakers,
);
addresses = await Promise.all(
Array.from({ length: unstakerCount }, async (_, index) => {
const address = await adapter.unstakers(index);
const address = await adapter.unstakers(index, blockTag);
return { address, index };
}),
);
Expand All @@ -109,12 +114,15 @@ const fetchUnstakerStates = async (signer, adapter, addresses) => {
// Promise.all executes all RPC calls simultaneously
const states = await Promise.all(
addresses.map(async ({ address, index }) => {
const [cooldownEnd, underlyingAmount] = await contract.cooldowns(address);
const [cooldownEnd, underlyingAmount] = await contract.cooldowns(
address,
blockTag,
);
const shares = adapter
? await adapter["requestShares(address)"](address)
? await adapter["requestShares(address)"](address, blockTag)
: underlyingAmount;
const expectedAssets = adapter
? await adapter["requestAssets(address)"](address)
? await adapter["requestAssets(address)"](address, blockTag)
: underlyingAmount;
const amountStr = formatUnits(underlyingAmount, 18);
const isBalancePositive = underlyingAmount > 0 || shares > 0;
Expand Down Expand Up @@ -149,7 +157,12 @@ const fetchUnstakerStates = async (signer, adapter, addresses) => {

if (!adapter) return states;

return orderPendingUnstakerStates(states, requestCount, maxUnstakers);
return orderPendingUnstakerStates(
states,
requestCount,
maxUnstakers,
blockNumber,
);
};

// --- MAIN FUNCTIONS ---
Expand Down
11 changes: 9 additions & 2 deletions src/js/utils/ethenaQueue.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const orderPendingUnstakerStates = (states, totalRequests, maxUnstakers) => {
const orderPendingUnstakerStates = (
states,
totalRequests,
maxUnstakers,
blockNumber,
) => {
const activeStates = states.filter((state) => state.shares > 0n);
if (activeStates.length === 0) return [];

Expand All @@ -19,8 +24,10 @@ const orderPendingUnstakerStates = (states, totalRequests, maxUnstakers) => {
const unstakerIndex = Number(requestIndex % maxUnstakers);
const state = statesByIndex.get(unstakerIndex);
if (!state) {
const blockContext =
blockNumber === undefined ? "" : ` at block ${blockNumber}`;
throw new Error(
`Missing Ethena unstaker ${unstakerIndex} for pending request ${requestIndex}`,
`Missing Ethena unstaker ${unstakerIndex} for pending request ${requestIndex}${blockContext}`,
);
}
return state;
Expand Down
4 changes: 2 additions & 2 deletions test/js/ethenaQueue.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ const run = async () => {

{
assert.throws(
() => orderPendingUnstakerStates([state(0)], 45n, 42n),
/Missing Ethena unstaker 2 for pending request 44/,
() => orderPendingUnstakerStates([state(0)], 45n, 42n, 123456),
/Missing Ethena unstaker 2 for pending request 44 at block 123456/,
);
}
};
Expand Down
Loading