
<?php
declare(strict_types=1);
/*
|--------------------------------------------------------------------------
| Configuration
|--------------------------------------------------------------------------
*/
const API_URL = 'https://api.hive-engine.com/rpc/contracts';
const TOKEN = 'GLYPH';
const STAKE_REQUIRED = 5.0;
const REWARD_FOR_5 = 0.00071;
const PAGE_SIZE = 1000;
const EXCLUDED_ACCOUNT = 'we-are-ai';
/*
|--------------------------------------------------------------------------
| Helper functions
|--------------------------------------------------------------------------
*/
function format8(float $number): string
{
return number_format($number, 8, '.', '');
}
function apiFind(string $table, array $query, int $offset = 0): array
{
$payload = [
'jsonrpc' => '2.0',
'method' => 'find',
'params' => [
'contract' => 'tokens',
'table' => $table,
'query' => $query,
'limit' => PAGE_SIZE,
'offset' => $offset,
'indexes' => []
],
'id' => time()
];
$ch = curl_init(API_URL);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 120,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception('cURL error: ' . $error);
}
curl_close($ch);
$json = json_decode($response, true);
if (!is_array($json)) {
throw new Exception('Invalid API response.');
}
if (isset($json['error'])) {
throw new Exception(
$json['error']['message'] ?? 'Unknown API error.'
);
}
return is_array($json['result'] ?? null)
? $json['result']
: [];
}
/*
|--------------------------------------------------------------------------
| Get eligible GLYPH accounts
|--------------------------------------------------------------------------
*/
function getGlyphAccounts(): array
{
$accounts = [];
$offset = 0;
while (true) {
$rows = apiFind(
'balances',
['symbol' => TOKEN],
$offset
);
if (empty($rows)) {
break;
}
foreach ($rows as $row) {
$account = trim((string)($row['account'] ?? ''));
if ($account === '') {
continue;
}
$stake = (float)($row['stake'] ?? 0);
$delegationIn = (float)(
$row['receivedStake']
?? $row['received_stake']
?? $row['delegatedIn']
?? $row['delegationIn']
?? 0
);
if ($stake <= 0 && $delegationIn <= 0) {
continue;
}
$totalEligible = $stake + $delegationIn;
$dailyReward = (
$totalEligible / STAKE_REQUIRED
) * REWARD_FOR_5;
$accounts[$account] = [
'account' => $account,
'stake' => $stake,
'delegationIn' => $delegationIn,
'totalEligible' => $totalEligible,
'dailyReward' => $dailyReward
];
}
if (count($rows) < PAGE_SIZE) {
break;
}
$offset += PAGE_SIZE;
}
usort(
$accounts,
static function (array $a, array $b): int {
return $b['totalEligible'] <=> $a['totalEligible'];
}
);
return array_values($accounts);
}
/*
|--------------------------------------------------------------------------
| CSV export
|--------------------------------------------------------------------------
*/
if (
isset($_GET['download']) &&
$_GET['download'] === 'csv'
) {
try {
ini_set('display_errors', '0');
error_reporting(0);
$accounts = getGlyphAccounts();
if (ob_get_length()) {
ob_clean();
}
header('Content-Type: text/csv; charset=UTF-8');
header(
'Content-Disposition: attachment; filename="glyph_rewards.csv"'
);
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$output = fopen('php://output', 'w');
fwrite($output, "\xEF\xBB\xBF");
fputcsv($output, [
'ACCOUNT',
'DAILY REWARD',
'GLYPH',
'WIN STACK YOUR GLYPH'
], ',');
foreach ($accounts as $account) {
$accountName = strtolower(
trim($account['account'] ?? '')
);
if ($accountName === EXCLUDED_ACCOUNT) {
continue;
}
fputcsv($output, [
$account['account'],
format8((float)$account['dailyReward']),
'GLYPH',
'win stack your GLYPH'
], ',');
}
fclose($output);
exit;
} catch (Throwable $e) {
http_response_code(500);
header('Content-Type: text/plain; charset=UTF-8');
exit('CSV export error: ' . $e->getMessage());
}
}
/*
|--------------------------------------------------------------------------
| Normal page scan
|--------------------------------------------------------------------------
*/
$accounts = [];
$error = '';
$scanned = false;
if (isset($_GET['scan'])) {
try {
$accounts = getGlyphAccounts();
$scanned = true;
} catch (Throwable $e) {
$error = $e->getMessage();
}
}
$totalAccounts = count($accounts);
$totalGlyph = 0.0;
$totalReward = 0.0;
foreach ($accounts as $account) {
$totalGlyph += (float)$account['totalEligible'];
$totalReward += (float)$account['dailyReward'];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GLYPH Reward Dashboard</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
font-family: Arial, Helvetica, sans-serif;
color: #ffffff;
background:
radial-gradient(circle at top left, #ff7eb3, transparent 35%),
radial-gradient(circle at bottom right, #6c63ff, transparent 35%),
linear-gradient(135deg, #141e30, #243b55);
padding: 30px 15px;
}
.container {
max-width: 1350px;
margin: auto;
}
.hero {
text-align: center;
padding: 35px 20px;
margin-bottom: 25px;
border-radius: 25px;
background: linear-gradient(
135deg,
rgba(255, 126, 179, 0.95),
rgba(108, 99, 255, 0.95)
);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.35);
}
.hero h1 {
margin: 0;
font-size: 42px;
text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
}
.hero p {
margin: 12px 0 0;
font-size: 18px;
}
.panel {
padding: 25px;
border-radius: 22px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.25);
backdrop-filter: blur(12px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.25);
}
.buttons {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
margin-bottom: 25px;
}
.button {
display: inline-block;
padding: 14px 22px;
border-radius: 50px;
color: #ffffff;
text-decoration: none;
font-weight: bold;
transition: 0.25s;
box-shadow: 0 7px 15px rgba(0, 0, 0, 0.25);
}
.button:hover {
transform: translateY(-4px) scale(1.03);
box-shadow: 0 12px 22px rgba(0, 0, 0, 0.35);
}
.scan-button {
background: linear-gradient(135deg, #00c6ff, #0072ff);
}
.csv-button {
background: linear-gradient(135deg, #00f260, #0575e6);
}
.stats {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(220px, 1fr)
);
gap: 18px;
margin-bottom: 25px;
}
.stat-card {
padding: 22px;
border-radius: 18px;
text-align: center;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.25),
rgba(255, 255, 255, 0.08)
);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.stat-card .icon {
font-size: 32px;
}
.stat-card span {
display: block;
margin-top: 8px;
color: #e6e6e6;
font-size: 14px;
}
.stat-card strong {
display: block;
margin-top: 8px;
color: #ffffff;
font-size: 24px;
}
.error {
padding: 15px;
margin-bottom: 20px;
border-radius: 12px;
color: #ffffff;
background: #e63946;
font-weight: bold;
}
.empty {
padding: 35px;
text-align: center;
border-radius: 16px;
background: rgba(0, 0, 0, 0.2);
color: #eeeeee;
font-size: 18px;
}
.table-wrapper {
overflow-x: auto;
border-radius: 15px;
}
table {
width: 100%;
min-width: 850px;
border-collapse: collapse;
overflow: hidden;
background: rgba(255, 255, 255, 0.95);
color: #222222;
}
th {
padding: 15px 12px;
background: linear-gradient(135deg, #ff512f, #dd2476);
color: #ffffff;
text-align: left;
white-space: nowrap;
}
td {
padding: 13px 12px;
border-bottom: 1px solid #dddddd;
}
tr:nth-child(even) {
background: #f5f7ff;
}
tr:hover {
background: #ffe5f1;
}
.number {
text-align: right;
font-family: Consolas, monospace;
}
.account-name {
font-weight: bold;
color: #5b21b6;
}
.footer {
margin-top: 25px;
text-align: center;
color: #dddddd;
font-size: 13px;
}
@media (max-width: 600px) {
body {
padding: 15px 8px;
}
.hero h1 {
font-size: 30px;
}
.panel {
padding: 15px;
}
.button {
width: 100%;
text-align: center;
}
}
</style>
</head>
<body>
<div class="container">
<div class="hero">
<h1>🚀 GLYPH Reward Dashboard</h1>
<p>Track staking, incoming delegations and daily rewards ✨</p>
</div>
<div class="panel">
<div class="buttons">
<a class="button scan-button" href="?scan=1">
🔍 Refresh Scan
</a>
<?php if ($scanned && !empty($accounts)): ?>
<a class="button csv-button" href="?download=csv">
📥 Download CSV
</a>
<?php endif; ?>
</div>
<?php if ($error !== ''): ?>
<div class="error">
⚠️ <?= htmlspecialchars(
$error,
ENT_QUOTES,
'UTF-8'
) ?>
</div>
<?php endif; ?>
<?php if ($scanned): ?>
<div class="stats">
<div class="stat-card">
<div class="icon">👥</div>
<span>Eligible Accounts</span>
<strong><?= $totalAccounts ?></strong>
</div>
<div class="stat-card">
<div class="icon">💎</div>
<span>Total Eligible GLYPH</span>
<strong><?= format8($totalGlyph) ?></strong>
</div>
<div class="stat-card">
<div class="icon">🎁</div>
<span>Total Daily Rewards</span>
<strong><?= format8($totalReward) ?></strong>
</div>
</div>
<?php if (!empty($accounts)): ?>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>#</th>
<th>ACCOUNT</th>
<th>STAKE</th>
<th>INCOMING DELEGATION</th>
<th>TOTAL ELIGIBLE</th>
<th>DAILY REWARD</th>
</tr>
</thead>
<tbody>
<?php foreach (
$accounts as $index => $account
): ?>
<tr>
<td><?= $index + 1 ?></td>
<td class="account-name">
<?= htmlspecialchars(
$account['account'],
ENT_QUOTES,
'UTF-8'
) ?>
</td>
<td class="number">
<?= format8(
(float)$account['stake']
) ?>
</td>
<td class="number">
<?= format8(
(float)$account['delegationIn']
) ?>
</td>
<td class="number">
<?= format8(
(float)$account['totalEligible']
) ?>
</td>
<td class="number">
<?= format8(
(float)$account['dailyReward']
) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="empty">
😕 No eligible account found.
</div>
<?php endif; ?>
<?php else: ?>
<div class="empty">
👋 Click “Refresh Scan” to start scanning GLYPH accounts.
</div>
<?php endif; ?>
</div>
<div class="footer">
GLYPH staking reward calculator 🚀
</div>
</div>
<!-- ================= TOKEN TRANSFERS PATCH ================= -->
<style>
.token-transfer-patch {
margin-top: 25px;
padding: 25px;
border-radius: 22px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.25);
backdrop-filter: blur(12px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.25);
}
.token-transfer-patch h2 {
text-align: center;
margin-top: 0;
}
.token-transfer-patch label {
display: block;
margin-top: 12px;
margin-bottom: 6px;
font-weight: bold;
}
.token-transfer-patch input[type="text"],
.token-transfer-patch input[type="file"] {
width: 100%;
padding: 12px;
border: 0;
border-radius: 10px;
font-size: 15px;
}
.token-transfer-actions {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
margin: 20px 0;
}
.token-transfer-actions button {
border: 0;
cursor: pointer;
padding: 14px 22px;
border-radius: 50px;
color: white;
font-weight: bold;
font-size: 15px;
box-shadow: 0 7px 15px rgba(0, 0, 0, 0.25);
}
.token-import-button {
background: linear-gradient(135deg, #ff9966, #ff5e62);
}
.token-send-button {
background: linear-gradient(135deg, #00c853, #64dd17);
}
.token-transfer-status {
margin-top: 18px;
padding: 14px;
border-radius: 12px;
text-align: center;
background: rgba(0, 0, 0, 0.25);
font-weight: bold;
}
#tokenTransferTable {
width: 100%;
min-width: 650px;
border-collapse: collapse;
background: white;
color: #222;
}
#tokenTransferTable th {
padding: 12px;
background: linear-gradient(135deg, #ff512f, #dd2476);
color: white;
text-align: left;
}
#tokenTransferTable td {
padding: 11px;
border-bottom: 1px solid #ddd;
}
</style>
<div class="token-transfer-patch">
<h2>🪙 Token Transfers</h2>
<label for="tokenTransferSender">
Sender Account
</label>
<input
type="text"
id="tokenTransferSender"
placeholder="your-account"
>
<label for="tokenTransferCsv">
CSV File
</label>
<input
type="file"
id="tokenTransferCsv"
accept=".csv"
>
<div class="token-transfer-actions">
<button
type="button"
class="token-import-button"
onclick="importTokenTransferCSV()"
>
📥 Import CSV
</button>
<button
type="button"
class="token-send-button"
onclick="sendTokenTransfers()"
>
🚀 Send Tokens
</button>
</div>
<div class="table-wrapper">
<table id="tokenTransferTable">
<thead>
<tr>
<th>Recipient</th>
<th>Amount</th>
<th>Token</th>
<th>Memo</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div
id="tokenTransferStatus"
class="token-transfer-status"
>
No CSV file loaded.
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@hiveio/keychain/dist/keychain.min.js"></script>
<script>
let tokenTransferList = [];
function importTokenTransferCSV() {
const file = document.getElementById('tokenTransferCsv').files[0];
if (!file) {
alert('Please select a CSV file.');
return;
}
const reader = new FileReader();
reader.onload = function(event) {
const lines = event.target.result.split(/\r?\n/);
const tbody = document.querySelector(
'#tokenTransferTable tbody'
);
tokenTransferList = [];
tbody.innerHTML = '';
lines.forEach(function(line) {
line = line.trim();
if (!line) {
return;
}
const columns = line.split(',');
if (columns.length < 3) {
return;
}
const recipient = columns[0].trim();
const amount = columns[1].trim();
const token = columns[2].trim().toUpperCase();
const memo = columns.slice(3).join(',').trim();
if (
recipient.toLowerCase() === 'recipient' ||
recipient.toLowerCase() === 'account'
) {
return;
}
if (!recipient || !amount || !token) {
return;
}
const transfer = {
recipient: recipient,
amount: amount,
token: token,
memo: memo
};
tokenTransferList.push(transfer);
const row = document.createElement('tr');
[recipient, amount, token, memo].forEach(function(value) {
const cell = document.createElement('td');
cell.textContent = value;
row.appendChild(cell);
});
tbody.appendChild(row);
});
document.getElementById('tokenTransferStatus').textContent =
'✅ ' + tokenTransferList.length + ' transfer(s) loaded.';
};
reader.readAsText(file);
}
function sendTokenTransfers() {
const sender = document
.getElementById('tokenTransferSender')
.value
.trim();
if (!sender) {
alert('Please enter the sender account.');
return;
}
if (tokenTransferList.length === 0) {
alert('Please import a CSV file first.');
return;
}
if (
typeof hive_keychain === 'undefined' ||
!hive_keychain.requestCustomJson
) {
alert('Hive Keychain is not available.');
return;
}
sendNextTokenTransfer(0, sender);
}
function sendNextTokenTransfer(index, sender) {
if (index >= tokenTransferList.length) {
document.getElementById('tokenTransferStatus').textContent =
'✅ All transfers have been completed.';
return;
}
const transfer = tokenTransferList[index];
const customJson = JSON.stringify({
contractName: 'tokens',
contractAction: 'transfer',
contractPayload: {
symbol: transfer.token,
to: transfer.recipient,
quantity: transfer.amount,
memo: transfer.memo
}
});
document.getElementById('tokenTransferStatus').textContent =
'⏳ Sending transfer ' +
(index + 1) +
'/' +
tokenTransferList.length +
'...';
hive_keychain.requestCustomJson(
sender,
'ssc-mainnet-hive',
'Active',
customJson,
'Send ' + transfer.token,
function(response) {
if (response.success) {
setTimeout(function() {
sendNextTokenTransfer(index + 1, sender);
}, 1200);
} else {
document.getElementById(
'tokenTransferStatus'
).textContent =
'❌ Transfer error: ' +
(
response.message ||
response.error ||
'Unknown error'
);
}
}
);
}
</script>
<!-- ================= END TOKEN TRANSFERS PATCH ================= -->
</body>
</html>
------------------------------------------------------
Good morning everyone, I know that some of you enjoy coding, so here is a small, simple, and interesting piece of code with a few explanations.
# 🚀 Creating a Dashboard for the GLYPH Token
This PHP code creates a small web page capable of analyzing accounts that hold or receive delegations of **GLYPH**, and calculating a theoretical daily reward for each account.
The page also includes:
- A button to launch the analysis.
- A display showing the number of eligible accounts.
- The total amount of GLYPH taken into account.
- The total daily rewards.
- A detailed table for each account.
- A CSV export feature.
- A CSV import system.
- A token-sending function using Hive Keychain.
---
## 🔎 1. How does the code work?
### Configuration
At the beginning of the file, several constants make it easy to modify the main settings:
```php
const API_URL = 'https://api.hive-engine.com/rpc/contracts';
const TOKEN = 'GLYPH';
const STAKE_REQUIRED = 5.0;
const REWARD_FOR_5 = 0.00071;
const PAGE_SIZE = 1000;
const EXCLUDED_ACCOUNT = 'we-are-ai';
Here is the role of each element:
| Constant | Function |
|---|---|
API_URL | The Hive Engine API address used to retrieve the data |
TOKEN | The token being analyzed |
STAKE_REQUIRED | The amount of tokens required to obtain the reference reward |
REWARD_FOR_5 | The reward planned for that amount |
PAGE_SIZE | The number of results retrieved per request |
EXCLUDED_ACCOUNT | The account excluded from the CSV export |
In this example, the calculation is:
$dailyReward = (
$totalEligible / STAKE_REQUIRED
) * REWARD_FOR_5;
If an account has 10 GLYPH and 5 GLYPH generates 0.00071, then the account will theoretically receive twice the reward:
10 / 5 × 0.00071 = 0.00142
This is therefore a proportional calculation.
The apiFind() function sends a request to the Hive Engine API.
function apiFind(string $table, array $query, int $offset = 0): array
It receives three pieces of information:
In the code, the request is used like this:
$rows = apiFind(
'balances',
['symbol' => TOKEN],
$offset
);
This means:
Search the
balancestable for all accounts holding the token defined in theTOKENconstant.
The API then returns information such as:
The main function used to analyze accounts is:
function getGlyphAccounts(): array
It goes through the results returned by the API.
The code retrieves the account name:
$account = trim((string)($row['account'] ?? ''));
Then it retrieves the staked amount:
$stake = (float)($row['stake'] ?? 0);
And the incoming delegations:
$delegationIn = (float)(
$row['receivedStake']
?? $row['received_stake']
?? $row['delegatedIn']
?? $row['delegationIn']
?? 0
);
This part is interesting because it checks several possible field names:
receivedStakereceived_stakedelegatedIndelegationInThis makes the code more flexible if the field name changes depending on the API or the contract being used.
The code then ignores accounts that do not hold anything:
if ($stake <= 0 && $delegationIn <= 0) {
continue;
}
Then it adds the staking and incoming delegations together:
$totalEligible = $stake + $delegationIn;
Finally, it calculates the daily reward:
$dailyReward = (
$totalEligible / STAKE_REQUIRED
) * REWARD_FOR_5;
Each account is then stored in an array with several pieces of information:
$accounts[$account] = [
'account' => $account,
'stake' => $stake,
'delegationIn' => $delegationIn,
'totalEligible' => $totalEligible,
'dailyReward' => $dailyReward
];
After retrieving the accounts, they are sorted from the highest eligible amount to the lowest:
usort(
$accounts,
static function (array $a, array $b): int {
return $b['totalEligible'] <=> $a['totalEligible'];
}
);
The first account displayed will therefore be the one holding the largest amount of eligible tokens.
When the following address is used:
your-page.php?download=csv
The code generates a CSV file containing:
The file header is created here:
fputcsv($output, [
'ACCOUNT',
'GLYPH',
'DAILY REWARD',
'WIN STACK YOUR GLYPH'
], ',');
Each account is then added to the file:
fputcsv($output, [
$account['account'],
'GLYPH',
format8((float)$account['dailyReward']),
'win stack your GLYPH'
], ',');
The account defined here:
const EXCLUDED_ACCOUNT = 'we-are-ai';
is excluded from the CSV file.
To analyze another token, the main modification is very simple.
You only need to change:
const TOKEN = 'GLYPH';
For example, to analyze a token called TEST:
const TOKEN = 'TEST';
Or another Hive Engine token:
const TOKEN = 'BEE';
The rest of the system will continue to work in the same way.
Even though the search will use the new token correctly, the CSV file currently contains the text written directly into the code:
'GLYPH',
It is better to replace it with:
TOKEN,
The complete line becomes:
fputcsv($output, [
$account['account'],
TOKEN,
format8((float)$account['dailyReward']),
'win stack your ' . TOKEN
], ',');
That way, if you change the constant:
const TOKEN = 'BEE';
The CSV will automatically contain:
BEE
instead of GLYPH.
The current title is written directly into the HTML:
<title>GLYPH Reward Dashboard</title>
You can replace it with:
<title><?= htmlspecialchars(TOKEN) ?> Reward Dashboard</title>
The title will then automatically adapt to the selected token.
You can also change the main heading:
<h1>🚀 GLYPH Reward Dashboard</h1>
To:
<h1>🚀 <?= htmlspecialchars(TOKEN) ?> Reward Dashboard</h1>
And the subtitle:
<p>Track staking, incoming delegations and daily rewards ✨</p>
For example:
<p>
Track staking, delegations and rewards
for <?= htmlspecialchars(TOKEN) ?> ✨
</p>
To summarize, the three most useful changes are:
const TOKEN = 'ANOTHER_TOKEN';
Replace:
'GLYPH',
With:
TOKEN,
Replace text containing GLYPH with:
<?= htmlspecialchars(TOKEN) ?>
With these few changes, the page becomes reusable for several Hive Engine tokens.
The current code mainly analyzes:
However, depending on the token, the API may provide other information.
For example, you could display:
The current code mainly uses:
$row['stake']
To also display the liquid balance, you could retrieve a field such as:
$balance = (float)($row['balance'] ?? 0);
Then add it to the account data:
$accounts[$account] = [
'account' => $account,
'balance' => $balance,
'stake' => $stake,
'delegationIn' => $delegationIn,
'totalEligible' => $totalEligible,
'dailyReward' => $dailyReward
];
You would then need to add a column to the HTML table:
<th>AVAILABLE BALANCE</th>
And display the value:
<td class="number">
<?= format8((float)$account['balance']) ?>
</td>
You could then compare:
Available balance + staking + incoming delegations
To find out how many tokens an account has delegated to other users, you would need to retrieve another field, if it is available in the API response:
$delegationOut = (float)(
$row['delegatedOut']
?? $row['delegationOut']
?? 0
);
Then add it to the account data:
'delegationOut' => $delegationOut,
You could then calculate the amount that is actually available:
$netEligible = $stake
+ $delegationIn
- $delegationOut;
This would prevent tokens delegated to someone else from being counted as available.
The current ranking uses:
$b['totalEligible'] <=> $a['totalEligible']
You can easily rank the accounts according to another criterion.
usort(
$accounts,
static function (array $a, array $b): int {
return $b['dailyReward'] <=> $a['dailyReward'];
}
);
usort(
$accounts,
static function (array $a, array $b): int {
return $b['stake'] <=> $a['stake'];
}
);
usort(
$accounts,
static function (array $a, array $b): int {
return $b['balance'] <=> $a['balance'];
}
);
You only need to change one logical line to modify the ranking.
The current calculation is linear:
$dailyReward = (
$totalEligible / STAKE_REQUIRED
) * REWARD_FOR_5;
This means:
You could also add a maximum limit:
$dailyReward = min(
($totalEligible / STAKE_REQUIRED) * REWARD_FOR_5,
0.05000000
);
In this example, the reward can never exceed 0.05.
You could also create a tier-based system:
if ($totalEligible >= 100) {
$dailyReward = 0.02000000;
} elseif ($totalEligible >= 50) {
$dailyReward = 0.01000000;
} elseif ($totalEligible >= 5) {
$dailyReward = 0.00071000;
} else {
$dailyReward = 0;
}
This allows you to assign a different reward depending on several levels.
The current code queries the following table:
'balances'
To analyze token movements, you would need to query a table related to transfers, usually called:
'contracts'
or another specific table depending on the possibilities offered by the API.
You could search for token operations and display:
The principle would be similar to the current function:
$rows = apiFind(
'contracts',
['symbol' => TOKEN],
$offset
);
However, you should verify the exact table and field names available for the token or contract being used. Not all tokens provide exactly the same data.
If the API returns a date or block identifier, you could add information such as:
$lastTransfer = $row['timestamp'] ?? '';
Then display it in the table:
<td>
<?= htmlspecialchars(
(string)$account['lastTransfer'],
ENT_QUOTES,
'UTF-8'
) ?>
</td>
This would allow you to see which accounts have been recently active.
Although this code is useful for creating a dashboard, a few improvements are recommended.
The code contains:
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false
This disables SSL security verification. In a production environment, it is better to use:
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2
Fields such as stake, balance, or receivedStake may vary depending on the contract and API version. It is therefore important to check the actual response before using them.
The code uses:
const columns = line.split(',');
This works for a basic CSV file, but it can cause problems if a field contains a comma inside quotation marks. For a larger project, it would be better to use a proper CSV parser.
The sending function uses Hive Keychain and requests a transaction for each line in the file. It is recommended to:
Transactions are signed by Hive Keychain, which is safer than storing a private key on the server. You should never place a private key directly in this PHP file or in the JavaScript code.
This code is a good starting point for creating a simple dashboard around a Hive Engine token.
It already allows you to:
To adapt it to another token, the essential change is very limited:
const TOKEN = 'ANOTHER_TOKEN';
Then replace text written directly into the code, such as:
'GLYPH'
With:
TOKEN
Depending on the information available through the API, you can also add:
With only two or three well-placed changes, this small dashboard can become a much more complete tool for tracking, analyzing, and distributing rewards for a token.
Posted Using INLEO
Excelent
Thank you brother
!INDEED
Eh ben, on se spécialise, je vois… 😉
!ALIVE
!BBH 👍
oui je prend des cours et c'est super intéressant à faire , là j'apprend aussi comment on fait un witness même si j'aurais JAMAIS le matériel et j'essaie même plus de faire de l'argent , je m'amuse
Hahaha, c’est sûr que ce n’est pas ici qu’on fera fortune 😂 Mais c’est super de prendre des cours ! En ligne ou en présentiel le soir ?
Moi, ça m’a toujours amusé de bidouiller un peu avec JavaScript et PHP, et pour ça, Hive est parfait 😉
!ALIVE
!BBH
!LOLZ 👍
View more
Before your final
</body>tag, at the very bottom of your code, you can also add this small patch. It automatically fills in the token transfers, so you do not have to download your CSV file and reload it below.Congratulations @we-are-ai! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)
You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP