Skip to content

Commit 6b6eaf4

Browse files
committed
fix: keeper bot hardening to better handle many iterations for delayed stakes execution
1 parent 18766cd commit 6b6eaf4

File tree

1 file changed

+42
-16
lines changed

1 file changed

+42
-16
lines changed

contracts/scripts/keeperBot.ts

+42-16
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import hre = require("hardhat");
88
const { ethers } = hre;
99
const MAX_DRAW_ITERATIONS = 30;
1010
const MAX_EXECUTE_ITERATIONS = 20;
11+
const MAX_DELAYED_STAKES_ITERATIONS = 50;
1112
const WAIT_FOR_RNG_DURATION = 5 * 1000; // 5 seconds
12-
const ITERATIONS_COOLDOWN_PERIOD = 20 * 1000; // 20 seconds
13-
const HIGH_GAS_LIMIT = { gasLimit: 50000000 }; // 50M gas
13+
const ITERATIONS_COOLDOWN_PERIOD = 10 * 1000; // 10 seconds
14+
const HIGH_GAS_LIMIT = { gasLimit: 50_000_000 }; // 50M gas
1415
const HEARTBEAT_URL = env.optionalNoDefault("HEARTBEAT_URL_KEEPER_BOT");
1516
const SUBGRAPH_URL = env.require("SUBGRAPH_URL");
1617
const MAX_JURORS_PER_DISPUTE = 1000; // Skip disputes with more than this number of jurors
@@ -219,7 +220,7 @@ const drawJurors = async (dispute: { id: string; currentRoundIndex: string }, it
219220
try {
220221
await core.callStatic.draw(dispute.id, iterations, HIGH_GAS_LIMIT);
221222
} catch (e) {
222-
logger.info(`Draw: will fail for ${dispute.id}, skipping`);
223+
logger.error(`Draw: will fail for ${dispute.id}, skipping`);
223224
return success;
224225
}
225226
try {
@@ -241,7 +242,7 @@ const executeRepartitions = async (dispute: { id: string; currentRoundIndex: str
241242
try {
242243
await core.callStatic.execute(dispute.id, dispute.currentRoundIndex, iterations, HIGH_GAS_LIMIT);
243244
} catch (e) {
244-
logger.info(`Execute: will fail for ${dispute.id}, skipping`);
245+
logger.error(`Execute: will fail for ${dispute.id}, skipping`);
245246
return success;
246247
}
247248
try {
@@ -260,7 +261,7 @@ const executeRuling = async (dispute: { id: string }) => {
260261
try {
261262
await core.callStatic.executeRuling(dispute.id);
262263
} catch (e) {
263-
logger.info(`ExecuteRuling: will fail for ${dispute.id}, skipping`);
264+
logger.error(`ExecuteRuling: will fail for ${dispute.id}, skipping`);
264265
return success;
265266
}
266267
try {
@@ -290,7 +291,7 @@ const withdrawAppealContribution = async (
290291
contribution.choice
291292
);
292293
} catch (e) {
293-
logger.info(
294+
logger.warn(
294295
`WithdrawFeesAndRewards: will fail for dispute #${disputeId}, round #${roundId}, choice ${contribution.choice} and beneficiary ${contribution.contributor.id}, skipping`
295296
);
296297
return success;
@@ -323,6 +324,40 @@ const withdrawAppealContribution = async (
323324
return success;
324325
};
325326

327+
const executeDelayedStakes = async () => {
328+
const { sortition } = await getContracts();
329+
330+
// delayedStakes = 1 + delayedStakeWriteIndex - delayedStakeReadIndex
331+
const delayedStakesRemaining = BigNumber.from(1)
332+
.add(await sortition.delayedStakeWriteIndex())
333+
.sub(await sortition.delayedStakeReadIndex());
334+
335+
const delayedStakes = delayedStakesRemaining.lt(MAX_DELAYED_STAKES_ITERATIONS)
336+
? delayedStakesRemaining
337+
: BigNumber.from(MAX_DELAYED_STAKES_ITERATIONS);
338+
339+
if (delayedStakes.eq(0)) {
340+
logger.info("No delayed stakes to execute");
341+
return true;
342+
}
343+
logger.info(`Executing ${delayedStakes} delayed stakes, ${delayedStakesRemaining} remaining`);
344+
let success = false;
345+
try {
346+
await sortition.callStatic.executeDelayedStakes(delayedStakes);
347+
} catch (e) {
348+
logger.error(`executeDelayedStakes: will fail because of ${JSON.stringify(e)}`);
349+
return success;
350+
}
351+
try {
352+
const gas = (await sortition.estimateGas.executeDelayedStakes(delayedStakes)).mul(150).div(100); // 50% extra gas
353+
const tx = await (await sortition.executeDelayedStakes(delayedStakes, { gasLimit: gas })).wait();
354+
logger.info(`executeDelayedStakes txID: ${tx?.transactionHash}`);
355+
} catch (e) {
356+
handleError(e);
357+
}
358+
return success;
359+
};
360+
326361
const getMissingJurors = async (dispute: { id: string; currentRoundIndex: string }) => {
327362
const { core } = await getContracts();
328363
const { nbVotes, drawnJurors } = await core.getRoundInfo(dispute.id, dispute.currentRoundIndex);
@@ -594,18 +629,9 @@ async function main() {
594629
// ----------------------------------------------- //
595630
// EXECUTE DELAYED STAKES //
596631
// ----------------------------------------------- //
597-
// delayedStakes = 1 + delayedStakeWriteIndex - delayedStakeReadIndex
598-
const delayedStakes = BigNumber.from(1)
599-
.add(await sortition.delayedStakeWriteIndex())
600-
.sub(await sortition.delayedStakeReadIndex());
601632

602633
if (await isPhaseStaking()) {
603-
if (delayedStakes.gt(0)) {
604-
logger.info("Executing delayed stakes");
605-
await sortition.executeDelayedStakes(delayedStakes);
606-
} else {
607-
logger.info("No delayed stakes to execute");
608-
}
634+
await executeDelayedStakes();
609635
}
610636

611637
await sendHeartbeat();

0 commit comments

Comments
 (0)