This guide covers the local mainnet-derived development workflow, runtime trace analysis, deployment verification, and mutation testing additions introduced for the devtooling issues.
Stellar Quickstart supports local, testnet, futurenet, and pubnet modes. It does not support a mutable pubnet fork in the same way an EVM fork does. The local StellarLend workflow therefore uses:
pubnetas the read-only reference network for realistic assumptions.localas the writable sandbox for contract deployment, hot-reload, and state manipulation.scripts/dev/default-seed.jsonas the compatibility layer between the reference network and the writable sandbox.
npm run dev:forkUseful flags:
bash scripts/dev/start-fork-env.sh --source-network testnet --reset
bash scripts/dev/start-fork-env.sh --seed-file scripts/dev/default-seed.jsonThe script writes .dev/fork.env so tooling can reuse the selected source network and seed manifest.
npm run dev:watch:contractsIf you want every rebuild redeployed into the local sandbox:
bash scripts/dev/watch-contracts.sh --deploy-localThis requires cargo-watch:
cargo install cargo-watchnpm run dev:seedThis generates .dev/seed.env from scripts/dev/default-seed.json. If ADMIN_SECRET_KEY and LENDING_CONTRACT_ID are exported, the helper also applies the emergency pause flag on-chain so pause-path debugging can be exercised in the sandbox.
pubnetreference:bash scripts/dev/start-fork-env.sh --source-network pubnettestnetreference:bash scripts/dev/start-fork-env.sh --source-network testnetfuturenetreference:bash scripts/dev/start-fork-env.sh --source-network futurenet
The sandbox itself always runs in local mode so contracts remain writable and disposable.
pubnetreference mode uses substantially more CPU, RAM, and disk thantestnetorlocal.- The sandbox is intended for focused contract debugging, not soak testing.
- Keep trace collection scoped to a single transaction or contract family; full-session tracing is noisy and expensive.
The oracle workspace now ships a trace analysis helper that can summarize nested contract invocations, gas breakdown, state changes, and tracing overhead.
npm run trace:contract -- path/to/trace.jsonInput format:
{
"transactionHash": "...",
"network": "local",
"ledger": 123,
"elapsedMs": 12,
"tracingElapsedMs": 16,
"invocations": [
{
"contractId": "CLEND",
"functionName": "borrow",
"gasUsed": 12,
"children": []
}
]
}The analyzer returns:
- flattened call frames suitable for call-stack reconstruction
- cumulative gas hot paths
- aggregated state-change summaries
- overhead warnings when tracing materially changes runtime characteristics
After deployment and initialization, run:
npm run deploy:verify -- --network testnetThe verifier checks:
- lending admin readability
- risk parameter defaults
- emergency pause state
- local artifact hash against the deployment manifest
- AMM settings readability when an AMM deployment is present
To verify that deployed contracts match their source code:
# Verify a specific contract
npm run verify:contract -- --contract-id <contract_id> --source <source_path> --network testnet
# Verify during deployment
npm run deploy -- --network testnet --build --verifyThe verification rebuilds the contract from source and compares the bytecode with the deployed contract. This ensures:
- Automated compilation verification
- Metadata matching
- Multi-file verification support
- Proxy pattern verification (for upgradeable contracts)
Verification results are stored in the deployment manifest with a verification_status field.
Mutation testing is configured in the API workspace with Stryker.
The initial mutation gate targets the pagination cursor helpers (src/utils/pagination.ts) because that slice has a clean, deterministic Jest baseline today. The scope can be widened as the rest of the API test suite is stabilized.
npm run mutation:api- high: 80
- low: 70
- break: 70
Mutation reports are written to api/reports/mutation/.
- Latest local run on this branch: 78.87 mutation score for
src/utils/pagination.ts - Surviving mutants show the highest-value follow-up tests are:
- assert the exact thrown messages for malformed and empty cursor cases
- cover the
parsedLimit > maxLimitrejection path explicitly - verify
decodeCursorrejects empty decoded payloads more directly - verify cursor trimming remains part of the opaque cursor contract
The repository includes a dedicated GitHub Actions workflow for scheduled or manually triggered mutation runs so regular PR validation remains fast while mutation testing still has a persistent quality gate.