Three Anchor-based Solana programs (Token B, Token C, Token D) were deployed to devnet
and subjected to automated static analysis via Sec3 X-Ray, manual code review, and
end-to-end devnet integration testing. All three programs passed with zero outstanding
critical or high-severity findings. One medium finding was identified by Sec3 X-Ray in
Token C (unvalidated AccountInfo in recover_identity) and has
been remediated. A total of 80 on-chain instructions across the three programs were reviewed.
| Program | Devnet Address | Instructions | Sec3 X-Ray | Status |
|---|---|---|---|---|
| Token B | 8d6hMwqgodjZGG4Wkc1J2UyDZRwXFdcgKBaueA2VUuu | 15 | 0 Issues | Pass |
| Token C | d3SpEuALT8eJFz41hLG7TMaB3a8ZXdRoVJJ74UxtQVT | 34 | 1 Fixed | Pass |
| Token D | Dg4jqcsvZ2yfxu2uHL5EFF4YcPn8HX1MmrvVh4eorSTa | 31 | 0 Issues | Pass |
| 1 | initialize_token | Mint + TransferFeeConfig init |
| 2 | distribute_pda_wallets | Phase 1: 4 PDA vaults |
| 3 | distribute_external_wallets | Phase 2: 5 Squads multisigs |
| 4 | finalize_distribution | Revoke mint authority forever |
| 5 | revenue_burn | Stage-aware burn from reserve |
| 6 | advance_stage | Revenue threshold + burn unmined |
| 7 | disburse_mining_rewards | Stage-limited mining payouts |
| 8 | process_collected_fees | Harvest + 4-way fee split |
| 9 | disburse_token_d_ops | Token D operations vault |
| 10 | disburse_token_c_ops | Token C operations vault |
| 11 | burn_tokens | Manual burn by Burner role |
| 12 | pause_token | Emergency pause |
| 13 | unpause_token | Resume operations |
| 14 | grant_role | RBAC role assignment |
| 15 | revoke_role | RBAC role removal |
6 PDAs serve as autonomous authorities for isolated vault operations:
has_role()CannotRevokeOwner guard)MaxRolesExceeded)has_one = authority constraint on TokenConfig for distribution instructionsSigner<'info>checked_add / checked_subu128 intermediates to prevent overflow (total_fees * 7000 can exceed u64)total - burn - dev - treasury to avoid rounding loss.reload() before burninganchor_spl::token_2022 typed wrappersCpiContext::new_with_signer used for all PDA-authorized operationsProgram<'info, Token2022> (Anchor type check)constraint = revenue_burn_ata.key() == token_config.revenue_burn_vaultconstraint = fee_staging_ata.owner == fee_collector_authority.key()finalize_distribution — no further minting possible| 1–2 | initialize_config, update_config | Program authority management |
| 3 | initialize_identity | Soul mint + freeze + guardian setup |
| 4–6 | update_biometric, add_credential, remove_credential | Identity data management |
| 7–9 | update_trust_score, lock_identity, unlock_identity | Trust & security controls |
| 10 | recover_identity | Guardian-mediated soul transfer |
| 11–12 | grant_document_access, revoke_document_access | Token D document ACL |
| 13 | add_certification | Certification management |
| 14–15 | stake_token_b, unstake_token_b | Token B staking via CPI |
| 16–17 | subscribe_to_service, verify_subscription | Service subscription layer |
| 18 | verify_age_proof | SPARK-verified age attestation |
| 19 | update_auth_key | Auth key rotation |
| 20 | process_nft_payment | NFT-authenticated payment |
| 21–22 | generate_quantum_totp, verify_quantum_totp | PQ-TOTP with Dilithium |
| 23 | risk_based_authentication | Behavioral risk scoring |
| 24–26 | initiate_multi_sig_recovery, approve_recovery, complete_recovery | Multi-guardian recovery flow |
| 27–28 | verify_zkp_data_access, grant_compliant_data_access | ZKP-gated data sharing |
| 29 | real_time_recovery | Biometric + guardian emergency recovery |
| 30 | grant_travel_access | Travel/event credential |
| 31–34 | register_mfa_factor, update_mfa_threshold, remove_mfa_factor, process_mfa_authentication | Multi-factor auth system |
Soul mint PDA uses identity account as seed, ensuring 1:1 binding between identity and its soulbound token. Freeze authority is the mint PDA itself, enforcing non-transferability.
ProgramConfig PDA stores trust_oracle, security_authority, risk_authority addressesupdate_trust_score requires trust_oracle signer matching configlock_identity / unlock_identity require security_authority signerrisk_based_authentication requires risk_authority signerhas_one = owner + Signeridentity.guardians.contains(&guardian.key())initiate_multi_sig_recovery requires initiator to be a guardian (prevents arbitrary initiation).push(): guardians (5), credentials (5), document_access (20), certifications (10), auth_factors (10), compliant_data_access (10), payment_history (20), service_subscriptions (10)CapacityExceeded error prevents unbounded account growth and potential DoSchecked_add / checked_sub with ArithmeticOverflow errors| 1 | initialize_service_config | Service vault config PDA |
| 2–8 | initialize_document .. reactivate_document | Document CRUD + NFT mint |
| 9–11 | create_collection, add_to_collection, remove_from_collection | Collection management |
| 12 | set_encryption_status | Encryption toggle |
| 13–14 | create_share_link, deactivate_share_link | Shareable links |
| 15–16 | create_template, create_from_template | Document templates |
| 17 | pay_for_document_service | Token B payment processing |
| 18–20 | grant_document_role, revoke_document_role, set_payment_tier | Role & tier management |
| 21–22 | verify_token_b_access, check_document_access | Access verification |
| 23–25 | create_private_nft, transfer_private_nft, spend_private_nft | ZK private NFTs (SPARK) |
| 26–28 | stake_document, unstake_document, claim_token_b_rewards | Document staking |
| 29 | claim_token_d_milestone | Milestone Token D minting |
| 30 | update_mining_rewards | Daily reward accrual |
| 31 | report_fraud | Fraud reporting (event only) |
Document mint PDA uses document account key as seed, creating a unique NFT per document. Service config PDA prevents payment redirection attacks by storing the authorized vault address.
grant_document_role / revoke_document_role require Admin role via has_role()set_payment_tier requires Admin (prevents free tier bypass by owners)has_one = ownerowner_hint == sender.key() constraintServiceConfig PDA (InvalidServiceVault)TOKEN_B_PROGRAM_ID constant checkkeccak(commitment || similarity_bps || "range_proof")keccak(commitment || similarity_bps || match_result || "threshold_proof")keccak(commitment || owner || timestamp)u128 intermediates: (time * rate) / SECONDS_PER_DAYu128 for (rewards * percentage) / 100checked_add / checked_sub with CalculationErroroverflow-checks = true enabled in [profile.release]| Program | Critical | High | Medium | Low | Info | Status |
|---|---|---|---|---|---|---|
| Token B | 0 | 0 | 0 | 0 | 0 | Clean |
| Token C | 0 | 0 | 1 | 0 | 0 | Fixed |
| Token D | 0 | 0 | 0 | 0 | 0 | Clean |
| Severity | Medium |
| Category | Missing Account Validation |
| Location | RecoverIdentity context — new_owner field |
| Description | The new_owner AccountInfo in the RecoverIdentity account context was not validated against the new_owner: Pubkey instruction argument, allowing a guardian to pass a different account than the declared new owner. |
| Fix Applied | Added constraint in instruction handler: require!(ctx.accounts.new_owner.key() == new_owner, IdentityError::InvalidOwner). Also added old_owner constraint: constraint = old_owner.key() == identity.owner. |
| Verified | Sec3 X-Ray re-scan returned 0 findings after fix. |
The following security hardening measures were identified during audit and applied prior to the devnet deployment under review.
new_owner account validation against instruction argument and
old_owner validation against identity.owner. Without this,
an attacker could substitute a different account for the new owner while the guardian
approved the legitimate pubkey.
[profile.release]. This provides a safety net for any arithmetic
operations that may have been missed by checked_* methods. Token B
inherits this from the workspace-level Cargo.toml.
Signer<'info>
on the authority account. Token B uses RBAC role checks; Token C uses ProgramConfig PDA
authority matching; Token D uses has_one = owner and Admin role checks.
initiate_multi_sig_recovery now requires the initiator to be a registered
guardian: require!(identity.guardians.contains(&ctx.accounts.initiator.key())).
Previously, any account could initiate recovery, creating a DoS vector where arbitrary
parties could flood identities with recovery requests.
ServiceConfig PDA that stores the authorized service vault address.
pay_for_document_service validates the vault against config:
constraint = service_vault.key() == service_config.service_vault.
Prevents payment redirection to attacker-controlled accounts.
report_fraud now only emits an event — it does not directly create
milestone rewards. This prevents attackers from self-reporting fake fraud to mint
unlimited Token D milestone rewards. Milestone creation requires admin confirmation
via a separate instruction.
constraint = payment_source.owner == owner.key() (Token C) and
constraint = token_b_source.owner == payer.key() (Token D).
Prevents spending from another user's token account.
InvokeTokenB context validates the Token B program account key
against the hardcoded TOKEN_B_PROGRAM_ID constant:
constraint = token_b_program.key() == TOKEN_B_PROGRAM_ID.
Prevents CPI to a malicious program masquerading as Token B.
All end-to-end integration tests executed successfully on Solana devnet against the deployed programs.
| # | Step | Result |
|---|---|---|
| 1 | Initialize Token B mint with TransferFeeConfig | Pass |
| 2 | Phase 1: Distribute to 4 PDA vaults | Pass |
| 3 | Phase 2: Distribute to 5 external wallets | Pass |
| 4 | Finalize distribution & revoke mint authority | Pass |
| 5 | Verify total supply = 21,000,000,000 tokens | Pass |
| 6 | Revenue burn from stage 1 reserve | Pass |
| 7 | Fee harvest + 4-way split (70/15/12.5/2.5) | Pass |
| 8 | Mining pool disbursement (stage-limited) | Pass |
| 9 | Stage advance with unmined token burn | Pass |
| # | Step | Result |
|---|---|---|
| 1 | Initialize soulbound identity with guardians | Pass |
| 2 | Update biometric hash | Pass |
| 3 | Add WebAuthn credential | Pass |
| 4 | Guardian-mediated identity recovery | Pass |
| 5 | Lock and unlock identity via security authority | Pass |
| # | Step | Result |
|---|---|---|
| 1 | Initialize document with ownership NFT mint | Pass |
| 2 | Grant access and verify permissions | Pass |
| 3 | Create private NFT with SPARK proof | Pass |
| 4 | Transfer private NFT (spend old + mint new) | Pass |
| 5 | Document staking + reward accrual | Pass |
initiate_multi_sig_recovery calls for the same identity to prevent recovery spam.close instructions for expired share links, completed recovery states, and spent private NFTs to allow lamport recovery.process_collected_fees uses u128 intermediates. Consider formal verification or property-based testing (proptest) that burn + dev + treasury + staker == total for all inputs.[profile.release] overflow-checks = true at the workspace level (currently in Token C and Token D Cargo.toml only). Token B may inherit this from the parent workspace but should be explicitly declared.Document.version_history has no explicit cap. For documents with many updates, consider limiting to the last N versions to prevent unbounded growth.keccak(commitment || owner || timestamp). Consider additionally binding to a nonce provided by the user for stronger replay protection.Risk categories rated by likelihood (rows) and impact (columns). Current residual risk after all fixes applied.
| Negligible | Minor | Moderate | Major | Critical | |
|---|---|---|---|---|---|
| Almost Certain | |||||
| Likely | |||||
| Possible | |||||
| Unlikely | |||||
| Rare |
| Risk Category | Program | Likelihood | Impact | Residual | Mitigation |
|---|---|---|---|---|---|
| Unauthorized minting | Token B | Rare | Critical | Low | Mint authority permanently revoked on-chain |
| Supply manipulation | Token B | Rare | Major | Low | All arithmetic uses checked_*, u128 intermediates for fees |
| Admin key compromise | All | Unlikely | Major | Medium | Recommend Squads multisig (not yet deployed on mainnet) |
| Identity theft via recovery | Token C | Unlikely | Major | Low | DAO removed, guardian-only, initiator must be guardian, account validated |
| Payment redirection | Token D | Rare | Moderate | Low | ServiceConfig PDA validates vault address |
| Private NFT double-spend | Token D | Rare | Minor | Low | is_spent flag + nullifier derivation |
| ZK proof forgery | Token C/D | Unlikely | Moderate | Low | SPARK proof binds commitment to identity; range/threshold hashes verified |
| Dilithium bypass | Token C | Possible | Minor | Medium | Structural validation only; off-chain attestation service required for production |
| Milestone reward abuse | Token D | Unlikely | Minor | Low | Fraud report decoupled from reward minting; admin confirmation required |
| Fee split rounding error | Token B | Rare | Negligible | Low | Staker rewards = remainder (total - burn - dev - treasury) |
programs/token-b/programs/token_b/src/lib.rs (1,810 lines)programs/token-c/src/lib.rs (2,362 lines)programs/token-d/src/lib.rs (2,063 lines)This security audit report was prepared based on source code review, Sec3 X-Ray automated static analysis, and Solana devnet integration testing of the three H33 programs. This report reflects the state of the programs as deployed on devnet on February 17, 2026.
H33 Security Audit — v2.0
February 17, 2026
Programs reviewed: Token B (8d6hMwq...), Token C (d3SpEuA...), Token D (Dg4jqcs...)
Framework: Anchor 0.31.0 · Solana SDK 2.3 · Network: Devnet