Skip to content

feat: add GMGN exchange support and trending source#1473

Open
deepfeature wants to merge 1 commit intoNoFxAiOS:devfrom
deepfeature:codex-gmgn-exchange-trending
Open

feat: add GMGN exchange support and trending source#1473
deepfeature wants to merge 1 commit intoNoFxAiOS:devfrom
deepfeature:codex-gmgn-exchange-trending

Conversation

@deepfeature
Copy link
Copy Markdown

Summary

  • add GMGN as a first-class exchange with spot-only trader behavior and wallet discovery
  • add GMGN market data support plus a Strategy Studio GMGN SOL template
  • add gmgn_trending as a dynamic coin source in Strategy Studio and runtime candidate loading

Verification

  • go test ./...
  • cd web && npm run build

@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented Apr 14, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


yangzhenhui seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@shinchan-zhai
Copy link
Copy Markdown
Collaborator

Thanks for the big contribution! Solid scope — spot-only guards are layered across prompt / loop / trader, and the integration generally fits NOFX's exchange adapter style. A few things need to be addressed before we can merge.

Context for framing: NOFX is a single-user local agent (each user self-hosts their own nofx-server). So classic multi-tenant concerns (cross-account isolation, global state pollution across users) don't apply. The risks below are the ones that still matter in the single-user model — mostly UI footguns, external API robustness, and fund-math correctness.

Blockers

  1. "Private Key" label is dangerously misleading. The ExchangeConfigModal field is called GMGN Private Key *, but the value is really the GMGN OpenAPI signing key (RSA/ed25519 PEM) — the wallet itself is custodied by GMGN. A user who reads that label will very likely paste their Solana wallet seed/private key into it. That instantly lands their seed in local storage, backend logs, and potentially telemetry error payloads. Please:

    • Rename the field to GMGN API Signing Key (PEM)
    • Add explicit helper text: "Do not paste a wallet private key or seed phrase"
    • Validate format on submit — reject anything that doesn't start with -----BEGIN
  2. PEM parsing is fragile and has dead code. In provider/gmgn/client.go, strings.ReplaceAll(..., \"\\\\n\", \"\\n\") tries to accept single-line PEMs with literal \\n, but it also corrupts legitimate multi-line PEMs. The PKCS8 fallback branch right after is identical to the branch above — dead code. Please tighten the parsing and drop the unreachable fallback.

  3. No retries / rate-limit handling on the HTTP client. Solana + GMGN get flaky under load; Swap and QueryOrder will stall and leave real orders in limbo. order_sync.go handles part of this, but Swap / CreateStrategyOrder themselves need exponential backoff + a saner timeout strategy.

Non-blocking (please address before merge if possible, otherwise fast follow-up)

  • RawAmountFromDecimal loses precision. float64 * scale + 0.0000001 → int64 drops lamports on memecoins with 9 decimals at large notional. Switch to math/big or shopspring/decimal.
  • provider/gmgn/client.go (678 LOC) has zero unit tests. At minimum please add mock-HTTP coverage for Swap, QueryOrder, and the signing path.
  • GetClosedPnL returns nil, nil. This will silently pollute PnL stats. Log a warning + TODO, or return an explicit "unsupported" error the caller can handle.
  • Global gmgnAPIKey + SetGMGNAPIKey() called from auto_trader.go and kernel/engine.go. Single-user means no cross-account pollution, but multiple goroutines writing the same package-level string is still a real data race — go test -race will flag it. Cleanest fix is per-trader client injection (mirroring alpaca/binance); a mutex is acceptable as a stopgap.
  • CreateLegacy exchange allow-list is missing "gmgn" — legacy path inconsistency.
  • kernel/engine.go builds a new GMGN client on every gmgn_trending fetch — no http pool reuse.
  • store/exchange.go uses ADD COLUMN IF NOT EXISTS — PostgreSQL-specific. Please confirm SQLite deployments still migrate cleanly.

Nits

  • engine_prompt.go — the spot-only symbol example is truncated; give a full sol:<mint> example so the model doesn't learn the wrong format.
  • web/src/components/trader/TraderConfigModal.tsx — the if nextExchange === 'gmgn' branch and the else branch do the same thing; collapse them.
  • auto_trader_loop.go spot-only check mixes switch cases with a trailing default reject. Logic is correct but hard to read — an explicit allowed := map[string]bool{...} would be clearer, and add a test for open_long with Leverage=0 (the zero-value default).
  • trader/gmgn/order_sync.go — 30s poll interval is hardcoded; consider making it configurable.
  • trader/auto_trader.go — import grouping got shuffled; goimports will clean it up.

Overall direction is good. Once the three blockers are resolved and one of our maintainers can run a quick live-sandbox validation on GMGN, we're good to land. Thanks again for the contribution!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants