feat(account): add GET /account/balances with CHEQ/USD conversion - #814
Merged
Merged
Conversation
Returns the on-chain balance of the authenticated customer's mainnet and testnet payment accounts in ncheq, CHEQ and USD. The CHEQ/USD rate is sourced from CoinGecko (coin id `cheqd-network`) via a new PriceHelper and cached in-process for CHEQ_USD_RATE_CACHE_TTL seconds. The endpoint degrades gracefully: if the rate lookup or a network's RPC endpoint is unavailable it still returns 200, with the affected usd/rate/balance fields set to null. Per-network balance queries are isolated so one RPC outage doesn't blank the other network or the addresses, and checkBalance is bounded by an 8s timeout. Also extracts the ncheq<->CHEQ helpers (cheqToNcheq, ncheqToCheq, toSafeFaucetAmount) out of account.ts into helpers/denom.ts, and generalises getTestnetBalanceNcheq to getBalanceNcheq(address, rpcUrl). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- unit: ncheqToCheq edge cases (zero, sub-CHEQ, > Number.MAX_SAFE_INTEGER) and the cheq * rate USD calculation - e2e: authenticated shape check + testnet address parity with GET /account, and an unauthenticated 401 check Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
testMatch used `\.no-auth.spec.ts`, requiring a literal dot before "no-auth", but every spec is named with a hyphen (e.g. balances-no-auth.spec.ts). These files were silently falling into the "Parallel Logged In User Tests" project instead, which injects a valid x-api-key on every request - so no-auth specs never actually ran unauthenticated. Also exclude them from that project so they don't now double-run and fail there once the pattern is corrected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/credential/verify requires the verify:credential:testnet/mainnet scope (unlike /did/search, /credential-status/search and /resource/search, which are explicitly registered with allowUnauthorized: true). verify-no-auth.spec.ts asserted 200 OK without auth, which was only ever passing because the previous testMatch regex bug routed it into the authenticated project by accident. Renamed to verify.spec.ts so it runs under the authenticated project as intended, and dropped the unauthenticated storageState override. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cheqd-bot Bot
pushed a commit
that referenced
this pull request
Sep 22, 2026
## [3.17.0-develop.6](3.17.0-develop.5...3.17.0-develop.6) (2026-09-22) ### Features * **account:** add GET /account/balances with CHEQ/USD conversion ([bfc013e](bfc013e)) * **account:** add GET /account/balances with CHEQ/USD conversion ([#814](#814)) ([6a2510e](6a2510e)) ### Bug Fixes * **test:** correct no-auth spec matching in Playwright projects ([348251a](348251a)) * **test:** move credential verify test out of no-auth suite ([36bae65](36bae65))
|
🎉 This PR is included in version 3.17.0-develop.6 🎉 The release is available on: Your semantic-release bot 📦🚀 |
cheqd-bot Bot
pushed a commit
that referenced
this pull request
Sep 29, 2026
## [3.17.0](3.16.1...3.17.0) (2026-09-29) ### Features * **account:** add GET /account/balances with CHEQ/USD conversion ([bfc013e](bfc013e)) * **account:** add GET /account/balances with CHEQ/USD conversion ([#814](#814)) ([6a2510e](6a2510e)) * Add Faucet API access headers ([#810](#810)) ([59d9f7e](59d9f7e)) * add guarded Stripe plan migration ([#816](#816)) ([e4e495d](e4e495d)) * Align Basic/Build plans and add testnet faucet API ([#811](#811)) ([7b00c1f](7b00c1f)) * Start Basic plan as trial for new Studio accounts ([#812](#812)) ([52c2249](52c2249)) ### Bug Fixes * Add missing body parameters for faucet topup requests ([#813](#813)) ([a61bfd5](a61bfd5)) * Improve error-handling for faucet requests ([89ed790](89ed790)) * revert semantic-release version for node 20 ([0915874](0915874)) * **test:** correct no-auth spec matching in Playwright projects ([348251a](348251a)) * **test:** move credential verify test out of no-auth suite ([36bae65](36bae65))
|
🎉 This PR is included in version 3.17.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
GET /account/balances— the on-chain balance of the authenticated customer's mainnet and testnet payment accounts, inncheq,CHEQandUSD.Why
GET /accountreturns only the payment-account addresses.checkBalancefrom@cheqd/sdkwas used internally (faucet top-up) but never surfaced, and there was no CHEQ→USD lookup anywhere. The Studio portal needs both to show customers their account funding status. Studio owns the price-feed dependency and caching (the same way it wraps Stripe); the portal just displays.How
src/helpers/price.ts— newPriceHelper.getCheqUsdRate(). Fetches the CHEQ/USD spot rate from CoinGecko (coin idcheqd-network, matchingcheqd/market-monitoring), caches it in-process forCHEQ_USD_RATE_CACHE_TTLseconds (default 300), 5s request timeout. Never throws — any failure logs and returnsnull.src/helpers/denom.ts— new. ExtractsncheqToCheq/cheqToNcheq/toSafeFaucetAmountout ofaccount.tsso the pure conversion logic has one home and is unit-testable in isolation.account.ts—getBalances()controller method (inline@openapi,[Account]tag).getTestnetBalanceNcheqgeneralised togetBalanceNcheq(address, rpcUrl)with an 8s timeout guard (StargateClientexposes no cancellation).app.ts,read:accountrule inaccount-auth.ts,AccountBalancesResponse/AccountNetworkBalanceschemas inswagger-api-types.ts(+ regeneratedswagger-api.json).COINGECKO_API_URL/COINGECKO_TOKEN_ID/COINGECKO_API_KEY/CHEQ_USD_RATE_CACHE_TTL(constants use the existingparseNumberEnv; documented inREADME.md+example.env).Edge cases handled
null.balanceisnull(the address is still returned); the other network is unaffected.rateisnulland everyusdisnull; balances still returned; response is still200.Number.MAX_SAFE_INTEGERncheq → conversion viaNumber(bigint) / 1e9, no throw (covered by a unit test).Deliberate deviations from existing conventions
PriceHelper(its ownnode-cache), notLocalStore—LocalStoreis coupled toPaymentAccountEntityand sits underdatabase/; a market-rate cache doesn't belong there.checkBalancetimeout — the existinggetTestnetBalanceNcheqlets RPC errors propagate./account/balancesisolates them so one RPC outage doesn't blank the other network or the addresses.AccountBalancesResponse) rather than fully inline like the newest/account/faucetblock — the response is structured enough that a named schema (likeAccountAnalyticsResponse) reads better.Testing
npm run build(swagger + tsc) — clean.npm run test:unit— 137/137 pass (6 new intests/unit/account/balances.test.ts).prettier --check 'src/**/*.ts' 'tests/**/*.ts'— clean.tests/e2e/parallel/account/(authenticated shape check + testnet address parity with/account, and an unauthenticated 401) — these need a deployed environment, so they run in CI, not locally./account), a live balance, and a live CoinGecko rate; with CoinGecko pointed at a dead URL,rate/usdwerenull, balances still returned,200, no crash.Follow-up
Per-account transaction history is tracked in the
paymenttable (PaymentEntity) but has no read endpoint — raised as #815.🤖 Generated with Claude Code