Section 3: SPL Token-2022 Transfer Hooks — Layer 2
|
OTCM PROTOCOL Comprehensive Technical Whitepaper — Version 7.0 ST22 Digital Securities Platform | March 2026 | Groovy Company, Inc. dba OTCM Protocol |
Section 3: SPL Token-2022 Transfer Hooks — Layer 2
The complete technical specification for OTCM Protocol's 42-control Transfer Hook security architecture — the foundational compliance enforcement mechanism that makes regulatory bypass structurally impossible.
|
Metric |
Value |
|
Document reference |
OTCM-TH-SPEC-001 v2.0 (aligned with Whitepaper V7.0) |
|
Total security controls |
42 |
|
Transfer coverage |
100% — every ST22 transfer invokes all 42 controls |
|
Bypass possibility |
Zero — enforced at SPL Token-2022 program level |
|
Target validation latency |
< 1,000ms (parallel execution) |
|
Compute unit budget |
< 5,000 CU per validation |
|
Implementation language |
Rust / Anchor framework |
|
Immutability |
Transfer Hook cannot be removed or disabled after mint creation |
|
V7 change vs V6 |
Control 24 updated: vesting schedule removed; Rule 144 / Reg S holding period enforcement added |
3.1 The Alesia Doctrine — Compliance by Encirclement
The Transfer Hook architecture embodies what OTCM Protocol calls the Alesia Doctrine: the principle that compliance should be enforced through structural encirclement rather than trust. In traditional securities markets, compliance depends on intermediaries — broker-dealers, clearinghouses, and regulators — acting in good faith. These actors can fail, be compromised, or simply be absent in the OTC microcap context where infrastructure has been abandoned.
OTCM Protocol's Transfer Hook architecture eliminates the dependency on good-faith intermediary behavior by embedding compliance enforcement in the token transfer primitive itself. Every ST22 token transfer — regardless of which wallet, front-end, or trading venue initiates it — must pass through all 42 controls before the transaction executes on-chain. There is no path around this enforcement. There is no administrative override. There is no whitelist for trusted parties. The controls execute identically for every participant on every transfer, including OTCM Protocol itself.
3.1.1 Why Application-Layer Compliance Fails
The January 28, 2026 Joint Staff Statement on Tokenized Securities explicitly identified the compliance gap that results when securities compliance is implemented at the application layer rather than the token standard level. When compliance is enforced by an issuer's portal or a specific trading venue, any participant who routes around that portal or venue bypasses all compliance controls. This is the architectural vulnerability that defines Category 2 (Third-Party Sponsored) tokenization — and it is precisely what the Transfer Hook architecture eliminates.
|
Compliance Location |
Vulnerability |
OTCM Protocol Approach |
|
Application layer (portal) |
Any alternative front-end bypasses all controls |
Layer 2 enforcement — no front-end can bypass |
|
Trading venue (exchange) |
Any DEX that doesn't enforce compliance disables all controls |
CEDEX exclusive — only venue that preserves all 42 hooks |
|
Issuer policy |
Policy can be changed, ignored, or overridden |
Immutable smart contract — policy is code, code is law |
|
Third-party custodian |
Custodian can withdraw backing assets, creating de-peg risk |
Transfer Hook Control 1 verifies custody on every transfer |
|
Transfer Hook (Layer 2) |
Cannot be bypassed — any failure reverts entire transaction |
This is the OTCM approach — compliance at the primitive |
3.2 Architecture
3.2.1 Transaction Flow
The Transfer Hook is invoked by the SPL Token-2022 program on every ST22 token transfer via Cross-Program Invocation (CPI). This invocation is mandatory at the protocol level — it cannot be disabled by the issuer, by OTCM Protocol, or by any trading venue. The six-step flow is as follows:
• Step 1 — Transfer initiation — User initiates transfer through any entry point: CEDEX order execution, hardware wallet, mobile wallet, or programmatic call
• Step 2 — Token-2022 receives instruction — SPL Token-2022 program receives the transfer instruction from the initiating party
• Step 3 — Transfer Hook invoked via CPI — Token-2022 automatically invokes OTCM's Transfer Hook program via Cross-Program Invocation. This step is mandatory and cannot be skipped
• Step 4 — All 42 controls execute — The Transfer Hook validates the transaction against all 42 security controls in parallel and sequential groups. Oracle data from Layer 6 is consumed in real-time
• Step 5 — Atomic result — If ANY control fails: the entire transaction reverts atomically with a specific error code. If ALL controls pass: execution proceeds to Layer 1 settlement
• Step 6 — Settlement and audit — Passing transactions settle on Solana. An immutable audit record of all control results is written on-chain for every transfer
3.2.2 Program Structure
The Transfer Hook program is implemented in Rust using the Anchor framework. The program structure follows strict separation of concerns, with each component having a well-defined responsibility:
|
programs/ └── transfer-hook/ └── src/ ├── lib.rs // Program entrypoint ├── instructions/ │ ├── mod.rs │ ├── initialize.rs // SecurityConfig initialization │ └── transfer_hook.rs // Main hook logic (42 controls) ├── state/ │ ├── mod.rs │ ├── security_config.rs // Security parameters per mint │ ├── circuit_breaker.rs // Circuit breaker state │ └── holding_period.rs // Rule 144 / Reg S enforcement (V7) ├── errors.rs // Custom error codes (6001–6042) └── utils/ ├── validation.rs // Validation helpers └── math.rs // u128 overflow-safe arithmetic |
3.2.3 Account Architecture
The Transfer Hook utilizes Program Derived Addresses (PDAs) to store security configuration and state for each token mint. One SecurityConfig account is created per ST22 Security Token at the time of minting, establishing the security parameters that govern all transfers of that token for its entire existence.
|
#[account] pub struct SecurityConfig { pub mint: Pubkey, // Associated token mint pub authority: Pubkey, // 5-of-9 multi-sig admin pub max_wallet_percent: u16, // 499 = 4.99% (basis points) pub circuit_breaker_threshold: u16, // 3000 = 30% daily volume pub circuit_breaker_cooldown: i64, // 86400 = 24 hours pub circuit_breaker_triggered: bool, // Current CB state pub circuit_breaker_triggered_at: i64, // CB trigger timestamp pub reference_price: u64, // TWAP baseline pub holding_period_config: HoldingPeriodConfig, // V7: Rule 144/Reg S pub volume_tracker: VolumeTracker, // 24h rolling window pub blacklisted_wallets: Vec<Pubkey>, // OFAC-blocked addresses pub is_paused: bool, // Emergency pause pub bump: u8, // PDA bump seed } |
3.2.4 V7 Architecture Change: HoldingPeriodConfig
In Version 7.0, the VestingConfig account structure used in V6 has been replaced by HoldingPeriodConfig. This reflects the change from the 5-tranche issuer vesting model (where 60% of tokens were withheld from issuers over 30 months) to the Reg D / Reg S issuance model (where 100% of tokens are distributed to accredited investors, who hold for the Rule 144 or Reg S compliance period before trading).
|
// V7 REPLACEMENT FOR VestingConfig #[account] pub struct HoldingPeriodAccount { pub version: u8, // Schema version — always 7 pub beneficiary: Pubkey, // Investor wallet address pub mint: Pubkey, // ST22 mint address pub purchase_timestamp: i64, // Unix timestamp at token delivery pub jurisdiction: Jurisdiction, // US or NonUS pub holding_period_secs: i64, // 15_778_800 (US) | 31_536_000 (non-US) pub is_locked: bool, // True until holding period elapses pub bump: u8, // PDA bump seed }
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)] pub enum Jurisdiction { US, // Rule 144 — 6-month holding period NonUS, // Regulation S — 12-month distribution compliance period }
// Constants pub const RULE_144_HOLDING_SECS: i64 = 15_778_800; // 6 months pub const REG_S_COMPLIANCE_SECS: i64 = 31_536_000; // 12 months |
3.3 The 42 Security Controls
The 42 controls are organized into six categories. Every control executes on every transfer — there are no exceptions, no bypass mechanisms, and no administrative overrides. The categories are not sequential phases; most controls execute in parallel. The sequential dependency exists only within the critical path (Controls 1→2→3→4→24).
|
Category 1: Custody & Asset Backing Verification · 9 controls (1–7, 38, 40) controls Verify at every transfer that circulating token supply never exceeds Series M preferred shares held in Empire custody |
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
1 |
Custody Oracle Check |
Circulating supply would exceed Empire-custodied share count |
Reject — Error 6001 (CustodyDiscrepancy) |
6001 |
|
2 |
Oracle Availability |
Empire custody feed unavailable or attestation > 400ms stale |
Reject — Error 6002 (CustodyOracleUnavailable) |
6002 |
|
3 |
Supply Integrity |
Total supply inconsistency detected across mint accounts |
Reject — atomic revert |
6007 |
|
4 |
Locked Balance |
Transfer would draw from locked (holding-period) balance |
Reject — Error 6024 per Control 24 |
6024 |
|
5 |
Zero Balance Guard |
Transfer from zero-balance account or dust amount |
Reject — zero transfer guard |
6014 |
|
6 |
Precision Validation |
Decimal precision exceeds mint-defined limit |
Reject — precision error |
6016 |
|
7 |
Supply Integrity Cross-Check |
Secondary oracle cross-reference disagrees with primary |
Circuit breaker — halt transfers for affected mint |
6038 |
|
38 |
Custody Discrepancy Halt |
Token supply confirmed > custodied shares (oracle verified) |
Halt all transfers for affected mint indefinitely |
6038 |
|
40 |
Oracle Consensus Failure |
< 2 of 3 oracle feeds available for > 5 minutes |
Halt affected hook category |
6040 |
|
Category 2: Sanctions & AML Compliance · 4 controls (8–11) controls OFAC/SDN real-time screening and AML risk scoring on every transfer — not just at onboarding |
Controls 8–11 implement continuous sanctions and AML compliance. Empire Stock Transfer performs these checks at investor onboarding — Controls 8–11 ensure they are re-applied on every subsequent transfer. A wallet that was clean at onboarding but later added to the OFAC SDN list is blocked immediately on its next transfer attempt.
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
8 |
OFAC Sender Screen |
Sender wallet matches OFAC SDN list (updated hourly) |
Reject permanently — Error 6003 (SenderSanctioned) |
6003 |
|
9 |
OFAC Receiver Screen |
Receiver wallet matches OFAC SDN list |
Reject permanently — Error 6004 (ReceiverSanctioned) |
6004 |
|
10 |
OFAC Oracle Staleness |
OFAC SDN feed not updated within 90 minutes |
Reject — stale oracle, Error 6005 |
6005 |
|
11 |
AML Risk Score |
ML risk score for sender or receiver exceeds 70/100 threshold |
Reject — Error 6006 (SenderHighRisk). Score 31–70: flag for compliance review |
6006 |
|
// Control 11: AML Risk Scoring — three-tier system pub fn check_aml_risk(risk_score: u8) -> Result<AMLDecision> { match risk_score { 0..=30 => Ok(AMLDecision::Approve), 31..=70 => Ok(AMLDecision::EnhancedReview), // flag, don't reject 71..=100 => Err(TransferHookError::SenderHighRisk.into()), _ => Err(TransferHookError::InvalidRiskScore.into()), } } |
|
Category 3: Investor Eligibility & Holding Period Enforcement · 8 controls (12–19) controls Accreditation verification, KYB entity checks, and Rule 144 / Reg S holding period enforcement on every transfer |
Category 3 is the most significant change in the V7 Transfer Hook specification. In V6, this category was called 'Vesting Enforcement' and enforced the 5-tranche issuer vesting schedule over 30 months. In V7, the vesting schedule has been removed. Control 24 (formerly the vesting enforcement control) now enforces the Rule 144 and Regulation S mandatory holding periods for accredited investors. The control number is retained for error code continuity; the underlying logic is completely rewritten.
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
12 |
Accreditation Check |
Buyer wallet not in Empire's verified accredited investor registry |
Reject — accreditation required. Error 6004 |
6004 |
|
13 |
KYC Status Check |
Buyer or seller KYC status expired or flagged in Empire system |
Reject — re-verification required |
6004 |
|
14 |
KYB Entity Check |
Entity investor UBO verification lapsed or flagged |
Reject — KYB re-verification required |
6004 |
|
15 |
Wallet Registration |
Destination wallet not registered in Empire Master Securityholder File |
Reject — unregistered wallet cannot receive ST22 tokens |
6004 |
|
16 |
Redemption Eligibility |
Redemption transaction: KYC completion and accreditation not current |
Reject — Error 6004 (KYCInvalid) |
6004 |
|
17 |
Jurisdiction Flag |
Wallet jurisdiction flag missing or invalid |
Reject — jurisdiction cannot be determined |
6024 |
|
18 |
Reg S US Person Check |
Non-US wallet attempting US market transaction during compliance period |
Reject — Reg S restriction |
6024 |
|
24 |
Holding Period Lock |
Purchase timestamp + holding_period_secs > current_timestamp |
Reject — Error 6024 (TokensLocked). US: 6-month Rule 144. Non-US: 12-month Reg S |
6024 |
3.3.1 Control 24 Deep Dive — Rule 144 / Reg S On-Chain Enforcement
Control 24 is the mechanism that makes OTCM Protocol's issuance model legally compliant without relying on investor self-discipline or post-hoc regulatory action. It is the on-chain expression of the mandatory holding periods that securities law imposes on restricted security holders.
|
// Control 24: Holding Period Enforcement pub fn check_holding_period( ctx: Context<TransferHook>, transfer_amount: u64, ) -> Result<()> { let holding = &ctx.accounts.holding_period_account; let current_ts = Clock::get()?.unix_timestamp;
// Check if holding period has elapsed let required_period = match holding.jurisdiction { Jurisdiction::US => RULE_144_HOLDING_SECS, // 15_778_800s (6 months) Jurisdiction::NonUS => REG_S_COMPLIANCE_SECS, // 31_536_000s (12 months) };
let elapsed = current_ts - holding.purchase_timestamp;
require!( elapsed >= required_period, TransferHookError::TokensLocked // Error 6024 );
Ok(()) } |
Key properties of Control 24 in V7:
• Purchase timestamp is immutable — Set by Empire Stock Transfer at token delivery and recorded on-chain. Cannot be altered by any party after it is set.
• Jurisdiction determines holding period — US investors (Reg D 506(c)) hold 6 months under Rule 144. Non-US investors (Reg S) hold 12 months under the Regulation S distribution compliance period. Determined by Empire's KYC/KYB determination at onboarding.
• No grace period — The check is exact to the second. A transfer attempt at 6 months minus 1 second is rejected identically to a transfer attempt at day 1.
• No administrative override — No wallet, no key, no multisig, and no DAO vote can override Control 24. The holding period is enforced by the immutable Transfer Hook program.
• Automatic clearance — Once the holding period elapses, Control 24 clears on the next transfer attempt with no action required from any party.
• Volume limits still apply after clearance — After Control 24 clears, the remaining 41 controls still apply. Investors can trade but are subject to the 4.99% wallet concentration limit, 1% daily sell limit, price impact circuit breaker, and all other protections.
|
Category 4: Market Integrity & Position Limits · 9 controls (20–28) controls Wallet concentration limits, circuit breakers, volume halts, and price impact controls |
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
20 |
Max Wallet Limit |
Post-transfer balance would exceed 4.99% of total supply for destination |
Reject — whale concentration limit. Error: WalletLimitExceeded |
6020 |
|
21 |
Pre-Transfer Balance |
Source wallet has insufficient transferable balance (excluding locked) |
Reject — insufficient balance |
6008 |
|
22 |
Zero-Amount Guard |
Transfer amount = 0 |
Reject — zero transfer |
6014 |
|
23 |
Self-Transfer Guard |
Source wallet = destination wallet |
Reject — self transfer |
6015 |
|
25 |
Price Impact Limit |
Transaction would cause > 2% price deviation from TWAP |
Reject — Error 6006 circuit breaker trigger |
6006 |
|
26 |
TWAP Oracle Integrity |
TWAP feed stale or deviation > 10% from secondary price feed |
Reject — stale oracle, cannot enforce price impact |
6019 |
|
27 |
Volume Halt |
Single wallet attempts to sell > 30% of circulating supply in 24 hours |
24-hour trading suspension for that wallet |
6037 |
|
28 |
Circuit Breaker |
> 30% price movement in single Solana slot for affected ST22 |
Halt mint-specific transfers for 1 hour |
6037 |
|
36 |
Global Circuit Breaker |
Protocol-wide pause activated by 3-of-4 multisig |
Reject all ST22 transfers platform-wide |
6036 |
|
// Control 20: Max Wallet Limit (4.99%) pub fn check_wallet_limit( destination_balance: u64, transfer_amount: u64, total_supply: u64, config: &SecurityConfig, ) -> Result<()> { let new_balance = destination_balance .checked_add(transfer_amount) .ok_or(TransferHookError::ArithmeticOverflow)?;
let max_allowed = (total_supply as u128) .checked_mul(config.max_wallet_percent as u128) // 499 bps .ok_or(TransferHookError::ArithmeticOverflow)? .checked_div(10_000) .ok_or(TransferHookError::ArithmeticOverflow)? as u64;
require!(new_balance <= max_allowed, TransferHookError::WalletLimitExceeded); Ok(()) } |
|
Category 5: CEI Pattern & Reentrancy Prevention · 9 controls (29–35, 41, 42) controls Checks-Effects-Interactions enforcement preventing reentrancy and CPI privilege escalation attacks |
Category 5 enforces the Checks-Effects-Interactions (CEI) pattern across all Cross-Program Invocations in the Transfer Hook execution chain. This prevents reentrancy attacks — a class of exploit that has drained hundreds of millions of dollars from DeFi protocols by calling back into a contract before state updates complete. In OTCM's context, a reentrancy attack could theoretically allow an attacker to transfer tokens multiple times before balance state is updated.
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
29 |
Signer Validation |
Expected signer(s) not present in transaction |
Reject — invalid signer. Error 6007 |
6007 |
|
30 |
Account Owner Check |
Account not owned by expected program |
Reject — wrong account owner. Error 6008 |
6008 |
|
31 |
Mint Consistency |
Source/destination token accounts reference different mints |
Reject — mint mismatch. Error 6009 |
6009 |
|
32 |
Account Size Validation |
Account data length < minimum expected struct size |
Reject — corrupt account. Error 6010 |
6010 |
|
33 |
Discriminator Check |
Account discriminator ≠ expected 8-byte Anchor prefix |
Reject — type confusion. Error 6011 |
6011 |
|
34 |
Token Account State |
Source or destination account in Frozen state |
Reject — account frozen. Error 6012 |
6012 |
|
35 |
Delegate Scope |
Delegated transfer amount > approved delegation amount |
Reject — delegation exceeded. Error 6013 |
6013 |
|
41 |
Controlled Migration |
Emergency contract migration: 4-of-7 multisig + 48h timelock required |
Allow migration only with full governance approval |
6041 |
|
42 |
Regulatory Compliance Override |
Law enforcement or SEC regulatory freeze order received |
Comply per legal process — targeted freeze only |
6042 |
|
Category 6: Transaction Integrity & Audit · 13 controls (43–55 mapped as 3, 5, 6, 16–19, 32–35, 37, 39) controls Arithmetic safety, atomic reversion guarantees, compute budget enforcement, and immutable audit log writes |
Note on numbering: The Transfer Hook technical specification numbers controls 1–42 with some controls mapped to operational sub-functions. The following table presents the transaction integrity and audit controls with their operational identifiers for reference. All execute on every transfer.
|
# |
Control Name |
Trigger Condition |
Enforcement Action |
Error |
|
16 |
Arithmetic Overflow Guard |
Any u64 arithmetic would overflow |
Reject — overflow detected. All arithmetic uses u128 intermediate |
6016 |
|
17 |
Atomic Revert Guarantee |
Any prior control in chain has failed |
Full atomic rollback — no partial state changes persist |
6017 |
|
18 |
Hook Order Validation |
Controls not executing in defined sequence |
Reject — hook order violation |
6018 |
|
19 |
Duplicate Detection |
Highly similar transaction signature seen within 1 slot |
Reject — probable duplicate |
6019 |
|
32 |
Compute Budget Enforcement |
Remaining CU < 50,000 before Hook 6 |
Reject — insufficient compute budget |
6032 |
|
33 |
Account Rent Validation |
Any account below rent-exempt minimum |
Reject — rent depleted |
6033 |
|
34 |
Program Version Check |
Caller program version < minimum compatible |
Reject — version mismatch |
6034 |
|
35 |
Key Rotation Compliance |
Oracle signer key not in current oracle registry |
Reject — unrecognized signer |
6035 |
|
37 |
Per-Mint Circuit Breaker |
> 30% price movement in single slot |
Halt mint-specific transfers 1 hour |
6037 |
|
39 |
OFAC Emergency Block |
New OFAC SDN entry matching active wallet |
Immediate freeze — no grace period |
6039 |
3.4 Complete Error Code Registry
Every control failure produces a specific error code enabling precise diagnosis and compliance reporting. The following is the authoritative error code registry for V7:
|
#[error_code] pub enum TransferHookError { // Custody & Asset Backing #[msg("Custody oracle: token supply exceeds custodied shares")] CustodyDiscrepancy = 6001, #[msg("Custody oracle: attestation unavailable or stale (>400ms)")] CustodyOracleUnavailable = 6002,
// Sanctions & AML #[msg("OFAC: sender wallet matches SDN list")] SenderSanctioned = 6003, #[msg("OFAC: receiver wallet matches SDN list")] ReceiverSanctioned = 6004, #[msg("OFAC: oracle data stale — cannot verify sanctions status")] OfacOracleStale = 6005, #[msg("AML: sender or receiver risk score exceeds 70/100")] SenderHighRisk = 6006,
// Investor Eligibility #[msg("KYC/Accreditation: buyer not verified accredited investor")] KYCInvalid = 6004, #[msg("Holding period: tokens locked — Rule 144 / Reg S not satisfied")] TokensLocked = 6024, // V7: replaces VestingLocked from V6
// Market Integrity #[msg("Wallet limit: destination would exceed 4.99% of supply")] WalletLimitExceeded = 6020, #[msg("Circuit breaker: price impact exceeds 2% TWAP deviation")] CircuitBreakerTriggered = 6006, #[msg("Volume halt: single wallet exceeds 30% daily sell limit")] DailySellLimitExceeded = 6037,
// CEI & Integrity 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,
// Emergency GlobalCircuitBreaker = 6036, PerMintCircuitBreaker = 6037, CustodyDiscrepancyHalt = 6038, OFACEmergencyBlock = 6039, OracleConsensusFail = 6040, ControlledMigration = 6041, RegulatoryOverride = 6042, } |
3.5 Main Transfer Hook Entry Point
The main Transfer Hook entry point coordinates all 42 controls through a structured validation sequence. Critical path controls (1, 8, 11, 12, 24) execute sequentially — each must pass before the next runs. All other controls execute in parallel groups to minimize total latency.
|
#[program] pub mod transfer_hook { use super::*;
pub fn transfer_hook(ctx: Context<TransferHook>, amount: u64) -> Result<()> { let config = &ctx.accounts.security_config;
// ── CRITICAL PATH: Sequential ───────────────────────── // Control 1: Custody verification (Empire oracle) verify_custody_oracle(&ctx.accounts.custody_oracle, amount, config)?;
// Controls 8-10: OFAC sanctions screening check_ofac_sender(&ctx.accounts.sender, config)?; check_ofac_receiver(&ctx.accounts.receiver, config)?; check_ofac_oracle_freshness(&ctx.accounts.ofac_oracle)?;
// Control 11: AML risk scoring check_aml_risk(ctx.accounts.aml_oracle.sender_score)?;
// Controls 12-18: Investor eligibility & holding period check_accreditation(&ctx.accounts.receiver, config)?; check_holding_period(&ctx.accounts.holding_period_account)?; // Control 24
// ── PARALLEL: Market integrity, CEI, Audit ─────────── check_wallet_limit(amount, config)?; // Control 20 check_price_impact(amount, config)?; // Control 25 check_volume_halt(amount, config)?; // Control 27 check_circuit_breaker(config)?; // Control 28 validate_cei_pattern(&ctx.accounts)?; // Controls 29-35 enforce_arithmetic_safety(amount)?; // Controls 16-19 // ... remaining controls 36-42 (emergency + audit) ...
// ── AUDIT: Write immutable on-chain compliance record ─ emit!(TransferValidated { mint: ctx.accounts.mint.key(), from: ctx.accounts.source.key(), to: ctx.accounts.destination.key(), amount, timestamp: Clock::get()?.unix_timestamp, all_controls_passed: true, });
Ok(()) } } |
3.6 Immutability Guarantee
The Transfer Hook cannot be removed or disabled after mint creation. This is a property of the SPL Token-2022 standard: once a token mint is created with a Transfer Hook extension, the hook program address is permanently stored in the mint account. The Token-2022 program will invoke that hook on every transfer for the entire existence of the token.
OTCM Protocol's Transfer Hook program upgrade authority is held by a 5-of-9 multi-signature wallet with geographically distributed key holders. All upgrades require a 24-hour timelock period during which the upgrade can be cancelled through DAO governance. Critically, even an upgrade cannot remove the 42 security controls — the controls are defined in the program logic, and any upgrade that would materially weaken investor protections requires a DAO vote that passes the 66.67% supermajority threshold and satisfies the account schema compatibility attestation requirement.
|
Property |
Mechanism |
Who Controls It |
|
Hook address permanence |
SPL Token-2022 stores hook address in mint account permanently |
No one — Token-2022 program level, cannot be changed |
|
Hook invocation |
Token-2022 automatically calls hook via CPI on every transfer |
No one — mandatory at protocol level |
|
Program upgrade authority |
5-of-9 multi-sig with 24-hour timelock |
5 of 9 designated key holders (geographically distributed) |
|
Security control immutability |
Controls in immutable on-chain program — outside DAO governance scope |
Cannot be weakened by any party including OTCM Protocol |
|
Parameter adjustments |
DAO vote with 66.67% supermajority, 48-hour timelock, audit attestation |
OTCM Security Token stakers |
|
V7 Change Summary — Transfer Hook Architecture The only architectural change in V7 vs V6 for Section 3 is Control 24. In V6, Control 24 enforced the 5-tranche issuer vesting schedule (20% unlocking at token minting, graduation, 6/12/18 months post-graduation). In V7, Control 24 enforces the Rule 144 holding period (6 months for US accredited investors) and the Regulation S distribution compliance period (12 months for non-US investors). The VestingConfig account structure is replaced by HoldingPeriodAccount. All other controls (1–23, 25–42) are unchanged from V6. Error code 6024 is retained for continuity with the Technical Specification document OTCM-TH-SPEC-001. |
|
Groovy Company, Inc. dba OTCM Protocol | CIK: 1499275 | Version 7.0 | March 2026 | Confidential |