Skip to content

Contract Architecture ​

Technical deep dive into how SUDIGITAL smart contracts are designed across both chains.

Hybrid Architecture ​

SUDIGITAL uses a minimal on-chain approach -- only financial operations that require trustless execution live on-chain. Everything else runs off-chain in PostgreSQL.

┌─────────────────────────────────────────────────────────────┐
│                      ON-CHAIN                               │
│                                                             │
│  Token operations    Escrow deposits    XP accounts         │
│  Staking/vesting     Winner recording   Claim proofs        │
│  NFT minting         Reward claims      Airdrop claims      │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                      OFF-CHAIN                              │
│                                                             │
│  Mission logic       User profiles      Leaderboards        │
│  Game rules          Participant lists   Error messages     │
│  Analytics           Notifications       Complex validation │
│                                                             │
└─────────────────────────────────────────────────────────────┘

This yields 87% storage cost reduction compared to fully on-chain alternatives.

Claim Proof Pattern ​

The primary interaction pattern across both chains is the backend-signed claim proof:

  1. User requests an action (mint NFT, claim airdrop, record win)
  2. Backend validates eligibility (checks DB, rules, limits)
  3. Backend signs a ClaimProof with its private key (ECDSA for EVM, Ed25519 for Solana)
  4. User submits the signed proof on-chain
  5. Contract verifies the signature matches the trusted backend_authority
  6. Contract executes the operation and increments the user's nonce

ClaimProof Structure ​

FieldEVM TypeSolana TypeDescription
claim_iduint64u64Unique operation ID
claim_typeClaimType (0-3)ClaimType (1-4)Token, Nft, Reward, Airdrop
useraddressPubkeyRecipient
amountuint256u64Token amount
nonceuint64u64Monotonically increasing per user
expiryuint256i64Proof expiration timestamp
signaturebytes[u8; 64]Backend authority signature

Signature message: claim_id || claim_type || user || amount || nonce || expiry

  • EVM: keccak256(abi.encodePacked(...)) + ethSignedMessageHash, recovered via ECDSA ecrecover
  • Solana: Ed25519 signature verified via ed25519_program sysvar instruction introspection

ClaimType values differ between chains:

TypeEVMSolana
Token01
Nft12
Reward23
Airdrop34

Anti-replay: Each user maintains a last_nonce counter. Every proof must have nonce > last_nonce.

EVM Architecture ​

UUPS Proxy Pattern ​

All EVM contracts use OpenZeppelin's UUPS (Universal Upgradeable Proxy Standard):

User ──> Proxy (storage) ──> Implementation (logic)
                │
                └── delegatecall
  • Proxy holds all state (storage slots)
  • Implementation holds all logic
  • Upgrade deploys new implementation, proxy points to it
  • Storage gaps -- all contracts reserve uint256[48-50] __gap for future slots
  • AUTHORIZED_DEPLOYER -- immutable, set in constructor, prevents unauthorized initialization

Module Versions ​

ContractVersionPausableReentrancy Guard
SudigitalToken—Yes (OZ)No
CoreModule0.6.6Yes (OZ)No
StakingModule0.6.6Yes (OZ)Yes (OZ)
VestingModule0.6.6Yes (OZ)Yes (OZ)
MissionModule0.6.6Yes (OZ)Yes (OZ)
NftModule1.2.0No (checks CoreModule)Yes (OZ)
EconomyModule0.6.6Yes (OZ)No
AirdropModule1.0.0Yes (OZ)Yes (custom)

Library Files (10 libraries) ​

LibraryLinesPurpose
Constants.sol87Mission limits, NFT constants, XP thresholds/multipliers
Errors.sol235~80 error codes as string constants (6100-7499)
Events.sol388~50 event definitions across all modules
Roles.sol29Role enum + bit flag utilities
Types.sol198All struct definitions (PlatformConfig, ClaimProof, etc.)
MissionTypes.sol68Mission enums (6 statuses, 6 types, 5 difficulties)
MissionValidator.sol114Creation validation, auto-start, state transitions
SignatureVerifier.sol144ECDSA verification for claim/winner/mint proofs
TokenAllocation.sol92Supply allocation constants (6 decimals)
Security.sol191Rate limiting, circuit breaker, timelock structs/logic

Interface Files (6 interfaces) ​

ICoreModule, IEconomyModule, IMissionModule, INftModule, ISudigitalToken, IVestingModule

Build Configuration ​

Solidity:  ^0.8.28
Optimizer: enabled, 20 runs (dev) / 1,000,000 runs (production)
via_ir:    true
Deps:      OpenZeppelin Contracts Upgradeable

Solana Architecture ​

Anchor Program Structure ​

Each Solana program follows Anchor conventions:

programs/sudigital-{module}/src/
├── lib.rs              — program entrypoint, instruction handlers
├── constants.rs        — module constants
├── errors.rs           — error codes (#[error_code])
├── events.rs           — event definitions (Token only)
├── instructions/       — instruction context structs
├── state/              — account data structures
└── utils/              — helper functions

All 5 programs are version 0.6.6, built with Anchor 1.0.2 + init-if-needed feature.

PDA (Program Derived Address) Seeds ​

AccountSeedsProgram
PlatformConfig["platform_config"]Core
UserClaimState["user_claim_state", user]Core
TokenConfig["token_config"]Token
TokenStats["token_stats"]Token
StakingConfig["staking_config"]Token
StakeAccount["stake", user]Token
VestingAccount["vesting", beneficiary]Token
AirdropEscrow["airdrop_escrow", airdrop_id_le]Token
EscrowToken["escrow_token", airdrop_id_le]Token
EscrowAuthority["escrow_authority", airdrop_id_le]Token
AirdropClaimState["airdrop_claim_state", user, airdrop_id_le]Token
NftMetadata["nft_metadata", nft_id_le]NFT
HolderShare (NFT)["holder_share", nft_id_le, holder]NFT
MissionEscrow["mission_escrow", mission_id_32]Mission
MissionWinner["mission_winner", mission_id_32, winner]Mission
HolderPool["holder_pool", mission_id_32]Mission
HolderShare (Mission)["holder_share", mission_id_32, holder]Mission
CircuitBreaker["circuit_breaker"]Mission
XpAccount["xp_account", user]Economy

Account Sizes ​

AccountSize (bytes)Program
PlatformConfig90Core
UserClaimState53Core
XpAccount76Economy
NftMetadata49NFT
HolderShare (NFT)46NFT
MissionEscrow130Mission
MissionWinner92Mission
HolderPool74Mission
HolderShare (Mission)90Mission
CircuitBreaker60Mission

Build Configuration ​

Anchor:          1.0.2
Package Manager: yarn
Deploy Order:    token -> economy -> core -> nft -> mission

Error Code Ranges ​

Both chains use consistent error code ranges:

ModuleSource RangeAnchor IDL Range (+6000)
Core100-1996100-6199
Economy200-2996200-6299
NFT300-4996300-6499
Mission500-7996500-6799
Token800-9996800-6999
Staking1000-11997000-7199
Vesting1200-13997200-7399
Airdrop1400-14997400-7499

Error messages are stored off-chain in PostgreSQL for cost optimization. On-chain errors contain only the numeric code as a string (e.g., "6100" for MathOverflow).

Event System ​

Solana: Only the Token program emits events (11 event types: StakeEvent, UnstakeEvent, RewardsClaimedEvent, VestingCreatedEvent, VestedReleasedEvent, VestingRevokedEvent, StakingConfigUpdatedEvent, MintEvent, BurnEvent, AirdropCreatedEvent, AirdropClaimedEvent). Core, NFT, Mission, and Economy programs emit no events.

EVM: All modules emit events via the shared Events.sol library (~50 event types covering missions, XP, rewards, NFTs, admin, security, platform config, escrow, burns, and more).

Cross-Program Invocations (Solana) ​

CallerReads FromCPIs Into
Token (claim_airdrop)Core PlatformConfig—
NFT (mint_nft)Core PlatformConfig, UserClaimStateCore process_claim
MissionCore PlatformConfig—
Economy (award_xp)Core WALLET_CREATOR constant—

Core is the root program and does not CPI into anything. All programs use Core's WALLET_CREATOR constant for access control on admin operations.

One backend. Three products. One token.