Skip to content

feat: support TRC20 token balances for inactive accounts#190

Open
ulissesferreira wants to merge 2 commits intomainfrom
NEB-461-feat-inactive-account-trc20-balances
Open

feat: support TRC20 token balances for inactive accounts#190
ulissesferreira wants to merge 2 commits intomainfrom
NEB-461-feat-inactive-account-trc20-balances

Conversation

@ulissesferreira
Copy link
Contributor

@ulissesferreira ulissesferreira commented Feb 3, 2026

Summary

  • Add support for TRC20 token balances on inactive accounts (accounts that haven't paid the 1 TRX activation fee)
  • Refactor fetchAssetsAndBalancesForAccount to improve code organization and readability

Changes

New TRC20 fallback for inactive accounts:

  • Added getTrc20BalancesByAddress client method that works for inactive accounts (unlike getAccountInfoByAddress which throws)
  • Updated JSDoc on getAccountInfoByAddress and getAccountResources to document their behavior with inactive accounts

Asset extraction refactor:

  • Introduced NormalizedAccountData type to provide a consistent data shape for both active and inactive accounts
  • Added #buildAccountData to normalize API responses, centralizing active/inactive branching logic
  • Added #extractAssets coordinator that calls all individual extractors uniformly
  • Simplified extract function signatures to accept direct values instead of container objects
  • Added #enrichAssetsWithMetadata to encapsulate metadata enrichment logic
  • Added #getPriceableAssetTypes helper for filtering assets that can be priced
  • Removed #createZeroBalanceNativeAsset (no longer needed after normalization)

@ulissesferreira ulissesferreira requested a review from a team as a code owner February 3, 2026 14:21
@ulissesferreira ulissesferreira marked this pull request as draft February 3, 2026 14:24
@ulissesferreira ulissesferreira changed the title feat(NEB-461): support TRC20 token balances for inactive accounts feat: support TRC20 token balances for inactive accounts Feb 3, 2026
@ulissesferreira ulissesferreira force-pushed the NEB-461-feat-inactive-account-trc20-balances branch 2 times, most recently from beea8e5 to 82f12a3 Compare February 4, 2026 12:51
@ulissesferreira ulissesferreira marked this pull request as ready for review February 4, 2026 12:58
Comment on lines 79 to 91
mockTrongridApiClient = {
getAccountInfoByAddress: jest.fn(),
getTrc20BalancesByAddress: jest.fn(),
} as unknown as jest.Mocked<TrongridApiClient>;
mockTronHttpClient = {
getAccountResources: jest.fn(),
} as unknown as jest.Mocked<TronHttpClient>;
mockPriceApiClient = {
getMultipleSpotPrices: jest.fn().mockResolvedValue({}),
} as unknown as jest.Mocked<PriceApiClient>;
mockTokenApiClient = {
getTokensMetadata: jest.fn().mockResolvedValue({}),
} as unknown as jest.Mocked<TokenApiClient>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we force types this way, when we'll change something in the mocked functions, we won't see any error here, and finding where the issue is will be way harder without visible type errors. Do you think we can avoid all these type casts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep yep, a little more verbose I believe but worth the effort, let's leverage typescript fully

id: trc20AssetId,
price: 1.0,
},
} as never);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, is there a way to avoid this type cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

address: mockAccount.address,
balance: 1000000,
trc20: [],
} as never);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, is there a way to avoid this type cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@ulissesferreira ulissesferreira force-pushed the NEB-461-feat-inactive-account-trc20-balances branch 4 times, most recently from 38156a6 to cd1e61d Compare February 4, 2026 23:31
});

describe('extreme values (original bug scenarios)', () => {
describe('values out of `Number` 64-bit size', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nit: @mikesposito, didn't forget about it

Copy link
Contributor Author

@ulissesferreira ulissesferreira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Particularly happy with the structure here. I am going for a method that orchestrates the whole thing, doing the data fetching at first, data parsing, enriching and finally returning. The fallbacks are handled right at the start and everything else is shared. Took some iterations to get here, feel free to suggest even more improvements

* Get TRC20 token balances for an account address.
* This endpoint works for inactive accounts that haven't been activated yet.
*
* @see https://developers.tron.network/reference/get-trc20-token-holder-balances
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the key endpoint missing for us to be able to offer this feature

@@ -1,14 +1,15 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-restricted-globals */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed the other PRs comment and also removed these file-wide exclusions. Bit by bit we can make them line specific. The previous team preferred avoiding the repetition hence why they are usually above

@@ -0,0 +1,253 @@
/* eslint-disable no-restricted-globals */
/* eslint-disable @typescript-eslint/naming-convention */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed the other PRs comment and also removed these file-wide exclusions. Bit by bit we can make them line specific. The previous team preferred avoiding the repetition hence why they are usually above

@ulissesferreira ulissesferreira force-pushed the NEB-461-feat-inactive-account-trc20-balances branch from cd1e61d to 0203a95 Compare February 5, 2026 00:03
Add fallback TRC20 balance fetching for inactive Tron accounts using the
`/v1/accounts/{address}/trc20/balance` endpoint when the main account
info endpoint returns no data (inactive accounts that haven't paid the
1 TRX activation fee).

- Add `getTrc20BalancesByAddress()` method to TrongridApiClient
- Implement fallback logic in AssetsService for inactive accounts
- Add helper methods for zero-balance native assets and TRC20 extraction
- Add comprehensive unit tests for new functionality
Use InMemoryCache in TrongridApiClient tests and drop redundant section comments in AssetsService.
@ulissesferreira ulissesferreira force-pushed the NEB-461-feat-inactive-account-trc20-balances branch from 0203a95 to a3724b5 Compare February 5, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants