All notable changes to the Finchippay-Solution smart contract will be documented in this file.
- Hardened anchors config: ANCHORS_CONFIG is now required in production. The dev-only testanchor default triggers a startup error in production environments instead of silently falling back to an insecure default.
- Cryptographically random webhook seeds: Development seed data now uses
crypto.randomBytesfor webhook secrets instead of a hardcoded value. - Structured frontend logging: Replaced 67 scattered
console.errorcalls across all pages and components with a centralized logger that forwards errors to Sentry in production and sanitizes sensitive context data (keys, tokens, passwords).
- TypeScript strictness: Replaced 34
anytype annotations with proper interfaces across all frontend source files (components, libs, hooks). CreatedTransactionReceipt,PendingTransaction,LedgerTransport,LedgerStellarApp, andGlobalWithFetchPatchtypes. - Contract module organization: Extracted data types into
types.rsand event symbols intoevents.rsto reduce the monolithiclib.rs. - TODO resolution: Replaced 3 outstanding TODO comments with resolved architecture notes and clear upgrade paths.
- ESLint strict mode: Frontend now enforces
no-explicit-any: warn, import ordering, and prefer-optional-chain rules.
- Governance model (GOVERNANCE.md): Complete community governance framework with roles, RFC process, voting mechanics, code of conduct, and release cadence.
- Security audit framework (docs/SECURITY_AUDIT_FRAMEWORK.md): Comprehensive checklist covering contracts, backend API, and frontend with incident response protocol.
- Testing guide (docs/testing.md): Multi-layer testing strategy with coverage targets.
- RFC template (docs/rfc/TEMPLATE.md): Standardized format for architectural proposals.
- Coverage thresholds: Backend (60% lines, 50% branches) and Frontend (65% lines, 60% branches) via Istanbul/NYC.
- Automated storage TTL management — audited every persistent storage operation in
FinchippayContractfor TTL coverage and closed the gaps. Entries are now created at aMIN_TTL_LEDGERSfloor (535,680 ledgers, ≈31 days) and refreshed by any read or update that finds them belowMIN_TTL_THRESHOLD(100,000 ledgers), so live escrows, streams, vesting schedules, and multi-sig proposals cannot expire while in use. Read paths that previously returned data without extending its TTL —is_paused,get_escrow_count,get_stream_count,get_multisig_count,get_emergency_withdrawal_count,get_arbitrators,verify_receipt,get_claimable_vesting,list_streams_by_payer,locked_balance,get_contract_balance, and the escrow claim/cancel paths' recipient lookup — now bump the entries they touch. Addedbump_all_ttls(admin, max_keys), an admin-only sweep for cold entries that nobody reads: it walks the enumerable key classes, processes at mostmin(max_keys, 100)keys per call, and persists a cursor so repeated calls complete a pass without exceeding one transaction's resource budget. Addedget_min_ttl()→(ledgers, class)reporting the lowest lifetime the contract can still prove, for off-chain alerting. Tip records, locked balances, and cached contract balances are keyed by arbitrary addresses with no on-chain registry, so they are not sweepable and rely on the per-operation bumps; this is documented onbump_all_ttls. - SBOM generation & supply-chain vulnerability scanning — added Software Bill of Materials generation in both SPDX and CycloneDX formats for the frontend (npm), backend (npm), and Soroban contract (cargo) via syft, exposed through new
make sbom/make sbom-scantargets. CI now generates SBOMs on every build, uploads them as thesbomsartifact, and fails on CRITICAL/HIGH vulnerabilities via grype (SBOM & Vulnerability Scanjob inci.yml). Container images are scanned with Trivy indocker-publish.yml(build → scan → push, so a vulnerable image never reaches ghcr.io). A new scheduledsbom-scan.ymlworkflow regenerates and scans SBOMs weekly and opens a tracking issue on new CRITICAL vulnerabilities. Releases now attach all SBOMs as assets (release.yml), andSECURITY.mddocuments SBOM availability (for Executive Order 14028 compliance). - #240 (Issue #74): Server-side cursor pagination for list endpoints — standardized cursor-based pagination across the backend list APIs. New
middleware/pagination.jsparses?limit=(default 20, capped at 100 server-side) and an opaque?cursor=, and a newutils/paginate.jshelper builds pages, applies Knex keyset predicates, and emits RFC 5988Link(rel="next") plusX-Total-Countheaders. Applied to tips (received/sent, true keyset), webhooks (list + failures), scheduled-transactions, and events (offset retained, opaque cursor added); the events router is now mounted. The referencepaymentsendpoint was fully aligned to emit the sameLink(from Horizon's native paging token) andX-Total-Countheaders — the payments total is an approximate, bounded count (Horizon exposes no exact total; floored at 200 viastellarService.countPaymentsApprox). Analytics endpoints (/summary,/top-recipients,/activity) are documented as intentionally excluded — they are bounded aggregations, not unbounded lists. AddedVAL_INVALID_CURSORerror code, Swagger docs (reusablelimit/cursorparams + pagination headers), and__tests__/pagination.test.js(16 cases).
- Improved screen reader support in the
MultiSigFlowcomponent by replacing genericdivelements with semantic<ol>/<li>lists and addingaria-current="step". - Added
role="alert"andaria-live="polite"toMultiSigFlowerror containers to ensure validation messages are announced immediately. - Added programmatic accessibility testing using
jest-axeto theMultiSigFlowtest suite.
- Client-side encryption for contacts, payment templates & federation cache — the address book (
frontend/lib/addressBook.ts), the federation-resolution cache (finchippay:federation-cache, which links a federation address to the Stellar account it resolves to) and a new payment-template store (frontend/lib/paymentTemplates.ts) are now encrypted at rest inlocalStorageusing AES-GCM via the Web Crypto API (frontend/lib/encryption.ts+ a genericfrontend/lib/encryptedStorage.ts). The key is derived (PBKDF2) from the connected Stellar public key plus a random salt, held in memory only for the active wallet session, and cleared on disconnect; stored data is a Base64{v,owner,data}envelope. Switching wallets is detected and surfaces a re-encryption prompt, and the contacts page shows a lock indicator. Legacy plaintext contacts are migrated to ciphertext on first unlock. Also repaired the frontend Jest runner (removed a duplicatejest.config.jsthat collided withjest.config.ts, and movedjest.setup.tstosetupFilesAfterEnv) and added__tests__/encryption.test.ts+__tests__/encryptedStorage.test.ts(11 cases). - #58: Pauser role enforcement —
pauseandunpausenow accept either the stored admin or the designated pauser address (set viaset_pauser), so the separate pause-only role is actually consulted instead of being ignored. This lets a low-exposure "hot key" trigger the emergency circuit breaker during an incident without bringing the admin key online. The pauser remains strictly pause-only — it cannotupgrade,transfer_admin,set_pauser, orrescue_tokens. Added unit tests covering pauser pause/unpause, stranger rejection, and pauser being denied upgrade/admin-transfer, and documented both roles indocs/architecture.md. - #54: Mandatory multi-sig expiration —
create_multisignow requiresexpiration_ledgerto be strictly greater than the current ledger sequence, and rejects a TTL longer than the newMAX_MULTISIG_TTL(518,400 ledgers, ≈ 30 days). Theexpiration_ledger == 0escape hatch ("no expiration") has been removed fromapprove_multisig, so every proposal now has a bounded lifetime and can no longer accumulate approvals indefinitely from signers whose keys may have since been rotated or compromised.
- Callers of
create_multisigthat previously passedexpiration_ledger = 0to mean "never expires" must now pass an explicit ledger sequence in the future, no more thanMAX_MULTISIG_TTL(518,400) ledgers out. Passing0, a past/current ledger, or a value beyond the cap now panics.
- #69: soroban-sdk v20 → v27.0.1 — Upgraded the Soroban SDK to the latest stable release.
- Updated build target to
wasm32v1-none(required by soroban-sdk v27+). - Migrated
register_contract→registerwith new signature(contract, salt). - Migrated
register_stellar_asset_contract→register_stellar_asset_contract_v2which returnsStellarAssetContractinstead ofAddress. - Added
testutils::Ledgerimport forwith_muton test ledger. - Guarded
bump()call inrequire_not_pausedwith.has()check — soroban-env-host v27 panics onextend_ttlfor non-existent keys.
- Updated build target to
- Updated escrow test amounts to meet
MIN_ESCROW_AMOUNT(1,000 base units). - Fixed stream overflow safety test to advance enough ledgers for full deposit coverage.
- Deprecation warnings: 25
publishdeprecation warnings remain from pre-existing code. Migration to#[contractevent]macro is tracked separately; suppressed with#[allow(deprecated)]on the test module. - Testnet deployment: Not verified in this environment; pending manual verification via
scripts/deploy-contract.sh.
- #1: Initialization guard — Added
require_initialized()guard to all operational entry points (send_tip,mint_receipt,create_escrow,open_stream,create_multisig,batch_send). Prevents use of the contract beforeinitialize()is called. - #2: Batch size enforcement — Added
MAX_BATCH_SIZEconstant (50 recipients) and validation inbatch_sendto prevent DoS via oversized batch operations. - #3: Duplicate signer detection —
create_multisignow rejects signer lists containing duplicate addresses, preventing threshold spoofing attacks. - #4: Self-tipping prevention —
send_tipnow rejects transfers wherefrom == to, preventing on-chain stat inflation. - #5: Self-escrowing prevention —
create_escrownow rejects transfers wherefrom == to, preventing state bloat from self-escrows. - #6: Atomic batch pre-validation —
batch_sendvalidates all amounts are positive before initiating any token transfers, ensuring atomicity. - #16: Self-streaming prevention —
open_streamnow rejects streams wherepayer == recipient. - #17: Self-multisig prevention —
create_multisignow rejects proposals whereproposer == recipient. - #18: Minimum amount enforcement — Added
MIN_ESCROW_AMOUNTandMIN_MULTISIG_AMOUNT(1,000 base units) to prevent dust attacks. - #22: Empty input validation — Rejects empty signers lists in
create_multisigand empty recipient arrays inbatch_send. - #23: Memo length validation — Added
MAX_MEMO_LENGTH(32 chars) and enforcement inmint_receipt.
- #7: RBAC pauser role — Introduced a separate
Pauserrole viaset_pauser()/get_pauser(). The pauser can callpause()andunpause()without holding admin upgrade rights. - #12: Approval progress in events —
multisig_approveevent now emits(signer, current_approvals, threshold)for real-time indexer tracking. - #13: Escrow recipient index — Added
get_user_escrows(recipient)andEscrowByRecipientstorage key for querying all escrows directed to an address. - #14: Multi-sig expiration — Added
expiration_ledgerfield andtimeout_multisig()function. Expired proposals can be closed by anyone, refunding locked funds. - #15: Recipient stream rejection — Added
reject_stream()allowing recipients to opt out of incoming streams for compliance or personal reasons. - #19: Partial escrow claims — Added
claim_escrow_partial(id, amount)supporting incremental withdrawals from escrows. - #20: Memo support — Added optional
memo: Symbolfield toTipRecord,Escrow,send_tip(), andcreate_escrow(). - #21: Admin token rescue — Added
rescue_tokens()to sweep accidentally-sent tokens from the contract address. - #25: Stream recipient transfer — Added
transfer_stream()allowing recipients to reassign incoming streams to a new address. - Diagnostic endpoint — Added
get_contract_stats()returning(escrow_count, stream_count, multisig_count)for monitoring dashboards.
- #8: Extended error enum — Added
SelfTransfer,BatchTooLarge,DuplicateSigner, andProposalExpiredvariants toContractError. - #9/#10: DRY helpers — Introduced
get_token_client()helper for token client instantiation, adopted across all 15+ call sites in production code. - #11: Iterator usage — Replaced manual for-loop indexing in
approve_multisigwith idiomatic.iter().any()closures.
- #21: Pause/circuit-breaker tests — Added 3 tests verifying
send_tip,create_escrow, andopen_streamare blocked when paused. - #22/#24: Batch send & stream rejection tests — Added success and error-path tests for
batch_sendandreject_stream. - #23: Stream overflow safety — Added test verifying claimable amount caps at deposit for extreme ledger values.
- #24: Escrow boundary tests — Added tests for
MAX_ESCROW_LEDGERSenforcement and minimum amount rejection. - #25: Initialization guard tests — Added tests verifying
send_tipandcreate_escrowpanic before initialization. - Multi-sig tests — Added tests for duplicate signer rejection, proposal timeout/expiry, and minimum amount enforcement.
- Partial escrow tests — Added test verifying incremental claim lifecycle from Pending to Released.
- Contract stats test — Added test verifying aggregate counts are correctly reported.
- Self-transfer tests — Added tests verifying self-tipping and self-escrowing panics.
send_tip()now requires amemo: Symbolparameter.create_escrow()now requires amemo: Symbolparameter.create_multisig()now requires anexpiration_ledger: u32parameter (pass 0 for no expiration).pause()andunpause()parameter renamed fromadmintocaller(supports either admin or pauser).CONTRACT_VERSIONbumped from 2 to 3.
- Updated
contracts/finchippay-contract/README.mdwith all new functions, security features, and event emissions.
- Initial production-grade Soroban contract with tips, receipts, escrow, streaming, multi-sig, batch send, pause/unpause, and upgrade functionality.
- Critical: health.test.js syntax error (missing closing braces in disk-space and postgres test blocks).
- Critical: validateEnv.js duplicated SMTP comment blocks from merge artifact removed.
- Critical: VAPID keys made optional in non-production environments; app no longer fails to start without push notification config.
- High: GraphQL context no longer falls back to insecure
finchippay_secret_keydefault when JWT_SECRET is unset. - High: Webhook restoration now awaited at startup instead of fire-and-forget.
- Medium: Contract Cargo.toml version bumped from 0.0.0 to 3.1.0 to match on-chain CONTRACT_VERSION.
- Medium: Stellar SDK updates re-enabled in Renovate (previously ignored), with minor/major requiring review.
- Medium: Test coverage thresholds raised to 70% lines / 60% branches.
- Medium: Better CONTRACT_ID error messaging via
requireContractId()helper with setup instructions. - Low: Replaced bare
console.logwithconsole.infoin SEP-0007 protocol handler; documented rationale.
- Documentation: ROADMAP updated with v1.4 quality & stability milestones.
- Documentation: Audit status page added with self-audit preparation checklist.
- Documentation: PGP key section updated with key discovery instructions.
- Documentation: VAPID keys documented as optional in non-production environments.