Skip to content

[Enhancement] Multi-asset settlement: asset registry and per-asset trustlines beyond hardcoded USDC #60

Description

@zeemscript

Summary

Generalize the payment stack from "USDC only" to a small asset registry so educators can price and settle in more than one Stellar asset (e.g. USDC, EURC, XLM, or an anchored local-currency stablecoin) while the rest of the platform keeps working. Right now a single hardcoded USDC asset is threaded through every Stellar function and the Transaction model's currency is a one-value enum, which blocks any non-USDC pricing and makes the codebase assume one issuer forever.

Current state

  • src/services/stellar/stellarService.js constructs exactly one asset: const USDC = new StellarSdk.Asset("USDC", USDC_ISSUER) with USDC_ISSUER hardcoded per network, and exports USDC, USDC_ISSUER. Every payment op is built with asset: USDC in buildPaymentTransaction, and getAccountBalance finds the balance by asset_code === "USDC" && asset_issuer === USDC_ISSUER. There is no way to express any other asset.
  • The data model is USDC-locked: Transaction.currency is { type: String, default: "USDC", enum: ["USDC"] } (src/models/Transaction.js). Course.price / Book.price / Space.price are bare Numbers with no currency dimension (src/models/Course.js, Book.js, Space.js).
  • Trustline logic is USDC-specific: hasUsdcTrustline and the hasTrustline field in getAccountBalance only ever look for USDC, so a creator who wants to be paid in EURC can't be validated.
  • Item purchase in initializePayment (src/controllers/stellar/paymentController.js) reads item.price.toString() and passes it to a USDC-only builder — there is no asset selection anywhere in the flow.

What to build

  1. Asset registry (src/config/assets.js or src/services/stellar/assets.js): a network-aware map of supported assets keyed by a short code (USDC, EURC, XLM, …) → { code, issuer (null for native), decimals, displayName, isDefault }. Derive StellarSdk.Asset instances from it; keep USDC as the default so nothing breaks. Testnet vs mainnet issuers resolved the same way stellarService.js already switches on NETWORK.
  2. Refactor the Stellar service to be asset-parametric: buildPaymentTransaction({ ..., assetCode }), getAccountBalance returns all relevant balances plus a trustlines map, and a new hasTrustline(publicKey, assetCode) supersedes the USDC-only helper (keep hasUsdcTrustline as a thin wrapper for back-compat). Resolve the StellarSdk.Asset from the registry, not a module constant.
  3. Model changes (non-breaking): add Course.currency / Book.currency (default "USDC") alongside price; widen Transaction.currency enum to the registry's codes and add assetIssuer. Existing rows (no currency) default to USDC.
  4. Purchase flow: initializePayment resolves the item's asset from its currency, validates the destination trustline for that asset (ties into the pre-flight work in [Enhancement] Wire up the unused express-validator layer on auth and payment endpoints #17), and builds the payment in the chosen asset. Reject items priced in an asset the platform doesn't support with a clear 400.
  5. Platform/creator trustline guidance: expose the per-asset trustline status in the wallet endpoints (walletController.js getMyWallet/getWalletBalance) so the UI can prompt "add a EURC trustline" when needed.
  6. stellar.toml linkage: the [[CURRENCIES]] blocks from [Enhancement] Implement a secure, verified password reset flow #16 should be generated from this same registry (single source of truth).

Acceptance criteria

  • A single asset registry is the only place issuers/codes are defined; stellarService.js no longer hardcodes a lone USDC constant for payment building (a back-compat USDC export may remain but must be derived from the registry).
  • buildPaymentTransaction builds a valid payment in any registry asset (including native XLM, which uses Asset.native() and no issuer); getAccountBalance reports balances and trustline status per asset.
  • Course/Book can be priced in a supported non-USDC asset and purchased end-to-end; Transaction records the correct currency/assetIssuer.
  • Existing USDC-priced items and existing transactions continue to work with no migration required (defaults applied).
  • Purchasing an item priced in an unsupported asset returns a 400 naming the supported codes; a destination lacking that asset's trustline is rejected pre-flight.
  • Jest tests cover: registry resolution per network, native-asset payment building, trustline check for a non-USDC asset, and the USDC default path unchanged.

Pointers

Difficulty

High — a cross-cutting refactor touching the Stellar service, three schemas, and the purchase flow, with strict back-compat requirements and correct handling of native vs issued assets and per-asset trustlines.


🏆 GrantFox OSS — Official Campaign | FWC26. Apply for this issue through the GrantFox campaign page. The maintainer assigns one contributor before work starts; unassigned PRs may not be reviewed. PRs target the dev branch. Quality bar: CI must stay green.

💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions