Introduction
If you've been following the Xahau blockchain, you know that claiming rewards is a fundamental part of participating in the ecosystem. Every account that opts in can claim rewards approximately every 30 days. But here's the thing: manually tracking when to claim and executing transactions every month can become tedious. The closest thing that exists today is the fantastic Balance Adjustment app for Xaman, which sends you a push notification as the date approaches so you can claim your XAH. What if we could automate this entire process?
Today, I'm excited to share a complete example solution that automates reward claiming on Xahau using Hooks and CronSet transactions. This tutorial covers the entire lifecycle of reward automation. Of course, it's an example meant to inspire others to build better solutions.
The Problem We're Solving
Let me paint you a picture. You have multiple Xahau accounts, each eligible for rewards every 30 days, 2 hours, and 13 minutes (precisely 2,599,980 seconds). Missing a claim means delaying your rewards. Tracking multiple accounts manually? That's a recipe for mistakes and missed opportunities.
The traditional approach requires:
- Remembering when each account last claimed
- Calculating the next eligible time
- Being available to submit transactions at the right moment
- Repeating this process indefinitely
There has to be a better way, right?
The Solution: Combining Hooks with CronSet
You can find the repository with the entire process at this link: https://github.com/Ekiserrepe/cron-claimreward-xahau
Our automated reward claiming system leverages two powerful Xahau features:
- Hooks: Smart contracts that execute automatically on transactions
- CronSet: Scheduled transactions that run at predetermined intervals
By combining these technologies, we create a "set it and forget it" system that claims your rewards automatically for months or even years ahead.
Understanding the Architecture
The system consists of several interconnected components:
Account Setup → Hook Installation → Cron Scheduling → Automated Execution
Each component plays a crucial role:
1. The ClaimReward Hook (C Code)
The heart of our automation is a Hook written in C that executes ClaimReward transactions automatically. This hook is a simplified copy of the TreasuryHook which I recommend checking out.
The Hook hash (805351CE26FB79DA00647CEFED502F7E15C2ACCCE254F11DEFEDDCE241F8E9CA) is consistent across both testnet and mainnet, ensuring reliability and trustworthiness.
#include "hookapi.h"
// Macro to flip endianness for 32-bit values
#define FLIP_ENDIAN(x) ((((x) & 0xFF000000) >> 24) | \
(((x) & 0x00FF0000) >> 8) | \
(((x) & 0x0000FF00) << 8) | \
(((x) & 0x000000FF) << 24))
// ClaimReward transaction template
uint8_t ctxn[251] =
{
/* size,upto */
/* 3, 0, tt = ClaimReward */ 0x12U, 0x00U, 0x62U,
/* 5, 3 flags */ 0x22U, 0x00U, 0x00U, 0x00U, 0x00U,
/* 5, 8, sequence */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U,
/* 6, 13, firstledgersequence */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U,
/* 6, 19, lastledgersequence */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U,
/* 9, 25, fee */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
/* 35, 34, signingpubkey */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* 22, 69, account */ 0x81U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* 22, 91, issuer */ 0x84U, 0x14U, 0xB5U, 0xF7U, 0x62U, 0x79U, 0x8AU, 0x53U, 0xD5U, 0x43U, 0xA0U, 0x14U, 0xCAU, 0xF8U, 0xB2U, 0x97U, 0xCFU, 0xF8U, 0xF2U, 0xF9U, 0x37U, 0xE8U,
/* 138, 113 emit details */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* 0, 251 */
};
#define CFLS_OUT (ctxn + 15U)
#define CLLS_OUT (ctxn + 21U)
#define CFEE_OUT (ctxn + 26U)
#define CACCOUNT_OUT (ctxn + 71U)
#define CEMIT_OUT (ctxn + 113U)
#define BE_DROPS(drops)\
{\
uint64_t drops_tmp = drops;\
uint8_t* b = (uint8_t*)&drops;\
*b++ = 0b01000000 + (( drops_tmp >> 56 ) & 0b00111111 );\
*b++ = (drops_tmp >> 48) & 0xFFU;\
*b++ = (drops_tmp >> 40) & 0xFFU;\
*b++ = (drops_tmp >> 32) & 0xFFU;\
*b++ = (drops_tmp >> 24) & 0xFFU;\
*b++ = (drops_tmp >> 16) & 0xFFU;\
*b++ = (drops_tmp >> 8) & 0xFFU;\
*b++ = (drops_tmp >> 0) & 0xFFU;\
}
// Callback function
int64_t cbak(uint32_t reserve)
{
accept((uint32_t)"Callback completed", 18, __LINE__);
return 0;
}
// Main hook function using state_foreign for custom namespace
int64_t hook(uint32_t reserved)
{
// Proceed with ClaimReward emission
uint32_t current_ledger = ledger_seq();
uint32_t fls = current_ledger + 1;
uint32_t lls = fls + 4;
etxn_reserve(1);
uint8_t emithash[32];
hook_account((uint32_t)CACCOUNT_OUT, 20);
*((uint32_t *)(CFLS_OUT)) = FLIP_ENDIAN(fls);
*((uint32_t *)(CLLS_OUT)) = FLIP_ENDIAN(lls);
etxn_details((uint32_t)CEMIT_OUT, 138U);
int64_t fee = etxn_fee_base((uint32_t)ctxn, sizeof(ctxn));
BE_DROPS(fee);
*((uint64_t*)(CFEE_OUT)) = fee;
if (emit(SBUF(emithash), (uint32_t)ctxn, sizeof(ctxn)) != 32)
rollback((uint32_t)"AutoReward: Failed to emit claim transaction.", 45, __LINE__);
accept((uint32_t)"AutoReward: Claim emitted successfully.", 39, __LINE__);
_g(1, 1);
return 0;
}
2. Account Configuration Scripts
Before automating, we need to prepare the account properly:
- accountSet.js: Configures basic account settings
- checkAccount.js: Analyzes account status and calculates optimal claiming times
- installHook.js: Deploys the ClaimReward Hook to your account
3. The CronSet Orchestrator
This is where the magic happens. The CronSet transaction schedules automatic executions with these key parameters:
- StartTime: When to begin automated claiming
- DelaySeconds: The interval between claims (2,603,580 seconds with safety margin)
- RepeatCount: How many times to repeat (up to 256, covering over 21 years!)
The Technical Deep Dive
Let's explore the critical timing calculations that make this system reliable:
Understanding Xahau Time
Xahau uses its own epoch, starting 946,684,800 seconds after Unix epoch. This means we need to convert between Unix timestamps and Xahau time:
const RIPPLED_EPOCH_OFFSET = 946684800;
const xahauTime = unixTime - RIPPLED_EPOCH_OFFSET;
The Safety Margin Philosophy
The official reward delay is 2,599,980 seconds (30 days, 2 hours, 13 minutes). However, our system adds a 1-hour safety margin, resulting in 2,603,580 seconds. Why?
Network conditions, ledger closing times, and transaction queues can introduce minor delays. By adding this buffer, we ensure our automated claims always execute successfully rather than failing due to timing edge cases. (Feel free to remove or edit it)
Calculating Next Claim Time
The checkAccount.js script performs sophisticated calculations:
- Checks current RewardTime from account data
- Adds the delay period (2,599,980 seconds)
- Converts to human-readable format
- Suggests both exact time and safety-margin time
This dual approach lets users choose between aggressive timing (exact) or conservative timing (with 1 hour buffer).
Real-World Implementation
Let me walk you through setting up automated claiming for a real account:
Step 1: Initial Setup
First, clone the repository and install dependencies:
git clone https://github.com/Ekiserrepe/cron-claimreward-xahau.git
cd cron-claimreward-xahau
npm install
Step 2: Configure Your Account
Edit the seed in accountSet.js and run:
const xahau = require('xahau');
const { derive, utils, signAndSubmit } = require("xrpl-accountlib");
const seed = 'sYourSeedHere'; // Replace with your actual seed, get one at https://xahau-test.net/
const network = "wss://xahau-test.net";
async function setAccountFlag() {
const client = new xahau.Client(network);
const account = derive.familySeed(seed, { algorithm: "secp256k1" });
console.log(`Account: ${account.address}`);
try {
await client.connect();
console.log('Connected to Xahau');
const networkInfo = await utils.txNetworkAndAccountValues(network, account);
const prepared = {
"TransactionType": "AccountSet",
"Account": account.address,
"SetFlag": 11, // asfTshCollect - Enable Transaction Signature Hook Collection
...networkInfo.txValues,
};
console.log("Prepared AccountSet transaction:", JSON.stringify(prepared, null, 2));
const tx = await signAndSubmit(prepared, network, account);
console.log("Transaction result:", JSON.stringify(tx, null, 2));
} catch (error) {
console.error('Error:', error);
} finally {
await client.disconnect();
console.log('Disconnected from Xahau');
}
}
setAccountFlag();
This prepares your account for Hook installation.
Step 3: Install the Hook
Deploy the ClaimReward Hook to your account:
const xahau = require('xahau');
const { derive, utils, signAndSubmit } = require("xrpl-accountlib");
const seed = 'sYourSeedHere'; // Replace with your actual seed, get one at https://xahau-test.net/
const network = "wss://xahau-test.net";
async function installHook() {
const client = new xahau.Client(network);
const account = derive.familySeed(seed, { algorithm: "secp256k1" });
console.log(`Account: ${account.address}`);
try {
await client.connect();
console.log('Connected to Xahau');
const networkInfo = await utils.txNetworkAndAccountValues(network, account);
const prepared = {
"TransactionType": "SetHook",
"Account": account.address,
"Hooks": [
{
"Hook": {
"HookHash": "805351CE26FB79DA00647CEFED502F7E15C2ACCCE254F11DEFEDDCE241F8E9CA",
"HookNamespace": "0000000000000000000000000000000000000000000000000000000000000000",
"HookOn": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFBFFFFF",
"HookCanEmit": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFFFFFFBFFFFF", //Can emit ClaimReward
"Flags": 4
}
}
],
...networkInfo.txValues,
};
console.log("Prepared SetHook transaction:", JSON.stringify(prepared, null, 2));
const tx = await signAndSubmit(prepared, network, account);
console.log("Transaction result:", JSON.stringify(tx, null, 2));
} catch (error) {
console.error('Error:', error);
} finally {
await client.disconnect();
console.log('Disconnected from Xahau');
}
}
installHook();
The Hook is now ready to execute claims when triggered.
Step 4: Analyze Your Account
Run the account checker to get precise timing information:
const { Client } = require('xahau');
// Constants
const RIPPLED_EPOCH_OFFSET = 946684800;
const REWARD_DELAY_SECONDS = 2599980; // 30 days, 2 hours, 13 minutes
async function checkAccount() {
const client = new Client('wss://xahau-test.net');
const address = 'yourAddress';
try {
await client.connect();
console.log('Connected to Xahau Testnet\n');
const { result } = await client.request({
command: 'account_info',
account: address,
ledger_index: 'validated'
});
console.log('=== ACCOUNT INFO ===\n');
console.log('Address:', address);
console.log('Balance:', result.account_data.Balance, 'drops');
console.log('Balance (XAH):', (parseInt(result.account_data.Balance) / 1000000).toFixed(6), 'XAH');
console.log('Sequence:', result.account_data.Sequence);
console.log('RewardTime:', result.account_data.RewardTime || 'Not set (not opted in)');
console.log('RewardLgrFirst:', result.account_data.RewardLgrFirst || 'Not set');
console.log('RewardLgrLast:', result.account_data.RewardLgrLast || 'Not set');
console.log('\nAccount exists:', true);
console.log('Is opted in to rewards:', !!result.account_data.RewardTime);
// Calculate next claim time if opted in
if (result.account_data.RewardTime) {
const rewardTime = parseInt(result.account_data.RewardTime);
const nextClaimTime = rewardTime + REWARD_DELAY_SECONDS;
const nextClaimDate = new Date((nextClaimTime + RIPPLED_EPOCH_OFFSET) * 1000);
// Calculate with 1 hour safety margin
const nextClaimTimeSafe = nextClaimTime + 3600; // +1 hour (3600 seconds)
const nextClaimDateSafe = new Date((nextClaimTimeSafe + RIPPLED_EPOCH_OFFSET) * 1000);
// Format date as YYYY/MM/DD, time as 12-hour format
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const timeString = date.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true
});
return `${year}/${month}/${day}, ${timeString}`;
};
console.log('\n=== NEXT CLAIM INFO ===\n');
console.log('Next Claim Date:', formatDate(nextClaimDate));
console.log('\n⭐ For cronSet.js:');
console.log('StartTime:', nextClaimTime);
console.log('StartTime + 1 hour (for safety):', nextClaimTimeSafe, '-', formatDate(nextClaimDateSafe));
// Calculate DelaySeconds for 30 days, 3 hours, 13 minutes
const delaySeconds = (30 * 24 * 60 * 60) + (3 * 60 * 60) + (13 * 60);
console.log('\nDelaySeconds (30d 3h 13m):', delaySeconds);
}
} catch (error) {
console.error('Error:', error.message);
} finally {
await client.disconnect();
}
}
checkAccount();
You'll see output like:
=== NEXT CLAIM INFO ===
Next Claim Date: 2025/12/21, 1:09:41 PM
⭐ For cronSet.js:
StartTime: 819634181
StartTime + 1 hour (for safety): 819637781
Step 5: Schedule Automation
Edit cronSet.js with the calculated StartTime from last code (or 0 if its the first time you are opt-in your rewards) and run:
const xahau = require('xahau');
const { derive, utils, signAndSubmit } = require("xrpl-accountlib");
const seed = 'sYourSeedHere'; // Replace with your seed
const network = "wss://xahau-test.net";
async function createCronSet() {
const client = new xahau.Client(network);
const account = derive.familySeed(seed, { algorithm: "secp256k1" });
console.log(`Account: ${account.address}`);
try {
await client.connect();
console.log('Connected to Xahau');
const networkInfo = await utils.txNetworkAndAccountValues(network, account);
// Convert current time to Ripple Epoch (seconds since January 1, 2000 00:00 UTC)
const RIPPLE_EPOCH_OFFSET = 946684800;
const currentUnixTime = Math.floor(Date.now() / 1000);
const startTimeRippleEpoch = currentUnixTime - RIPPLE_EPOCH_OFFSET + 60; // Start in 1 minute
const prepared = {
"TransactionType": "CronSet",
"Account": account.address, // Your Hook address
"StartTime": 0,//Check checkAccount.js to get your StartTime number if you already opt-in, Or use 0 for immediate start if you didn't top-in yet.
"RepeatCount": 256, // Max number of times to repeat the task
"DelaySeconds": 2603580, // 30 days, 3 hours, 13 minutes (Regular claim interval + 1 hour safety margin)
...networkInfo.txValues,
};
console.log("Prepared CronSet transaction:", JSON.stringify(prepared, null, 2));
const tx = await signAndSubmit(prepared, network, account);
console.log("Transaction result:", JSON.stringify(tx, null, 2));
} catch (error) {
console.error('Error:', error);
} finally {
await client.disconnect();
console.log('Disconnected from Xahau');
}
}
createCronSet();
Congratulations! Your rewards will now be claimed automatically.
Advanced Considerations
Handling First-Time Users
If you've never opted into rewards before, use StartTime: 0 in your CronSet configuration. This triggers an immediate first claim and starts your reward cycle.
Choosing RepeatCount Wisely
Each repeat covers approximately 30 days. Here's a quick reference:
- 12 repeats = ~1 year of automation
- 24 repeats = ~2 years
- 256 repeats (maximum) = ~21 years
Consider your long-term commitment to the network when choosing this value.
Network Selection
Always test on testnet first:
const network = 'wss://xahau-test.net'; // Testing
// const network = 'wss://xahau.network'; // Production
Monitoring Your Automation
Even with automation, periodic checks are wise:
- Verify CronSet transactions are executing
- Check account balance for fees
- Monitor Hook execution logs
- Validate reward accumulation
The Business Layer Perspective
This automated reward system exemplifies what I discussed in my previous article about building business layers on Xahau. By abstracting the complexity of reward claiming into an automated system, we're creating infrastructure that enables:
- Portfolio Management: Manage multiple accounts efficiently
- Institutional Adoption: Enterprises can automate treasury operations
- User Experience: Remove friction from reward claiming
- Scalability: Handle hundreds of accounts without manual intervention
Security Considerations
Hook Verification
The provided Hook hash is public and verifiable. If you prefer maximum security, compile the claimReward.c file yourself and verify the hash matches or create a new one!
Transaction Fees
Ensure your account maintains sufficient XAH balance for:
- CronSet execution fees
- ClaimReward transaction fees
Troubleshooting Common Issues
"Account not eligible for rewards yet"
Solution: Wait for the exact RewardTime + delay period, or use the safety margin suggestion.
"Insufficient balance for fees"
Solution: Maintain at least 10 XAH for fee reserves.
"Hook is not being triggered"
Solution: Ensure account is properly configured with accountSet.js first and you installed your hook with proper HookOn and HookCanEmit and Flags values.
The Bigger Picture
This automated reward system represents more than just convenience, it's a glimpse into the future of blockchain automation. By combining Hooks (smart contracts) with CronSet (scheduled execution), we're creating autonomous financial systems that operate without human intervention.
Imagine scaling this concept:
- Automated DeFi strategies
- Scheduled payment distributions
- Autonomous treasury management
- Self-executing compliance operations
The infrastructure we're building today lays the foundation for tomorrow's decentralized autonomous organizations.
Future Enhancements
While the current system is production-ready, several enhancements could add value:
- Multi-account Management: Handle multiple accounts from a single interface
- Notification System: Alert users when claims execute
- Analytics Dashboard: Track reward accumulation over time
- Dynamic Optimization: Adjust timing based on network conditions
- Integration APIs: Connect with existing portfolio management tools
Conclusion
Automating reward claims on Xahau isn't just about saving time, it's about building reliable, scalable infrastructure for the future of finance solutions. By combining Hooks and CronSet transactions, we've created a system that runs autonomously for years, ensuring you never miss a reward claim again.
The code is open source and available at github.com/Ekiserrepe/cron-claimreward-xahau. I encourage you to explore it, test it on testnet, and create new improvements.
Remember: the best automation is the one you set up once and forget about. With this system, your Xahau rewards practically claim themselves.
Acknowledgments
Special thanks to Denis Angell and Satish from XRPL Labs for their pioneering work on Hooks and assistance in developing this solution.
Top comments (0)