This guide prepares the FlowPay protocol team for a formal Soroban smart-contract security audit. Use it to assemble artifacts, document invariants, scope the engagement, and brief auditors on intentional design choices that can look like bugs.
Related
- Security overview:
docs/SECURITY.md - Threat matrix:
docs/security/threat_matrix.md - Error recovery:
docs/ERROR-CODES.md - Contract source:
contract/src/
Complete these items before kickoff with an external auditor.
- This guide (invariants, scope, known limitations)
-
threat_matrix.mdwith current mitigations -
SECURITY.mdauth model table -
ARCHITECTURE.mdand storage/TTL notes -
API.mdpublic ABI reference - Latest testnet deployment addresses and WASM hashes
-
cargo test/ CI green report for the audit-tagged commit - Diff since last reviewed commit (if any prior review)
- Tag the audit candidate:
git tag audit-vX.Y.Z <commit> - Freeze non-critical merges to the tagged branch
- Publish SHA256 of the release WASM artifact
- Read access to the GitHub repo (or a mirror) at the tagged commit
- Build instructions: Rust toolchain,
wasm32-unknown-unknown,cd contract && cargo test - Point of contact for private questions (security@payflow.dev)
Auditors should treat the following as required properties of a correct deployment. Statements use RFC-style language.
- The contract MUST NOT transfer more tokens from a user than that user’s current Stellar Asset Contract (SAC) allowance to the FlowPay contract.
- A successful
charge()/pay_per_use()MUST move funds only via the token contract’s authorized transfer path (transfer_fromor equivalent). - The protocol fee portion MUST NOT exceed
amount * bps / 10_000for the configuredbps, andbpsMUST be in[0, 10000].
initialize()MUST succeed at most once per deployed instance (AlreadyInitializedthereafter).- Subscription amount and interval MUST NOT change without the required admin (or documented privileged) authorization path.
- A charge MUST NOT succeed unless the subscription exists, is active, is not user-paused, and the billing interval has elapsed.
- A charge MUST NOT succeed after
last_charged + interval + grace_period(GracePeriodElapsed). - User-authenticated entrypoints (
subscribe,cancel,pause,resume,set_daily_limit, etc.) MUST require the subject user’s signature.
- Daily spent for
pay_per_useMUST NOT exceed the user-configured daily limit while that limit is present in temporary storage. batch_chargeinput size MUST NOT exceed the configured / hard maximum (BatchTooLarge).- Global volume accounting MUST NOT allow a charge that would exceed the configured global volume cap (
GlobalVolumeExceeded).
- Admin-only functions MUST verify the caller is the current admin (or complete the documented two-step transfer / proposal flow).
- While the contract pause flag is set, charge and subscribe paths MUST fail closed (
ContractPaused/ContractPausedError). - Two-step commits (admin transfer, fee, grace) MUST NOT succeed without a pending, non-expired proposal (
NoPendingProposal).
- When whitelist mode is enabled, subscribe MUST NOT succeed for a non-whitelisted merchant.
- Subscribe MUST NOT succeed for a frozen merchant.
- Self-referral MUST NOT be accepted (
referrer != user). - Invalid fee collectors (e.g. the contract’s own address) MUST be rejected.
Mapped from threat_matrix.md and SECURITY.md. Ratings are qualitative for briefing (Likelihood × Impact: L/M/H).
| Threat | Category | L | I | Primary mitigations |
|---|---|---|---|---|
| Token allowance draining via repeated charges | Access / funds | M | H | Per-tx max amount; min interval; grace window; SAC allowance ceiling |
| Unauthenticated admin actions | Access control | L | H | require_auth / admin checks on privileged entrypoints; two-step admin transfer |
| Short-interval spam / resource exhaustion | DoS | M | M | Min interval floor; batch size caps; pagination |
Excessive permissionless charge() calls |
Abuse | H | L | Interval + grace checks; fail closed; no auth does not bypass amount rules |
| Keeper downtime / delayed billing | Liveness | M | M | Off-chain HA keepers; monitoring; user re-subscribe after grace |
| Admin key compromise | Access control | L | H | Hardware wallet / multisig before mainnet; pause circuit breaker |
| Storage TTL expiry / stale temporary proposals | Integrity | M | M | Persistent TTL refresh on lifecycle; temp data treated as non-authoritative |
| Malicious or buggy upgrade | Upgrade | L | H | Explicit migrate; governance of upgrade authority; WASM hash verification |
- Access control — admin, merchant freeze/whitelist, user auth boundaries
- Arithmetic / accounting — fees, daily limits, global volume, revenue balances
- Reentrancy / external calls — token contract interactions, fail-closed panics
- Liveness & operational — keeper permissionless charge, pause behavior
| Component | Path / artifact | Notes |
|---|---|---|
| FlowPay Soroban contract | contract/src/** |
All public entrypoints and modules |
| Error taxonomy | contract/src/errors.rs |
Panic codes and fail-closed behavior |
| Build & tests | contract/Cargo.toml, contract/src/test.rs |
Unit/integration tests in-repo |
| Deployed WASM at audit tag | Release build flow_pay.wasm |
Hash must match tag |
| Component | Reason |
|---|---|
Frontend (frontend/) |
UX / wallet integration; not on-chain fund custody logic |
Scripts (scripts/) |
Operational helpers; not consensus-critical |
| Keeper bots / off-chain schedulers | Liveness dependency; document assumptions but not full code audit unless contracted |
| Third-party SAC / Stellar core | Platform trust assumptions |
| Marketing site / docs typos | Non-security unless they contradict invariants |
- Stellar/Soroban consensus and the SAC implementation behave correctly.
- Users understand allowance risks when approving the contract.
- Admin keys are held by trusted operators until governance hardens.
Document these so reviewers do not file them as unexpected vulnerabilities without context.
| Behavior | Why it exists | Residual risk |
|---|---|---|
charge() / batch_charge() are permissionless |
Keepers (or anyone) must trigger billing without holding user keys | Spam txs / griefing gas; mitigated by interval checks and fail-closed transfers |
| Broad admin powers (pause, freeze, fee, upgrade-related ops) | Early protocol needs emergency control | Key compromise impact is high — require HW wallet / multisig for mainnet |
| External keeper liveness | Soroban has no native cron | Missed cycles → grace elapsed → user re-subscribe |
| No on-chain dispute layer | Product scope | Failed/delayed charges handled operationally |
| Single-token-per-deployment default | Simplifies accounting | Multi-token needs separate instances or future design |
| Temporary storage for daily limits / proposals | TTL auto-reset / short-lived commits | Entries can disappear; code must tolerate absence |
| Upgrade wrapper present | Allows bugfix evolution | Upgrade authority must be governed carefully |
Permissionless charge is by design: correctness comes from allowance + interval + active-state checks, not from keeper identity. See FAQ in
README.mdandSECURITY.md.
Provide auditors with a short written brief containing:
- Tag / commit SHA and WASM hash
- Link to this document + threat matrix
- Top three concerns you want emphasized (e.g. fee math, pause paths, batch_charge)
- Timeline and severity triage SLA
- Disclosure preference (GitHub Security Advisories / email)
- Triage findings by severity; assign owners
- Patch in private forks if needed; re-run full
cargo test - Re-audit or delta-review critical fixes
- Publish report (or summary) before mainnet
- Update
SECURITY.mdstatus from “not audited” when complete
For mainnet go-live gates after audit, see MAINNET-DEPLOYMENT.md (when present) and DEPLOYMENT.md.