You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
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.
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.
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.
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.
Platform/creator trustline guidance: expose the per-asset trustline status in the wallet endpoints (walletController.jsgetMyWallet/getWalletBalance) so the UI can prompt "add a EURC trustline" when needed.
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.
Gotchas: native XLM has asset_type: "native" and no issuer — the registry and balance-matching must special-case it. Amounts stay strings to preserve precision (as Transaction.amount already does). PRs target dev.
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
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
Transactionmodel'scurrencyis a one-value enum, which blocks any non-USDC pricing and makes the codebase assume one issuer forever.Current state
src/services/stellar/stellarService.jsconstructs exactly one asset:const USDC = new StellarSdk.Asset("USDC", USDC_ISSUER)withUSDC_ISSUERhardcoded per network, and exportsUSDC,USDC_ISSUER. Every payment op is built withasset: USDCinbuildPaymentTransaction, andgetAccountBalancefinds the balance byasset_code === "USDC" && asset_issuer === USDC_ISSUER. There is no way to express any other asset.Transaction.currencyis{ type: String, default: "USDC", enum: ["USDC"] }(src/models/Transaction.js).Course.price/Book.price/Space.priceare bareNumbers with no currency dimension (src/models/Course.js,Book.js,Space.js).hasUsdcTrustlineand thehasTrustlinefield ingetAccountBalanceonly ever look for USDC, so a creator who wants to be paid in EURC can't be validated.initializePayment(src/controllers/stellar/paymentController.js) readsitem.price.toString()and passes it to a USDC-only builder — there is no asset selection anywhere in the flow.What to build
src/config/assets.jsorsrc/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 }. DeriveStellarSdk.Assetinstances from it; keepUSDCas the default so nothing breaks. Testnet vs mainnet issuers resolved the same waystellarService.jsalready switches onNETWORK.buildPaymentTransaction({ ..., assetCode }),getAccountBalancereturns all relevant balances plus atrustlinesmap, and a newhasTrustline(publicKey, assetCode)supersedes the USDC-only helper (keephasUsdcTrustlineas a thin wrapper for back-compat). Resolve theStellarSdk.Assetfrom the registry, not a module constant.Course.currency/Book.currency(default"USDC") alongsideprice; widenTransaction.currencyenum to the registry's codes and addassetIssuer. Existing rows (nocurrency) default to USDC.initializePaymentresolves the item's asset from itscurrency, 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 clear400.walletController.jsgetMyWallet/getWalletBalance) so the UI can prompt "add a EURC trustline" when needed.[[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
stellarService.jsno longer hardcodes a loneUSDCconstant for payment building (a back-compatUSDCexport may remain but must be derived from the registry).buildPaymentTransactionbuilds a valid payment in any registry asset (including native XLM, which usesAsset.native()and no issuer);getAccountBalancereports balances and trustline status per asset.Course/Bookcan be priced in a supported non-USDC asset and purchased end-to-end;Transactionrecords the correctcurrency/assetIssuer.400naming the supported codes; a destination lacking that asset's trustline is rejected pre-flight.Pointers
src/services/stellar/stellarService.js(USDC/USDC_ISSUER,buildPaymentTransaction,getAccountBalance,hasUsdcTrustline,NETWORKswitch),src/models/Transaction.js(currencyenum, addassetIssuer),src/models/Course.js/Book.js/Space.js(price),src/controllers/stellar/paymentController.js(initializePayment),src/controllers/stellar/walletController.js(balance responses).[[CURRENCIES]]should read this registry), [Enhancement] Wire up the unused express-validator layer on auth and payment endpoints #17 (per-asset pre-flight/trustline checks), [Enhancement] Path payments: pay in any Stellar asset with USDC settlement via path-payment-strict-receive #27 (path payments — a different mechanism: pay in asset X, settle in USDC; this issue is settling natively in asset X). [Enhancement] Educator earnings ledger and atomic batch payouts from the platform wallet #28 (educator payouts) will later need to pay out in the item's asset.asset_type: "native"and no issuer — the registry and balance-matching must special-case it. Amounts stay strings to preserve precision (asTransaction.amountalready does). PRs targetdev.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
devbranch. Quality bar: CI must stay green.💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0