Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased (dev)

### Fixes

* **`/v2/state/get_tokens` missing balances — token-contract detection in `sync accounts`**: `./hyp-control sync accounts` (and `sync all`) resolved a contract's transfer parameter struct by the hard-coded struct name `"transfer"`. Per the ABI spec the struct backing an action is named by the action's `type` field, which is frequently *not* the action name — e.g. several contracts declare a fully standard transfer (`from:name, to:name, quantity:asset, memo:string`) under the struct name `transfer_token`. Those contracts were silently skipped, so their balances were never backfilled into the MongoDB `accounts` collection and `get_tokens` returned only the symbols the live indexer happened to capture — producing a "same contract, some symbols present and some missing" result on upgraded nodes. The struct is now resolved through the transfer action's declared `type`. **After upgrading, re-run `./hyp-control sync accounts <chain>` (or `sync all`) to backfill the previously skipped contracts.**

## 4.0.7 (2026-05-16)

> **4.0.6 was skipped.** The 4.0.6 version sat on `main` untagged for a period and some operators deployed it directly from `main` before any release tag existed. To avoid ambiguity between those pre-tag production deployments and the official artifact, this work is released as **4.0.7**. There is no separate 4.0.6 entry. Going forward, `main` only advances to tagged releases and active development happens on `dev`.
Expand Down
17 changes: 12 additions & 5 deletions src/cli/sync-modules/sync-accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,25 @@ export class AccountSynchronizer extends Synchronizer<IAccount> {
const abi = await this.client.v1.chain.get_abi(contract);
const tables = new Set(abi.abi?.tables?.map(value => value.name));
if (tables.has("accounts") && tables.has("stat")) {
const actions = new Set(abi.abi?.actions?.map(value => value.name));
if (actions.has("transfer")) {
const transferType = abi.abi?.structs?.find(s => s.name === 'transfer');
// Resolve the transfer parameter struct through the transfer action's
// declared `type`, not the literal name "transfer". Per the ABI spec the
// struct backing an action is named by the action's `type` field, which is
// frequently NOT the action name — e.g. the paycash contracts (token.pcash,
// cashescashes, swap.pcash, ...) declare a standard transfer under the struct
// name "transfer_token". Looking the struct up by name === "transfer" returned
// undefined and silently dropped these valid token contracts from the backfill.
const transferAction = abi.abi?.actions?.find(a => a.name === "transfer");
if (transferAction) {
const transferType = abi.abi?.structs?.find(s => s.name === transferAction.type);
if (transferType && transferType.fields) {
const fields = transferType.fields;
let valid = true;
for (let i = 0; i < transferFields.length; i++) {
if ((fields[i].name === "from" || fields[i].name === "to") && fields[i].type === 'account_name') {
if (fields[i] && fields[i].name === transferFields[i].name && fields[i].type === 'account_name') {
valid = true;
continue;
}
if (fields[i].name !== transferFields[i].name || fields[i].type !== transferFields[i].type) {
if (!fields[i] || fields[i].name !== transferFields[i].name || fields[i].type !== transferFields[i].type) {
console.error(`Invalid token contract ${contract} ->> ${fields.map(f => (f.name + "(" + f.type + ")").padEnd(24, " ")).join(' ')}`);
valid = false;
break;
Expand Down
Loading