Build a Self-Scheduling ERC20 Airdrop on Hedera
A hands-on workshop project demonstrating how smart contracts can autonomously schedule their own future execution using Hedera Schedule Service (HSS) and HIP-1215. No off-chain bots. No cron jobs. Just a contract that runs itself.
A self-scheduling ERC20 token contract that:
- Lets users register for airdrops
- Picks random recipients using Hedera's on-chain PRNG (0x169)
- Mints tokens to winners at configurable intervals
- Schedules its own next execution via HSS (0x16b)
- Handles scheduling congestion with exponential backoff and jitter
- Stops automatically after a configurable number of drops
- Hedera Schedule Service (HSS) -- The system contract at
0x16bthat enables on-chain scheduling - HIP-1215 -- The Hedera Improvement Proposal that allows contracts to schedule calls to themselves
- Self-Scheduling Loop -- The pattern where
executeAirdrop()schedules the nextexecuteAirdrop() - Capacity-Aware Scheduling -- Using
hasScheduleCapacity()with exponential backoff to find available time slots - On-Chain PRNG -- Hedera's pseudorandom number generator at
0x169for random recipient selection
- Foundry installed
- A Hedera testnet account (portal.hedera.com)
- Testnet HBAR from the faucet
# Clone the repository
git clone https://github.com/hedera-dev/tutorial-hello-scheduled-airdrop.git
cd tutorial-hello-scheduled-airdrop
# Install dependencies
forge install
# Configure environment
cp .env.example .env
# Edit .env with your Hedera testnet credentials# Compile
forge build
# Run tests
forge test# Load environment variables
source .env
# Deploy with 10 HBAR for scheduled execution gas
forge create src/HelloScheduledAirdrop.sol:HelloScheduledAirdrop \
--rpc-url $HEDERA_RPC_URL \
--broadcast \
--private-key $HEDERA_PRIVATE_KEY \
--value 10ether \
--constructor-args "Workshop Token" "WKSP" 0
# Save the deployed address
export CONTRACT_ADDR=0x<your-deployed-address># Register yourself as a recipient
cast send $CONTRACT_ADDR 'registerForAirdrop()' \
--rpc-url $HEDERA_RPC_URL --private-key $HEDERA_PRIVATE_KEY
# Start the airdrop: 1 token every 30 seconds, 10 drops max
cast send $CONTRACT_ADDR \
'startAirdrop(uint256,uint256,uint256,string)' \
1000000000000000000 30 10 'Hello from the future!' \
--rpc-url $HEDERA_RPC_URL --private-key $HEDERA_PRIVATE_KEY
# Check status
cast call $CONTRACT_ADDR 'getStatus()(bool,uint256,uint256,uint256,uint256,uint256)' --rpc-url $HEDERA_RPC_URLOpen hashscan.io/testnet and navigate to your contract address. Click the Events tab to see AirdropExecuted events appearing every ~30 seconds -- all triggered autonomously by the Hedera network.
hello-scheduled-airdrop/
├── .env.example # Environment variable template
├── .gitignore # Git ignore rules
├── .gitmodules # Git submodule definitions
├── .vscode/
│ └── settings.json # VS Code Solidity settings
├── LICENSE # MIT License
├── README.md # This file
├── docs/
│ ├── GUIDE.md # Full step-by-step workshop guide
│ ├── HOW_IT_WORKS.md # Deep dive on the self-scheduling pattern
│ ├── PREREQUISITES.md # Setup instructions
│ └── diagrams/
│ └── architecture.md # Mermaid diagram of the self-scheduling loop
├── foundry.toml # Foundry configuration with remappings
├── lib/
│ ├── forge-std/ # Foundry standard library (submodule)
│ ├── hiero-contracts/ # Hiero system contracts (submodule)
│ └── openzeppelin-contracts/ # OpenZeppelin contracts (submodule)
├── src/
│ └── HelloScheduledAirdrop.sol # The main contract
└── test/
└── HelloScheduledAirdrop.t.sol # Foundry tests
The contract uses a self-scheduling loop pattern:
- Owner calls
startAirdrop()with campaign parameters - The contract calls
scheduleCall()on the HSS system contract (0x16b) to scheduleexecuteAirdrop()for a future time - When the scheduled time arrives, the Hedera network automatically calls
executeAirdrop() executeAirdrop()picks a random recipient via PRNG (0x169), mints tokens, and schedules the nextexecuteAirdrop()- The loop continues until
maxDropsis reached or the owner callsstopAirdrop()
For a detailed technical explanation, see docs/HOW_IT_WORKS.md.