Skip to main content

Query daily wallet rewards

Use this example to fetch the most recent UTC day of MOBILE rewards for every gateway owned by a Solana wallet. It calls GET /v0/solaccounts/{address}/gateways/mobile/rewards/sum with a daily bucket and prints totals plus PoC/Data splits for each gateway.

Prerequisites

  • Node.js 18 or newer (ships with the Fetch API).
  • An API key exported as HELIUMGEEK_API_KEY.
  • A Solana wallet address provided as SOLANA_WALLET_ADDRESS (or ACCOUNT_ADDRESS) or supplied as the first CLI argument.
  • (Optional) Set PAGE_LIMIT (or WALLET_PAGE_LIMIT) to override the default 1000 gateway page size while testing pagination.

Run the example

  1. Download wallet-mobile-daily.js and save it locally.

  2. Export the environment variables and run:

    export HELIUMGEEK_API_KEY="your-api-key"
    export SOLANA_WALLET_ADDRESS="your-wallet-address"
    node wallet-mobile-daily.js

    Or pass the address inline:

    HELIUMGEEK_API_KEY="your-api-key" node wallet-mobile-daily.js <wallet-address>

Script

wallet-mobile-daily.js
#!/usr/bin/env node

const BASE_URL = (process.env.HELIUMGEEK_API_BASE_URL ?? 'https://api.heliumgeek.com/v0').replace(/\/$/, '');
const API_KEY = process.env.HELIUMGEEK_API_KEY;
const WALLET_ADDRESS =
(process.argv[2] ?? process.env.SOLANA_WALLET_ADDRESS ?? process.env.ACCOUNT_ADDRESS ?? '').trim();
const BASE_URL_OBJECT = new URL(`${BASE_URL}/`);
const PAGE_LIMIT = Number(process.env.WALLET_PAGE_LIMIT ?? process.env.PAGE_LIMIT ?? 100);

if (!API_KEY) {
console.error('Set HELIUMGEEK_API_KEY before running this script.');
process.exit(1);
}

if (!WALLET_ADDRESS) {
console.error('Provide the Solana wallet address as an argument or set SOLANA_WALLET_ADDRESS.');
process.exit(1);
}

async function main() {
const { minTime, maxTime } = previousUtcDayRange();

const rewardSums = await fetchGatewayRewardSums(minTime, maxTime);
console.log(`Fetched ${rewardSums.length} reward records for ${minTime.toISOString()}${maxTime.toISOString()}.`);

if (rewardSums.length === 0) {
console.log(
`Wallet ${WALLET_ADDRESS} earned no MOBILE rewards between ${minTime.toISOString()} and ${maxTime.toISOString()}.`
);
return;
}

rewardSums.sort((a, b) => Number(b.tokenSum ?? 0) - Number(a.tokenSum ?? 0));

console.log(`Mobile rewards by gateway for wallet ${WALLET_ADDRESS}`);
console.log(`Window: ${minTime.toISOString()}${maxTime.toISOString()}`);
console.log('='.repeat(80));

let walletTotal = 0;
for (const entry of rewardSums) {
walletTotal += Number(entry.tokenSum ?? 0);
report(entry);
}

console.log('-'.repeat(80));
console.log(`Wallet total: ${formatToken(walletTotal)} MOBILE`);
}

async function fetchGatewayRewardSums(minTime, maxTime) {
return await fetchPaginated(
`solaccounts/${encodeURIComponent(WALLET_ADDRESS)}/gateways/mobile/rewards/sum`,
{
min_time: minTime.toISOString(),
max_time: maxTime.toISOString(),
bucket: 'day',
limit: PAGE_LIMIT,
}
);
}

async function fetchPaginated(path, query = {}) {
const results = [];
let nextUrl = buildUrl(path, query);
let page = 1;

while (nextUrl) {
console.log(`[wallet-mobile-daily] Requesting page ${page}: ${nextUrl}`);
const response = await fetch(nextUrl, {
headers: {
'x-api-key': API_KEY,
},
});

if (response.status === 404) {
break;
}

if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}

const payload = await response.json();
if (!payload?.data) {
break;
}

results.push(...payload.data);
nextUrl = payload.next ? new URL(payload.next, nextUrl).toString() : null;
page += 1;
}

return results;
}

function report(summary) {
const unit = (summary.unit ?? 'mobile').toUpperCase();
const pocShare = formatPercentage(summary.pocSum, summary.sum);
const dataShare = formatPercentage(summary.dcTransferSum, summary.sum);

console.log(
`${summary.address}${formatToken(summary.tokenSum)} ${unit} | PoC share ${pocShare} | Data share ${dataShare}`
);
}

function formatToken(value) {
if (value === undefined || value === null) {
return '0.0000';
}

return new Intl.NumberFormat('en-US', {
minimumFractionDigits: 4,
maximumFractionDigits: 4,
}).format(Number(value));
}

function formatPercentage(portion, total) {
const ratio =
Number(total ?? 0) > 0 ? (Number(portion ?? 0) / Number(total ?? 0)) * 100 : 0;
return `${ratio.toFixed(2)}%`;
}

function previousUtcDayRange() {
const end = startOfUtcDay(new Date());
const start = new Date(end);
start.setUTCDate(start.getUTCDate() - 1);
return { minTime: start, maxTime: end };
}

function startOfUtcDay(date) {
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
}

function buildUrl(path, query = {}) {
const url = /^https?:\/\//.test(path)
? new URL(path)
: new URL(path, BASE_URL_OBJECT);
applyQuery(url, query);
return url.toString();
}

function applyQuery(url, query = {}) {
Object.entries(query).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
url.searchParams.set(key, String(value));
}
});
}

main().catch(error => {
console.error(error.message);
process.exit(1);
});