Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/s03-indexer/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
ENDPOINT=https://horizon-testnet.stellar.org
CHAIN_ID="Test SDF Network ; September 2015"
SOROBAN_ENDPOINT=https://soroban-testnet.stellar.org
INDEXER_NETWORK=testnet
# INDEXER_CONTRACTS_CONFIG=config/contracts.testnet.json
# SO4_CONTRACTS_REPO=../contracts

# Local standalone defaults. Uncomment these when running against a local
# stellar/quickstart or standalone network instead of public testnet.
# ENDPOINT=http://host.docker.internal:8000
# CHAIN_ID="Standalone Network ; February 2017"
# SOROBAN_ENDPOINT=http://host.docker.internal:8000/soroban/rpc
# INDEXER_NETWORK=local
# INDEXER_CONTRACTS_CONFIG=config/contracts.local.json
41 changes: 41 additions & 0 deletions apps/s03-indexer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The same commands are available inside the workspace package:
```bash
bun run --cwd apps/s03-indexer codegen
bun run --cwd apps/s03-indexer build
bun run --cwd apps/s03-indexer sync:contracts:testnet
bun run --cwd apps/s03-indexer sync:contracts:local
bun run --cwd apps/s03-indexer dev
bun run --cwd apps/s03-indexer start
```
Expand Down Expand Up @@ -62,6 +64,7 @@ The default configuration targets public SDF testnet:
ENDPOINT=https://horizon-testnet.stellar.org
CHAIN_ID=Test SDF Network ; September 2015
SOROBAN_ENDPOINT=https://soroban-testnet.stellar.org
INDEXER_NETWORK=testnet
```

Use these defaults when indexing public testnet data or when contributors need
Expand All @@ -74,6 +77,7 @@ variables to the local values shown in `.env.example`:
ENDPOINT=http://host.docker.internal:8000
CHAIN_ID=Standalone Network ; February 2017
SOROBAN_ENDPOINT=http://host.docker.internal:8000/soroban/rpc
INDEXER_NETWORK=local
```

Use local endpoints when you are testing against a local Stellar network,
Expand All @@ -86,6 +90,43 @@ Endpoint and chain settings are read while SubQuery generates/builds
`bun run indexer:build` before `bun run indexer:start`. `bun run indexer:dev`
does all three steps for you.

## Contract Manifests

The indexer reads SO4 contract IDs from generated JSON files in `config/`.
Refresh them after deploying or bootstrapping contracts:

```bash
bun run --cwd apps/s03-indexer sync:contracts:testnet
bun run --cwd apps/s03-indexer sync:contracts:local
```

The sync script reads the contracts repo deployment outputs:

- `.deployed/<network>.env`
- `.deployed/tokens-<network>.env`
- `.deployed/frontend-<network>.env`
- `.deployed/frontend-<network>.ts`
- `.stellar/contract-ids/<network>.json`

By default it looks for the sibling contracts repo used by the SO4 workspace
layout. Override that path when needed:

```bash
SO4_CONTRACTS_REPO=/path/to/contracts bun run --cwd apps/s03-indexer sync:contracts:testnet
```

`contracts.testnet.json` and `contracts.local.json` are separate files, so local
and testnet manifests can coexist without overwriting each other. `project.ts`
uses `INDEXER_NETWORK` to select `config/contracts.<network>.json`, or
`INDEXER_CONTRACTS_CONFIG` when you need to point at a specific manifest.

The generated config includes the network name, network passphrase, Horizon
endpoint, Soroban RPC endpoint, core protocol contracts, test token contracts,
faucet contract, and complete market token/index/long/short triplets. The sync
fails fast on malformed contract IDs and required missing values. Missing
`MARKET_TOKEN_*` values are reported as warnings because they are expected before
market bootstrap.

## Generated Artifacts

`project.ts`, `schema.graphql`, and the TypeScript mapping sources are the
Expand Down
65 changes: 65 additions & 0 deletions apps/s03-indexer/config/contracts.testnet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"generatedAt": "2026-06-23T17:51:38.413Z",
"source": {
"contractsRepoPath": "/home/sunny/zero/so4-market-project/contracts",
"files": {
"contractIds": ".stellar/contract-ids/testnet.json",
"deployedEnv": ".deployed/testnet.env",
"tokensEnv": ".deployed/tokens-testnet.env",
"frontendEnv": ".deployed/frontend-testnet.env",
"frontendTs": ".deployed/frontend-testnet.ts"
}
},
"network": {
"name": "testnet",
"passphrase": "Test SDF Network ; September 2015",
"horizonEndpoint": "https://horizon-testnet.stellar.org",
"sorobanRpcEndpoint": "https://soroban-testnet.stellar.org"
},
"contracts": {
"role_store": "CBSUAIAMIFFS4AXQYZ7KR7FNO7IMKAPS5WF4DXANVXDTPKH2F7YUIN6Q",
"data_store": "CCZ3VKBEDLNBO2JM3EXL3SNBDJOV5BTN52FVQPER7F6D5GCE53PITQ3J",
"oracle": "CBEMTV23SIJJBIST3V5HTMWHR4MHYGHNBIG4M26U4LGUJTWZXTFSVQEY",
"market_factory": "CBGX3EJFI3JRHSN5B533O2L5P57JFPTCRS55IPWFS5BNDXLJLXDWA5Z2",
"deposit_handler": "CDWOFIP4YQJGMCYAOWLSRBAWN2OTJUG2I5WOFC32O2TX2SRU56RWBE5C",
"withdrawal_handler": "CBRWM6PNRRFL5RSTJH6HWEXBTMWCGLQRO45NTRDB6BBABWXZ4ZE7DGTO",
"order_handler": "CC35OFZVWUTAZPV3B6UKSDVAVORZEWUUMOMTHO33H4YR4C5FKPEFODKY",
"liquidation_handler": "CBXUAR5GCHIRFQL75WTZS3FLA6SMWDPIKG4EKNPWVQVNGVFXBHGTJHTM",
"adl_handler": "CACFPG3QAKG6DCAJSOP7YGDTM44NV6NPI3SKAG7GUGIV6DMGXPCAMMME",
"fee_handler": "CC4P3FJ7EAH6F3RYJPQ2T7VIB4I7UJ4EEYGVWTZVXTAUN647QRVSDHS4",
"referral_storage": "CDHTPQO4RRJ6OUBIW3GDXTIVLVOMIKPJC65PGDJH2G5OLDJRE5KTROWK",
"reader": "CC6OZUHF3LVO6PNP3V2EB36ORB3YSVYSH3LWD3RFLO4NUO3BYCXSWSYC",
"exchange_router": "CBD6BQSQFROWIIT5QCYN7KL5LJJWUIH7CEWUSZIFMUJO6NPXE6CVGYNW"
},
"tokens": {
"TUSDC": "CBAN5YU3KRDKPTQ2H76D6S7HQFPRBGUD524F65BUM2RQCITPTRLKWKES",
"TWBTC": "CCFTOPHUPSUDO2MB4X5D3XYJ2HRJ7NJPAW4UVPAVN7ZLE63EZLSMXDUO",
"TETH": "CAJ6BZKGFT47ALGMVFZZGAOXBV2RWIVYVCU4WJCQIURKRNXU346RWVAU",
"TXLM": "CAHNXBBSXVMGI6G3FUBY3OTNWKQ7434FDDEEE7ZT733WIW6NUZL4ONU6",
"faucet": "CCWXXBKXHHP5DXC6TYVIL22XUNHD5A75O6WM5D2KM5PY45IOV5VDMARJ"
},
"markets": [
{
"name": "TETH/TUSDC",
"marketToken": "CCBUUSYZJTGVA6PYUNQDFPZFHTBZ2QSHOUO7YAGRQVA46T3ZLSIYULS4",
"indexToken": "CAJ6BZKGFT47ALGMVFZZGAOXBV2RWIVYVCU4WJCQIURKRNXU346RWVAU",
"longToken": "CAJ6BZKGFT47ALGMVFZZGAOXBV2RWIVYVCU4WJCQIURKRNXU346RWVAU",
"shortToken": "CBAN5YU3KRDKPTQ2H76D6S7HQFPRBGUD524F65BUM2RQCITPTRLKWKES"
},
{
"name": "TWBTC/TUSDC",
"marketToken": "CDDVSLBGGDV2UOFN5W72R4LW7ABYL7H7ZWVSFHGMXXB3D52ZYANC5G3L",
"indexToken": "CCFTOPHUPSUDO2MB4X5D3XYJ2HRJ7NJPAW4UVPAVN7ZLE63EZLSMXDUO",
"longToken": "CCFTOPHUPSUDO2MB4X5D3XYJ2HRJ7NJPAW4UVPAVN7ZLE63EZLSMXDUO",
"shortToken": "CBAN5YU3KRDKPTQ2H76D6S7HQFPRBGUD524F65BUM2RQCITPTRLKWKES"
},
{
"name": "TXLM/TUSDC",
"marketToken": "CDIBR7BDCDWGAG3CC6PBKRSLMISPYKNDGE57DCZO5TMTLZK34TMGKFQQ",
"indexToken": "CAHNXBBSXVMGI6G3FUBY3OTNWKQ7434FDDEEE7ZT733WIW6NUZL4ONU6",
"longToken": "CAHNXBBSXVMGI6G3FUBY3OTNWKQ7434FDDEEE7ZT733WIW6NUZL4ONU6",
"shortToken": "CBAN5YU3KRDKPTQ2H76D6S7HQFPRBGUD524F65BUM2RQCITPTRLKWKES"
}
],
"warnings": []
}
4 changes: 3 additions & 1 deletion apps/s03-indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
"description": "This project can be use as a starting point for developing your new Stellar Soroban Test Network SubQuery project",
"main": "dist/index.js",
"scripts": {
"build": "subql build",
"build": "bun run codegen && subql build",
"codegen": "subql codegen",
"sync:contracts:testnet": "bun run scripts/sync-contracts.ts --network testnet",
"sync:contracts:local": "bun run scripts/sync-contracts.ts --network local",
"start": "docker compose pull && docker compose up --remove-orphans",
"start:docker": "bun run start",
"dev": "bun run codegen && bun run build && bun run start",
Expand Down
109 changes: 72 additions & 37 deletions apps/s03-indexer/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {
StellarHandlerKind,
StellarProject,
} from "@subql/types-stellar";
import { Horizon } from "@stellar/stellar-sdk";

import * as dotenv from 'dotenv';
import { existsSync, readFileSync } from 'fs';
import path from 'path';

const mode = process.env.NODE_ENV || 'production';
Expand All @@ -14,12 +14,38 @@ const mode = process.env.NODE_ENV || 'production';
const dotenvPath = path.resolve(__dirname, `.env${mode !== 'production' ? `.${mode}` : ''}`);
dotenv.config({ path: dotenvPath, quiet: true });

type IndexerContractsConfig = {
network?: {
name?: string;
passphrase?: string;
horizonEndpoint?: string;
sorobanRpcEndpoint?: string;
};
contracts?: Record<string, string>;
tokens?: Record<string, string>;
markets?: Array<{
name: string;
marketToken: string;
indexToken: string;
longToken: string;
shortToken: string;
}>;
};

const contractConfig = loadContractConfig();
const endpoint =
process.env.ENDPOINT ?? "https://horizon-testnet.stellar.org";
process.env.ENDPOINT ??
contractConfig?.network?.horizonEndpoint ??
"https://horizon-testnet.stellar.org";
const chainId =
process.env.CHAIN_ID ?? "Test SDF Network ; September 2015";
process.env.CHAIN_ID ??
contractConfig?.network?.passphrase ??
"Test SDF Network ; September 2015";
const sorobanEndpoint =
process.env.SOROBAN_ENDPOINT ?? "https://soroban-testnet.stellar.org";
process.env.SOROBAN_ENDPOINT ??
contractConfig?.network?.sorobanRpcEndpoint ??
"https://soroban-testnet.stellar.org";
const indexedContractIds = getIndexedContractIds(contractConfig);

/* This is your project configuration */
const project: StellarProject = {
Expand Down Expand Up @@ -68,44 +94,53 @@ const project: StellarProject = {
startBlock: 228206,
mapping: {
file: "./dist/index.js",
handlers: [
{
handler: "handleOperation",
kind: StellarHandlerKind.Operation,
filter: {
type: Horizon.HorizonApi.OperationResponseType.payment,
},
},
{
handler: "handleCredit",
kind: StellarHandlerKind.Effects,
filter: {
type: "account_credited",
},
handlers: indexedContractIds.map((contractId) => ({
handler: "handleEvent",
kind: StellarHandlerKind.Event,
filter: {
contractId,
},
{
handler: "handleDebit",
kind: StellarHandlerKind.Effects,
filter: {
type: "account_debited",
},
},
{
handler: "handleEvent",
kind: StellarHandlerKind.Event,
filter: {
/* You can optionally specify a smart contract address here
contractId: "" */
topics: [
"transfer", // Topic signature(s) for the events, there can be up to 4
],
},
},
],
})),
},
},
],
};

// Must set default to the project instance
export default project;

function loadContractConfig(): IndexerContractsConfig | undefined {
const network = process.env.INDEXER_NETWORK ?? "testnet";
const configPath = process.env.INDEXER_CONTRACTS_CONFIG
? path.resolve(process.cwd(), process.env.INDEXER_CONTRACTS_CONFIG)
: path.resolve(__dirname, "config", `contracts.${network}.json`);

if (!existsSync(configPath)) {
return undefined;
}

return JSON.parse(readFileSync(configPath, "utf8")) as IndexerContractsConfig;
}

function getIndexedContractIds(config: IndexerContractsConfig | undefined): string[] {
if (!config) {
const fallbackContractId = process.env.INDEXER_CONTRACT_ID;
return fallbackContractId ? [fallbackContractId] : [];
}

const ids = new Set<string>();
for (const value of Object.values(config.contracts ?? {})) {
ids.add(value);
}
for (const value of Object.values(config.tokens ?? {})) {
ids.add(value);
}
for (const market of config.markets ?? []) {
ids.add(market.marketToken);
ids.add(market.indexToken);
ids.add(market.longToken);
ids.add(market.shortToken);
}

return [...ids].sort();
}
Loading
Loading