From a6a3d416d980d5b7e7b0cb08919b2b01c9ba7724 Mon Sep 17 00:00:00 2001 From: Clawdia Date: Wed, 25 Feb 2026 09:58:01 -0600 Subject: [PATCH 1/5] fix: paginate getLogs in 9500-block chunks, switch RPC to publicnode - base.drpc.org free tier rejects ranges >10k blocks - mainnet.base.org also limits to 10k AND was returning 503 - paginate the 25k-block SeedAndRulerRevealed lookup in 9500-block chunks - switch RPC to base-rpc.publicnode.com (reliable, no range issues) --- bot/spellblock-finalize.mjs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/bot/spellblock-finalize.mjs b/bot/spellblock-finalize.mjs index bbb1fd9..d94f709 100755 --- a/bot/spellblock-finalize.mjs +++ b/bot/spellblock-finalize.mjs @@ -19,7 +19,7 @@ import { db } from './lib/db.mjs'; const __dir = dirname(fileURLToPath(import.meta.url)); const CONTRACT = '0x43F8658F3E85D1F92289e3036168682A9D14c683'; -const RPC = 'https://base.drpc.org'; +const RPC = 'https://base-rpc.publicnode.com'; // paginated getLogs in 9500-block chunks — most public RPCs limit to 10k const FOUNDRY = '/Users/starl3xx/.foundry/bin'; const BOT_BANKR_HANDLE = 'ClawdiaBotAI'; const CLAWDIA_DECIMALS = 18n; @@ -99,12 +99,17 @@ async function main() { const client = createPublicClient({ chain: base, transport: http(RPC) }); const latest = await client.getBlockNumber(); - const seedLogs = await client.getLogs({ - address: CONTRACT, - event: parseAbiItem('event SeedAndRulerRevealed(uint256 indexed roundId, uint8 spellId, bytes32 spellParam, uint8[3] validLengths)'), - fromBlock: latest - 25000n, // ~14h of Base blocks (2s/block) covers reveal→finalize gap - toBlock: 'latest', - }); + // Paginate getLogs in 9500-block chunks — public RPCs limit ranges to 10k blocks + const CHUNK = 9500n; + const lookback = 25000n; + const startBlock = latest - lookback; + const seedAbi = parseAbiItem('event SeedAndRulerRevealed(uint256 indexed roundId, uint8 spellId, bytes32 spellParam, uint8[3] validLengths)'); + const seedLogs = []; + for (let from = startBlock; from <= latest; from += CHUNK) { + const to = from + CHUNK - 1n < latest ? from + CHUNK - 1n : latest; + const chunk = await client.getLogs({ address: CONTRACT, event: seedAbi, fromBlock: from, toBlock: to }); + seedLogs.push(...chunk); + } const seedLog = seedLogs.filter(l => Number(l.args.roundId) === round.round_id).pop(); From d15026b7c75b38aa3c3a11514490d19fb8ea243f Mon Sep 17 00:00:00 2001 From: Clawdia Date: Thu, 26 Feb 2026 02:03:05 -0600 Subject: [PATCH 2/5] fix: switch to 1rpc.io/base (publicnode flaky at 2am) --- bot/spellblock-finalize.mjs | 2 +- bot/spellblock-social-reveal.mjs | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/bot/spellblock-finalize.mjs b/bot/spellblock-finalize.mjs index d94f709..c78ed57 100755 --- a/bot/spellblock-finalize.mjs +++ b/bot/spellblock-finalize.mjs @@ -19,7 +19,7 @@ import { db } from './lib/db.mjs'; const __dir = dirname(fileURLToPath(import.meta.url)); const CONTRACT = '0x43F8658F3E85D1F92289e3036168682A9D14c683'; -const RPC = 'https://base-rpc.publicnode.com'; // paginated getLogs in 9500-block chunks — most public RPCs limit to 10k +const RPC = 'https://1rpc.io/base'; // paginated getLogs in 9500-block chunks — most public RPCs limit to 10k const FOUNDRY = '/Users/starl3xx/.foundry/bin'; const BOT_BANKR_HANDLE = 'ClawdiaBotAI'; const CLAWDIA_DECIMALS = 18n; diff --git a/bot/spellblock-social-reveal.mjs b/bot/spellblock-social-reveal.mjs index 9252a85..dce321d 100644 --- a/bot/spellblock-social-reveal.mjs +++ b/bot/spellblock-social-reveal.mjs @@ -16,7 +16,7 @@ import { db } from './lib/db.mjs'; const __dir = dirname(fileURLToPath(import.meta.url)); const CONTRACT = '0x43F8658F3E85D1F92289e3036168682A9D14c683'; -const RPC = 'https://base.drpc.org'; +const RPC = 'https://1rpc.io/base'; const SPELL_NAMES = { 0: { name: 'Veto 🚫', desc: (p) => `word must NOT contain ${p}` }, @@ -87,15 +87,24 @@ async function main() { log(`Round ${roundId} | letters: ${round.letters}`); // Read SeedAndRulerRevealed event from contract + // Paginate in 9500-block chunks to stay under public RPC limits (10k max) const latest = await client.getBlockNumber(); - const logs = await client.getLogs({ - address: CONTRACT, - event: parseAbiItem( - 'event SeedAndRulerRevealed(uint256 indexed roundId, uint8 spellId, bytes32 spellParam, uint8[3] validLengths)' - ), - fromBlock: latest - 25000n, // ~14h of Base blocks; covers the reveal→post window - toBlock: 'latest', - }); + const LOOKBACK = 25000n; + const CHUNK = 9500n; + const eventAbi = parseAbiItem( + 'event SeedAndRulerRevealed(uint256 indexed roundId, uint8 spellId, bytes32 spellParam, uint8[3] validLengths)' + ); + const logs = []; + for (let from = latest - LOOKBACK; from <= latest; from += CHUNK) { + const to = from + CHUNK - 1n < latest ? from + CHUNK - 1n : latest; + const chunk = await client.getLogs({ + address: CONTRACT, + event: eventAbi, + fromBlock: from, + toBlock: to, + }); + logs.push(...chunk); + } const revealLog = logs.filter(l => Number(l.args.roundId) === roundId).pop(); if (!revealLog) { From 5fd8718004b12a7170dd113bab5cebad3606dfea Mon Sep 17 00:00:00 2001 From: Clawdia Date: Sat, 28 Feb 2026 23:02:17 -0600 Subject: [PATCH 3/5] fix: social-reveal roundId off-by-one getCurrentRound() returns the new round N, but SeedAndRulerRevealed event is for round N-1 (the round just revealed). Event filter was using roundId=N and finding nothing, causing silent failure every round. Fix: derive revealedRoundId = roundId - 1, use for event filter and announcement text. --- bot/spellblock-social-reveal.mjs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/spellblock-social-reveal.mjs b/bot/spellblock-social-reveal.mjs index dce321d..32959d5 100644 --- a/bot/spellblock-social-reveal.mjs +++ b/bot/spellblock-social-reveal.mjs @@ -79,12 +79,14 @@ async function main() { const { base } = await import('viem/chains'); const client = createPublicClient({ chain: base, transport: http(RPC) }); - // Get current round from DB + // Get current round from DB — reveal event is for the PREVIOUS round (N-1), + // because reveal-seed-and-ruler.sh opens round N then we run to announce N-1. const round = await db.getCurrentRound(); if (!round) { log('No open round in DB'); await db.end(); return; } const roundId = round.round_id; - log(`Round ${roundId} | letters: ${round.letters}`); + const revealedRoundId = roundId - 1; // The round that was just revealed on-chain + log(`Current round: ${roundId} | letters: ${round.letters} | Announcing reveal for round: ${revealedRoundId}`); // Read SeedAndRulerRevealed event from contract // Paginate in 9500-block chunks to stay under public RPC limits (10k max) @@ -106,9 +108,9 @@ async function main() { logs.push(...chunk); } - const revealLog = logs.filter(l => Number(l.args.roundId) === roundId).pop(); + const revealLog = logs.filter(l => Number(l.args.roundId) === revealedRoundId).pop(); if (!revealLog) { - log('❌ No SeedAndRulerRevealed event found — round not yet revealed or event window too narrow'); + log(`❌ No SeedAndRulerRevealed event found for round ${revealedRoundId} — not yet revealed or event window too narrow`); await db.end(); return; } @@ -127,7 +129,7 @@ async function main() { // Compose announcement const text = - `🔮 SpellBlock Round ${roundId} — Spell + Ruler revealed!\n\n` + + `🔮 SpellBlock Round ${revealedRoundId} — Spell + Ruler revealed!\n\n` + `${spell.name}: ${spellDesc}\n` + `📏 Valid lengths: ${validLengths.join(', ')}\n\n` + `Scoring + payouts happen automatically at 15:45 UTC (9:45 AM CT).\n\n` + From 6da5d418fda33fc032a63c930739a884ef37ac93 Mon Sep 17 00:00:00 2001 From: Clawdia Date: Sat, 28 Feb 2026 23:09:27 -0600 Subject: [PATCH 4/5] fix: finalize-round.sh set -e silent exit on already-finalized round cast send returns non-zero when round is already finalized, which caused set -e to kill the script before the 'Already finalized' check ran. Adding || true prevents the premature exit while preserving all logic. Observed: round 7 was already finalized but script reported exit 1 (2026-02-27). --- scripts/finalize-round.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/finalize-round.sh b/scripts/finalize-round.sh index ed62eef..5be840d 100755 --- a/scripts/finalize-round.sh +++ b/scripts/finalize-round.sh @@ -28,11 +28,13 @@ if [ -n "$REVEAL_DEADLINE" ] && [ "$NOW" -lt "$REVEAL_DEADLINE" ]; then fi echo "Finalizing Round $CURRENT_ROUND..." +# Use || true so set -e doesn't exit on non-zero cast return (e.g. "Already finalized"), +# letting the if/elif/else below handle all cases gracefully. TX=$(/Users/starl3xx/.foundry/bin/cast send $CONTRACT \ "finalizeRound()" \ --private-key $PRIVATE_KEY \ --rpc-url https://mainnet.base.org \ - --json 2>&1) + --json 2>&1) || true if echo "$TX" | grep -q "transactionHash"; then TX_HASH=$(echo $TX | /opt/homebrew/bin/jq -r '.transactionHash') From 4fe9ebd8b4e84e0537b151b3d0e7cb6559a2f706 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 1 Mar 2026 05:14:21 +0000 Subject: [PATCH 5/5] Fix reveal announcement round ID --- bot/spellblock-social-reveal.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/spellblock-social-reveal.mjs b/bot/spellblock-social-reveal.mjs index 32959d5..83a4547 100644 --- a/bot/spellblock-social-reveal.mjs +++ b/bot/spellblock-social-reveal.mjs @@ -79,13 +79,13 @@ async function main() { const { base } = await import('viem/chains'); const client = createPublicClient({ chain: base, transport: http(RPC) }); - // Get current round from DB — reveal event is for the PREVIOUS round (N-1), - // because reveal-seed-and-ruler.sh opens round N then we run to announce N-1. + // Get current round from DB — reveal-seed-and-ruler.sh reveals for this same + // currently open round. const round = await db.getCurrentRound(); if (!round) { log('No open round in DB'); await db.end(); return; } const roundId = round.round_id; - const revealedRoundId = roundId - 1; // The round that was just revealed on-chain + const revealedRoundId = roundId; // The round that was just revealed on-chain log(`Current round: ${roundId} | letters: ${round.letters} | Announcing reveal for round: ${revealedRoundId}`); // Read SeedAndRulerRevealed event from contract