Skip to content
Open
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
19 changes: 12 additions & 7 deletions bot/spellblock-finalize.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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://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;
Expand Down Expand Up @@ -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();

Expand Down
41 changes: 26 additions & 15 deletions bot/spellblock-social-reveal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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}` },
Expand Down Expand Up @@ -79,27 +79,38 @@ 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-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;
log(`Round ${roundId} | letters: ${round.letters}`);
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
// 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 revealLog = logs.filter(l => Number(l.args.roundId) === roundId).pop();
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) === 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;
}
Expand All @@ -118,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` +
Expand Down
4 changes: 3 additions & 1 deletion scripts/finalize-round.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down