Skip to content

Solana Programs

SUDIGITAL deploys 5 Anchor programs on Solana — live on mainnet-beta as of July 18, 2026 (also deployed on devnet). All programs are version 0.6.6, built with Anchor 1.0.2.

Program IDs (Devnet + Mainnet-beta)

The same program IDs are used on both networks (deployed from the same keypairs → same declare_id!).

ProgramProgram IDStatus
sudigital-core4fguJHsq7LtJQSSqEmMTtgoSKrbyNT2G8JG741nwCQv5Devnet + Mainnet ✅
sudigital-tokenEkd3idiCkcx7wWZC6U8NVfTumkcHbSoSAwTWsaCKsCDhDevnet + Mainnet ✅
sudigital-nft3SmbvyXJgY9v2cnwxNXob3d178rTxaTDQxxWgVYeTi62Devnet + Mainnet ✅
sudigital-mission9SUUfJPZ7hqtkVL9ig22UkFnb1uZZhYDASbfCGLdb3vxDevnet + Mainnet ✅
sudigital-economy3nHRQq7VWivtjdThgdHnKhebboJJiMfMhWv88s32abMbDevnet + Mainnet ✅

Devnet: initial deploy Feb 12, 2026; fresh deploy June 16, 2026 (Anchor 1.0.2 migration, new keypairs).

Mainnet-beta: deployed 2026-07-18, 10:24:11–10:25:03 UTC (all 5 programs in ~52 s, ~9.7 SOL; slots 433,671,022–433,671,152), initialized via scripts/mainnet/init.ts (~0.024 SOL). Program upgrade authority (all 5) transferred to the Squads multisig vault 3EkpyVQAzFSynkxCfDJKUp2YdUEPG2H63hZZU4vsbwFx.

Deploy Order

1. sudigital-token    — SPL token, staking, vesting, airdrops
2. sudigital-economy  — XP system, levels, streaks
3. sudigital-core     — Platform config, claim proofs
4. sudigital-nft      — NFT minting, fractional shares
5. sudigital-mission  — Escrow, winners, rewards

Post-deploy (mainnet, done Jul 18 2026): program upgrade authority (all 5) and the full 1B supply are held by the Squads multisig vault (3Ekp…). Creator/admin (WALLET_CREATOR), treasury, pool/ecosystem, and backend authority remain the 6xWB wallet. Migrating the WALLET_CREATOR admin to Squads (via a program upgrade) is a planned follow-up.

Wallet Addresses

For mainnet-beta launch, one wallet (6xWB…) serves as creator, treasury, fee/pool receiver, ecosystem fund, and backend authority. This is intentional for launch; governance moves to Squads post-launch.

WalletDevnetMainnet-beta
Creator (Authority)2VNKqH3aL3xQkZGZ7wj3F6GWZ5E6VSovs3svLesaxwCo6xWB9KGjXfMFS7DPGojgXFSuBgLzXnFahtuAbUJLbNMC
Pool / Platform9zz4mScwD22kC7qt4QPytRM9fgM48as5d2Fwug1M8Xdj6xWB9KGjXfMFS7DPGojgXFSuBgLzXnFahtuAbUJLbNMC
Ecosystem FundSame as Pool (devnet)Same as Pool (6xWB…)
TreasuryEconomy authority PDA6xWB9KGjXfMFS7DPGojgXFSuBgLzXnFahtuAbUJLbNMC
Backend Authoritydevnet signer6xWB9KGjXfMFS7DPGojgXFSuBgLzXnFahtuAbUJLbNMC
Squads Multisig3Wt7pitDH9ufwpsYwnbbYhYWXZqsGWNqmZRUgHQXa5Z4planned (migrate authority post-launch)
Squads Vault3EkpyVQAzFSynkxCfDJKUp2YdUEPG2H63hZZU4vsbwFxplanned

Token Mint Addresses

AddressNetworkDescription
AwweVVcQ9uubfZoJt6dCE4E64CywU2i2gG57FrtCE8jkMainnet-betaSUDIGITAL SPL mint (init.ts, July 18 2026). Decimals 6. Fixed supply 1,000,000,000 minted (cap-enforced).
E45nzKpUvL6JFthpqX7UgQWQ4yVAzhcm9XdpNYZLerSGDevnetSUDIGITAL SPL mint (init.ts, devnet)

