fix(cli): resolve transfer struct via action type in sync-accounts (get_tokens missing balances)#170
Conversation
scanABIs() looked up a contract's transfer parameter struct by the hard-coded 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. paycash contracts declare a standard transfer under the struct "transfer_token"). The lookup returned undefined, so those valid token contracts were silently skipped and never backfilled into the MongoDB accounts collection. get_tokens then returned only the symbols the live indexer captured, giving "same contract, some symbols missing" on upgraded nodes. Resolve the struct through the transfer action's declared type, and guard against short field arrays. Verified against api.eosrio.io: the 10 previously-skipped contracts held by bu.klnk (token.pcash, cashescashes, swap.pcash, jqpua4jqkqwz, testnewswap1, testrenminbi, testrublesrb, testuralsurl, uralsuralsru, testusdbxusd) are now accepted, with no change to standard-transfer contracts.
There was a problem hiding this comment.
Code Review
This pull request fixes an issue where token contracts were skipped during account synchronization by resolving the transfer parameter struct using the action's declared type instead of a hard-coded name. It also adds safety checks to prevent out-of-bounds errors when validating fields. The reviewer suggested a code improvement to simplify and secure the field validation check by directly comparing the field name with the expected name.
| if (fields[i] && (fields[i].name === "from" || fields[i].name === "to") && fields[i].type === 'account_name') { | ||
| valid = true; | ||
| continue; | ||
| } |
There was a problem hiding this comment.
The current check allows any field at index i to be accepted as valid if its name is either "from" or "to" and its type is "account_name", regardless of whether the index matches the expected field name (e.g., if from and to are swapped, or if a different field is named "from").
Since transferFields[i].name already contains the expected name ("from" at index 0, "to" at index 1), we can simplify and secure this check by verifying that fields[i].name matches transferFields[i].name directly.
| if (fields[i] && (fields[i].name === "from" || fields[i].name === "to") && fields[i].type === 'account_name') { | |
| valid = true; | |
| continue; | |
| } | |
| if (fields[i] && fields[i].name === transferFields[i].name && fields[i].type === 'account_name') { | |
| valid = true; | |
| continue; | |
| } |
There was a problem hiding this comment.
Good catch — applied in 584db2b. Matching transferFields[i].name positionally (consistent with the standard-type branch). Verified no behavior change for real contracts (still 22/22 accepted against api.eosrio.io); it only rejects malformed/swapped ABIs.
Address Gemini review on #170: the account_name legacy branch accepted any field named from/to regardless of position. Match transferFields[i].name positionally, consistent with the standard-type branch below it. No change for real contracts (verified 22/22 still accepted); only tightens malformed/ swapped ABIs.
Problem
Operators report
/v2/state/get_tokensreturning an incomplete token list after upgrading (e.g.bu.klnkshows 13/37 tokens), with the tell-tale symptom that some symbols of the same contract show while others are missing.Root cause
AccountSynchronizer.scanABIs()decides whether a contract is a token contract before backfilling balances. It looked up the 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
typefield, which is frequently not the action name. Several real token contracts declare a fully standard transfer (from:name, to:name, quantity:asset, memo:string) under a differently-named struct — e.g. the paycash contracts usetransfer_token. For those, the lookup returnedundefined, the field validation never ran, and the contract was silently skipped → its balances were never backfilled into the MongoDBaccountscollection.The live indexer's
*:accountshandler has no such requirement, so it captured whatever balances changed post-upgrade — hence "same contract, some symbols present, some missing."Fix
Resolve the transfer struct through the transfer action's declared
type, and guard against short field arrays.Verification (against
api.eosrio.io)Applying the old vs new detection to the contracts held by
bu.klnk+ a sample fromeosriobrazil:cashescashes, token.pcash, swap.pcash, jqpua4jqkqwz, testnewswap1, testrenminbi, testrublesrb, testuralsurl, uralsuralsru, testusdbxusdAll recovered contracts have
accounts+stattables, anaccountsrow ofbalance:asset, and atransferaction whosetransfer_tokenstruct is exactlyfrom,to,quantity,memo. Standard-transfer contracts are unaffected.tsc --noEmitpasses.Operator action after merge
Pull, rebuild, then re-run
./hyp-control sync accounts <chain>(orsync all <chain>) to backfill the previously skipped contracts.