Sol.College

Full lesson

Confidential-Creator-Support

Support creators on Solana where everyone can see that you support them — and nobody can see how much. Token-2022 confidential transfers, devnet demo.

What it does

From the project README:

Recurring support for creators — journalists, activists, independent media — on Solana. The **fact** of support is public and verifiable on chain; the **amount** of every contribution is encrypted (Token-2022 confidential transfers) and readable only by the supporter, the creator and the holder of the audit key.
Original
ConfidentialCreator/Confidential-Creator-Support
SuperStack fork
Blockchains/Confidential-Creator-Support @ d6f1aa7
Stars
0
Last push (original)
2026-10-26
Languages
TypeScript, Rust, Shell, HTML, CSS, JavaScript
Detected stack
@solana/kitAnchor
Categories
Consumer & SocialInfrastructure & Tooling
Licence
Apache-2.0
Homepage
https://confidentialcreator.github.io/Confidential-Creator-Support/

Architecture

248 source files. By extension: .ts ×204, .rs ×26, .tsx ×17, .js ×1.

Top-level directorySource files
apps110
packages77
tools35
programs26

On-chain programs: programs/ccsupport

Dapp: devnet deploy pending

⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/confidential-creator-support/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.

Key code, explained

Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.

ConfidentialCreator/Confidential-Creator-Support · programs/ccsupport/src/instructions/pledge.rs L1–24 @ d6f1aa7 · Apache-2.0 · original
use anchor_lang::prelude::*;use anchor_spl::associated_token::get_associated_token_address_with_program_id;use anchor_spl::token_2022; use crate::errors::CcsError;use crate::events::Pledged;use crate::introspect::{find_confidential_transfer, ExpectedTransfer};use crate::periods::{extend_expiry, MAX_PERIODS};use crate::state::{Config, Creator, Pledge}; #[derive(Accounts)]pub struct MakePledge<'info> {    #[account(mut)]    pub supporter: Signer<'info>,    #[account(seeds = [Config::SEED], bump = config.bump)]    pub config: Account<'info, Config>,    #[account(        mut,        seeds = [Creator::SEED, creator.wallet.as_ref()],        bump = creator.bump,    )]    pub creator: Account<'info, Creator>,    #[account(        init_if_needed,
  1. Line 3 use anchor_spl::token_2022;: Targets the Token-2022 program instead of the original Token Program.
  2. Line 14 pub supporter: Signer<'info>,: Requires this account to have signed the transaction.
  3. Line 15 #[account(seeds = [Config::SEED], bump = config.bump)]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 15 #[account(seeds = [Config::SEED], bump = config.bump)]: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  5. Line 24 init_if_needed,: Creates the account only if it doesn't exist. Watch for re-initialisation bugs.
ConfidentialCreator/Confidential-Creator-Support · programs/ccsupport/src/instructions/register_creator.rs L1–24 @ d6f1aa7 · Apache-2.0 · original
use anchor_lang::prelude::*; use crate::errors::CcsError;use crate::events::CreatorRegistered;use crate::state::{    is_valid_handle, Creator, Handle, DESCRIPTION_MAX_LEN, HANDLE_LEN, NAME_MAX_LEN,}; #[derive(Accounts)]#[instruction(handle: String)]pub struct RegisterCreator<'info> {    #[account(mut)]    pub wallet: Signer<'info>,    #[account(        init,        payer = wallet,        space = 8 + Creator::INIT_SPACE,        seeds = [Creator::SEED, wallet.key().as_ref()],        bump,    )]    pub creator: Account<'info, Creator>,    #[account(        init,        payer = wallet,
  1. Line 13 pub wallet: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 15 init,: Creates and pays for a new account. Space and payer must be specified.
  3. Line 18 seeds = [Creator::SEED, wallet.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 19 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
ConfidentialCreator/Confidential-Creator-Support · programs/ccsupport/src/instructions/set_visibility.rs L1–24 @ d6f1aa7 · Apache-2.0 · original
use anchor_lang::prelude::*; use crate::events::VisibilityChanged;use crate::state::Pledge; #[derive(Accounts)]pub struct SetVisibility<'info> {    pub supporter: Signer<'info>,    #[account(        mut,        seeds = [Pledge::SEED, pledge.creator.as_ref(), supporter.key().as_ref()],        bump = pledge.bump,    )]    pub pledge: Account<'info, Pledge>,} pub fn set_visibility_handler(ctx: Context<SetVisibility>, show_publicly: bool) -> Result<()> {    let pledge = &mut ctx.accounts.pledge;    pledge.show_publicly = show_publicly;     emit!(VisibilityChanged {        creator: pledge.creator,        supporter: pledge.supporter,        show_publicly,
  1. Line 8 pub supporter: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 11 seeds = [Pledge::SEED, pledge.creator.as_ref(), supporter.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 12 bump = pledge.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 21 emit!(VisibilityChanged {: Emits an Anchor event that clients can parse from the logs.
ConfidentialCreator/Confidential-Creator-Support · programs/ccsupport/src/instructions/update_creator.rs L1–24 @ d6f1aa7 · Apache-2.0 · original
use anchor_lang::prelude::*; use crate::errors::CcsError;use crate::events::CreatorUpdated;use crate::state::{Creator, DESCRIPTION_MAX_LEN, NAME_MAX_LEN}; #[derive(Accounts)]pub struct UpdateCreator<'info> {    pub wallet: Signer<'info>,    #[account(        mut,        seeds = [Creator::SEED, wallet.key().as_ref()],        bump = creator.bump,    )]    pub creator: Account<'info, Creator>,} pub fn update_creator_handler(    ctx: Context<UpdateCreator>,    name: String,    description: String,    suggested_amount: u64,) -> Result<()> {    require!(name.len() <= NAME_MAX_LEN, CcsError::NameTooLong);
  1. Line 9 pub wallet: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 12 seeds = [Creator::SEED, wallet.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 13 bump = creator.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 24 require!(name.len() <= NAME_MAX_LEN, CcsError::NameTooLong);: An explicit validation that aborts the instruction with an error.

How to run, build and test

These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.

DirectoryCommandRuns
.npm run devpnpm -r --parallel dev
.npm run buildpnpm -r build
.npm run testpnpm -r test
.npm run lintbiome check .
packages/dbnpm run testvitest run
packages/chainnpm run testvitest run
packages/sharednpm run testvitest run
apps/webnpm run devvite
apps/webnpm run buildvite build
apps/webnpm run testvitest run
apps/apinpm run devnode --watch --env-file-if-exists=../../.env src/main.ts
apps/apinpm run startnode --env-file-if-exists=../../.env src/main.ts
apps/apinpm run testvitest run
apps/workernpm run devnode --watch --env-file-if-exists=../../.env src/main.ts
apps/workernpm run startnode --env-file-if-exists=../../.env src/main.ts
apps/workernpm run testvitest run

What you could build from it

Used in lessons