Mint + freeze authority = tokenConfig PDA 8niT3VAVsZYBFs51zpSAnYwakjSBk6kMnU92hKaZzbdw. Minting is capped at TOTAL_SUPPLY (1B) in mint_supply, so supply is permanently fixed. The full 1B is custodied in the Squads vault token account 7qFhmHep97tdLyanyMPvt5YNKn1Mx1pZwZZBFGBzXh5Y.

Mainnet Deployment Runbook (executed 2026-07-18, ~10:24 UTC)

Reproducible steps used for the mainnet-beta launch. Deploy ~9.7 SOL, init ~0.024 SOL.

Pre-flight (verify before building):

  1. Confirm declare_id! == target/deploy/*-keypair.json == Anchor.toml == @sudi/solana client program IDs.
  2. programs/*/src/constants.rs — set WALLET_CREATOR, WALLET_POOL, WALLET_ECOSYSTEM, WALLET_PLATFORM to the mainnet wallet (6xWB…). These are baked into the binary; the defaults were devnet.
  3. scripts/mainnet/init.ts — ensure PROGRAM_IDS match the real deployed IDs (not stale placeholders).

Deploy + initialize:

bash
# wallet (creator/deployer) + mainnet RPC
export SOLANA_RPC_URL="https://<mainnet-rpc>"     # Helius/QuickNode
# ~/.config/solana/sudigital-mainnet.json = the 6xWB keypair, funded ~11 SOL

anchor build                                       # bakes in mainnet constants
./scripts/mainnet/deploy.sh                        # deploys token->economy->core->nft->mission (~9.7 SOL)
bun run scripts/mainnet/init.ts --confirm          # platform_config, token_config, mint, metadata, staking, circuit_breaker
bun run scripts/mainnet/mint-supply.ts --confirm   # mints fixed 1B to treasury (signed by WALLET_CREATOR)

Custody + governance (recommended — multisig):

bash
# move the 1B supply into the Squads vault
spl-token transfer <MINT> 1000000000 <SQUADS_VAULT> --fund-recipient --allow-unfunded-recipient \
  --owner ~/.config/solana/sudigital-mainnet.json --url "$SOLANA_RPC_URL"

# transfer program upgrade authority (all 5) to the Squads vault
for ID in <5 program ids>; do
  solana program set-upgrade-authority $ID --new-upgrade-authority <SQUADS_VAULT> \
    --skip-new-upgrade-authority-signer-check -k ~/.config/solana/sudigital-mainnet.json -u "$SOLANA_RPC_URL"
done

Verify on-chain: solana program show <ID> (authority + executable), spl-token supply <MINT> (= 1000000000), spl-token balance --address <VAULT_ATA> (= 1000000000).

Post-deploy: update packages/solana/src/constants.ts MAINNET_ADDRESSES (mint + treasury) and app env (SOLANA_NETWORK=mainnet, SOLANA_BACKEND_PRIVATE_KEY = the backend_authority); rotate the RPC key. For backend-signer hygiene, see Backend-Signer Rotation below.

Backend-Signer Rotation

platform_config.backend_authority is the key the online backend uses to sign reward claims (process_claim / process_claim_on_behalf) and mission writes (record_winner, set_holder_shares). It should be a dedicated, low-privilege hot key — never WALLET_CREATOR (6xWB…), which also controls treasury, mint, and program-upgrade authority and must stay cold / offline.

Why rotate. The signing key lives on an always-online server, so it is the most exposed key. Keeping it separate from WALLET_CREATOR means a server compromise only leaks a key that can sign claims (rate-/logic-limited on-chain) — not treasury / mint / admin. Rotate on a schedule and immediately on suspected compromise.

How it works. update_platform_config(backend_authority?, is_active?) (sudigital-core, WALLET_CREATOR-only) overwrites backend_authority in place. It is repeatable / unlimited — the hot key can never rotate itself (fails Unauthorized); only the cold creator can appoint a successor.

Rotate with the script:

bash
# DRY RUN (prints old -> new; warns if the signer != on-chain creator)
SOLANA_RPC_URL="https://<rpc>" bun run blockchains/solana/scripts/update-platform-config.ts \
  --wallet ~/.config/solana/<creator>.json \
  --backend-authority <NEW_HOT_ADDRESS|new-hot-keypair.json>

# EXECUTE — add --confirm; reads the account back and verifies backend_authority changed
SOLANA_RPC_URL="https://<rpc>" bun run blockchains/solana/scripts/update-platform-config.ts \
  --wallet ~/.config/solana/<creator>.json \
  --backend-authority <NEW_HOT_ADDRESS|new-hot-keypair.json> --confirm
  • --backend-authority accepts a base58 address or a keypair .json path (its pubkey is derived).
  • --is-active true|false optionally toggles the platform on/off in the same tx.
  • After success: set the server's SOLANA_BACKEND_PRIVATE_KEY to the new hot key and restart API + worker. Revoke a leaked key by rotating again, or pause with --is-active false.

⚠️ WALLET_CREATOR differs per network (baked-in constant). Sign with the creator that matches the deployed binary, or the tx fails NotCreator:

ClusterWALLET_CREATORSigner keypair
devnet2VNK…~/.config/solana/sudigital-dev.json
mainnet6xWB…~/.config/solana/sudigital-mainnet.json

Key storage.

EnvironmentHot-key storage
devnet / local.envSOLANA_BACKEND_PRIVATE_KEY (throwaway; never promoted to prod)
production (Cloudflare Worker)wrangler secret put SOLANA_BACKEND_PRIVATE_KEY (read from the env binding)

WALLET_CREATOR (6xWB…) is never stored in .env or a Cloudflare secret — it only signs update_platform_config from an offline machine. When moving to production, generate a fresh hot key for the Cloudflare secret and rotate to it (the devnet .env key stays devnet-only).

Airdrop Escrow (devnet + mainnet)

create-airdrop.ts creates + funds an on-chain AirdropEscrow (the program-controlled reward pool the airdrop-processor distributes from) via create_airdrop_escrow. It is network-agnostic — it targets whatever SOLANA_RPC_URL points at and reads the mint from token_config. The signer must be token_config.mint_authority (= WALLET_CREATOR), and tokens are pulled from the creator's ATA (fund it first).

Signer resolution: --wallet <path>SOLANA_CREATOR_KEYPAIRSOLANA_MAINNET_WALLET~/.config/solana/sudigital-mainnet.json (mainnet default).

bash
# DEVNET (creator = sudigital-dev.json) — for the SUDI-106 A6 claim rehearsal
SOLANA_RPC_URL="https://devnet.helius-rpc.com/?api-key=<devnet-key>" \
SOLANA_AIRDROP_ID=1 SOLANA_AIRDROP_AMOUNT=1000 \
bun run scripts/mainnet/create-airdrop.ts \
  --wallet ~/.config/solana/sudigital-dev.json --confirm

# MAINNET (creator = sudigital-mainnet.json, 6xWB) — default wallet, omit --wallet
SOLANA_RPC_URL="https://<mainnet-rpc>" \
SOLANA_AIRDROP_ID=1 SOLANA_AIRDROP_AMOUNT=400000000 \
bun run scripts/mainnet/create-airdrop.ts --confirm

Dry run by default (omit --confirm) — it prints the escrow accounts + source ATA balance and warns if the creator ATA holds less than the fund amount. Afterward, set the worker's SOLANA_AIRDROP_ID + SOLANA_AIRDROP_TOKEN_MINT to match.

Program Details

sudigital-core (5 instructions)

Platform configuration, backend authority management, and claim proof processing. Core is the root program -- all other programs depend on it for access control via WALLET_CREATOR.

Instructions:

