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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions