Skip to content
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
solanabr:mainfrom
dcccrypto:main
Open

feat: Solana Stablecoin Standard — Complete Implementation (SSS v1)#152
dcccrypto wants to merge 1052 commits into
solanabr:mainfrom
dcccrypto:main

Conversation

@dcccrypto

Copy link
Copy Markdown

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

  • SSS-1 (Simple): Basic mint/burn stablecoin preset
  • SSS-2 (Standard): Multi-collateral CDP engine with Pyth oracle integration, liquidation, and timelock governance
  • SSS-3 (Enterprise): Full regulatory compliance — travel rule enforcement, freeze/seize authority, OFAC sanctions screening hooks
  • TypeScript SDK: Client library for all program interactions
  • CLI: Command-line tooling for deployment and administration
  • Rust Backend: On-chain Anchor programs with full instruction coverage

Devnet Deployment

  • Program ID: 2haUR6bUPcWXkCG9bZCPvVJYvtkGRDHnLtX1X1j9zbUY
  • Deployed from: main@a287ff9
  • Status: Live and verified on Solana devnet

Quality Assurance

  • AUDIT3-D Adversarial Testing: 7/7 tests PASS
  • 34 PRs merged across all workstreams
  • Pre-existing CI findings (non-blocking):
    • SSS-154: Existing CI configuration issue
    • travel_rule 400 vs 200 status code — test expectation mismatch, does not affect program correctness

Architecture

programs/
├── stablecoin/          # Core SSS program (all 3 presets)
├── oracle-adapter/      # Pyth price feed integration
sdk/
├── src/                 # TypeScript SDK
cli/
├── src/                 # Admin CLI tooling
tests/
├── adversarial/         # AUDIT3-D security tests
├── integration/         # CDP lifecycle + devnet tests

SSS Backend Agent 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.
…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
SSS Backend Agent and others added 30 commits March 27, 2026 20:49
…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.
…(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>
Includes PR #331 (SSS-147A RequiresSquadsForSSS3 enforcement) and PR #336 (SDK audit fixes).
Program: ApQTVMKdtUUrGXgL6Hhzt9W2JFyLt6vGnHuimcdXe811
Slot: 452237697
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>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant