Closes #392
Use this checklist before engaging an external smart-contract security auditor. Each item links to the relevant file or contract so the reviewer can verify completion status quickly.
| Area | Key Files |
|---|---|
| Smart contracts | contracts/*/src/lib.rs |
| Shared errors | contracts/errors/src/lib.rs |
| Backend auth | novaRewards/backend/routes/auth.js, middleware/authenticateUser.js |
| Token service | novaRewards/backend/services/tokenService.js |
| Security docs | docs/security/, SECURITY.md |
| Threat model | docs/security/threat-model.md |
| Contract docs | docs/contracts-full-reference.md, docs/abi-reference.md |
| Error codes | docs/error-codes.md |
| Monitoring | monitoring/, monitoring/CHECKLIST.md |
| Deployment | novaRewards/deploy/, scripts/deploy-contracts.sh |
| DB migrations | novaRewards/database/ |
- Every public contract function that modifies state calls
require_auth()on the appropriate signer (admin,owner, or the acting address). -
admin_rolescontract is the single source of truth for role grants — no contract hard-codes an admin address. - Two-step admin transfer (
propose_admin/accept_admin) is used inadmin_rolesto prevent accidental ownership loss. - Multisig threshold (
Threshold) is set to ≥ 2 on all production contract deployments — verify indeployments/. - Backend
authenticateUsermiddleware uses RS256 JWT verification and checks the Redis blocklist (isRevoked) before accepting a token. - Backend
requireAdminmiddleware logs privilege-escalation attempts viaAuditServiceandSecurityAlertService.
- All multiplication/division in
calculate_payoutuseschecked_mul/checked_div— overflow panics deterministically. -
nova-rewardsusesi128for all balance × rate intermediate products to avoid overflow (SCALE_FACTOR = 1_000_000). -
saturating_addis used for balance credit operations innova_tokento cap ati128::MAXrather than wrapping. - No bare
*or/on user-supplied numeric inputs anywhere in any contract.
- Soroban's execution model is single-threaded and atomic per transaction — cross-contract reentrancy is not possible, but verify no contract calls back into itself via the router.
-
swap_for_xlminnova-rewardsdeducts balance before calling the DEX router. -
distributein the distribution contract deducts token balance before emitting the event.
-
amount <= 0is rejected withAmountMustBePositive/InvalidAmountin every function that accepts a numeric amount. -
start_ledger >= end_ledgeris rejected withInvalidLedgerRangein campaign and vesting creation. -
max_budget == 0is rejected withInvalidBudget. - Batch size == 0 or > 50 is rejected with
EmptyBatch/BatchTooLarge. - Parallel arrays (
recipients,amounts) length mismatch is rejected withLengthMismatch. - Backend DTOs (
registerDto,loginDto) validate all fields before any DB query.
- Batch operations cap at 50 recipients (
MAX_TOKENS = 5in campaign, 50 in distribution) to stay within Soroban compute budget. - Daily withdrawal limit (
DailyLimit) prevents a single wallet from drainingnova-rewardsorreward_pool. - Backend rate limiters: global (100 req/min), login (10/15min), refresh (30/15min) — verify
novaRewards/backend/server.jsmounting. - Abuse detection (
checkIpBlock,recordFailedLogin) is applied to/auth/login.
- No hardcoded private keys, mnemonics, or secrets in any contract source file.
- Backend reads
JWT_PRIVATE_KEYandJWT_PUBLIC_KEYviaconfigService.getRequiredConfig()— never fromprocess.envdirectly. -
.envfiles are in.gitignore— verify withgit ls-files novaRewards/.env. - Production secrets are stored in AWS Secrets Manager (
infrastructure/secrets/).
- All M-of-N upgrade paths require ≥ 2 unique signers in
UpgradeApprovalsbeforeupdate_current_contract_wasmis called. -
nova-rewardsmigrate()panics ifmigrated_version >= migration_version(idempotency guard). - WASM hashes are recorded in
deployments/after every upgrade.
- Every state-changing public function emits at least one event.
- All events include
schema_versionas the first data element (EVENT_SCHEMA_VERSION = 1). - Event topic pairs are unique across contracts (no topic collision between
("gov", "voted")and any other contract).
-
contracts/errors/src/lib.rscontains a complete, non-overlapping set of error codes (1–30). -
docs/error-codes.mdmatches the current source — run a diff to verify. - Each contract-local error enum (e.g.
ReferralError,RedemptionError) uses codes starting at 1 and does not collide withContractError.
- Every public function in every contract has a
///rustdoc comment covering: parameters, return value, authorization requirement, panics/errors, and events. -
contracts/errors/src/lib.rstable comment is up to date with all 30 codes. - Inline comments explain non-obvious arithmetic (e.g. fixed-point scaling in
calculate_payout).
-
cargo fmt --all -- --checkpasses with zero diff incontracts/. -
cargo clippy --all -- -D warningsproduces zero warnings incontracts/. - No
unwrap()calls onOptionorResultin production paths — all use.expect("descriptive message")or proper error propagation. - No
allow(dead_code)orallow(unused)attributes in production code. - Shared logic (math, events, constants) lives in
contracts/nova-rewards/src/utils/— not duplicated across contracts.
- Every persistent storage write is followed by
extend_ttl(key, TTL, TTL)to prevent premature eviction. - Instance storage is used for admin/config data; persistent storage for per-user data.
- No storage key collisions between contracts (each contract has its own
DataKeyenum).
-
contracts/Cargo.tomlpinssoroban-sdkto an exact version. - No unused crate dependencies in any
Cargo.toml. -
cargo auditreports zero high/critical CVEs incontracts/Cargo.lock.
- Every public function has a happy-path unit test.
- Every error variant has a corresponding negative-path test that asserts the expected panic or error code.
- Tests cover boundary values: 0 amounts,
i128::MAX, empty vecs, expired ledger numbers. -
nova-rewardstests cover: initialize, set_balance, stake, unstake, claim_staking_reward, swap_for_xlm, pause/unpause, upgrade/migrate. -
nova_tokentests cover: initialize, mint, burn, transfer, approve, transfer_from, expired allowance. -
campaigntests cover: create, activate, deactivate, join, issue_reward, end, budget exhaustion. -
vestingtests cover: fund_pool, create_schedule, claim_vested (pre-cliff, mid-vesting, post-vesting), revoke. -
referraltests cover: register_referral, self-referral rejection, double-referral rejection, claim_referral_reward, insufficient pool. -
distributiontests cover: distribute, batch_distribute (at limit, over limit), clawback (within window, after window). -
redemptiontests cover: issue_reward, redeem, redeem-after-expiry, reclaim_expired. -
admin_rolestests cover: propose_admin, accept_admin, unauthorized accept, grant_role, revoke_role.
-
contracts/integration_tests/covers cross-contract interaction: campaign → distribution → nova_token flow. - Integration test covers the full staking lifecycle across
nova-rewards+nova_token. - Integration test covers governance proposal → vote → finalise → execute flow.
-
contracts/fuzz/fuzz_targets/fuzz_calculate_payout.rshas non-trivial corpus entries covering zero, negative, and near-overflow inputs. -
contracts/fuzz/fuzz_targets/fuzz_vesting.rscovers edge cases in cliff + duration arithmetic. -
contracts/fuzz/fuzz_targets/fuzz_staking.rscovers rapid stake/unstake sequences. -
contracts/fuzz/fuzz_targets/fuzz_token_transfer.rscovers transfer amounts up toi128::MAX. - Fuzz targets have been run for at least 10 minutes each:
cargo fuzz run fuzz_calculate_payout -- -max_total_time=600.
- Contract unit test coverage ≥ 80% of lines (measure with
cargo-llvm-cov). - Backend unit test coverage ≥ 80% of lines for
services/androutes/. - CI enforces coverage gate — see
.github/workflows/ci.yml.
-
novaRewards/backend/tests/includes tests for: auth register, login, refresh, logout, password reset. -
middleware/authenticateUseris tested with: valid token, expired token, revoked token, missing header. -
services/tokenServiceis tested with: signAccessToken, signRefreshToken, verifyToken, revokeToken, isRevoked.
-
docs/contracts-full-reference.mdexists and covers all 13 contracts. -
docs/abi-reference.mdfunction signatures match the current contract source. -
docs/error-codes.mdlists all 30 shared error codes plus contract-local errors. -
docs/upgrade-guide.mdcovers both upgrade patterns (two-step and M-of-N multisig). -
contracts/README.mdcontract address table is populated with testnet contract IDs fromdeployments/. -
SECURITY.mddisclosure policy, scope, and severity tiers are current. -
CHANGELOG.mdentry exists for this audit cycle (date, scope, auditor name TBD). - OpenAPI spec (
docs/api/openapi.json) is regenerated:npm run generate:openapi.
- All contract IDs are recorded in
deployments/with network, deployer address, and deploy timestamp. -
MigrationVersion == MigratedVersionfornova-rewardson each deployed network (verify withstellar contract invoke -- get_migration_version). - Multisig threshold is ≥ 2 for all upgradeable contracts on mainnet.
- Admin keys are stored in AWS Secrets Manager — not in
.envfiles or source control. -
validateEnvmiddleware startup check passes for all required environment variables. - Docker images are built from pinned base images (no
latesttags inDockerfiles). -
novaRewards/database/migrations run cleanly on a fresh PostgreSQL instance:npm run migrate. - DB migration 023 (
create_refresh_tokens) and 024 (add_password_reset_tokens) are applied on all environments. - TLS certificates are configured and auto-renewed (
infrastructure/ssl/certbot-renewal.service). - Rate limiter Redis key prefix is namespaced per environment to prevent cross-env bleed.
- Prometheus metrics endpoint (
/metrics) is active and scraped on all backend instances. - Grafana dashboard (
monitoring/grafana/) includes panels for: contract event volume, auth failure rate, API error rate, DB connection count. - Alert rules (
monitoring/prometheus/rules/) fire for: high error rate (> 5%), service down, high latency (p99 > 2s), low disk space. - Alertmanager (
monitoring/alertmanager/alertmanager.yml) routes critical alerts to on-call channel. - Audit log retention policy (
novaRewards/database/021_audit_logs_retention_policy.sql) is applied — logs retained for ≥ 90 days. -
monitoring/runbooks/runbooks exist for all alert types: high-error-rate, high-latency, postgres-down, redis-down, service-down, high-cpu, high-memory, low-disk-space, high-db-connections. - Contract event indexing is operational —
contractEventService.jsis processing events without lag. - Blackbox exporter (
monitoring/blackbox/blackbox.yml) probes/healthendpoint every 30 seconds. - On-call rotation is documented in
docs/ops/on-call.md. - Incident response plan (
docs/security/incident-response-plan.md) is reviewed and current.