Labels / Complexity: bug, financial, soroban, protocol · Extremely High — 500
Problem
The same "USDC" token is resolved to two different Stellar issuers in different layers of the codebase:
quantara/web_app/contract_tools/constants.py defines USDC_ASSET_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NOJ4VBH6THS2G2V" and builds USDC_ASSET_ID = "USDC:<issuer>".
quantara/soroban/adapters/soroswap_adapter.py and quantara/soroban/adapters/blend_adapter.py hardcode USDC as "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGCS3FOGTICSJCWV5X2HGM" in their _TokenResolver._TOKENS.
quantara/frontend/src/utils/constants.js defaults USDC_ASSET.issuer to GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NOJ4VBH6THS2G2V (matching constants.py).
CollateralManager (quantara/soroban/adapters/CollateralManager.py) imports USDC_ASSET_ID from web_app.contract_tools.constants, so its USDC config resolves to GBBD47...S2G2V, while BlendLendingAdapter / SoroswapAMMAdapter resolve "USDC" to GA5ZSE...X2HGM.
Consequence: the collateral engine and the AMM/lending adapters disagree about which on-chain asset "USDC" is. A position whose collateral is valued using the GBBD47... asset while its debt is quoted/borrowed against the GA5ZSE... asset operates on two different tokens; balances, health ratios, and swap quotes will not match, and in production this silently routes real user funds against the wrong issuer. There is no single source of truth, and no code cross-checks that the three layers agree.
Root cause
# constants.py
USDC_ASSET_ISSUER = os.getenv("USDC_ASSET_ISSUER", "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NOJ4VBH6THS2G2V")
# soroswap_adapter.py / blend_adapter.py
"USDC": {"addresses": ["USDC", "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGCS3FOGTICSJCWV5X2HGM"], ...} # ← different issuer
Why this is architecturally hard
- The shortcut — hardcode one issuer everywhere — reintroduces the exact failure mode the code comments warn about ("no TODO placeholders or hardcoded addresses"). The fix must make the issuer a single environment-driven source of truth consumed by Python and the frontend, and ideally validated at startup.
- The adapters use their own private
_TokenResolver with a duplicated token table rather than importing TokenParams from web_app.contract_tools.constants. Consolidating means deciding whether the adapters may depend on web_app (they currently import nothing from it), or whether the constants module moves to a shared location.
- The frontend has its own independent default in
quantara/frontend/src/utils/constants.js (VITE_USDC_ISSUER), so the fix must also reconcile the build-time JS default with the backend env var, or emit it from the API.
CollateralManager builds SUPPORTED_TOKENS once at class-definition time from the constants, so any change must ensure the env var is resolved before import and remains consistent across the worker process.
Proposed design
Single source of truth, consumed by all three layers:
| Layer |
Source |
Action |
| Backend |
USDC_ASSET_ISSUER env var (one default) |
constants.py remains canonical; adapters import it or a shared resolver |
| Adapters |
same env var |
replace _TokenResolver USDC entry with the canonical issuer |
| Frontend |
backend-provided value or same VITE_USDC_ISSUER default |
align the default string with the backend |
Add a startup assertion (quantara/web_app/config_validator.py already does env validation in assert_valid_config()) that fails fast if a USDC issuer is unset or mismatched.
Downstream impact
This touches the canonical asset identifier used by CollateralManager, DepositMixin/TokenParams (quantara/web_app/contract_tools/), and the frontend balance display (quantara/frontend/src/services/wallet.jsx). Any consumer that has persisted the old GA5ZSE... asset must be migrated or the change gated by environment.
Acceptance criteria
Backend
Frontend
Tests
Out of scope
Do not add new tokens or change issuer-selection UX in this issue; only reconcile USDC (and, if trivially adjacent, the ETH issuer already shared between layers).
Getting started
Files in scope: quantara/web_app/contract_tools/constants.py, quantara/soroban/adapters/soroswap_adapter.py, quantara/soroban/adapters/blend_adapter.py, quantara/frontend/src/utils/constants.js. Verify with:
cd quantara && poetry run pytest web_app/tests
cd quantara/frontend && yarn test:run
Good first files to read: quantara/web_app/contract_tools/constants.py, quantara/soroban/adapters/soroswap_adapter.py, quantara/soroban/adapters/CollateralManager.py.
Labels / Complexity: bug, financial, soroban, protocol · Extremely High — 500
Problem
The same "USDC" token is resolved to two different Stellar issuers in different layers of the codebase:
quantara/web_app/contract_tools/constants.pydefinesUSDC_ASSET_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NOJ4VBH6THS2G2V"and buildsUSDC_ASSET_ID = "USDC:<issuer>".quantara/soroban/adapters/soroswap_adapter.pyandquantara/soroban/adapters/blend_adapter.pyhardcode USDC as"USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGCS3FOGTICSJCWV5X2HGM"in their_TokenResolver._TOKENS.quantara/frontend/src/utils/constants.jsdefaultsUSDC_ASSET.issuertoGBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NOJ4VBH6THS2G2V(matchingconstants.py).CollateralManager(quantara/soroban/adapters/CollateralManager.py) importsUSDC_ASSET_IDfromweb_app.contract_tools.constants, so its USDC config resolves toGBBD47...S2G2V, whileBlendLendingAdapter/SoroswapAMMAdapterresolve "USDC" toGA5ZSE...X2HGM.Consequence: the collateral engine and the AMM/lending adapters disagree about which on-chain asset "USDC" is. A position whose collateral is valued using the
GBBD47...asset while its debt is quoted/borrowed against theGA5ZSE...asset operates on two different tokens; balances, health ratios, and swap quotes will not match, and in production this silently routes real user funds against the wrong issuer. There is no single source of truth, and no code cross-checks that the three layers agree.Root cause
Why this is architecturally hard
_TokenResolverwith a duplicated token table rather than importingTokenParamsfromweb_app.contract_tools.constants. Consolidating means deciding whether the adapters may depend onweb_app(they currently import nothing from it), or whether the constants module moves to a shared location.quantara/frontend/src/utils/constants.js(VITE_USDC_ISSUER), so the fix must also reconcile the build-time JS default with the backend env var, or emit it from the API.CollateralManagerbuildsSUPPORTED_TOKENSonce at class-definition time from the constants, so any change must ensure the env var is resolved before import and remains consistent across the worker process.Proposed design
Single source of truth, consumed by all three layers:
USDC_ASSET_ISSUERenv var (one default)constants.pyremains canonical; adapters import it or a shared resolver_TokenResolverUSDC entry with the canonical issuerVITE_USDC_ISSUERdefaultAdd a startup assertion (
quantara/web_app/config_validator.pyalready does env validation inassert_valid_config()) that fails fast if a USDC issuer is unset or mismatched.Downstream impact
This touches the canonical asset identifier used by
CollateralManager,DepositMixin/TokenParams(quantara/web_app/contract_tools/), and the frontend balance display (quantara/frontend/src/services/wallet.jsx). Any consumer that has persisted the oldGA5ZSE...asset must be migrated or the change gated by environment.Acceptance criteria
Backend
constants.py,soroswap_adapter.py,blend_adapter.py, andCollateralManager.py.assert_valid_config()fails fast when the issuer is missing or inconsistent.Frontend
Tests
CollateralManagerresolve "USDC" to the same issuer.cd quantara && poetry run pytest web_app/tests.Out of scope
Do not add new tokens or change issuer-selection UX in this issue; only reconcile USDC (and, if trivially adjacent, the ETH issuer already shared between layers).
Getting started
Files in scope:
quantara/web_app/contract_tools/constants.py,quantara/soroban/adapters/soroswap_adapter.py,quantara/soroban/adapters/blend_adapter.py,quantara/frontend/src/utils/constants.js. Verify with:Good first files to read:
quantara/web_app/contract_tools/constants.py,quantara/soroban/adapters/soroswap_adapter.py,quantara/soroban/adapters/CollateralManager.py.