From 7588ba1a0511b43c063166195729b69f492cfc54 Mon Sep 17 00:00:00 2001 From: wang1r <923096443@qq.com> Date: Fri, 9 Oct 2026 23:32:31 +0800 Subject: [PATCH] fix(indexer): correct native SAC network defaults --- .env.example | 14 +++++++------- docs/DUAL_NETWORK.md | 2 +- src/__tests__/sacContractIds.test.ts | 12 ++++++++++++ src/__tests__/sacDetect.test.ts | 21 +++++++++++++++------ src/indexer.ts | 4 ++-- src/indexer/sac-detect.ts | 4 ++-- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/.env.example b/.env.example index 1d8ed5ef1..2f773a89d 100644 --- a/.env.example +++ b/.env.example @@ -45,8 +45,8 @@ POLL_INTERVAL_MS=6000 # ─── SAC Contract IDs ──────────────────────────────────────── # Comma-separated list of Soroban SAC (Stellar Asset Contract) IDs to watch. # When unset, defaults to the native XLM SAC for the configured STELLAR_NETWORK: -# testnet → CDMLFMKMMD7MWZP3FKUBZPVHTUEDLSX4BYGYKH4GCESXYHS3IHQ4EIG4 -# mainnet → CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC +# testnet → CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC +# mainnet → CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA # # How to derive a SAC address for any Stellar asset: # import { Asset, Networks } from '@stellar/stellar-sdk'; @@ -57,15 +57,15 @@ POLL_INTERVAL_MS=6000 # .contractId(Networks.PUBLIC); # # Known mainnet SAC addresses: -# XLM (native): CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC -# USDC (Centre): CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA +# XLM (native): CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA +# USDC (Centre): CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75 # # Example (watch XLM + USDC on mainnet): -# SAC_CONTRACT_IDS=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC,CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA +# SAC_CONTRACT_IDS=CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA,CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75 # # Per-network watch lists (for dual-network deployments when NETWORKS=testnet,mainnet): -# SAC_CONTRACT_IDS_TESTNET=CDMLFMKMMD7MWZP3FKUBZPVHTUEDLSX4BYGYKH4GCESXYHS3IHQ4EIG4 -# SAC_CONTRACT_IDS_MAINNET=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC +# SAC_CONTRACT_IDS_TESTNET=CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC +# SAC_CONTRACT_IDS_MAINNET=CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA # # Soroban RPC's getEvents accepts multiple contract IDs in a single request — # there is no N+1 query penalty for watching multiple contracts. diff --git a/docs/DUAL_NETWORK.md b/docs/DUAL_NETWORK.md index 2bf30281a..f29d9d836 100644 --- a/docs/DUAL_NETWORK.md +++ b/docs/DUAL_NETWORK.md @@ -27,7 +27,7 @@ behave exactly as before; #161 and #163 pass it explicitly. | `NETWORKS` | `testnet` | `testnet,mainnet` to index both in one process | | `STELLAR_NETWORK` | `testnet` | `mainnet` | | `SOROBAN_RPC_URL` | `https://soroban-testnet.stellar.org` | external provider endpoint (**secret — host env only**) | -| `SAC_CONTRACT_IDS` | testnet SAC `CDMLFMKM…` | mainnet XLM SAC `CDLZFC3SY…` | +| `SAC_CONTRACT_IDS` | testnet SAC `CDLZFC3SY…` | mainnet XLM SAC `CAS3J7GY…` | | `DATABASE_URL` | testnet DB | **separate** mainnet DB | | `HORIZON_URL` | optional testnet | optional `https://horizon.stellar.org` | diff --git a/src/__tests__/sacContractIds.test.ts b/src/__tests__/sacContractIds.test.ts index e67c55166..549b92f80 100644 --- a/src/__tests__/sacContractIds.test.ts +++ b/src/__tests__/sacContractIds.test.ts @@ -9,6 +9,7 @@ * - SAC_CONTRACT_IDS takes precedence over CONTRACT_IDS when both set */ +import { Asset, Networks } from '@stellar/stellar-sdk'; import { resolveSacContractIds, DEFAULT_XLM_SAC_MAINNET, @@ -22,6 +23,8 @@ beforeEach(() => { // Shallow-clone so mutations don't bleed between tests process.env = { ...ORIGINAL_ENV }; delete process.env.SAC_CONTRACT_IDS; + delete process.env.SAC_CONTRACT_IDS_MAINNET; + delete process.env.SAC_CONTRACT_IDS_TESTNET; delete process.env.CONTRACT_IDS; delete process.env.STELLAR_NETWORK; }); @@ -69,6 +72,15 @@ describe('resolveSacContractIds — legacy CONTRACT_IDS fallback', () => { // ── Default XLM SAC fallback ───────────────────────────────────────────────── describe('resolveSacContractIds — default XLM SAC when no env var is set', () => { + it.each([ + ['mainnet', Networks.PUBLIC, DEFAULT_XLM_SAC_MAINNET], + ['testnet', Networks.TESTNET, DEFAULT_XLM_SAC_TESTNET], + ] as const)('uses the SDK-derived native SAC for %s', (network, passphrase, defaultId) => { + const derivedId = Asset.native().contractId(passphrase); + expect(defaultId).toBe(derivedId); + expect(resolveSacContractIds(network)).toEqual([derivedId]); + }); + it('defaults to the testnet XLM SAC when STELLAR_NETWORK is unset', () => { const result = resolveSacContractIds(); expect(result).toEqual([DEFAULT_XLM_SAC_TESTNET]); diff --git a/src/__tests__/sacDetect.test.ts b/src/__tests__/sacDetect.test.ts index 8082d6d7f..a6b2734e0 100644 --- a/src/__tests__/sacDetect.test.ts +++ b/src/__tests__/sacDetect.test.ts @@ -6,7 +6,7 @@ * fake instance fetcher is injected throughout. */ -import { xdr } from "@stellar/stellar-sdk"; +import { Asset, Networks, xdr } from "@stellar/stellar-sdk"; import { executableIsSac, instanceValIsSac, @@ -17,9 +17,6 @@ import { type InstanceFetcher, } from "../indexer/sac-detect"; -// The native XLM SACs are hard-coded as known SACs in the module. -const TESTNET_XLM_SAC = "CDMLFMKMMD7MWZP3FKUBZPVHTUEDLSX4BYGYKH4GCESXYHS3IHQ4EIG4"; - function sacInstanceVal(): xdr.ScVal { return xdr.ScVal.scvContractInstance( new xdr.ScContractInstance({ @@ -67,12 +64,24 @@ describe("instanceValIsSac", () => { }); describe("detectSac", () => { - it("returns true for a known native XLM SAC without fetching", async () => { + it.each([ + ["mainnet", Networks.PUBLIC], + ["testnet", Networks.TESTNET], + ] as const)("recognizes the SDK-derived %s native XLM SAC without fetching", async (network, passphrase) => { const fetcher = jest.fn, [string]>(); - await expect(detectSac(TESTNET_XLM_SAC, fetcher)).resolves.toBe(true); + const contractId = Asset.native().contractId(passphrase); + await expect(detectSac(contractId, fetcher, network)).resolves.toBe(true); expect(fetcher).not.toHaveBeenCalled(); }); + it("does not treat the standalone native SAC as a known mainnet or testnet contract", async () => { + const contractId = Asset.native().contractId(Networks.STANDALONE); + const fetcher = jest.fn(async () => null); + await expect(detectSac(contractId, fetcher, "mainnet")).resolves.toBe(false); + await expect(detectSac(contractId, fetcher, "testnet")).resolves.toBe(false); + expect(fetcher).toHaveBeenCalledTimes(2); + }); + it("classifies a SAC via the injected fetcher", async () => { const fetcher: InstanceFetcher = async () => sacInstanceVal(); await expect(detectSac("CSACTOKEN", fetcher)).resolves.toBe(true); diff --git a/src/indexer.ts b/src/indexer.ts index 03363be74..c9e4a8597 100644 --- a/src/indexer.ts +++ b/src/indexer.ts @@ -34,9 +34,9 @@ export function resolveNftContractIds(network?: Network): string[] { // These are derived from Asset.native().contractId(Networks.PUBLIC / Networks.TESTNET) // and serve as the backwards-compatible default when SAC_CONTRACT_IDS is unset. export const DEFAULT_XLM_SAC_MAINNET = - "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"; + "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA"; export const DEFAULT_XLM_SAC_TESTNET = - "CDMLFMKMMD7MWZP3FKUBZPVHTUEDLSX4BYGYKH4GCESXYHS3IHQ4EIG4"; + "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"; /** * Resolve the list of SAC contract IDs to watch. diff --git a/src/indexer/sac-detect.ts b/src/indexer/sac-detect.ts index 6687e6ae3..a1a934a84 100644 --- a/src/indexer/sac-detect.ts +++ b/src/indexer/sac-detect.ts @@ -26,8 +26,8 @@ import { resolveNetwork, type Network } from "../network"; // The native XLM SAC on mainnet and testnet. These are fixed by the network and // never change, so we short-circuit detection (and any RPC call) for them. const KNOWN_SAC_IDS = new Set([ - "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", // mainnet native XLM - "CDMLFMKMMD7MWZP3FKUBZPVHTUEDLSX4BYGYKH4GCESXYHS3IHQ4EIG4", // testnet native XLM + "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA", // mainnet native XLM + "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", // testnet native XLM ]); // ─── Pure helpers ─────────────────────────────────────────────────────────────