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
30 changes: 30 additions & 0 deletions script/SendRewardsToStaker.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.28;

import {Script, console2} from "forge-std/Script.sol";
import {RewardAccumulator} from "../src/RewardAccumulator.sol";

/// @notice Calls RewardAccumulator.sendRewardsToStaker() — and only that. Reverts with
/// RewardAccumulator.WaitForNextRewardTime if the reward window hasn't elapsed yet.
/// @dev sendRewardsToStaker() is permissionless in open mode (see RewardAccumulator's dev notes), so
/// PRIVATE_KEY only needs to be funded for gas, no special role required.
/// Use `script/send-rewards-to-staker.sh` to poll and wait (with live progress output) for the
/// window to open before invoking this — a Solidity wait loop inside the script can't stream
/// progress, since forge only prints console2 logs after the whole run() call finishes.
/// Required environment variables:
/// REWARD_ACCUMULATOR_ADDRESS — address of the deployed RewardAccumulator
/// PRIVATE_KEY — broadcaster private key (hex, with or without 0x prefix)
contract SendRewardsToStaker is Script {
function run() external {
address accumulatorAddress = vm.envAddress("REWARD_ACCUMULATOR_ADDRESS");
uint256 broadcasterKey = vm.envUint("PRIVATE_KEY");

RewardAccumulator accumulator = RewardAccumulator(accumulatorAddress);

vm.startBroadcast(broadcasterKey);
accumulator.sendRewardsToStaker();
vm.stopBroadcast();

console2.log("Sent accumulated rewards to staker");
}
}
49 changes: 49 additions & 0 deletions script/send-rewards-to-staker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Polls RewardAccumulator.nextRewardTime() and waits — printing live progress — until the
# reward window elapses, then broadcasts SendRewardsToStaker.s.sol (which calls only
# sendRewardsToStaker()).
#
# The wait/poll loop lives here in bash rather than inside the Solidity script: forge only
# flushes console2 logs after the whole run() call returns, so a Solidity-side wait loop
# prints nothing while it's waiting, even across a multi-hour/day gap.
#
# Required environment variables (e.g. from .env):
# REWARD_ACCUMULATOR_ADDRESS — address of the deployed RewardAccumulator
# PRIVATE_KEY — broadcaster private key (hex, with or without 0x prefix)
#
# Optional:
# RPC_ALIAS — foundry.toml [rpc_endpoints] alias (or a raw RPC URL), defaults to "mainnet"
# POLL_INTERVAL — seconds between chain checks while waiting, defaults to 60

set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."

if [ -f .env ]; then
set -a
# shellcheck disable=SC1091
source .env
set +a
fi

: "${REWARD_ACCUMULATOR_ADDRESS:?REWARD_ACCUMULATOR_ADDRESS must be set}"
: "${PRIVATE_KEY:?PRIVATE_KEY must be set}"

RPC_ALIAS="${RPC_ALIAS:-mainnet}"
POLL_INTERVAL="${POLL_INTERVAL:-2}"

while true; do
next_reward_time=$(cast call "$REWARD_ACCUMULATOR_ADDRESS" "nextRewardTime()(uint256)" \
--rpc-url "$RPC_ALIAS" | awk '{print $1}')
now=$(date +%s)
remaining=$((next_reward_time - now))

if [ "$remaining" -le 0 ]; then
echo "[$(date -u +%FT%TZ)] Reward window reached, sending rewards to staker..."
break
fi

echo "[$(date -u +%FT%TZ)] Waiting for reward window: ${remaining}s remaining (next check in ${POLL_INTERVAL}s)"
sleep "$POLL_INTERVAL"
done

forge script script/SendRewardsToStaker.s.sol --rpc-url "$RPC_ALIAS" --broadcast -vvvv