- Queries network capacity before scheduling operations
- Uses exponential backoff with jitter to find optimal execution times
- Self-sustains by automatically rescheduling after each execution
- Gracefully handles network congestion and capacity constraints
- Supports multiple scheduling methods (
scheduleCallandscheduleCallWithPayer) - Demonstrates one-shot immediate execution using
executeCallOnPayerSignature
You can take a look at the complete code in the
tutorial-hss-rebalancer-capacity-aware
repository.
What You’ll Build
ARebalancerCapacityAware contract that:
- Starts a rebalancing loop with configurable intervals
- Checks network capacity using
hasScheduleCapacity()before scheduling - Applies intelligent retry logic with exponential backoff and randomized jitter
- Supports two scheduling methods:
scheduleCallandscheduleCallWithPayer - Executes rebalances automatically via scheduled transactions
- Reschedules itself after each execution, creating a self-sustaining loop
- Demonstrates one-shot execution using
executeCallOnPayerSignature - Can be stopped by canceling pending scheduled transactions
- DeFi vault rebalancing
- Periodic token distributions
- Automated treasury management
- Time-based protocol operations
Prerequisites
- Completion of Part 1: Schedule Smart Contract Calls
- ECDSA account from the Hedera Portal
- Understanding of DeFi rebalancing concepts
Table of Contents
- Setup Project
- Step 1: Understanding the Architecture
- Step 2: Create the Rebalancer Contract
- Step 3: Deploy the Contract
- Step 4: Configure the Contract
- Step 5: Start Rebalancing
- Step 6: Monitor Rebalancing Operations
- Step 7: Stop Rebalancing
- Step 8: One-Shot Immediate Execution (Optional)
- Step 9: Run Tests (Optional)
- Conclusion
- Additional Resources
Setup Project
If you completed Part 1, you can use the same project. Otherwise, set up a new project:🚧 What's new: Hardhat 2 → 3
🚧 What's new: Hardhat 2 → 3
Key differences in Hardhat 3:
- compile → build
npx hardhat compileis nownpx hardhat build. This is the big one. The v3 migration guide explicitly shows using thebuildtask. - project init switch
v2 commonly usednpx hardhatornpx hardhat initto bootstrap. In v3 it’snpx hardhat --init.
- keystore helper commands are new
v3’s recommended flow includes a keystore plugin with commands likenpx hardhat keystore set HEDERA_RPC_URLandnpx hardhat keystore set HEDERA_PRIVATE_KEY. These weren’t standard in v2. - Foundry-compatible Solidity tests
In addition to offering Javascript/Typescript integration tests, Hardhat v3 also integrates Foundry-compatible Solidity tests that allows developers to write unit tests directly in Solidity
- Enhanced Network Management
v3 allows tasks to create and manage multiple network connections simultaneously which is a significant improvement over the single, fixed connection available in version 2. This provides greater flexibility for scripts and tests that interact with multiple networks.
HEDERA_RPC_URL, we’ll have https://testnet.hashio.io/api
HEDERA_PRIVATE_KEY, enter the HEX Encoded Private Key for your ECDSA account from the Hedera Portal.
We also need a second private key for testing purposes:
HEDERA_PRIVATE_KEY_2, enter another HEX Encoded Private Key for a second ECDSA account.
Now let’s remove the default contracts and scripts that come with the Hardhat project:
Install Dependencies
Next, install the required dependencies:github:hashgraph/hedera-smart-contracts. This also gets installed at @hashgraph/smart-contracts so we can easily call these contracts from our own contract.
Configure hardhat.config.ts:
hardhat.config.ts
Step 1: Understanding the Architecture
Before diving into code, let’s understand the key concepts that make this rebalancer capacity-aware.The Capacity Problem
When multiple contracts schedule transactions for the same future time:- Network capacity for that second may be exhausted
- Subsequent scheduling attempts fail
- Operations get delayed or fail entirely
The Solution: Capacity-Aware Scheduling
Our rebalancer uses three key Hedera features: 1. hasScheduleCapacity(expirySecond, gasLimit)- Queries if a specific future second can accept a scheduled transaction
- Returns
trueif capacity is available,falseotherwise - Allows contracts to “probe” future availability
- If desired time lacks capacity, try progressively later times: +1s, +2s, +4s, +8s…
- Add random jitter to avoid “thundering herd” where all contracts retry at the same moment
- Spreads load across multiple seconds
- Provides pseudorandom seeds for jitter calculation
- Enables true on-chain randomness without external oracles
- Each contract gets different jitter, naturally distributing load
Scheduling Methods
This tutorial demonstrates three different scheduling approaches:| Method | Use Case | Payer | Loopable |
|---|---|---|---|
scheduleCall | Automated recurring operations | Caller | ✅ Yes |
scheduleCallWithPayer | Recurring with contract as payer | Contract | ✅ Yes |
executeCallOnPayerSignature | One-shot immediate execution | Contract | ❌ No |
How It Works Together
Why This MattersOn traditional EVM chains, you’d need:
- Off-chain service to monitor network congestion
- Manual intervention to adjust timing
- External keeper network that understands capacity
Step 2: Create the Rebalancer Contract
CreateRebalancerCapacityAware.sol in your contracts directory:
contracts/RebalancerCapacityAware.sol
How It Works
- setPayer(): Configures which address will pay for scheduled transactions (typically the contract itself)
-
setSchedulingMethod(): Switches between
scheduleCall(false) andscheduleCallWithPayer(true) - startRebalancing(): Initializes the loop and schedules the first rebalance using capacity-aware logic
-
_findAvailableSecond(): The core capacity-awareness algorithm:
- First checks if desired time has capacity
- If not, tries exponentially increasing delays: +1s, +2s, +4s, +8s…
- Adds random jitter (0 to baseDelay) to each attempt
- Uses Hedera’s PRNG for true on-chain randomness
-
rebalance(): Executed automatically by scheduled transactions:
- Increments counter (in real DeFi, would perform actual rebalancing)
- Schedules next execution using capacity-aware logic
- Creates self-sustaining loop
- stopRebalancing(): Cancels pending schedule and marks loop inactive
-
demoImmediateExecution(): Demonstrates one-shot execution using
executeCallOnPayerSignature - HBAR Requirement: Contract must hold HBAR to pay for all scheduled executions
Step 3: Deploy the Contract
Createdeploy. ts in the scripts directory:
scripts/deploy.ts
Copy the deployed contract address and set it as an environment variable for
the next steps.
verify-bundles/RebalancerCapacityAware/metadata.json file to Hashscan to verify this contract.
Step 4: Configure the Contract
Before starting the rebalancing loop, you need to configure the payer and scheduling method.Set the Contract as Payer
CreatesetPayer.ts in the scripts directory:
scripts/setPayer. ts
Choose a Scheduling Method
You have two options for scheduling. Choose one: Option 1: scheduleCall (default) CreatesetSchedulingMethodScheduleCall.ts:
scripts/setSchedulingMethodScheduleCall.ts
setSchedulingMethodScheduleCallWithPayer.ts:
scripts/setSchedulingMethodScheduleCallWithPayer. ts
Step 5: Start Rebalancing
CreatestartRebalancing.ts in the scripts directory:
scripts/startRebalancing.ts
What’s Happening
startRebalancing(15)calculates desired time:now + 15 seconds- Contract checks:
hasScheduleCapacity(desiredTime, 2_000_000)? - If capacity available → schedules at desired time
- If not → applies exponential backoff with jitter to find available slot
- Emits
RebalancingStartedwith actual scheduled time and scheduling method - After ~15 seconds, network automatically executes
rebalance() rebalance()schedules next execution → creates self-sustaining loop
Step 6: Monitor Rebalancing Operations
CreatemonitorRebalancing. ts to observe the rebalancing loop:
scripts/monitorRebalancing.ts
Rebalance Count increments every ~15 seconds as scheduled transactions execute automatically. When the contract runs out of HBAR, scheduling will fail, and the count will stop increasing however the state remains Active: true until you explicitly stop rebalancing.
Check Contract Config
You can also create a simple script to check the current configuration:scripts/getConfig.ts
View Events on HashScan
Navigate to your contract’s events page to see: RebalanceScheduled Events:- Shows when capacity-aware scheduling found an available slot
chosenTime === desiredTimemeans ideal time had capacitychosenTime > desiredTimemeans backoff was neededschedulingMethodshows which method was used
- Confirms automatic execution by the network
- Tracks total rebalance operations performed
https://hashscan.io/testnet/contract/$CONTRACT_ADDRESS/events
Step 7: Stop Rebalancing
CreatestopRebalancing.ts to halt the loop:
scripts/stopRebalancing.ts
What Happened
stopRebalancing()calleddeleteSchedule(lastScheduleAddress)- Pending scheduled transaction was canceled (best effort)
config.activeset tofalse- Even if a scheduled
rebalance()executes, therequire(config.active)check prevents further scheduling - Loop is fully stopped
Step 8: One-Shot Immediate Execution (Optional)
This demo shows how to useexecuteCallOnPayerSignature for a single, immediate function call. This method is not loopable due to Hedera’s recursion protection.
Create demoImmediateExecution.ts:
scripts/demoImmediateExecution.ts
DemoActionExecuted event emitted.
Step 9: Run Tests (Optional)
You can find both types of tests in the tutorial-hss-rebalancer-capacity-aware repository. You will find the following files: The repository includes both Solidity unit tests and TypeScript integration tests.Solidity Unit Tests (contracts/RebalancerCapacityAware.t.sol)
These tests validate:
- Initial state: Verifies contract deploys with inactive configuration
- Payer configuration: Tests setting and changing the payer address
- Scheduling method switching: Verifies switching between
scheduleCallandscheduleCallWithPayer - Start/stop logic: Confirms only inactive rebalancers can be started and active ones can be stopped
- Configuration validation: Ensures interval must be greater than zero
- HBAR handling: Verifies contract can receive HBAR for funding scheduled operations
- State management: Tests that rebalance count and timestamps are properly maintained
TypeScript Integration Tests (test/RebalancerCapacityAware.ts)
These tests run against Hedera testnet and validate:
- Deployment and funding: Deploys with substantial HBAR balance and validates initial state
- scheduleCall method: Tests automated recurring rebalancing with
scheduleCall - scheduleCallWithPayer method: Tests automated recurring rebalancing with
scheduleCallWithPayer(contract as payer) - executeCallOnPayerSignature: Demonstrates one-shot immediate execution
- deleteSchedule: Verifies schedule deletion via
stopRebalancing - Capacity awareness: Tests that the contract successfully finds available time slots using
hasScheduleCapacity - Input validation: Tests error handling for invalid inputs
- Scheduling method switching: Verifies switching between scheduling methods
Conclusion
You’ve built a sophisticated capacity-aware DeFi rebalancer that demonstrates advanced patterns with Hedera’s Schedule Service! In this tutorial, you learned how to:- Query network capacity using
hasScheduleCapacity() - Implement exponential backoff with randomized jitter
- Use Hedera’s PRNG for true on-chain randomness
- Build self-sustaining loops that automatically reschedule
- Choose between scheduling methods:
scheduleCallvsscheduleCallWithPayer - Handle one-shot execution using
executeCallOnPayerSignature - Handle network congestion gracefully
- Cancel scheduled operations when needed
Key Takeaways
- Capacity-aware scheduling prevents network congestion. Contracts cooperate with the network’s throttling model
- Exponential backoff + jitter distributes load. Avoids “thundering herd” where all contracts compete for the same slot
- True on-chain randomness via PRNG. No external oracles needed for jitter calculation
- Multiple scheduling methods for different use cases. Use
scheduleCallorscheduleCallWithPayerfor recurring operations,executeCallOnPayerSignaturefor one-shots - This level of network awareness doesn’t exist on most EVM chains. Hedera enables truly intelligent on-chain automation
Real-World Applications
This pattern can be extended to:- DeFi Vaults: Automatic portfolio rebalancing based on price oracles
- Liquidity Management: Periodic adjustment of AMM positions
- Treasury Operations: Scheduled fund distributions or buybacks
- Yield Optimization: Regular harvesting and compounding of rewards
- DAO Governance: Time-delayed execution of approved proposals