InstructionAccessDescription
initialize_platform_config(backend_authority)WALLET_CREATOR onlySet trusted signer
update_platform_config(backend_authority?, is_active?)WALLET_CREATOR onlyUpdate config fields
initialize_user_claim_state()Any user (for self)Create nonce tracker
process_claim(claim_proof)Any user (for self)User-initiated claim with Ed25519 verification
process_claim_on_behalf(claim_proof)Backend authority onlyBackend-initiated claim (e.g., Stripe auto-airdrop)

Key difference: process_claim requires the user to be the signer. process_claim_on_behalf lets the backend authority sign on behalf of a beneficiary who is NOT a signer -- the backend pays the transaction fee.

Accounts:

AccountSeedsSizeFields
PlatformConfig["platform_config"]90 bytescreator, backend_authority, is_active, created_at, updated_at, bump
UserClaimState["user_claim_state", user]53 bytesuser, last_nonce, total_claims, bump

Enums:

EnumValues
RoleMemer(1), Worker(2), Player(3), Trader(4), Builder(5), Owner(6)
ClaimTypeToken(1), Nft(2), Reward(3), Airdrop(4)

Events: None. Core emits no events.

CPIs: None. Core is the root program.

sudigital-token (18 instructions)

SPL token operations, staking, vesting, and airdrop distribution. The largest program with the most functionality.

Token Instructions (4):

InstructionAccessDescription
initialize_token(decimals)WALLET_CREATORCreate SPL mint + TokenConfig + TokenStats
create_token_metadata(name, symbol, uri)WALLET_CREATORCreate Metaplex metadata PDA (raw CPI)
mint_supply(amount)WALLET_CREATORMint tokens to treasury (capped at TOTAL_SUPPLY)
burn_tokens(amount)Any holderBurn own tokens

Staking Instructions (4):

InstructionAccessDescription
initialize_staking_config()WALLET_CREATORCreate config with Phase 1 defaults
update_staking_config(params)Config authorityModify staking parameters
stake_tokens(amount, lock_duration)Any userStake with lock period
unstake_tokens(amount)StakerWithdraw after lock (60s grace period)
claim_staking_rewards()StakerClaim accumulated rewards

Vesting Instructions (3):

InstructionAccessDescription
create_vesting(amount, start, cliff, duration)WALLET_CREATORCreate schedule
release_vested()BeneficiaryClaim vested tokens
revoke_vesting()WALLET_CREATORSet revoked flag (unvested tokens stay in treasury)

Airdrop Instructions (6):

InstructionAccessDescription
create_airdrop_escrow(airdrop_id, amount)Mint authorityCreate and fund SPL token escrow
refill_airdrop_escrow(amount)Escrow creatorAdd more tokens
toggle_airdrop()Escrow creatorActivate/deactivate
close_airdrop_escrow()Escrow creatorClose and return remaining
claim_airdrop(claim_proof)Any user (with proof)Claim with Ed25519 signature verification (user pays gas)
claim_airdrop_on_behalf(claim_proof)Backend authorityAuto-airdrop: backend signs, pays gas, creates user ATA

Accounts:

AccountSeedsKey Fields
TokenConfig["token_config"]mint, treasury, mint_authority, total_minted, total_burned, is_active
TokenStats["token_stats"]total_staked, total_rewards_distributed, total_burned, unique_holders
StakingConfig["staking_config"]is_enabled, annual_emission_cap, max_stake_per_wallet, total_staked
StakeAccount["stake", user]staked_amount, locked_until, reward_rate, rewards_accumulated
VestingAccount["vesting", beneficiary]total_amount, released_amount, cliff_duration, revoked
AirdropEscrow["airdrop_escrow", airdrop_id_le]total_amount, claimed_amount, is_active
EscrowToken["escrow_token", airdrop_id_le]SPL token account (PDA authority)
EscrowAuthority["escrow_authority", airdrop_id_le]PDA signer for escrow transfers
AirdropClaimState["airdrop_claim_state", user, airdrop_id_le]last_nonce, total_claims

Events (11 types -- only program with events):

EventKey Fields
StakeEventuser, amount, lock_duration, locked_until, apy_bps, timestamp
UnstakeEventuser, amount, remaining_staked, timestamp
RewardsClaimedEventuser, amount, total_claimed, timestamp
VestingCreatedEventbeneficiary, total_amount, start_time, cliff_end, vesting_end
VestedReleasedEventbeneficiary, amount, total_released, remaining
VestingRevokedEventbeneficiary, released_before_revoke, revoked_amount
StakingConfigUpdatedEventauthority, staking_enabled, caps, timestamp
MintEventauthority, recipient, amount, total_supply, timestamp
BurnEventuser, amount, total_supply, timestamp
AirdropCreatedEventairdrop_id, creator, amount, mint, timestamp
AirdropClaimedEventairdrop_id, claimer, amount, timestamp

sudigital-nft (2 instructions)

NFT minting with fractional share ownership. Uses SUDIGITAL SPL token for payment.

Instructions:

InstructionAccessDescription
mint_nft(claim_proof, nft_id, role, max_shares)Any user (with proof)Mint NFT, CPIs into Core process_claim
validate_nft_holder(nft_id, min_shares)Any (view-like)Check holder eligibility

First Mint (founder == default):

  • 25% of price burned via SPL token::burn
  • 75% transferred to platform wallet
  • Creates NftMetadata (founder = minter, shares = 1)
  • Creates HolderShare (is_founder = true)

Subsequent Mint:

  • Uses calculate_revenue_split_with_burn(price): 1% founder, 1% creator, ~73.5% platform, ~24.5% burned
  • Increments existing NftMetadata.current_shares
  • Creates or increments HolderShare

Accounts:

AccountSeedsSizeKey Fields
NftMetadata["nft_metadata", nft_id_le]49 bytesnft_id, role, founder, current_shares, max_shares, founder_active
HolderShare["holder_share", nft_id_le, holder]46 bytesnft_id, holder, shares, is_founder

Revenue Split Constants:

ConstantValue
FOUNDER_FEE_BPS100 (1%)
CREATOR_FEE_BPS100 (1%)
PLATFORM_FEE_BPS9,800 (98%)
BURN_SHARE_OF_PLATFORM_BPS2,500 (25% of platform)

Events: None. NFT program emits no events.

CPI: Calls sudigital_core::cpi::process_claim to validate claim proof before minting.

sudigital-mission (10 instructions)

Mission escrow management and reward distribution. Uses SOL (native lamports) for escrow, not SPL tokens.

Instructions:

InstructionAccessDescription
deposit_escrow(mission_id, total_amount)Creator or Backend authorityFund escrow with SOL
record_winner(mission_id, winner, rank, reward_amount, proof)Backend authorityRecord winner (runtime signer verification)
claim_reward(mission_id)WinnerClaim with 80/15/5 split via lamport manipulation
refund_escrow(mission_id)Depositor or WALLET_CREATORRefund remaining SOL
set_holder_share(mission_id, holder, shares)Backend authoritySet individual holder share
batch_set_holder_shares(mission_id, holders)Backend authorityBatch set (max 10)
finalize_holder_pool(mission_id)Backend authorityLock holder distribution
claim_holder_reward(mission_id)HolderClaim proportional reward
initialize_circuit_breaker()Any (init, once only)Create circuit breaker account
reset_circuit_breaker()Circuit breaker authorityManual reset

Reward Distribution (in claim_reward):

PoolBPS%Transfer Method
Winner8,00080%Raw lamport manipulation
Holder Pool1,50015%Into HolderPool PDA
Ecosystem Fund5005%To WALLET_ECOSYSTEM

Key difference from EVM: Solana mission escrow uses native SOL, not SPL tokens. The payment_token field is always system_program::ID.

Winner recording: Does NOT use on-chain Ed25519 verification. Instead, relies on Solana runtime's native signer verification -- the backend_authority must be a Signer, which proves identity.

Accounts:

AccountSeedsSizeKey Fields
MissionEscrow["mission_escrow", mission_id]130 bytestotal_amount, claimed_amount, is_active
MissionWinner["mission_winner", mission_id, winner]92 bytesrank, reward_amount, claimed
HolderPool["holder_pool", mission_id]74 bytestotal_amount, total_shares, finalized
HolderShare["holder_share", mission_id, holder]90 bytesshares, claimed
CircuitBreaker["circuit_breaker"]60 bytesfailure_count, is_open, cooldown_seconds

Constants:

ConstantValue
MAX_RANK100
PROOF_VALIDITY_SECONDS300 (defined but not enforced in code)
MAX_BATCH_SIZE10

Events: None. Mission program emits no events.

sudigital-economy (3 instructions)

XP system, leveling, and daily engagement streaks.

Instructions:

InstructionAccessDescription
initialize_xp()Any user (for self)Create XP account (sets initial login)
award_xp(base_xp)WALLET_CREATOR onlyAward XP with level multiplier
check_in()Any user (for self)Daily check-in (10 XP base + streak bonus)

Check-in Details:

  • Min interval: 82,800 seconds (23 hours)
  • Base XP: 10 (BASE_CHECK_IN_XP)
  • Streak bonus: CONSECUTIVE_BONUS_XP (5) * (consecutive_days - 1) per check-in
  • Streak continues if time since last login <= 90,000 seconds (25 hours)
  • Streak resets to 1 if gap > 25 hours

Note: Solana awards 10 base XP per check-in, EVM awards 20. Both award 150 XP at 7-day streak and 1,000 XP at 30-day streak.

Accounts:

AccountSeedsSizeKey Fields
XpAccount["xp_account", user]76 bytestotal_xp (u32), current_level (u8), consecutive_login_days (u16), last_login (i64)

XP Level Thresholds (11 levels):

LevelNameThresholdMultiplier
0Newcomer01.0x (10,000 BPS)
1Holder1011.05x
2Stacker5011.1x
3Whale1,5011.15x
4Diamond Hands3,5011.2x
5Alpha7,0011.3x
6Titan12,0011.4x
7Mogul20,0011.5x
8Legend35,0011.7x
9Sovereign60,0011.9x
10Super Wealthy100,0012.0x (20,000 BPS)

Events: None. Economy program emits no events. Placeholder modules: instructions/reward/ and instructions/refund/ are empty placeholders.

Token Constants

ConstantValue
TOKEN_DECIMALS6
TOTAL_SUPPLY1,000,000,000,000,000 (raw)
ECOSYSTEM_ALLOCATION400,000,000,000,000 (40%)
TREASURY_ALLOCATION250,000,000,000,000 (25%)
TEAM_ALLOCATION150,000,000,000,000 (15%)
SALE_ALLOCATION100,000,000,000,000 (10%)
LIQUIDITY_ALLOCATION150,000,000,000,000 (15%)

Staking Constants

ConstantValue
MAX_STAKE_PER_WALLET100,000,000,000 (100K tokens)
ANNUAL_EMISSION_CAP10,000,000,000,000 (10M tokens)
MIN_STAKE_AMOUNT1,000,000 (1 token)
LOCK_GRACE_PERIOD60 seconds
MIN_HOLDERS_FOR_STAKING1,111
MIN_TVL_FOR_STAKING_USD$111,111

Time Constants

ConstantValue
SECONDS_PER_DAY86,400
SECONDS_PER_YEAR31,536,000
VESTING_CLIFF_DURATION31,536,000 (1 year)
VESTING_TOTAL_DURATION126,144,000 (4 years)

Error Codes

ModuleSource RangeIDL RangeKey Errors
Core100-1996100-6199Unauthorized(6110), ProofExpired(6120), PlatformNotActive(6130)
Economy200-2996200-6299InvalidXpAmount(6211), AlreadyCheckedInToday(6230)
NFT300-4996300-6499MaxSharesReached(6312), InsufficientPayment(6331)
Mission500-7996500-6799EscrowNotActive(6500), CircuitBreakerOpen(6560)
Token800-9996800-6999SupplyExceeded(6810), ProgramPaused(6840)
Staking1000-11997000-7199StakingDisabled(7004), ExceedsMaxStake(7005)
Vesting1200-13997200-7399CliffNotPassed(7201), VestingRevoked(7202)
Airdrop1400-14997400-7499AirdropNotActive(7400), InvalidProof(7401)

One backend. Three products. One token.