sanction-sss-token
v1.0.1
Published
Solana Stablecoin Standard SDK — Create and manage SSS-1 and SSS-2 compliant stablecoins on Solana using Token-2022 extensions
Maintainers
Readme
The Problem
Building a stablecoin on Solana today means:
- Writing thousands of lines of raw Anchor + SPL-Token-2022 code
- Manually wiring Transfer Hooks, Permanent Delegates, and metadata extensions
- Implementing your own role system, quota engine, and blacklist infrastructure
- Praying your fund-seizure logic doesn't have a $100M bug
SSS-Token solves all of this with a single import.
10-Second Quickstart
npm install sanction-sss-token @coral-xyz/anchor @solana/web3.jsimport { SolanaStablecoin } from "sanction-sss-token";
// Deploy a fully compliant stablecoin in 4 lines
const stablecoin = await SolanaStablecoin.create(connection, provider, program, {
name: "USD Coin",
symbol: "USDC",
decimals: 6,
preset: "SSS_2", // ← Blacklist + Seize + Transfer Hook
authority: wallet.payer,
});
// Mint $1,000,000 worth of tokens
await stablecoin.mint({ recipient, amount: 1_000_000_000_000n, minter: authority });
// Freeze a suspicious account
await stablecoin.freeze(suspiciousAccount, authority);
// Blacklist an OFAC-sanctioned address (SSS-2)
await stablecoin.compliance.blacklistAdd(sanctionedAddr, "OFAC SDN List", authority);
// Seize funds from a blacklisted account (SSS-2)
await stablecoin.compliance.seize(sanctionedAddr, treasury, authority);That's it. No raw CPI calls. No manual PDA derivation. No extension wiring.
Architecture
┌─────────────────┐ ┌────────────────────────┐ ┌────────────────────┐
│ Your dApp │ → │ sanction-sss-token SDK │ → │ Solana Blockchain │
└─────────────────┘ └────────────────────────┘ └────────────────────┘
├─ Client ├─ sss-token
├─ Compliance ├─ Transfer Hook
└─ Presets └─ Token-2022How a Mint Operation Flows
- App calls
sss.mint() - SDK derives Config & Role PDAs and validates minter
- SDK sends transaction to Solana RPC
- sss-token program executes:
- Checks if paused
- Checks if minter role exists and is active
- Checks if quota is not exceeded
- Updates total_minted (overflow protected)
- sss-token issues CPI to Token-2022 program (MintTo)
- Token-2022 mints the tokens to recipient
- Transaction succeeds and returns Signature!
How Transfer Hook Blocks Sanctioned Transfers (SSS-2)
When a user attempts to transfer SSS-2 tokens:
- Token-2022 halts transfer and calls the Transfer Hook program.
- Transfer Hook checks Blacklist PDA for the Source address.
- If Blacklist PDA exists → ❌ Transaction Rejected (SourceBlacklisted)
- Transfer Hook checks Blacklist PDA for the Destination address.
- If Blacklist PDA exists → ❌ Transaction Rejected (DestinationBlacklisted)
- If both addresses are clean → ✅ Transfer Approved!
Presets
SSS-Token ships with opinionated presets — think of them as the OpenZeppelin of Solana stablecoins.
| Capability | SSS-1 Minimal | SSS-2 Compliant | Custom Mix & Match |
|:---|:---:|:---:|:---:|
| Mint & Burn with role checks | Yes | Yes | Yes |
| 5-Role RBAC (Minter, Burner, Pauser, Blacklister, Seizer) | Yes | Yes | Yes |
| Minter Quotas — 24h rolling window | Yes | Yes | Yes |
| Individual Account Freeze | Yes | Yes | Yes |
| Emergency Global Pause | Yes | Yes | Yes |
| Master Authority Timelock (24h two-step) | Yes | Yes | Yes |
| On-chain Blacklist — PDA per address | No | Yes | Opt |
| Transfer Hook — block sanctioned transfers at protocol level | No | Yes | Opt |
| Permanent Delegate — seize funds from any account | No | Yes | Opt |
| Default Frozen Accounts — whitelist-first model | No | No | Opt |
Choosing a Preset
// SSS-1: DeFi stablecoins, community tokens, protocol-native dollars
const defiCoin = await SolanaStablecoin.create(conn, provider, program, {
name: "DeFi Dollar", symbol: "DFD", decimals: 6,
preset: "SSS_1",
authority: wallet.payer,
});
// SSS-2: Regulated issuers, institutional dollars, OFAC-compliant tokens
const complianceCoin = await SolanaStablecoin.create(conn, provider, program, {
name: "Regulated USD", symbol: "RUSD", decimals: 6,
preset: "SSS_2",
authority: wallet.payer,
});
// Custom: Cherry-pick extensions for your exact use case
const customCoin = await SolanaStablecoin.create(conn, provider, program, {
name: "Custom Token", symbol: "CUST", decimals: 9,
authority: wallet.payer,
extensions: {
permanentDelegate: true, // Fund recovery
transferHook: false, // No blacklist enforcement
defaultAccountFrozen: true, // Whitelist-first model
},
});Full API Reference
SolanaStablecoin — The Main Client
Creating & Loading
// Deploy a brand new stablecoin
const sss = await SolanaStablecoin.create(connection, provider, program, {
name: string; // Token name (max 32 chars)
symbol: string; // Token symbol (max 10 chars)
decimals: number; // 0–9
uri?: string; // Metadata URI (Arweave, IPFS, etc.)
preset?: "SSS_1" | "SSS_2";
authority: Keypair; // Master authority keypair
extensions?: { // Only when no preset
permanentDelegate?: boolean;
transferHook?: boolean;
defaultAccountFrozen?: boolean;
};
});
// Load an existing stablecoin by mint address
const sss = await SolanaStablecoin.load(connection, provider, program, mintPublicKey);Token Operations
// Minting
await sss.mint({
recipient: PublicKey, // Destination token account
amount: bigint, // Amount in base units (e.g. 1_000_000n = $1 with 6 decimals)
minter: Keypair, // Must hold active Minter role
});
// Burning
await sss.burn({
amount: bigint, // Amount to burn
burner: Keypair, // Must hold active Burner role
});
// Freeze / Thaw Individual Accounts
await sss.freeze(tokenAccountPubkey, authority);
await sss.thaw(tokenAccountPubkey, authority);
// Global Pause / Unpause
await sss.pause(authority); // Halts ALL operations instantly
await sss.unpause(authority); // Resume operationsRole Management
// Grant minter role with 10M daily quota
await sss.updateMinter(minterPubkey, 10_000_000_000_000n, authority);
// Revoke minter role
await sss.revokeMinter(minterPubkey, authority);
// List all active minters with quota usage
const minters = await sss.getMinters();
// → [{ address, quota, mintedThisPeriod, isActive }]Authority Transfer (24-Hour Safety Timelock)
// Step 1: Initiate (starts 24h countdown)
await sss.initiateTransferAuthority(newAuthorityPubkey, currentAuthority);
// Step 2: Wait 24 hours, then accept
await sss.acceptTransferAuthority(newAuthorityKeypair);
// Or cancel anytime before acceptance
await sss.cancelTransferAuthority(currentAuthority);On-Chain Queries
// Full configuration
const config = await sss.getConfig();
// → { mint, name, symbol, decimals, isPaused, totalMinted, totalBurned, masterAuthority, ... }
// Supply breakdown
const supply = await sss.getSupply();
// → { totalMinted: 50_000_000n, totalBurned: 2_000_000n, circulating: 48_000_000n, decimals: 6 }Real-Time Event Listeners
// Listen for mint events in real-time
const unsub = sss.onMint((event) => {
console.log(`[MINT] ${event.amount} tokens minted to ${event.recipient}`);
console.log(` by minter: ${event.minter}`);
console.log(` new supply: ${event.newSupply}`);
});
// Listen for freeze/thaw events
sss.onFreeze((event) => {
console.log(`[${event.action.toUpperCase()}] Account ${event.account} ${event.action}`);
});
// Listen for blacklist changes (SSS-2)
sss.onBlacklist((event) => {
console.log(`[BLACKLIST] ${event.account} ${event.action} — ${event.reason}`);
});
// Unsubscribe when done
unsub();ComplianceClient — SSS-2 Regulatory Toolkit
Available via sss.compliance when using SSS-2 preset (or custom config with transferHook: true).
// Blacklist Management
// Add address with reason (stored on-chain for audit)
await sss.compliance.blacklistAdd(address, "OFAC SDN List match", authority);
// Remove from blacklist
await sss.compliance.blacklistRemove(address, authority);
// Check a single address
const blocked: boolean = await sss.compliance.isBlacklisted(address);
// Get all blacklisted addresses with metadata
const entries = await sss.compliance.getBlacklist();
// → [{ mint, target, reason, addedBy, addedAt, bump }]
// Fund Seizure (via Permanent Delegate)
// Seize specific amount
await sss.compliance.seize(sourcePubkey, treasuryPubkey, authority, 1_000_000n);
// Seize entire balance
await sss.compliance.seize(sourcePubkey, treasuryPubkey, authority);PDA Derivation Helpers
Every on-chain account is derived deterministically. Use these helpers for direct account lookups:
import {
getConfigPda,
getRolePda,
getBlacklistPda,
getExtraAccountMetaListPda,
SSS_TOKEN_PROGRAM_ID,
SSS_TRANSFER_HOOK_PROGRAM_ID,
} from "sanction-sss-token";
// Config PDA (one per mint)
const [configPda, bump] = getConfigPda(mintAddress);
// Seeds: ["config", mint_pubkey]
// Role PDA (one per mint × account × role_type)
const [rolePda] = getRolePda(mintAddress, accountPubkey, RoleType.Minter);
// Seeds: ["role", mint_pubkey, account_pubkey, role_type_u8]
// Blacklist PDA (one per mint × target — SSS-2 only)
const [blacklistPda] = getBlacklistPda(mintAddress, targetPubkey);
// Seeds: ["blacklist", mint_pubkey, target_pubkey]
// Transfer Hook Extra Account Metas
const [metasPda] = getExtraAccountMetaListPda(mintAddress);
// Seeds: ["extra-account-metas", mint_pubkey]Security Model
Role-Based Access Control (5 Roles)
Master Authority
├── Minter → Can mint new tokens
├── Burner → Can burn tokens
├── Pauser → Can pause/unpause all operations
├── Blacklister → Can add/remove addresses from blacklist
└── Seizer → Can seize funds from any account| Security Feature | Implementation |
|:---|:---|
| Overflow Protection | All arithmetic uses checked_add / checked_sub — panics are impossible |
| Immutable Feature Flags | enableTransferHook, enablePermanentDelegate are set at init and can never be changed |
| Quota Engine | 24-hour rolling window with per-minter tracking and checked_add overflow guard |
| Authority Timelock | 24-hour mandatory delay on master authority transfers — prevents instant hostile takeover |
| PDA-Based Roles | All roles are deterministic PDAs — no account confusion, no spoofing |
| Event Audit Trail | Every operation emits an on-chain event (13 event types total) |
| Zero unwrap() | All fallible operations use proper Result<> error propagation |
Compliance & Regulatory
Built for Regulated Issuers
SSS-2 implements the compliance primitives that regulators expect:
┌─────────────────────────────────────────────────────────────┐
│ COMPLIANCE LIFECYCLE │
│ │
│ 1. SCREEN → 2. BLOCK → 3. SEIZE → 4. AUDIT │
│ │
│ Sanctions Transfer Permanent Complete │
│ API check Hook blocks Delegate on-chain │
│ (OFAC/EU) transfers seizure event log │
└─────────────────────────────────────────────────────────────┘| Regulatory Requirement | SSS-2 Implementation | |:---|:---| | OFAC Sanctions Screening | Pluggable sanctions API (Chainalysis, Elliptic, TRM Labs) | | Transfer Restrictions | Token-2022 Transfer Hook blocks blacklisted addresses at protocol level | | Asset Freezing | Individual account freeze + global pause | | Asset Seizure / Forfeiture | Permanent Delegate enables fund recovery to treasury | | Audit Trail | 13 on-chain event types + PostgreSQL indexer for off-chain queries | | KYC/KYB Gating | Default Frozen Accounts extension enables whitelist-first model |
Why sanction-sss-token?
Compared to Building from Scratch
| | DIY Stablecoin | sanction-sss-token |
|:---|:---:|:---:|
| Lines of code to deploy a stablecoin | ~3,000+ | 4 |
| Token-2022 extension wiring | Manual CPI | Automatic |
| Role-based access control | Build yourself | Built-in (5 roles) |
| Minter quotas | Build yourself | Built-in (24h rolling) |
| On-chain blacklist | Build yourself | Built-in (SSS-2) |
| Transfer Hook enforcement | Complex CPI | One flag: preset: "SSS_2" |
| Fund seizure | Complex CPI | compliance.seize() |
| Authority transfer timelock | Build yourself | Built-in (24h) |
| Audit trail events | Build yourself | 13 event types emitted |
| TypeScript types | Write yourself | Full .d.ts included |
| Time to production | Weeks/months | Minutes |
Compared to Other Solana Token Libraries
| | @solana/spl-token | @coral-xyz/anchor | sanction-sss-token | |:---|:---:|:---:|:---:| | Purpose | Generic SPL tokens | Generic programs | Stablecoins specifically | | Compliance features | No | No | Yes | | Role management | No | No | Yes | | Blacklist + Seize | No | No | Yes | | Preset system | No | No | Yes | | Transfer Hook integration | Manual | Manual | Automatic | | Stablecoin events | No | Generic | 13 stablecoin-specific events |
Error Handling
Every on-chain error is mapped to a typed SDK error:
import { SssSdkError, SssErrorCode } from "sanction-sss-token";
try {
await sss.mint({ recipient, amount: 999_999_999_999n, minter });
} catch (err) {
if (err instanceof SssSdkError) {
switch (err.code) {
case SssErrorCode.QuotaExceeded:
console.log("Daily minting quota exceeded — try again after 24h");
break;
case SssErrorCode.GlobalPause:
console.log("Operations are paused by the Pauser");
break;
case SssErrorCode.Unauthorized:
console.log("Caller doesn't hold the required role");
break;
case SssErrorCode.SourceBlacklisted:
console.log("Source address is sanctioned");
break;
}
}
}All Error Codes
| Code | Meaning |
|:---|:---|
| Unauthorized | Caller doesn't hold the required role for this operation |
| MasterAuthorityRequired | Only the master authority can perform this action |
| AuthorityTransferNotReady | 24h timelock hasn't elapsed yet |
| ComplianceNotEnabled | Tried to use SSS-2 feature on an SSS-1 token |
| PermanentDelegateNotEnabled | Seize attempted on a non-delegate mint |
| GlobalPause | All operations halted — contact the Pauser |
| QuotaExceeded | Minter hit their 24h rolling quota |
| Overflow | Arithmetic overflow detected (amount too large) |
| NameTooLong | Token name exceeds 32 characters |
| SymbolTooLong | Token symbol exceeds 10 characters |
| InvalidDecimals | Decimals must be 0–9 |
| ZeroAmount | Amount must be greater than zero |
| AlreadyBlacklisted | Address is already on the blacklist |
| NotBlacklisted | Tried to remove an address that isn't blacklisted |
| SourceBlacklisted | Transfer Hook blocked: source is sanctioned |
| DestinationBlacklisted | Transfer Hook blocked: destination is sanctioned |
On-Chain Programs
This SDK interacts with two Solana programs deployed on devnet and mainnet-beta:
| Program | Devnet Address | Purpose |
|:---|:---|:---|
| sss-token | 2rERwq2PwRJf38WUrbgkr8qHK1gxbCFmL6vz2jJKs9no | Core stablecoin logic (mint, burn, freeze, roles, blacklist, seize) |
| transfer-hook | 8q1UWafjPK3JuYwiqFcQcfVYzns8zAxsEzT33kokceJ | Token-2022 Transfer Hook — enforces blacklist on every transfer |
Verify on Solana Explorer
Package Exports
// Main entry — everything re-exported
import { SolanaStablecoin, ComplianceClient, SssSdkError, ... } from "sanction-sss-token";
// Subpath exports for tree-shaking
import { getConfigPda, getRolePda } from "sanction-sss-token/accounts";
import { SSS1_PRESET, SSS2_PRESET } from "sanction-sss-token/presets";
import { SssSdkError, SssErrorCode } from "sanction-sss-token/errors";
import { StablecoinConfig, RoleType } from "sanction-sss-token/types";
import { ComplianceClient } from "sanction-sss-token/compliance";Testing
The SDK is backed by comprehensive test coverage:
- 30+ Rust unit tests — parameter validation, quota enforcement, overflow protection, PDA derivation
- 22 TypeScript integration tests — SSS-1 full flow (11 steps) + SSS-2 compliance flow (11 steps)
- 40,000 fuzz iterations — mint, initialize, seize, and role assignment edge cases
- TypeScript SDK unit tests — preset resolution, PDA determinism, config immutability
# Run SDK unit tests
cd tests && npm run test:unit
# Run on-chain integration tests
anchor test
# Run fuzz tests
cd tests/fuzz/sss-token && cargo runEcosystem
SSS-Token is part of the Sanction framework:
- On-Chain Programs
- sss-token (Core Program)
- transfer-hook (Blacklist Enforcement)
- oracle-module (Price Feeds)
- sss-private (Confidential Transfers)
- SDK & Tools
- sanction-sss-token (TypeScript SDK)
- sss CLI (Command-Line Tool)
- Admin TUI (Terminal Dashboard)
- React Frontend (Web Dashboard)
- Backend Services
- Event Indexer
- Mint Service API
- Compliance API
- PostgreSQL DB- Full Repository
- Architecture Docs
- SSS-1 Specification
- SSS-2 Specification
- Operations Runbook
- Backend API Reference
- Compliance Guide
Use Cases
| Who | Use Case | Preset | |:---|:---|:---:| | DeFi protocols | Protocol-native stablecoin for lending/borrowing | SSS-1 | | Fintech startups | USD-backed digital dollar with compliance | SSS-2 | | DAOs | Treasury-backed community currency | SSS-1 | | Banks / Institutions | Regulated digital asset with OFAC compliance | SSS-2 | | Remittance platforms | Cross-border payment token | SSS-2 | | Gaming / Metaverse | In-game currency with admin controls | Custom | | RWA platforms | Real-world asset tokenization | Custom |
Contributing
We welcome contributions! See the GitHub repository for:
- Bug reports and feature requests
- Pull requests
- Community discussions
License
MIT — use it in production, modify it, build on it. No strings attached.
