This repository was archived by the owner on Jun 8, 2026. It is now read-only.
feat: Solana Stablecoin Standard — Complete Implementation (SSS v1) - #152
Open
dcccrypto wants to merge 1052 commits into
Open
feat: Solana Stablecoin Standard — Complete Implementation (SSS v1)#152dcccrypto wants to merge 1052 commits into
dcccrypto wants to merge 1052 commits into
Conversation
added 30 commits
March 25, 2026 02:04
…, bridge_in hardening Critical fixes for PR #230: 1. upgrade.rs — v0 realloc unreachable: Use UncheckedAccount for config PDA in MigrateConfig context. Anchor's typed Account<StablecoinConfig> would fail to deserialize an undersized v0 account before realloc runs. Now: verify PDA seeds, discriminator, and authority manually from raw bytes, then realloc + write version field. 2. transfer-hook/src/lib.rs — WRL bypass via omitted PDA: FLAG_WALLET_RATE_LIMITS now REJECTS transfers if the WRL PDA is not provided in remaining_accounts. Previously: omitting the PDA silently allowed unlimited transfers, defeating the rate-limit feature entirely. 3. transfer-hook/src/lib.rs — WRL write-back note: Direct try_borrow_mut_data on sss-token-owned WRL PDA is architecturally unsafe. Added TODO note with correct CPI path (future upgrade task). 4. bridge.rs — permissionless bridge_in hardening: Require proof_bytes.len() >= 32 (was: just non-empty). Add explicit check that bridge_config.authority != Pubkey::default() to prevent bridge operation when authority is unset. Security note added documenting that on-chain proof verification via CPI to bridge_program is required for mainnet.
…ritical fixes - CROSS-CHAIN-BRIDGE.md: bridge_in guards now reflect proof_bytes>=32, authority!=default, verified flag is NOT checked on-chain; mainnet authority requirement documented - WALLET-RATE-LIMIT.md: WRL PDA omission now REJECTS transfer (not silently bypasses); added security note on future CPI write-path upgrade - MAINNET-CHECKLIST.md: added bridge on-chain CPI gap + WRL CPI gap to known-gaps table Reflects commit f7abd1d (fix(sss-138): address 4 QA criticals).
…e legacy pyth pre-check in cdp_borrow_stable - cdp_liquidate.rs: debt_usd_e6 → effective_debt_usd_e6 (debt + accrued_fees) for liquidatability ratio check; partial liquidation post-ratio check now uses remaining_effective_debt (remaining_debt + accrued_fees) - cdp_borrow_stable.rs: remove duplicate expected_pyth_feed pre-check (lines 88-105); oracle::get_oracle_price handles feed validation internally Fixes QA HOLD CRITICAL + MAJOR on PR #235
…in role - Add is_admin INTEGER column to api_keys table (DEFAULT 0) - validate_api_key now returns Option<bool> (None=not found, Some(is_admin)) - Add create_api_key_with_role(label, is_admin) for explicit role assignment - Add ApiKeyInfo request extension populated by require_api_key middleware - Add require_admin middleware: returns 403 Forbidden for non-admin keys - Mount /api/admin/* routes under require_admin nested router - Bootstrap API key seeded with is_admin=1 - create_api_key route accepts is_admin field in request body - list_api_keys response includes is_admin field - Add 6 tests in admin_role_tests module covering 403/200/401 paths - All 120 tests pass, clippy clean CRITICAL security fix: any valid API key could previously call /api/admin/circuit-breaker, /api/admin/keys (create/delete). Now only keys with is_admin=true can reach these endpoints.
…t Cannot find module
…ity Cannot find module sinon
…A CRIT-04/HIGH-06/07) Previously only 3 ops were timelocked (authority_transfer, set/clear_feature_flag). This commit extends timelock enforcement to all critical admin ops: New ADMIN_OP constants (state.rs): - ADMIN_OP_SET_PYTH_FEED (4) - ADMIN_OP_SET_ORACLE_PARAMS (5) - ADMIN_OP_SET_STABILITY_FEE (6) - ADMIN_OP_SET_PSM_FEE (7) - ADMIN_OP_SET_BACKSTOP_PARAMS (8) - ADMIN_OP_SET_SPEND_LIMIT (9) - ADMIN_OP_TRANSFER_COMPLIANCE_AUTHORITY (10) - ADMIN_OP_SET_ORACLE_CONFIG (11) - ADMIN_OP_SET_MIN_RESERVE_RATIO (12) - ADMIN_OP_SET_TRAVEL_RULE_THRESHOLD (13) - ADMIN_OP_SET_SANCTIONS_PARAMS (14) - ADMIN_OP_SET_TIMELOCK_DELAY (15) — min 216k slots to prevent self-reset - ADMIN_OP_PAUSE (16) - ADMIN_OP_UNPAUSE (17) execute_timelocked_op extended to apply all new ops. Direct-call handlers for all ops now call require_timelock_executed() which returns TimelockRequired when admin_timelock_delay > 0. register_collateral / update_collateral_config: allow via Squads multisig even when timelock > 0 (Squads itself provides the multi-party timelock). compliance_authority transfer via update_roles: blocked when timelock > 0, must use ADMIN_OP_TRANSFER_COMPLIANCE_AUTHORITY propose+execute path. 15 new anchor tests added covering propose acceptance and direct-call rejection. New errors: TimelockRequired, InvalidTimelockOpKind, InvalidTimelockDelay, InvalidStabilityFee, InvalidBackstopParams, InvalidReserveRatio.
…protected AUDIT-A CRIT-05: dao_committee.rs — previously only authority could create proposals, making DAO governance fully authority-captured. Changes: - propose_action: remove authority-only constraint; allow any committee member OR authority to create proposals (NotAuthorizedToPropose if neither) - execute_timelocked_op (ADMIN_OP_CLEAR_FEATURE_FLAG): block clearing FLAG_DAO_COMMITTEE via timelock path (DaoFlagProtected) - feature_flags.rs already blocks direct set/clear when DAO active (unchanged) - error.rs: add DaoFlagProtected + NotAuthorizedToPropose errors - tests: BUG-011 — member can propose, non-member/non-authority rejected, authority cannot clear FLAG_DAO_COMMITTEE via timelock Fixes: AUDIT-A CRIT-05
…protected AUDIT-A CRIT-05: dao_committee.rs — previously only authority could create proposals, making DAO governance fully authority-captured. Changes: - propose_action: remove authority-only constraint; allow any committee member OR authority to create proposals (NotAuthorizedToPropose if neither) - execute_timelocked_op (ADMIN_OP_CLEAR_FEATURE_FLAG): block clearing FLAG_DAO_COMMITTEE via timelock path (DaoFlagProtected) - feature_flags.rs already blocks direct set/clear when DAO active (unchanged) - error.rs: add DaoFlagProtected + NotAuthorizedToPropose errors - tests: BUG-011 — member can propose, non-member/non-authority rejected, authority cannot clear FLAG_DAO_COMMITTEE via timelock Fixes: AUDIT-A CRIT-05
BUG-010 (AUDIT-A CRIT-04/HIGH-06/07) — Timelock coverage extended to all ~17 privileged admin ops: - on-chain-sdk-admin-timelock.md: expand ADMIN_OP constants table (ops 4–17), add BUG-010 audit finding, add new errors (TimelockRequired, InvalidTimelockOpKind, InvalidTimelockDelay, InvalidStabilityFee, InvalidBackstopParams, InvalidReserveRatio, DaoFlagProtected), note compliance authority transfer must use op 10 path BUG-011 (AUDIT-A CRIT-05) — DAO governance authority capture fix: - docs/DAO-GOVERNANCE.md: new doc covering proposal lifecycle, member-proposable governance, FLAG_DAO_COMMITTEE protection from timelock clear path, error reference and audit finding BUG-033 — Admin role separation for backend API: - authentication.md: document is_admin flag, require_admin middleware, 403 Forbidden on /api/admin/* for non-admin keys, updated create/list key request/response schemas, bootstrap key is always is_admin=true, updated implementation notes - README: update authentication row, add DAO-GOVERNANCE row
…est body (E-2) Previously POST /api/admin/circuit-breaker accepted a raw 64-byte Solana keypair (base58 or byte array) in the request body, transmitting the secret key over HTTP. Fix: drop the authority_keypair field entirely. The endpoint now accepts a pre-signed, base64-encoded Solana legacy transaction (signed_transaction). The backend: 1. Decodes the base64 transaction bytes 2. Validates the transaction structure and instruction discriminator/flag 3. Verifies the instruction targets the correct SSS-token program 4. Forwards the signed tx bytes to the RPC cluster unchanged The secret key never reaches the backend. Signer identity is derived from the transaction's account key list (not caller-supplied metadata). Audit: SECURITY_AUDIT E-2 / SSS-BUG-034 Tests: 113 pass, clippy clean
… mint/burn (E-4) Previously POST /api/mint and /api/burn accepted tx_signature as an optional field that was never checked against the Solana chain. A caller could fabricate supply inflation by submitting mint events with no real on-chain transaction. Changes: - MintRequest.tx_signature and BurnRequest.tx_signature: Option<String> → String (required; serde returns 422 if missing) - New routes/onchain.rs: verify_tx_signature() calls getTransaction RPC and verifies the tx exists and succeeded before the event is recorded - mint.rs, burn.rs: call verify_tx_signature() before db.record_mint/burn - SOLANA_TX_VERIFY_SKIP=1 env var allows skipping RPC call in unit tests - All test payloads updated to include tx_signature Audit: SECURITY_AUDIT E-4 / SSS-BUG-035 Tests: 117 pass (4 new onchain unit tests), cargo clippy clean
… API security checklist - api.md: mark tx_signature as required (✓) on POST /api/mint and POST /api/burn - api.md: document on-chain RPC verification behaviour (BUG-035 / audit E-4) - api.md: update 400 error descriptions to include invalid/unconfirmed tx_signature - MAINNET-CHECKLIST.md: add section 8b Backend API Security covering BUG-035/E-4, BUG-010 (timelock coverage), BUG-011 (DAO governance), BUG-033 (role separation)
… double-count, keeper-callable
AUDIT-A CRIT-06+CRIT-07+HIGH-04+HIGH-05:
(1) cdp_liquidate.rs: already fixed (BUG-012 effective_debt comments were present).
effective_debt = debt + accrued_fees used in all ratio/health checks.
(2) cdp_repay_stable.rs: total_burn = principal_amount + accrued_fees.
After burn: accrued_fees reset to 0 (prevents phantom uncollectable debt).
Proportional collateral release based on principal debt only.
(3) stability_fee.rs:
- collect_stability_fee: KEEPER-CALLABLE — removes debtor Signer constraint.
keeper provides keeper key; debtor is AccountInfo (validated via PDA seed).
Accrues to accrued_fees WITHOUT burning (deferred to repay/liquidation).
- burn_accrued_fees: NEW instruction — debtor signs to burn accrued_fees.
Resets accrued_fees to 0 after burn (CRIT-07 double-count fix).
- Double-count fix: accrued_fees = PENDING un-burned only.
After burn via repay or burn_accrued_fees: reset to 0.
effective_debt = debt + accrued_fees always reflects real obligation.
(4) backend/Cargo.toml: remove duplicate hmac dependency (CI fix).
Tests: 5 BUG-012 tests added — keeper-callable verification, repay settles fees,
burn_accrued_fees instruction exists, signer enforcement, double-count invariant.
Fixes: AUDIT-A CRIT-06, CRIT-07, HIGH-04, HIGH-05
Adds scripts/deploy-wizard.ts — interactive 10-step CLI wizard guiding
issuers through safe SSS stablecoin deployment:
Step 1: Preset selection (SSS-1/2/3/4-Institutional) with descriptions
Step 2: Supply params — name, symbol, decimals, max_supply (0=unlimited
warning), minter cap
Step 3: Compliance config — feature flag selection with irreversible
FLAG_SQUADS_AUTHORITY guard, sanctions oracle endpoint
Step 4: Governance setup — Squads V4 multisig address, guardian pubkeys
Step 5: Oracle config — Pyth feed, staleness threshold, confidence bps
Step 6: Reserve vault — generate new keypair or use existing, backed-up
Step 7: Insurance vault — minimum seed amount calculation
Step 8: Dry-run — simulate all PDAs, show full param summary, cost
estimate; no on-chain tx
Step 9: Confirmation — typed token-symbol confirmation of critical params
Step 10: Deploy + verify — initialise on-chain, save mint keypair + manifest
Adds scripts/check-deployment.ts — post-deploy validation script that
verifies program executable, Token-2022 mint, StablecoinConfig PDA,
MintAuthority PDA correctness, reserve vault, Squads msig, and
cross-checks against deployment manifest. Exit 0 = all pass.
package.json: adds 'wizard' and 'check-deployment' script aliases.
Footgun protections included:
- Warns on max_supply=0 (unlimited)
- Guards FLAG_SQUADS_AUTHORITY with double confirmation + irreversible warning
- Validates all pubkeys before accepting
- Saves mint + vault keypairs with backup warnings
- Writes deploy manifest JSON for audit trail
Closes SSS-155
…stress test
- docs/RECOVERY-PLAN-TEMPLATE.md: fill-in-the-blank MiCA Art.46 recovery plan
- 5 trigger scenarios (peg break, reserve shortfall, oracle failure,
key compromise, redemption rush) with Level 1/2/3 thresholds
- Full escalation procedure (Watch → Alert → Crisis)
- Guardian multisig emergency actions (pause, rate-limit, oracle fallback,
reserve top-up, key rotation, orderly wind-down)
- Communication templates A–D (incident notice, crisis notice, all-clear,
NCA notification email)
- Regulator notification timeline: 2h/24h/30d obligations
- Recovery measures per scenario
- Contact matrix, testing schedule, appendices
- scripts/liquidity-stress-test.ts: MiCA Art.45 redemption rush simulator
- Models panic surge profile (2× Day1-3, 1.5× Day4-7, decay Day8+)
- Pool refill model (reserve access at 2%/day)
- 5 preset scenarios: normal/moderate/severe/bank-run/flash
- Outputs: pool drain day, insurance drain day, SLA breach probability,
daily breakdown table
- --json mode for NCA-submittable output
- MiCA Art.45 15% liquid reserve ratio check + recommendations
- README: added entries for SSS-149 doc + script, deployment wizard SSS-155,
GENIUS checker SSS-148
Closes SSS-149
…ening (#328) PR #325 (BUG-NEW-2) added two security fixes to AgentPaymentChannelModule: 1. FLAG_AGENT_PAYMENT_CHANNEL (bit 19) guard in openChannel() — throws descriptive error if APC feature flag is not set on StablecoinConfig. 2. _readConfigFeatureFlags() now throws on truncated/malformed account data instead of returning 0n, preventing a malformed config from silently bypassing the flag check (CodeRabbit ACTIONABLE from PR #325 review). Document both in on-chain-sdk-pbs-apc.md Prerequisites section. project_id: sss Co-authored-by: SSS Backend Agent <sss-backend@agent.local>
Old program AxE9NQ8z6tzNJT9AHBu2YRsVqX41uCjPmpN5RLavAaat was closed. New program: 2haUR6bUPcWXkCG9bZCPvVJYvtkGRDHnLtX1X1j9zbUY Deploy slot: 451673869 Signature: RvATRYs6EZpFPWqTB4Vf4VziYmczKyfajLbzkFGfiNTcEuNcRMA1abyKxHMzqNzc3zCYm9GHmsygyewUV6XEMWd
Old program AxE9NQ8z6tzNJT9AHBu2YRsVqX41uCjPmpN5RLavAaat closed. New program: 2haUR6bUPcWXkCG9bZCPvVJYvtkGRDHnLtX1X1j9zbUY (slot 451673869) Updated: DEVNET.md, DEPLOYMENT-GUIDE.md, ARCHITECTURE.md, devnet-deploy.md, on-chain-sdk-core.md, DEPLOYMENT.md, chain-events.md, GAPS-ANALYSIS-SDK.md, TRAVEL-RULE.md, INDEXER-GUIDE.md, API-REFERENCE.md, SSS-SPEC.md
- DEPLOYMENT.md: correct old/closed program ID to AxE9NQ8z (was wrongly showing 2haUR6b as both old and new) - sdk/src/SolanaStablecoin.ts: update SSS_TOKEN_PROGRAM_ID constant from AxE9NQ8z to 2haUR6b (SSS-DEVNET-002) — was stale from previous deploy - INDEXER-GUIDE.md: add transfer-hook program address (phAtzRy) to Helius accountAddresses so hook-originated events are captured - on-chain-sdk-core.md: replace program ID used as mintAddress example with a clear placeholder — program ID != token mint - TRAVEL-RULE.md: replace program ID in 'mint' example fields with a stablecoin mint placeholder address (lines 217, 243, 282)
Add RequiresSquadsForSSS3 error variant to SssError enum. Add require! guard in initialize() preset==3 block so that squads_multisig must be Some and non-default — closing the gap where initialize() silently accepted preset=3 with no multisig configured. Mirror the enforcement in the TypeScript simulation helper in tests/sss-147-trustless-hardening.ts so Tests 11/12/13 correctly reflect on-chain behaviour. Fixes: SSS-147A (QA finding from AUDIT3-D, sss-qa msg #1449)
…red for SSS-3 Add squads_multisig and maxSupply to SssConfig field table with SSS-3 required annotation. Add on-chain enforcement callouts for SSS-147A (RequiresSquadsForSSS3) and SSS-147B (RequiresMaxSupplyForSSS3). Add SSS-3 code example showing both required fields with comments explaining the on-chain rejection behaviour. Follows backend fix in PR #331 (commit 0828ceb).
…on + add test 14 - Add DEFAULT_PUBKEY const (PublicKey.default.toBase58()) - Move RequiresSquadsForSSS3 check inside preset==3 block in simulateInitialize() - Guard feature_flags FLAG_SQUADS_AUTHORITY against default pubkey (mirrors initialize.rs) - Add test 14: SSS-3 with squads_multisig=PublicKey.default is rejected Addresses CodeRabbit nitpick on PR #331.
…e-squads-required
…(SSS-DEVNET-002) SolanaStablecoin.ts already exports 2haUR6bUPcWXkCG9bZCPvVJYvtkGRDHnLtX1X1j9zbUY after PR #329 merge. All test files and setup.ts still referenced the old AxE9NQ8z6tzNJT9AHBu2YRsVqX41uCjPmpN5RLavAaat causing 1 CI regression (SSS_TOKEN_PROGRAM_ID assertion in SolanaStablecoin.test.ts:26). Updated files: - sdk/tests/SolanaStablecoin.test.ts (assertion now passes) - sdk/tests/anchor/setup.ts - sdk/src/*.test.ts (10 module test files — mock PROGRAM_ID updated) SolanaStablecoin.test.ts: 17/17 passing after fix. Co-authored-by: SSS Backend Agent <sss-backend@agent.local>
…ifecycle pass (#334) Pre-existing CI failures (SSS-154 redemption_queue, travel_rule backend) unrelated to docs changes.
- DEVNET.md: add AUDIT3-D section (7/7 PASS, binary a190a23, slot 452170389) - Test-5 (SSS-147A) re-confirmed on redeployed binary - initialize.rs lines 81-86 enforce RequiresSquadsForSSS3 for preset=3 - AUDIT3-D-RESULTS.md: full test matrix, history (initial 6/7 → final 7/7), Test-5 root cause + fix detail, related PRs AUDIT3-D closed. project_id=sss Co-authored-by: SSS Backend Agent <sss-backend@agent.local>
…types, stub CT module
… update, compact)
Add on-chain-sdk-*.md for KeeperModule, CustomOracleModule, GuardianModule, InsuranceVaultModule, LegalEntityModule, RedemptionQueueModule — all landed in PR #336 (fix/sdk-anchor-audit-fixes, merged 2026-03-31). Each page covers: overview table, installation, PDA helpers, per-method params/accounts/examples, and cross-links to design docs. Refs: BUG-015, SSS-119, SSS-121, SSS-122, SSS-151, SSS-154
…ed (AUDIT3C-M3) GET /api/travel-rule/records requires ?wallet= param since AUDIT3C-M3 fix. Test was calling without it, getting 400. Added wallet=SSSISSUER001.
Rewrite root README, SDK README, and CONTRIBUTING with badges, architecture diagrams, feature tables, and comprehensive docs index. Add new READMEs for backend, programs, CPI crate, and CLI. Includes accumulated program, SDK, and backend improvements from recent development. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Solana Stablecoin Standard — Complete Implementation (SSS v1)
Summary
Complete implementation of the Solana Stablecoin Standard (SSS), a configurable Anchor-based framework for launching regulation-ready stablecoins on Solana.
Features Delivered
Devnet Deployment
2haUR6bUPcWXkCG9bZCPvVJYvtkGRDHnLtX1X1j9zbUYmain@a287ff9Quality Assurance
travel_rule400 vs 200 status code — test expectation mismatch, does not affect program correctnessArchitecture