π Section 3: SPL Token-2022 Transfer Hooks β Layer 2
π The security primitive at the heart of OTCM Protocol β 42 mathematically enforced controls that execute atomically on every ST22
tokenDigital Securities transfer via SPL Token-2022 Transfer Hook extensions.
π‘ 3.1 Architectural Innovation
SPL Token-2022 Transfer Hooks represent a fundamental architectural innovation in blockchain-based securities infrastructure, enabling execution of arbitrary smart contract code during token transfers on the Solana blockchain. This capability transforms compliance from a discretionary administrative function into an immutable, mathematically-enforced property of the token itself.itself.
OTCM Protocol leverages Transfer Hooks to implement federal securities law requirements as non-discretionary smart contract code,code β creating a paradigm where regulatory compliance is not a matter of corporate policy but of cryptographic certainty.certainty. Every ST22 Digital Securities transfer triggers sequential execution of six independent verification hooks, completing comprehensive compliance verification before transaction settlement.
Under the SEC's March 17, 2026 interpretation (Release No. 33-11412), ST22 tokens are formally classified as Digital Securities. The Transfer Hook architecture is precisely the mechanism that makes this classification sustainable β it automates the enforcement obligations that come with that classification at the transfer primitive itself.
πΉ 3.1.1 SPL Token-2022 Standard Overview
SPL Token-2022 (also known as Token Extensions Program) is Solana's next-generation token standard, introducing programmable extensions that enable sophisticated token functionality impossible with the original SPL Token Program:
Extension | Capability | OTCM Usage |
|---|---|---|
Transfer Hook | Execute custom logic on every transfer | 42 security |
Permanent Delegate | Irrevocable transfer authority | Protocol-level recovery capability |
Confidential Transfers | Zero-knowledge encrypted amounts | Future: Privacy-preserving compliance |
Transfer Fee | Protocol-level fee collection | 5% fee distribution automation |
Metadata Pointer | On-chain token metadata | Security |
Non-Transferable | Soulbound token capability | Compliance |
πΉ 3.1.2 Transfer Hook Extension Mechanism
The Transfer Hook extension enables token mints to specify a program that must be invoked during every token transfer. This mechanism operates at the Solana runtime level,level, making it impossible to transfer tokens without executing the hook program:
// SPL Token-2022 Transfer Hook Mechanism
// Transfer Hook Extension Data Structurepub struct TransferHook {
/// The program ID that implements the transfer hookpub program_id: Pubkey,
/// Optional: Authority that can update the hook program}
// When a transfer occurs, Solana runtime automatically:
// 1. Checks if mint has TransferHook extension
// 2. If yes, invokes the specified program_id with transfer context
// 3. If hook returns error, ENTIRE transaction reverts
// 4. If hook succeeds, transfer completes
// This is MANDATORY - there is no way to bypass the hook
// The token literally cannot move without hook approval
πΉ 3.1.3 Compliance-as-Code Philosophy
OTCM's Transfer Hook implementation embodies the "Compliance-as-Code" philosophy: regulatory requirements are translated into deterministic smart contract logic that executes identically every time, without human discretion, administrative override, or interpretation variability.
"Traditional compliance asks: 'Did you follow the rules?' OTCM's Transfer Hooks ask: 'Can you mathematically prove compliance?' The answer is always verifiable on-chain."
Aspect | Traditional Compliance | Transfer Hook Compliance |
|---|---|---|
Enforcement | Policy- | Code- |
Override Capability | Admin can bypass | No bypass possible |
Audit Trail | Mutable | Immutable blockchain record |
Verification | Requires trust in operator | Cryptographically verifiable |
Consistency | Varies by | 100% identical execution |
Timing | Post-trade review (T+n) | Pre-trade blocking (T-0) |
πΉ 3.1.4 Regulatory Advantages
Transfer Hook execution provides regulatory advantages unattainable through traditional compliance procedures:
- Immutable Audit
Trails:Trails β Every compliance checkisrecorded on-chain with cryptographic proof of execution timestamp, inputs, and outcomes - Zero
Discretion:Discretion β Compliance decisions arealgorithmicβalgorithmic β no analyst judgment, no selective enforcement, no regulatory arbitrage - Real-Time
Enforcement:Enforcement β Non-compliant transfersareblocked before execution, not flagged for review after the fact - Cryptographic
Proof:Proof β Regulators can independently verify any historical compliance decision by replaying on-chain data - Continuous
Operation:Operation β 24/7/365 enforcement with no business hours, holidays, or staffing gapsπ‘ SEC Modernization Alignment
π‘ SEC Alignment: OTCM's Transfer Hook architecture
alignsdirectlywithimplementsSECtheChairinvestorGensler'protection principles articulated in the SEC'sstatedMarchgoal17,of2026'bringinginterpretationcrypto(ReleaseintoNo.compliance'33-11412) and theSECCrypto Task Force'sexplorationframeworkoffor blockchain-native compliance mechanisms.
π 3.2 Transfer Hook Execution Flow
This section details the precise execution sequence when an ST22 token transfer is initiated, from user action through final settlement.
πΉ 3.2.1 Hook Invocation Sequence
When a user initiates an ST22 transfer, the following sequence executes:
// Sequential Hook Execution Flow
β---β
β ST22 TRANSFER HOOK EXECUTION SEQUENCE β
β---βUser Initiates Transfer
β
βΌ
β---β
β SOLANA RUNTIME: Detects TransferHook extension on ST22 mint β
β Invokes OTCM Transfer
Hookβ
ProgramSolana withRuntime intercepts transfer
contextβ
β
β---β¬---β
β
βΌ
β---β
β HOOKHook 1: CUSTODYCustody VERIFICATIONVerification β
βOracle (100-100β150ms)
β
β Query Empire Stock Transfer β Verify 1:1 backing β Error 6001 β
β---β¬---β
ββ PASS
βΌ
β---β
β HOOKHook 2: OFAC SCREENINGSanctions β
βScreening (200-200β500ms)
β
β Check SDN List β Address clustering β Error 6002 β
β---β¬---β
ββ PASS
βΌ
β---β
β HOOKHook 3: Blockchain Analytics / AML ANALYTICS β
β (300-300β400ms)
β
β ML risk scoring β 200+ features β Error 6003 β
β---β¬---β
ββ PASS
βΌ
β---β
β HOOKHook 4: REDEMPTIONRedemption ELIGIBILITY β
βEligibility (50-50β100ms)
β
β KYC status β Accreditation β 72hr cooling β Error 6004 β
β---β¬---β
ββ PASS
βΌ
β---β
β HOOKHook 5: PRICEPrice IMPACTImpact LIMITCircuit β
βBreaker (50-50β100ms)
β
β Calculate impact β Compare TWAP β 2% threshold β Error 6006 β
β---β¬---β
ββ PASS
βΌ
β---β
β HOOKHook 6: LIQUIDITYLiquidity SUFFICIENCYPool β
βSufficiency (50-50β100ms)
ββ β Check LP ratio β 150% minimum β Error 6007 β
β---β¬---β
βALL PASS
βΌTransaction β---βExecutes β& ALLSettles HOOKSon PASSEDSolana
ββ βANY TransferFAIL
executes,Entire transactionTransaction commitsReverts β
β Settlement: ~13 seconds (32 blocks) β
β---βAtomically
πΉ 3.2.2 Sequential vsvs. Parallel Execution
OTCM implements sequential hook execution rather than parallel for critical security reasons:
Mode | Advantage | Trade-off |
|---|---|---|
Sequential (OTCM) | Early exit on | Higher latency ( |
Parallel | Lower latency (~500ms total) | Non- |
Sequential execution ensures that if Hook 1 (Custody Verification) fails, Hooks 2-2β6 never executeβexecute β saving oracle costs and compute resources. It also provides deterministic error reporting: usersUsers always know which specific hook rejected their transaction.
πΉ 3.2.3 Atomic Transaction Guarantees
Solana's account model provides atomic transaction guarantees: if any hook fails, the entire transaction reverts as if it never occurred. There is no intermediate state where tokens have transferred but compliance verification is incomplete.
// Atomicity Guarantee
// Atomic Execution Guarantee
// Scenario A: All hooks pass
// Result: Transaction commits, tokens move, state updates
// Scenario B: Hook 3 (AML) rejects due to risk score
// Result: ENTIRE transaction reverts
// - No tokens moved
// - No fees charged
// - No state changes
// - Error returned to user: 6003 (AML_RISK_EXCEEDED)
// There is NO partial execution state
// This is guaranteed by Solana runtime, not OTCM codeπΉ 3.2.4 Execution Timing Diagram
Hook | Min (ms) | Max (ms) | Avg (ms) | Primary Bottleneck |
|---|---|---|---|---|
1. Custody Oracle | 100 | 150 | 125 | Empire API latency |
2. OFAC Screening | 200 | 500 | 350 | SDN list |
3. AML Analytics | 300 | 400 | 350 | ML inference time |
4. Redemption Eligibility | 50 | 100 | 75 | KYC database lookup |
5. Price Impact | 50 | 100 | 75 | TWAP calculation |
6. LP Sufficiency | 50 | 100 | 75 | Pool state read |
TOTAL | 750 | 1,350 | 1,050 | Sequential sum |
π 3.3 Hook 1: Custody Verification Oracle
The Custody Verification Oracle confirms that all circulating ST22 Digital Securities tokens are backed by equivalent equity shares held at Empire Stock Transfer,Transfer β the SEC-registered transfer agent providing qualified custody services for OTCM Protocol.
πΉ 3.3.1 Oracle Architecture
The custody oracle operates as a cryptographically-signed data feed from Empire Stock Transfer's registry systems:
// Custody Oracle Data Structurepub struct CustodyOracleData {
/// CIK number of the issuing companypub issuer_cik: [u8; 10],
/// CUSIP identifier for the securitypub cusip: [u8; 9],
/// Share class (Series M preferred)/// Total shares held in custodypub custodied_balance: u64,
/// Total ST22 tokens in circulationpub circulating_tokens: u64,
/// Ed25519 signature from Empire's HSMpub attestation_signature: [u8; 64],
/// MerkleEd25519 rootβ ofHSM-secured
current shareholder registrypub registry_hash: [u8; 32],
/// Unix timestamp of attestationpub attestation_timestamp: i64,
/// Block height when attestation was recordedpub attestation_slot: u64,
}
πΉ 3.3.2 Empire Stock Transfer Integration
Empire Stock Transfer provides real-time custody attestations through a dedicated API integration:
Component | Specification |
|---|---|
API Endpoint | |
Authentication | API Key + Ed25519 request signing |
Response Signing | HSM-secured Ed25519 signature on every attestation |
Update Frequency | Every Solana slot (~400ms) |
Failover | Multi-region deployment with automatic failover |
ππ PROOF-OF-RESERVEProof-of-Reserve β SUPPLYSupply RECONCILIATIONReconciliation SPECIFICATIONSpecification
Version: 6.01 | Applies To: Layer 6 Oracle Network β Hook 1 Custody Verification
The Reconciliation Invariant
At every Solana slot, the following invariant MUST hold for every active ST22 mint:
on_chain_token_supply(mint)circulating_supply <=β€ empire_custodied_shares(mint)custodied_shares
Any condition where on-chain supply EXCEEDSexceeds custodied shares is an immediate critical alert (Error 6001 β all transfers halted). The system is designed so that equality (1:1 backing) is the steady state andβ a deficit on the custody sidedeficit is impossible under normal operations because shares must be deposited BEFOREbefore tokens are minted.
Empire Stock Transfer API β Canonical Response Schema
rust
/// Canonical signed attestation payload from Empire Stock Transfer API
/// Received by the primary Oracle and published on-chain each slot
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct EmpireCustodyAttestation {
/// Schema version β must match EMPIRE_SCHEMA_VERSION constant
pub schema_version: u8, // Currently: 1
/// The ST22 mint this attestation covers
pub mint: Pubkey,
/// CUSIP of the underlying Series M preferred share class
pub cusip: [u8; 9],
/// Total shares of Series M held in irrevocable custody at Empire
/// This is the authoritative backing figure
pub custodied_shares: u64,
/// Total shares ever deposited (monotonically increasing)
pub total_deposited_lifetime: u64,
/// Solana slot number at time of attestation
pub slot: u64,
/// Unix timestamp at time of attestation
pub timestamp: i64,
/// Attestation expiry β slot after which Hook 1 rejects this message
pub expiry_slot: u64,
/// Monotonically increasing sequence number (replay prevention)
pub sequence_number: u64,
/// SHA-256 hash of Empire's internal ledger state at this slot
/// Enables independent verification against Empire's published quarterly hash
pub ledger_state_hash: [u8; 32],
/// Ed25519 signature over all fields above using Empire HSM key
pub signature: [u8; 64],
/// Empire's HSM public key (must match on-chain OracleRegistry entry)
pub signer_pubkey: Pubkey,
}
/// On-chain custody cache β stores latest valid attestation per mint
#[account]
pub struct CustodyCache {
pub version: u8,
pub mint: Pubkey,
pub custodied_shares: u64,
pub last_updated_slot: u64,
pub last_sequence_number: u64,
pub ledger_state_hash: [u8; 32],
pub bump: u8,
}
Reconciliation Algorithm β Hook 1 Execution
rust
/// Hook 1: Full supply reconciliation on every transfer
pub fn verify_custody_hook(verify_custody(
ctx: Context,Context<CustodyVerification>,
transfer_amount: u64,
) -> Result<()> {
// ---1. STEPVerify 1:oracle Validatesignature attestationauthenticity
schemaverify_ed25519_signature(
version&oracle_data.attestation_signature,
---&EMPIRE_PUBLIC_KEY,
&oracle_data.serialize(),
)?;
// 2. Reject stale attestations (>10 minutes)
require!(
ctx.accounts.oracle_attestation.schema_versioncurrent_time =- oracle_data.attestation_timestamp <= EMPIRE_SCHEMA_VERSION,600,
OracleError:CustodyError::SchemaMismatchStaleAttestation // 6001
);
// ---3. STEPCheck 2:discrepancy Verifywithin Ed255190.01% signaturetolerance
---require!(
discrepancy_bps <= MAX_DISCREPANCY_BPS,
CustodyError::CustodyDiscrepancy // Signature covers all fields except `signature` itself
let attestation_bytes = ctx.accounts.oracle_attestation
.try_to_vec_without_signature()?;
verify_ed25519_signature(
&attestation_bytes,
&ctx.accounts.oracle_attestation.signature,
&ctx.accounts.oracle_registry.empire_primary_pubkey,
).map_err(|_| error!(OracleError::InvalidSignature))?;
// --- STEP 3: Verify attestation is not stale ---
let current_slot = Clock::get()?.slot;
require!(
current_slot <= ctx.accounts.oracle_attestation.expiry_slot,
OracleError::StaleAttestation // Error 60196001
);
// ---4. STEPConfirm 4: Verify sequence number (replay prevention) ---
require!(
ctx.accounts.oracle_attestation.sequence_number
> ctx.accounts.custody_cache.last_sequence_number,
OracleError::ReplayAttack
);
// --- STEP 5: THE CORE INVARIANT CHECK ---
// Get current on-chain circulating supply from mint account
let on_chain_supply = ctx.accounts.mint.supply;
// Getsufficient custodied shares fromfor verifiedtransfer
attestationrequire!(
letoracle_data.custodied_balance custodied_shares >= ctx.accounts.oracle_attestation.custodied_shares;transfer_amount,
CustodyError::InsufficientCustody // Verify invariant: supply <= custody
// Allow 0.01% tolerance for timing edge cases (MAX_DISCREPANCY_BPS = 1)
let max_allowed_supply = custodied_shares
.checked_add(
custodied_shares
.checked_mul(MAX_DISCREPANCY_BPS)
.ok_or(OracleError::Overflow)?
.checked_div(10_000)
.ok_or(OracleError::Overflow)?
)
.ok_or(OracleError::Overflow)?;
require!(
on_chain_supply <= max_allowed_supply,
TransferHookError::CustodyDiscrepancy // Error 6001 β HALT
);
// --- STEP 6: Verify post-transfer supply still within bounds ---
// After this transfer, supply does not change (it's a transfer not a mint)
// But verify amount_in does not exceed available custodied backing
let post_transfer_supply = on_chain_supply; // transfers don't change supply
require!(
post_transfer_supply <= custodied_shares,
TransferHookError::CustodyDiscrepancy
);
// --- STEP 7: Update on-chain cache with latest attestation ---
// CEI pattern: all checks complete β now write state
let cache = &mut ctx.accounts.custody_cache;
cache.custodied_shares = custodied_shares;
cache.last_updated_slot = current_slot;
cache.last_sequence_number = ctx.accounts.oracle_attestation.sequence_number;
cache.ledger_state_hash = ctx.accounts.oracle_attestation.ledger_state_hash;
Ok(())
}
pub const MAX_DISCREPANCY_BPS: u64 = 1; // 0.01% maximum discrepancy
Quarterly Public Proof-of-Reserve Audit
In addition to per-slot on-chain verification, OTCM publishes a quarterly public proof-of-reserve:
Component | Frequency | Published At | Verifiable By |
|---|---|---|---|
Empire ledger state hash | Quarterly | otcm.io/proof-of-reserve | Anyone with Empire ledger export |
On-chain supply snapshot | Continuous | Solana block explorer | Anyone |
Signed custody attestation archive | Quarterly | IPFS + Arweave | Anyone with Empire public key |
Third-party attestation (auditor) | Annual | SEC EDGAR (8-K) | Anyone |
Public Verification Procedure:
bash# Anyone can verify the 1:1 backing at any point in time: # Step 1 β Query on-chain supply for a specific ST22 mint spl-token supply # Step 2 β Fetch latest custody attestation from on-chain cache solana account --output json # Step 3 β Verify Empire signature using public key otcm-verify-custody --attestation --pubkey # Step 4 β Compare: on-chain supply <= custodied_shares # Any discrepancy should be reported immediately to security@otcm.io
πΉ 3.3.3 Discrepancy Detection
The hook implements strict discrepancy detection with configurable thresholds:// Custody Verification Implementation (Rust/Anchor)
pub const MAX_DISCREPANCY_BPS: u64 = 1; // 0.01% maximum discrepancypub fn verify_custody(
oracle_data: &CustodyOracleData,
transfer_amount: u64,
) -> Result<()> {// Verify signature authenticity
verify_ed25519_signature(
&oracle_data.attestation_signature,
&EMPIRE_PUBLIC_KEY,
&oracle_data.serialize(),
)?;// Check attestation freshness (max 10 minutes old) let current_time = Clock::get()?.unix_timestamp; require!(
current_time - oracle_data.attestation_timestamp <= 600,
CustodyError::StaleAttestation // 6001
);// Calculate discrepancy let expected_tokens = oracle_data.custodied_balance; let actual_tokens = oracle_data.circulating_tokens; let discrepancy = if expected_tokens > actual_tokens {
expected_tokens - actual_tokens
} else {
actual_tokens - expected_tokens
};let discrepancy_bps = discrepancy * 10000 / expected_tokens; require!(
discrepancy_bps <= MAX_DISCREPANCY_BPS,
CustodyError::CustodyDiscrepancy // 6001
);// Verify sufficient custody for this transfer require!(
oracle_data.custodied_balance >= transfer_amount,
CustodyError::InsufficientCustody // 6001
);Ok(())
}π Regulatory Reference: 17 CFR Section 240.17a-1 et seq. β Transfer Agent Custody Requirements
β Error Code 6001: CUSTODY_VERIFICATION_FAILED
β Indicates custody verification failure. Possible causes: stale attestation (>10 min), discrepancy >0.01%, insufficient custodied shares, or invalid Empire signature. User should wait for fresh attestation or contact issuer.
π 3.4 Hook 2: OFAC Sanctions Screening
The OFAC Sanctions Screening hook prevents transactions to or from wallets associated with the Office of Foreign Assets Control's Specially Designated Nationals (SDN) list, implementing federal sanctions law requirements at the protocol level.
πΉ 3.4.1 SDN List Integration
OTCM maintains an on-chain oracle updated hourly from OFAC's official SDN list, supplemented by blockchain-specific address intelligence:
// OFAC Screening Implementationpub struct OfacOracleData {
/// Merkle root of current SDN listpub sdn_merkle_root: [u8; 32],
/// Number of entries in SDN listpub sdn_entry_count: u32,
/// Blockchain-specific blocked addressespub crypto_sdn_addresses: Vec<Pubkey>,
/// Last update timestamppub last_update: i64,
/// OFAC publication referencepub publication_id: [u8; 16],
}
// Screening checks BOTH sender and recipient
pub fn screen_ofac(
sender: &Pubkey,
recipient: &Pubkey,
oracle: &OfacOracleData,
) -> Result<OfacScreeningResult> {
// Direct address matching
let sender_blocked = oracle.crypto_sdn_addresses.contains(sender);
let recipient_blocked = oracle.crypto_sdn_addresses.contains(recipient);if sender_blocked || recipient_blocked {
return Err(OfacError::SanctionedAddress.into()); // 6002
}
// Address clustering analysis (see 3.4.2)check_cluster_association(sender, recipient, oracle)?;
Ok(OfacScreeningResult::Clear)}
πΉ 3.4.2 Address Clustering Analysis
Beyond direct SDN address matching, OTCM implements sophisticated address clustering analysis to detect wallets associated with sanctioned entities:
- Funding Source
Analysis:Analysis β Traces wallet funding sources to identify indirect SDN connections - Common Ownership
Detection:Detection β Identifies wallets controlled by the same entity through transaction pattern analysis - Mixer/Tumbler
Detection:Detection β Flags wallets with significant exposure to known mixing services - High-Risk Jurisdiction
Exposure:Exposure β Elevated scrutiny for wallets with connections to OFAC-sanctioned jurisdictions
πΉ 3.4.3 Screening Implementation
// Cluster Analysis Implementation
pub fn check_cluster_association(check_ofac_compliance(
sender: &Pubkey,
recipient: &Pubkey,
oracle: &OfacOracleData,
) -> Result<()> {
// QueryDirect clusteringSDN oracleaddress formatch
bothif addressessender_blocked let|| sender_clusterrecipient_blocked ={
get_cluster_data(sender)?;return let recipient_cluster = get_cluster_data(recipient)?Err(OfacError::SanctionedAddress.into()); // Check6002
for}
SDN// clusterCluster membershipassociation scoring (threshold: 70) if sender_cluster.sdn_association_score > 70 {
return Err(OfacError::ClusterAssociation {
address: *sender,
score: sender_cluster.sdn_association_score,
reason: "High SDN cluster association".to_string(),
}.into());
}
if recipient_cluster.sdn_association_score > 70 {
return Err(OfacError::ClusterAssociation {
address: *recipient,
score: recipient_cluster.sdn_association_score,
reason: "High SDN cluster association".to_string(),
}.into());
}
Ok(())
}
}π Regulatory Reference: 31 CFR Β§
500-500β598 β OFAC Sanctions Administration Regulations
β Error Code 6002: OFAC_SANCTIONS_MATCH
PERMANENT
Indicatesβwallet isWallet blocked due to OFAC sanctions. This is ablockβblock β the wallet cannot transact ST22 tokens. There is no appeal process through OTCM; affected parties must resolve sanctions status with OFAC directly.
π 3.5 Hook 3: Blockchain Analytics / AML Screening
The Blockchain Analytics Screening hook implements Bank Secrecy Act Anti-Money Laundering (AML)AML requirements through machine learning-based risk assessment,assessment, analyzing 200+ transaction features to identify money laundering patterns, sanctions evasion, theft proceeds, and coordinated suspicious activity.
πΉ 3.3.5.1 ML Risk Assessment
OTCM integrates with institutional-grade blockchain analytics providers to execute real-time risk assessment on every transfer:
// AML Risk Assessment Data StructureModel
pub struct AmlRiskAssessment {
/// Overall risk score (0-100)
pub risk_score: u8,
/// Risk category breakdown
pub categories: AmlRiskCategories,
/// Specific risk flags triggered
pub triggered_flags: Vec<AmlFlag>,
/// Confidence in assessment
pub confidence: u8,
/// Model version used
pub model_version: [u8; 8],
}
pub struct AmlRiskCategories {
pub sanctions_evasion: u8, // 0-0β100
pub money_laundering: u8, // 0-0β100
pub theft_proceeds: u8, // 0-0β100
pub darknet_exposure: u8, // 0-0β100
pub mixer_exposure: u8, // 0-0β100
pub gambling_exposure: u8, // 0-0β100
pub coordinated_activity: u8, // 0-0β100
}
πΉ 3.3.5.2 Risk Scoring ModelTiers
The risk scoring model classifies transactions into three tiers with automated responses:
Score | Risk Level | Automated Action | Follow-up |
|---|---|---|---|
| Low | Auto-approve | None required |
| Medium | Enhanced review queue | Manual analyst review within 24h |
| High | Auto-reject (Error 6003) | SAR filing evaluation |
πΉ 3.5.3.3 Chainalysis/TRMAnalytics Provider Integration
OTCM integrates with industry-leading blockchain analytics providers:
- Chainalysis KYT (Know Your Transaction)
:β Real-time transaction screening with 15-second SLA - TRM Labs
Forensics:Forensics β Deep historical analysis and entity resolution - Elliptic
Navigator:Navigator β Cross-chain exposure analysis for multi-chain actors
πΉ 3.5.3.4 Implementation
// AML Screening Implementationpub const LOW_RISK_THRESHOLD: u8 = 30;
pub const HIGH_RISK_THRESHOLD: u8 = 71;
pub fn screen_aml(
sender: &Pubkey,
recipient: &Pubkey,
amount: u64,
) -> Result<AmlScreeningResult> {
// Query analytics providers
let sender_risk = query_chainalysis(sender)?;
let recipient_risk = query_chainalysis(recipient)?;
// Use higher of two risk scores
let max_risk = sender_risk.risk_score.max(recipient_risk.risk_score);match max_risk {
0..=30 => Ok(AmlScreeningResult::AutoApprove),
31..=70 => {
//queue_enhanced_review(sender, Queuerecipient, foramount);
enhanced review but allow transaction
emit!(EnhancedReviewQueued {sender: *sender,
recipient: *recipient,
risk_score: max_risk,
amount,
});
Ok(AmlScreeningResult::ApproveWithReview)ProceedWithFlag)
},
71..=100 => {
// Auto-reject high-risk transactionsErr(AmlError::RiskThresholdExceeded {
score: max_risk,
threshold: HIGH_RISK_THRESHOLD,
}.into()), // 6003
},
_ => unreachable!(),
} }
}
}π Regulatory Reference: 31 CFR Β§ 1010 β Bank Secrecy Act AML Requirements
β Error Code 6003: AML_RISK_EXCEEDED
β Transaction rejected due to high AML risk score (
71-71β100). Wallet may have exposureto:to money laundering, sanctions evasion, theft proceeds, darknet markets, or coordinated suspicious activity. Appeal requires compliance review.
π 3.6 Hook 4: Redemption Eligibility Verification
The Redemption Eligibility Verification hook implements a critical distinction in OTCM's compliance architecture: while permissionless trading allows any verified wallet to purchase and transfer ST22 tokens, redemption (conversion to underlying equity shares) requires full investor verification in accordance with federal securities law.
πΉ 3.6.1 KYC/AML Requirements
Before a wallet can redeem ST22 tokens for underlying shares, the beneficial owner must complete comprehensive verification:
Requirement | Verification Method |
|---|---|
Identity Verification | Government-issued photo ID + liveness check via Jumio/Onfido |
Address Verification | Utility bill or bank statement within 90 days |
SSN/TIN Verification | IRS W-9 form with SSN/TIN matching |
Sanctions Screening | 72-hour waiting period after initial OFAC/SDN clear |
PEP Screening | Politically Exposed Person database check |
πΉ 3.6.2 Accredited Investor Verification
For offerings conducted under SEC Regulation D Rule 506(c), additional accreditation verification is required:
// Accreditation Status Typespub enum AccreditationStatus {
///NotVerified,
Not verified - cannot redeem Reg D offeringsNotVerified,
/// Verified accredited investorAccredited {
method: AccreditationMethod,
verification_date: i64,
expiration_date: i64, // Typically 90 days
verifier: String, // e.g., "VerifyInvestor.com"
},
/// Non-accredited but permitted (Reg A+ or Reg CF)NonAccreditedPermitted {
regulation: OfferingRegulation,
investment_limit: u64,
},
}
pub enum AccreditationMethod {
Income, // $200K/200K / $300K annual income
NetWorth, // $1M+ net worth excluding primary residence
Professional, // Series 7, 65, or 82 license
Entity, // Qualified entity (bank, trust, etc.)
KnowledgeTest, // SEC proposed "accreditation exam"
}
πΉ 3.6.3 Implementation
// Redemption Eligibility Verification
pub fn verify_redemption_eligibility(
wallet: &Pubkey,
token_mint: &Pubkey,
amount: u64,
) -> Result<()> {
// Load investor record
let investor = get_investor_record(wallet)?;
// Check KYC completionmust be verified
require!(
investor.kyc_status == KycStatus::Verified,
RedemptionError::KycIncomplete // 6004
);
// Check 72-hour sanctions cooling period
let current_time = Clock::get()?.unix_timestamp;
let cooling_period = 72 * 60 * 60; // 72 hours in seconds
require!(
current_time - investor.sanctions_clear_timestamp >= cooling_period,
RedemptionError::SanctionsCoolingPeriod // 6004
);
// Load offering details
let offering = get_offering_details(token_mint)?;
// Check accreditation if Reg D 506(c): accreditation required if offering.regulation == Regulation::RegD506c {
match &investor.accreditation {
AccreditationStatus::Accredited { expiration_date, .. } => {
require!(
current_time < *expiration_date,
RedemptionError::AccreditationExpired // 6004
);
},
_ => {
return Err(RedemptionError::AccreditationRequired.into());
}
}
}
Ok(())
}
}π Regulatory Reference: 17 CFR 230.506(c) β Regulation D Accredited Investor Requirements
β Error Code 6004: REDEMPTION_ELIGIBILITY_FAILED
β Wallet not eligible for redemption. Possible causes: KYC incomplete, sanctions cooling period active (<72h), accreditation
Note:expired/expired or missing for Reg D offerings.TradingRegularisST22stilltradingpermitted;remains permitted β only redemption to underlying shares is blocked.
π 3.7 Hook 5: Price Impact Circuit Breaker
The Price Impact Circuit Breaker prevents single transactions from causing excessive market movement, implementing regulatory objectives prohibiting manipulative trading devices. Any transaction causing greater than 2% price movement against the Time-Weighted Average Price (TWAP) oracle is automatically rejected.
πΉ 3.7.1 TWAP Oracle Integration
The TWAP oracle provides manipulation-resistant reference pricing:
// TWAP Oracle Implementationpub struct TwapOracle {
pub token_mint: Pubkey,
/// Rolling 1-hour TWAPpub twap_1h: u64,
/// Rolling 24-hour TWAPpub twap_24h: u64,
/// Number of observations in windowpub observation_count: u32,
/// Minimum observations requiredpub min_observations: u32,
/// Last update slotpub last_update_slot: u64,
}
impl TwapOracle {
pub fn get_reference_price(&self) -> Result {
// Use shorter TWAP for more responsive circuit breakers
// but require minimum observations to prevent manipulation
require!(
self.observation_count >= self.min_observations,
OracleError::InsufficientObservations
);
Ok(self.twap_1h)}
}
πΉ 3.7.2 2% Threshold Enforcement
The 2% threshold forces large market participants to execute trades gradually, preventing sudden price dislocations:
// Price Impact Circuit Breaker Implementationpub const MAX_PRICE_IMPACT_BPS: u64 = 200; // 2.00%
pub fn check_price_impact(
pool: &LiquidityPool,
trade_amount: u64,
trade_direction: TradeDirection,
) -> Result<()> {
// Get TWAP reference price
let twap = pool.twap_oracle.get_reference_price()?;
// Calculate expected post-trade price using CPMM formula
let (new_sol_reserve,post_sol_reserve, new_token_reserve)post_token_reserve) = match trade_direction {
TradeDirection::Buy => (
pool.sol_reserve + trade_amount,
pool.token_reserve - calculate_output(trade_amount, pool)?,
),
TradeDirection::Sell => (
pool.sol_reserve - calculate_output(trade_amount, pool)?,
pool.token_reserve + trade_amount,
),
};
let post_trade_price = new_sol_reservepost_sol_reserve * 1_000_000_0001_000_000 / new_token_reserve;
// Calculate deviation from TWAPpost_token_reserve;
let deviation = if post_trade_price > twap {
(post_trade_price - twap) * 10000 / twap
} else {
(twap - post_trade_price) * 10000 / twap
};
require!(
deviation <= MAX_PRICE_IMPACT_BPS,
CircuitBreakerError::PriceImpactExceeded {
expected_impact_bps: deviation,
max_allowed_bps: MAX_PRICE_IMPACT_BPS,
} // 6006
);
Ok(())
}
}π Regulatory Reference: 17 CFR 240.10b-5(b) β Prohibition on Manipulative Trading Devices
β Error Code 6006: PRICE_IMPACT_EXCEEDED
β Transaction would cause >2% price movement from TWAP. User should reduce trade size or split into multiple smaller transactions. This protects all market participants from sudden price manipulation.
π 3.8 Hook 6: Liquidity Pool Sufficiency
The Liquidity Pool Sufficiency hook ensures the OTCM Liquidity Pool maintains adequate reserves to support all outstanding ST22 Digital Securities tokens. The hook confirms the pool maintains a minimum 150% ratio of locked capital to circulating ST22 value before allowing redemption transactions.
πΉ 3.8.1 150% Ratio Requirement
The 150% over-collateralization provides a safety buffer ensuring redemptions can always be honored:
Component | Calculation |
|---|---|
Locked Capital | Total SOL in unified LP (permanently locked) |
Circulating Value | Sum of (ST22 supply Γ current price) for all tokens |
Sufficiency Ratio | Locked Capital |
Minimum Required | β₯ 150% β redemptions pause if below |
πΉ 3.8.2 Implementation
// Liquidity Sufficiency Checkpub const MIN_SUFFICIENCY_RATIO_BPS: u64 = 15000; // 150%
pub fn check_liquidity_sufficiency(
pool: &UnifiedLiquidityPool,
redemption_amount: u64,
) -> Result<()> {
// Calculate current ratio
let locked_capital = pool.total_sol_reserve;
let circulating_value = calculate_total_circulating_value(pool)?;
// Add redemption impact
let post_redemption_capital = locked_capital - redemption_amount;
let post_redemption_ratio = post_redemption_capital * 10000 / circulating_value;
require!(
post_redemption_ratio >= MIN_SUFFICIENCY_RATIO_BPS,
LiquidityError::InsufficientLiquidity {
current_ratio: post_redemption_ratio,
required_ratio: MIN_SUFFICIENCY_RATIO_BPS,
} // 6007
);
Ok(())
}
}π Regulatory Reference: Securities Exchange Act Rule 15c2-11 β Capital Adequacy Requirements
β Error Code 6007: LIQUIDITY_INSUFFICIENT
β Pool ratio below 150% threshold. Redemptions temporarily paused until trading volume rebuilds reserves. Regular ST22 trading (non-redemption) remains permitted.
π‘οΈ 3.9 Thirty-Six Supplementary Security Controls
Beyond the six primary Transfer Hooks, OTCM implements thirty-six supplementary security controls integrated into the transfer hook logic,logic β creating a comprehensive 42-point security framework.framework.
πΉ 3.9.1 Smart Contract Controls (1-1β6)
# | Control | Description |
|---|---|---|
1 | Formal Verification | Certora Prover mathematical proof of contract correctness |
2 | Multi-Audit Requirement | Independent audits by Quantstamp, Halborn, and OtterSec |
3 | Immutable Bytecode | No proxy |
4 | Bug Bounty Program | Up to $500K rewards for critical vulnerabilities |
5 | Reentrancy Guards |
|
6 | Integer Overflow Protection | Rust's checked arithmetic prevents overflow attacks |
πΉ 3.9.2 Access Control Framework (7-7β12)
# | Control | Description |
|---|---|---|
7 | Multi-Sig Administration | 4-of-7 threshold for administrative actions |
8 | Timelock Delays | 48-hour delay on all parameter changes |
9 | Role-Based Access | Granular permission system ( |
10 | Emergency Pause | 2-of-7 emergency pause with 24hr auto-unpause |
11 | DAO Override | 2/3 supermajority required for lock override |
12 | Key Rotation | Quarterly key rotation with HSM backing |
πΉ 3.9.3 Transaction Integrity Controls (13-13β18)
- Atomic
Execution:Execution β All-or-nothing transaction commitment - Orphan
Prevention:Prevention β No partial execution states possible - Replay
Protection:Protection β Unique transaction signatures prevent replay - Deadline
Enforcement:Enforcement β Transactions expire after specified block height - Slippage
Protection:Protection β User-configurable maximum price deviation
πΉ 3.9.4 Oracle & Data Controls (19-19β24)
- Multi-Oracle
Consensus:Consensus β Cross-reference multiple data sources - Staleness
Detection:Detection β Reject data older than threshold (10 min) - Signature
Verification:Verification β Ed25519 verification of all oracle data - Rate
Limiting:Limiting β Maximum oracle queries per block per mint - Fallback
Oracles:Oracles β Automatic failover to backup data sources - Data Freshness
Proofs:Proofs β On-chain attestation of data recency
πΉ 3.9.5 Monitoring & Alerting (25-25β30)
- Real-Time
Monitoring:Monitoring β 24/7 transaction pattern analysis - Anomaly
Detection:Detection β ML-based unusual activity identification - SEC
Notification:Notification β Automatic reporting of compliance failures - Compliance
Dashboard:Dashboard β Real-time visibility for regulators - Audit Log
Export:Export β On-demand historical data retrieval - Alert
Escalation:Escalation β Tiered notification for severity levels
πΉ 3.9.6 Governance & Recovery (31-31β36)
- Proposal
System:System β On-chain governance for parameter changes - Voting
Mechanism:Mechanism β Token-weighted voting with delegation - Quorum
Requirements:Requirements β Minimum participation thresholds - Veto
Power:Power β Compliance officer veto on security-relevant proposals - Migration
Capability:Capability β Controlled upgrade path for critical fixes - Disaster
Recovery:Recovery β Documented procedures for catastrophic scenarios
β 3.10 Error Handling and Recovery
Code | Hook | Condition | User Action |
|---|---|---|---|
6001 | Custody | Discrepancy >0.01% |
|
6002 | OFAC | Sanctioned address | PERMANENT β contact OFAC |
6003 | AML | Risk score | Request compliance review |
6004 | Redemption |
| Complete verification |
6006 | Price Impact | >2% TWAP deviation | Reduce |
6007 | Liquidity | <150% pool ratio | Wait for ratio recovery |
β‘ 3.11 Performance Specifications
Metric | Value | Notes |
|---|---|---|
Total Hook Latency |
| Sequential execution |
Compute Units | ~800,000 CU | Per complete transfer |
Effective TPS |
| Compliance overhead |
Oracle Cost/Transfer | $ |
|
π CROSS-PROGRAMCross-Program INVOCATIONInvocation (CPI) SECURITYSecurity SPECIFICATIONSpecification
CPI Boundary Map
Every CPI call in OTCM Transfer Hook programs is documented below. For each boundary, the privilege model, account validation, and escalation prevention mechanism is specified.
Calling Program | Target Program | CPI Type | Accounts Passed | Privilege Check |
|---|---|---|---|---|
TransferHook | TokenProgram (SPL Token-2022) |
|
| PDA authority verified β no signer escalation |
TransferHook | OracleRegistry |
|
| Read-only β no write authority passed |
TransferHook | AuditLog |
|
| PDA bump verified β canonical bump enforced |
CEDEX AMM | TransferHook |
| extra_account_metas | Hook invoked by token program β not directly by AMM |
IssuersPortal | TokenProgram |
| mint_authority_pda | Mint authority is PDA β program cannot overmint |
Privilege Escalation Prevention: All OTCM programs use invoke_signed only when the signing PDA is owned by the calling program. No OTCM instruction passes a user-provided signer to a CPI target. TheClient-supplied accounts remaining_accountsfieldnot in Transfer Hook extra account metas is validated against an on-chainthe ExtraAccountMetaList PDA β client-supplied accounts that are not in this registry cause immediate instruction failure.
PDA Derivation Canonical Reference
All Program Derived Addresses used by OTCM Protocol follow strict canonical derivation paths. Canonical bump enforcement (using the first valid bump, not a client-supplied bump) is enforced on every instruction.
rust
// --- TRANSFER HOOK PDAs ---
/// Extra account metas list β required by SPL Token-2022 Transfer Hook spec
pub fn extra_account_metas_pda(mint: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"extra-account-metas", mint.as_ref()],
program_id,
)
}
/// Per-token compliance state account
pub fn compliance_state_pda(mint: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"compliance-state", mint.as_ref()],
program_id,
)
}
/// Per-wallet KYC/accreditation record
pub fn investor_record_pda(wallet: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"investor-record", wallet.as_ref()],
program_id,
)
}
// --- ORACLE PDAs ---
/// Oracle sequence number registry β per oracle type
pub fn oracle_sequence_pda(oracle_type: u8, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"oracle-sequence", &[oracle_type]],
program_id,
)
}
/// Cached oracle attestation β latest valid message per oracle type
pub fn oracle_cache_pda(oracle_type: u8, mint: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"oracle-cache", &[oracle_type], mint.as_ref()],
program_id,
)
}
// --- LIQUIDITY POOL PDAs ---
/// Sovereign LP pool state β per ST22 mint
pub fn lp_pool_state_pda(mint: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"lp-pool-state", mint.as_ref()],
program_id,
)
}
/// Permanently locked LP token account β receives burned LP tokens
pub fn dead_lp_account_pda(mint: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"dead-lp", mint.as_ref()],
program_id,
)
}
// --- AUDIT LOG PDAs ---
/// Audit log entry β per transaction signature
pub fn audit_log_pda(tx_sig: &[u8; 64], program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[b"audit-log", &tx_sig[..32]],
program_id,
)
}Account Discriminator Registry
Anchor-generated 8-byte discriminators for all OTCM account types. Type confusion attacks (passing a valid account of the wrong type) are prevented by discriminator validation on every instruction.
Account Type | Discriminator (hex) | Program | Mutable |
|---|---|---|---|
|
| TransferHook | Yes |
|
| TransferHook | Yes |
|
| OracleRegistry | No |
|
| OracleRegistry | Yes |
|
| LiquidityPool | Yes |
|
| BondingCurve | Yes |
|
| AuditLog | No |
|
| IssuersPortal | Yes |
Note:β οΈ Discriminators above are specification targets. Actual deployed values are generated by Anchor at compile time fromsha256("account:")[..8]and must be verified against deployed program IDL before audit sign-off.
π‘οΈ SUPPLEMENTARYSupplementary SECURITYSecurity CONTROLSControls β COMPLETEComplete TECHNICALTechnical SPECIFICATIONSpecification
Controls 7β42 are enforced at the Transfer Hook layer. Each control is specified with its trigger condition, enforcement mechanism, and error code.layer
Control Classification
Class | Controls | Description |
|---|---|---|
Account Integrity | 7β13 | Validate account ownership, sizes, and relationships |
Transaction Integrity | 14β20 | Enforce transaction-level constraints |
Market Integrity | 21β27 | Prevent manipulation and abnormal trading patterns |
Operational Security | 28β35 | Logging, rate limiting, and operational controls |
Emergency Controls | 36β42 | Circuit breakers and emergency response mechanisms |
Controls 7β13: Account Integrity
# | Control | Trigger Condition | Enforcement | Error Code |
|---|---|---|---|---|
7 | Signer Verification | Transfer authority β token account owner or valid delegate | Reject β invalid signer | 6007 |
8 | Account Owner Check | Any passed account not owned by expected program | Reject β wrong owner | 6008 |
9 | Mint Consistency | Source/dest token accounts reference different mints | Reject β mint mismatch | 6009 |
10 | Account Size Validation | Any account data length < minimum expected struct size | Reject β corrupt account | 6010 |
11 | Discriminator Validation | Account discriminator β expected 8-byte prefix | Reject β type confusion | 6011 |
12 | Token Account State | Source or dest account in Frozen state | Reject β account frozen | 6012 |
13 | Delegate Scope Check | Delegated transfer amount > approved delegation amount | Reject β delegation exceeded | 6013 |
Controls 14β20: Transaction Integrity
# | Control | Trigger Condition | Enforcement | Error Code |
|---|---|---|---|---|
14 | Zero-Amount Guard | Transfer amount = 0 | Reject β zero transfer | 6014 |
15 | Self-Transfer Guard | Source wallet = destination wallet | Reject β self transfer | 6015 |
16 | Overflow Guard | Amount arithmetic produces u64 overflow | Reject β arithmetic overflow | 6016 |
17 | Atomic Execution | Any hook CPI fails mid-sequence | Full transaction revert | 6017 |
18 | Sequence Integrity | Hook execution order violated ( | Reject β hook order violation | 6018 |
19 | Timestamp Validity | Block timestamp > attestation expiry | Reject β stale attestation | 6019 |
20 | Duplicate Transaction | Same ( | Reject β probable duplicate | 6020 |
Controls 21β27: Market Integrity
# | Control | Trigger Condition | Enforcement | Error Code |
|---|---|---|---|---|
21 | Wash Trade Detection | Source and destination wallets share | Reject β wash trade | 6021 |
22 | Wallet Concentration Limit | Post-transfer balance > 5% of circulating supply | Reject β concentration limit | 6022 |
23 | Velocity Limit | Wallet transfers > 10 transactions within 100 slots | Reject β velocity exceeded | 6023 |
24 | Graduated Token Lock | Transfer of pre-graduation tokens before vesting cliff | Reject β locked tokens | 6024 |
25 | LP Drain Protection | Single transaction would reduce LP depth > 20% | Reject β LP drain attack | 6025 |
26 | Price Oracle Deviation | Submitted price deviates > 5% from TWAP oracle | Reject β price manipulation | 6026 |
27 | Coordinated Transfer Detection | >3 wallets with correlated transfer patterns in same slot | Flag + manual review | 6027 |
Controls 28β35: Operational Security
# | Control | Trigger Condition | Enforcement | Error Code |
|---|---|---|---|---|
28 | Immutable Audit Log | Every successful/failed transfer | Write audit PDA entry β always | None (non-rejecting) |
29 | Rate Limit per Wallet | >50 transfer attempts per wallet per epoch (2.6 days) | Reject after threshold | 6029 |
30 | Rate Limit per Mint | >10,000 transfers per ST22 mint per slot | Reject until next slot | 6030 |
31 | Oracle Query Rate Limit | >22 oracle lookups per single transaction | Reject β oracle abuse | 6031 |
32 | Compute Budget Enforcement | Remaining CU < 50,000 before Hook 6 | Reject β insufficient CU | 6032 |
33 | Account Rent Validation | Any account below rent-exempt minimum | Reject β rent depleted | 6033 |
34 | Program Version Check | Caller program version < minimum compatible version | Reject β version mismatch | 6034 |
35 | Key Rotation Compliance | Oracle signer key not in current oracle registry | Reject β unrecognized signer | 6035 |
Controls 36β42: Emergency Controls
# | Control | Trigger Condition | Enforcement | Error Code |
|---|---|---|---|---|
36 | Global Circuit Breaker | Protocol-wide pause activated by 3-of-4 multisig | Reject all transfers | 6036 |
37 | Per-Mint Circuit Breaker | >30% price movement in single slot for specific ST22 | Halt mint-specific transfers for 1 hour | 6037 |
38 | Custody Discrepancy Halt | Token supply > custodied shares (oracle verified) | Halt all transfers for affected mint | 6038 |
39 | OFAC Emergency Block | New OFAC SDN list entry matching active wallet | Immediate freeze β no grace period | 6039 |
40 | Oracle Consensus Failure | <2 of 3 oracle feeds available for >5 minutes | Halt affected hook category | 6040 |
41 | Controlled Migration | Emergency contract migration β 4-of-7 multisig + 48h timelock | Allow migration to new program | 6041 |
42 | Regulatory Compliance Override | Law enforcement or regulatory freeze order received | Comply per legal process | 6042 |
Complete Error Code Registry
rust
#[error_code]
pub enum TransferHookError {
// Hook 1: Custody Verification
#[msg("Custody oracle: token supply exceeds custodied shares")]
CustodyDiscrepancy = 6001,
#[msg("Custody oracle: attestation unavailable or stale")]
CustodyOracleUnavailable = 6002,
// Hook 2: OFAC Screening
#[msg("OFAC: sender wallet matches sanctions list")]
SenderSanctioned = 6003,
#[msg("OFAC: receiver wallet matches sanctions list")]
ReceiverSanctioned = 6004,
#[msg("OFAC: oracle data stale β cannot verify sanctions status")]
OfacOracleStale = 6005,
// Hook 3: AML Screening
#[msg("AML: sender risk score exceeds threshold")]
SenderHighRisk = 6006,
// Controls 7β42 (see table above)
InvalidSigner = 6007,
WrongAccountOwner = 6008,
MintMismatch = 6009,
CorruptAccountSize = 6010,
AccountTypeConfusion = 6011,
AccountFrozen = 6012,
DelegationExceeded = 6013,
ZeroTransfer = 6014,
SelfTransfer = 6015,
ArithmeticOverflow = 6016,
AtomicRevert = 6017,
HookOrderViolation = 6018,
StaleAttestation = 6019,
ProbableDuplicate = 6020,
WashTradeDetected = 6021,
ConcentrationLimitExceeded = 6022,
VelocityLimitExceeded = 6023,
TokensLocked = 6024,
LpDrainAttack = 6025,
PriceManipulation = 6026,
CoordinatedTransfer = 6027,
// 6028: reserved
WalletRateLimitExceeded = 6029,
MintRateLimitExceeded = 6030,
OracleQueryRateLimitExceeded = 6031,
InsufficientComputeBudget = 6032,
RentDepleted = 6033,
ProgramVersionMismatch = 6034,
UnrecognizedOracleSigner = 6035,
GlobalCircuitBreaker = 6036,
MintCircuitBreaker = 6037,
CustodyDiscrepancyHalt = 6038,
OfacEmergencyBlock = 6039,
OracleConsensusFailed = 6040,
ControlledMigration = 6041,
RegulatoryFreeze = 6042,
}π¨ INCIDENTIncident RESPONSEResponse ANDand OPERATIONALOperational MONITORING SPECIFICATIONMonitoring
Version: 6.0 | Classification: Critical Infrastructure Operations
Monitoring Stack
Layer | Tool | Metrics Collected | Alert Threshold |
|---|---|---|---|
Solana RPC | Helius Webhooks + custom indexer | Transaction success | Success rate < |
Transfer Hook | On-chain event indexer (Geyser plugin) | Hook rejections by error | >5% rejection rate per code per minute |
Oracle Network | Custom oracle monitor | Attestation | Any oracle > staleness threshold |
CEDEX AMM | Order flow metrics | Fill | LP depth drops >30% in 1 hour |
| Datadog APM | p95 | p95 > |
Smart Contracts | Solana program log indexer | Error code | Any Error 6001 (custody) or 6036 (global halt) |
Incident Severity Classification
Severity | Definition | Response Time | Escalation |
|---|---|---|---|
P0 β Critical | Trading | 15 minutes | Immediate β all hands |
P1 β High | Oracle consensus | 30 minutes | On-call engineer + CTO |
P2 β Medium | Single oracle feed | 2 hours | On-call engineer |
P3 β Low | Non-critical monitoring | Next business day | Ticket |
P0 Incident Runbooks
Runbook P0-A: Custody Discrepancy (Error 6001 Active)
TRIGGER: Hook 1 returning Error 6001 β token supply exceeds custodied shares
IMMEDIATE ACTIONS (within 15 minutes):
1. Confirm error is real β query on-chain token supply vs Empire oracle attestation
2. Contact Empire Stock Transfer emergency line: [redacted β in secure ops runbook]
3. Activate 3-of-5 Compliance Multisig to freeze all ST22 minting
4. Post status update to OTCM Protocol status page
5. Preserve all oracle attestation messages for forensic analysis
RESOLUTION PATH:
A. If oracle error (attestation stale/malformed):
β Force oracle refresh via emergency oracle admin key
β Verify fresh attestation resolves discrepancy
β Lift circuit breaker after 2-of-3 oracle consensus confirmed
B. If real custody gap (unauthorized minting):
β Engage legal counsel immediately
β File regulatory notification within 24 hours
β DO NOT lift circuit breaker until gap fully explained and closed
NEVER DO:
- Do not lift the circuit breaker without resolving the root cause
- Do not communicate cause publicly until forensics completeRunbook P0-B: Global Circuit Breaker Activated (Error 6036)
TRIGGER: 3-of-4 emergency multisig has activated global trading halt
IMMEDIATE ACTIONS (within 15 minutes):
1. Identify which multisig keyholders initiated the activation and why
2. Assess scope β is this protocol-wide or mint-specific?
3. Begin root cause analysis
4. Post status update β "Trading temporarily paused for security review"
RESOLUTION PATH:
A. Threat resolved β 3-of-4 multisig signs deactivation transaction
B. Threat ongoing β maintain halt, engage incident response team
C. False positive β post-mortem required, governance proposal to prevent recurrence
SLA: Global halt must be resolved or publicly explained within 4 hoursRunbook P0-C: Active Smart Contract Exploit
TRIGGER: Anomalous fund movement detected, suspected exploit in progress
IMMEDIATE ACTIONS (within 15 minutes):
1. Activate global circuit breaker immediately (Error 6036)
β Does NOT require root cause β halt first, investigate second
2. Snapshot all affected account states at current slot
3. Engage Halborn/Quantstamp emergency contact for triage support
4. Preserve all transaction signatures for forensic analysis
DO NOT:
- Do not attempt to "patch" on-chain state during active exploit
- Do not communicate exploit details publicly until contained
- Do not lift circuit breaker under any time pressureRTO / RPO Targets
Component | RTO | RPO | Degraded Mode |
|---|---|---|---|
Transfer Hook enforcement | Tied to Solana network | N/A β on-chain, always consistent | No degraded mode β transfers halt |
CEDEX order matching | 30 minutes | Zero β orders queued, not lost | Read-only order book visible |
Oracle network (primary) | 5 minutes | Last confirmed attestation | Secondary oracle takes over |
Investor portal | 1 hour | 5 minutes (session state) | Read-only mode |
API (external) | 2 hours | 15 minutes | Cached responses |
Operational Monitoring Dashboards
PRODUCTION MONITORING STRUCTURE:
Dashboard 1: Protocol Health (always-on)
β--- Transfer Hook success rate (last 5 min, 1 hr, 24 hr)
β--- Error code frequency heatmap
β--- Oracle freshness indicators (all 3 tiers, all types)
β--- Circuit breaker status (green/red per mint + global)
β--- Active trading halts (count + duration)
Dashboard 2: CEDEX Trading (always-on)
β--- 24h volume per ST22 mint
β--- LP depth per pool (absolute + % change)
β--- Price impact per trade (p50, p95, p99)
β--- Graduation pipeline (mints approaching $250K MC)
β--- Fee revenue (real-time)
Dashboard 3: Security Events (always-on, SOC-24/7)
β--- OFAC screening hits (Error 6003/6004)
β--- AML high-risk events (Error 6006)
β--- Velocity limit hits (Error 6023)
β--- Concentration limit hits (Error 6022)
β--- Coordinated transfer flags (Error 6027)π KEYKey CUSTODIANSHIPCustodianship REGISTERRegister
Classification: Institutional Security Documentation | Review Cadence: Quarterly or upon any keyholder change
Privileged Key Inventory
Key | Holder | Custody Type | Rotation Schedule |
|
|---|---|---|---|---|
Freeze Authority | Compliance Multisig (3-of-5) | AWS CloudHSM β dedicated partition | Annual + upon regulatory event |
|
Mint Authority | DISABLED β burned post-mint |
| N/A | Cannot be re-enabled |
Program Upgrade Authority | Admin Multisig (4-of-7) | Ledger hardware β geographically distributed | Semi-annual |
|
Oracle Signing Keys | Per-oracle HSM partitions | AWS CloudHSM (primary) | Quarterly |
|
Treasury SOL Multisig | Treasury Multisig (3-of-5) | Ledger hardware β 5 keyholders | Annual |
|
LP Emergency Override | Emergency Multisig (4-of-7) | Ledger hardware β air-gapped signing | Used only in emergency |
|
Freeze Authority Specification
Holder: Compliance Multisig β 3-of-5 threshold
Permitted Use Cases:
- OFAC SDN list match β mandatory freeze, no discretion
- Active law enforcement hold order β mandatory freeze
- Court-ordered asset freeze β mandatory freeze
- SAR-trigger investigation hold β discretionary, compliance officer approval required
Prohibited Use Cases:
- Competitive, commercial, or operational reasons
- Retaliation or dispute resolution
- Any use not related to regulatory compliance
Audit Requirement: Every freeze action generates an immutable on-chain audit record (Control 28) and a concurrent off-chain report to Empire Stock Transfer within 24 hours.
4-of-7 Admin Multisig Composition Requirements
The 7 keyholders for program upgrade and emergency override authority must satisfy the following distribution requirements to prevent simultaneous compromise:
Requirement | Specification |
|---|---|
Geographic distribution | Minimum 4 distinct countries |
Cloud provider distribution | No more than 2 keyholders on same cloud provider |
Organizational distribution | Minimum 3 keyholders external to Groovy Company, Inc. dba OTCM |
Hardware wallet requirement | All keyholders must use Ledger or equivalent FIPS 140-2 certified device |
Key ceremony requirement | Initial keyholder setup must occur via documented key ceremony with legal witness |
Replacement procedure | Lost keyholder: remaining 4-of-6 must authorize replacement via on-chain governance vote |
Oracle Key Rotation Procedure
Oracle key rotation must occur without creating a gap in Transfer Hook validation. The following zero-downtime rotation procedure is mandatory:
PHASE 1 β Preparation (T-7 days):
1. Generate new Ed25519 keypair in HSM β new_pubkey
2. Submit on-chain governance transaction to ADD new_pubkey to oracle registry
3. Oracle registry accepts both old_pubkey and new_pubkey for 7-day overlap window
PHASE 2 β Dual-Sign Period (T-0 to T+7 days):
4. Oracle begins signing ALL attestations with BOTH old_key and new_key
5. Both signatures included in attestation payload
6. Transfer Hooks accept either signature during overlap window
PHASE 3 β Cutover (T+7 days):
7. Submit on-chain governance transaction to REMOVE old_pubkey from oracle registry
8. Oracle ceases signing with old_key
9. old_key private key material destroyed via HSM key destruction ceremony
10. HSM destruction certificate published on-chain as audit record
PHASE 4 β Verification (T+8 days):
11. Verify 24-hour post-rotation: zero rejected transactions due to signature validation
12. Rotation complete β update Key Custodianship RegisterRotation Window Safety: During Phase 2, if any Transfer Hook rejects a valid transaction due to key mismatch, the oracle immediately reverts to old_key exclusively and the rotation procedure restarts from Phase 1.
π§ͺ SMARTSmart CONTRACTContract TESTTest COVERAGECoverage SPECIFICATIONSpecification
Version: 6.01 | Status: Pre-Mainnet Requirements β Must Be Met Before Launch Certification
Coverage Requirements
All OTCM Protocol smart contract programs must meet the following minimum test coverage thresholds before mainnet deployment certification. Coverage is measured using cargo-tarpaulin for unit tests and a custom integration test harness against a local validator.
Program | Unit Test Coverage | Integration Test Coverage | Fuzz Corpus Size | Status |
|---|---|---|---|---|
TransferHook | β₯ 95% line coverage | All 42 controls tested | β₯ 10,000 inputs | Required pre-launch |
CEDEX AMM | β₯ 92% line coverage | All order types + edge cases | β₯ 50,000 inputs | Required pre-launch |
LiquidityPool (FLP) | β₯ 90% line coverage | Graduation + lock mechanics | β₯ 20,000 inputs | Required pre-launch |
BondingCurve | β₯ 90% line coverage | Price invariant tests | β₯ 30,000 inputs | Required pre-launch |
OracleRegistry | β₯ 95% line coverage | Consensus + replay tests | β₯ 5,000 inputs | Required pre-launch |
IssuersPortal | β₯ 88% line coverage | KYC + accreditation flows | β₯ 5,000 inputs | Required pre-launch |
Mandatory Test Categories
rust
// --- TRANSFER HOOK β Required Test Cases ---
#[cfg(test)]
mod transfer_hook_tests {
// Hook 1 β Custody
fn test_hook1_valid_custody();
fn test_hook1_supply_exceeds_custody(); // Must reject with Error 6001
fn test_hook1_oracle_unavailable(); // Must reject with Error 6002
fn test_hook1_stale_attestation(); // Must reject with Error 6019
fn test_hook1_replay_attack(); // Must reject with Error 6019
// Hook 2 β OFAC
fn test_hook2_clean_sender_receiver();
fn test_hook2_sanctioned_sender(); // Must reject with Error 6003
fn test_hook2_sanctioned_receiver(); // Must reject with Error 6004
fn test_hook2_fuzzy_name_match(); // Near-match must trigger
// Hook 5 β Price Impact
fn test_hook5_within_2pct_limit();
fn test_hook5_exceeds_2pct_limit(); // Must reject with Error 6026
fn test_hook5_twap_unavailable(); // Must reject with Error 6040
// Cross-hook interaction
fn test_all_hooks_pass_valid_transfer();
fn test_atomic_revert_on_hook3_fail(); // Hooks 1-2 pass, Hook 3 fails
fn test_hook_ordering_enforced(); // Out-of-order hook call must fail
// Controls 7β42
fn test_control_7_invalid_signer();
fn test_control_14_zero_amount();
fn test_control_15_self_transfer();
fn test_control_22_concentration_limit();
fn test_control_36_global_circuit_breaker();
// ... one test per control (42 total mandatory)
}
// --- CPMM AMM β Required Test Cases ---
#[cfg(test)]
mod amm_tests {
fn test_swap_invariant_maintained(); // k must not decrease
fn test_u128_no_overflow_at_max_reserves(); // MAX_SAFE_RESERVE Γ MAX_SAFE_RESERVE
fn test_rounding_direction_floor_output();
fn test_fee_calculation_accuracy();
fn test_graduation_atomic_migration();
fn test_lp_tokens_burned_to_dead_address();
fn test_permanent_lock_no_withdrawal(); // Attempt withdrawal must fail
}Fuzz Testing Configuration
toml
# Cargo.toml fuzz configuration
[workspace.metadata.fuzz]
targets = [
"fuzz_transfer_hook", # Fuzz all 42 controls with random account states
"fuzz_cpmm_swap", # Fuzz reserve values from 1 to MAX_SAFE_RESERVE
"fuzz_oracle_attestation",# Fuzz oracle payloads including malformed signatures
"fuzz_bonding_curve", # Fuzz token amounts across full price range
]
minimum_corpus_size = 10_000
max_total_time_seconds = 3_600 # 1 hour minimum per target pre-launch
crash_on_oom = trueDifferential Testing Requirement
The CPMM calculate_swap_output function must be differentially tested against a reference Python implementation to verify that the Rust integer arithmetic produces results within 1 unit of precision of the floating-point reference:
python
# Reference implementation for differential testing
def calculate_swap_output_reference(amount_in, reserve_in, reserve_out, fee_bps):
fee_numerator = 10000 - fee_bps
amount_in_with_fee = amount_in * fee_numerator
numerator = amount_in_with_fee * reserve_out
denominator = reserve_in * 10000 + amount_in_with_fee
return int(numerator / denominator) # floor division matches Rust behaviorπ TRANSFERTransfer HOOKHook INTER-HOOKInter-Hook STATEState MODELModel
Version: 6.01 | Critical: Defines shared account access between hooks
Hook Isolation Guarantee
Each of the six Transfer Hooks is stateless with respect to the others during a single transaction execution. Hooks do NOT write shared mutable state that subsequent hooks read. This eliminates TOCTOU (time-of-check-time-of-use) vulnerabilities between hooks.
HOOK EXECUTION MODEL:
Transaction Input Accounts (immutable snapshot for all hooks)
β
β---βββΊ Hook 1 reads: oracle_cache_pda (readonly), compliance_state (readonly)
β Hook 1 writes: audit_log_pda (append-only)
β
β---βββΊ Hook 2 reads: ofac_oracle_pda (readonly), Β· investor_record (readonly)
β Hook 2 writes: audit_log_pda (append-only)
β
β---βββΊ Hook 3 reads: aml_oracle_pda (readonly), Β· investor_record (readonly)
β Hook 3 writes: audit_log_pda (append-only)
β
β---βββΊ Hook 4 reads: investor_record (readonly), Β· compliance_state (readonly)
β Hook 4 writes: audit_log_pda (append-only)
β
β---βββΊ Hook 5 reads: price_oracle_pda (readonly), Β· lp_pool_state (readonly)
β Hook 5 writes: audit_log_pda (append-only)
β
β---βββΊ Hook 6 reads: lp_pool_state (readonly)
Hook 6
writes: audit_log_pda (append-only)
KEY PROPERTY: All cross-hook reads are from (readonly) accounts.
No hook reads data that a prior hook has written in the same transaction.written.
audit_log_pda is append-only β no hook reads what another hook wrote.
Account Mutability Map Per Hook
Account | Hook 1 | Hook 2 | Hook 3 | Hook 4 | Hook 5 | Hook 6 |
|---|---|---|---|---|---|---|
(custody) | R | β | β | β | β | β |
| β | R | β | β | β | β |
| β | β | R | β | β | β |
| β | β | β | β | R | β |
| β | R | R | R | β | β |
| R | β | β | R | β | β |
| β | β | β | β | R | R |
| W | W | W | W | W | W |
| W | β | β | β | β | β |
R = Read-only access |Β· W = Write access |Β· β = Not accessed
COMPUTEβοΈ UNITCompute BUDGETUnit SPECIFICATIONBudget Specification
Version: 6.01 | Status: Pre-Mainnet Benchmarks Required Before Launch
All OTCM Protocol transactions prepend a ComputeBudgetInstruction::set_compute_unit_limit instruction. Total transaction CU must remain below Solana's 1,400,000 CU hard cap under all conditions.
Component | Operation | Measured CU (avg) | Worst-Case CU |
|---|---|---|---|
Hook 1 | Custody oracle balance read | ~18,000 | 28,000 |
Hook 2 | OFAC fuzzy name match | ~95,000 | 140,000 |
Hook 3 | AML risk score lookup | ~22,000 | 35,000 |
Hook 4 |
| ~14,000 | 20,000 |
Hook 5 | TWAP + 2% impact calculation | ~45,000 | 68,000 |
Hook 6 | LP reserve sufficiency check | ~12,000 | 18,000 |
Base AMM | CPMM swap logic | ~180,000 | 220,000 |
Account validation | Discriminator + owner checks | ~15,000 | 22,000 |
Audit logging | On-chain event emission | ~8,000 | 12,000 |
TOTAL | ~409,000 | ~563,000 (40% of cap) |
Priority Fee Strategy: Dynamic fee computed as base_fee . Transactions resubmitted with escalating fees at 5-second intervals, up to 3 retries before returning xΓ clamp(recent_fee_p75 / base_fee, 1.0, 10.0)TRANSACTION_TIMEOUT.
β οΈ Pre-Launch Requirement: All six Transfer Hook programs must complete CU profiling under worst-case conditions (500-entry OFAC list, minimum LP depth) before mainnet certification.
ORACLE MESSAGE AUTHENTICATION SPECIFICATION
See oracle authentication details in Oracle Fault Tolerance section.
π REENTRANCY PROTECTION β CHECKS-EFFECTS-INTERACTIONS SPECIFICATION
Version: 6.0 | Applies To: All OTCM Anchor programs
Why Reentrancy Matters on Solana
Unlike Ethereum where reentrancy is a well-understood attack vector via fallback functions, Solana's account model creates different reentrancy risks through Cross-Program Invocations (CPIs). A malicious program invoked via CPI can call back into the invoking program before state mutations complete β creating a classic TOCTOU vulnerability. OTCM enforces the Checks-Effects-Interactions (CEI) pattern on every instruction that performs CPIs.
CEI Pattern Enforcement
rust
/// CORRECT β CEI pattern enforced
pub fn process_swap(ctx: Context, amount_in: u64) -> Result<()> {
// --- CHECKS ---
// 1. Validate all account constraints (Anchor #[account] macros)
// 2. Verify oracle data freshness and consensus
// 3. Compute output amount β read-only, no state changes
let amount_out = calculate_swap_output(
amount_in,
ctx.accounts.pool.sol_reserve,
ctx.accounts.pool.token_reserve,
ctx.accounts.pool.fee_bps,
)?;
// Verify circuit breaker β no state write yet
let price_impact = compute_price_impact(amount_in, ctx.accounts.pool.sol_reserve)?;
require!(price_impact <= MAX_PRICE_IMPACT_BPS, AmmError::PriceImpactExceeded);
// --- EFFECTS ---
// 4. Write ALL state mutations BEFORE any CPI calls
// State is fully consistent before any external call can observe it
let pool = &mut ctx.accounts.pool;
pool.sol_reserve = pool.sol_reserve
.checked_add(amount_in)
.ok_or(AmmError::Overflow)?;
pool.token_reserve = pool.token_reserve
.checked_sub(amount_out)
.ok_or(AmmError::Underflow)?;
pool.k_invariant = (pool.sol_reserve as u128)
.checked_mul(pool.token_reserve as u128)
.ok_or(AmmError::Overflow)?;
pool.last_swap_slot = Clock::get()?.slot;
// --- INTERACTIONS ---
// 5. Execute CPIs ONLY after all state mutations are complete
// If CPI calls back into this program, state is already correct
token::transfer(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: ctx.accounts.pool_token_account.to_account_info(),
to: ctx.accounts.user_token_account.to_account_info(),
authority: ctx.accounts.pool_authority.to_account_info(),
},
),
amount_out,
)?;
// Emit audit event last β read-only CPI, no state impact
emit!(SwapExecuted {
user: ctx.accounts.user.key(),
amount_in,
amount_out,
slot: Clock::get()?.slot,
});
Ok(())
}
/// INCORRECT pattern β NEVER use this
/// pub fn process_swap_WRONG(ctx: Context, amount_in: u64) -> Result<()> {
/// let amount_out = calculate_swap_output(...)?;
/// // CPI BEFORE state mutation β reentrancy window open
/// token::transfer(cpi_ctx, amount_out)?; // <-- WRONG
/// pool.sol_reserve += amount_in; // <-- state written after CPI
/// Ok(())
/// }Reentrancy Guard β Mutex Pattern
For instructions where CEI cannot be strictly enforced (e.g. multi-step operations), a per-account mutex is implemented:
rust
#[account]
pub struct PoolState {
// ... other fields ...
/// Reentrancy guard β set to true on entry, false on exit
/// Prevents recursive invocation of pool instructions
pub locked: bool,
}
/// Reentrancy guard macro β wrap any instruction using CPIs
macro_rules! reentrancy_guard {
($account:expr, $body:block) => {{
require!(!$account.locked, AmmError::ReentrantCall);
$account.locked = true;
let result = { $body };
$account.locked = false;
result
}};
}
// Usage in instruction handler:
pub fn complex_operation(ctx: Context) -> Result<()> {
reentrancy_guard!(ctx.accounts.pool, {
// ... instruction body with CPIs ...
Ok(())
})
}CEI Compliance Checklist β Required for Audit Sign-off
Before audit certification, every instruction in every OTCM program must pass the following checklist:
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
π‘οΈ 3.12 Security Audit Status
Audit Program β Pre-Mainnet Certification Gates
All four audit engagements below are hard launch gates. Mainnet deployment is prohibited until every gate is cleared and each firm issues a final report with zero open Critical or High severity findings.
Firm | Scope | Status | Gate Requirement |
|---|---|---|---|
Quantstamp | TransferHook programs (all 42 controls) | Engagement initiated Q1 2026 | Final report β 0 Critical/High open |
Halborn | Oracle | Engagement in progress | Final report β 0 Critical/High open |
OtterSec |
| Preliminary review complete β full audit Q2 2026 | Final report β 0 Critical/High open |
Certora | Formal verification of CPMM invariant ( | Specification complete β proof development Q1 2026 | All specified invariants proved β no counterexamples |
Bug Bounty Program
Active bug bounty required minimum 30 days before mainnet launch.
Severity | Reward | Example Vulnerability |
|---|---|---|
Critical | Up to $100,000 | Transfer Hook |
High | Up to $25,000 | Oracle replay |
Medium | Up to $5,000 | Circuit breaker |
Low | Up to $500 | Gas optimization |
Submissions:Submissions: security@otcm.io β PGP key published at otcm.io/security.txt Scope: All on-chain programs,programs Β· oracle infrastructure,infrastructure Β· CEDEX order matching engine Out of scope: Frontend UI,UI Β· third-party dependencies,dependencies Β· known issues in public tracker
Bug
Bounty:Groovy
$500KCompany,programInc.launchingdbawithOTCMmainnetProtocol---