Full lesson
alexwu0209-ALLOY
ALLOY: Tokenizing Private Credit on Solana
What it does
From the project README:
Developed by the Alloy team (@alloyRWA) - Pioneering Real World Asset Tokenization on Solana
- Original
- alexwu0209/ALLOY
- SuperStack fork
- Blockchains/alexwu0209-ALLOY @
e33c954 - Stars
- 1
- Last push (original)
- 2025-05-15
- Languages
- TypeScript, Rust, JavaScript
- Detected stack
- Anchorweb3.js
- Categories
- Other
- Licence
- MIT
Architecture
57 source files. By extension: .ts ×33, .rs ×19, .js ×3, .tsx ×2.
| Top-level directory | Source files |
|---|---|
sdk | 32 |
programs | 19 |
tests | 6 |
On-chain programs: programs/alloy-tokenization, programs/alloy-transfer-hook
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/alexwu0209-alloy/. 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.
programs/alloy-tokenization/src/instructions/create_asset.rs L13–35 @ e33c954 · MIT · originalpub struct CreateTokenizedAsset<'info> { #[account(mut)] pub payer: Signer<'info>, /// The authority that will be set as mint authority pub mint_authority: Signer<'info>, /// The authority that will be set as freeze authority pub freeze_authority: Signer<'info>, /// The mint account to be created #[account(mut)] pub mint: Signer<'info>, /// Asset metadata account #[account( init, payer = payer, space = 8 + std::mem::size_of::<AssetMetadata>() + 1024, // Extra space for vectors seeds = [b"metadata", mint.key().as_ref()], bump )] pub asset_metadata: Account<'info, AssetMetadata>,- Line 15
pub payer: Signer<'info>,: Requires this account to have signed the transaction. - Line 29
init,: Creates and pays for a new account. Space and payer must be specified. - Line 32
seeds = [b"metadata", mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 33
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
programs/alloy-tokenization/src/instructions/confidential.rs L13–36 @ e33c954 · MIT · originaluse solana_program::program::invoke_signed; use crate::{state::*, errors::ErrorCode}; #[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]pub struct ConfidentialTransferConfig { pub auto_approve_new_accounts: bool, pub auditor_encryption_key: Option<[u8; 32]>,} #[derive(Accounts)]pub struct EnableConfidentialTransfers<'info> { #[account(mut)] pub authority: Signer<'info>, #[account(mut)] pub mint: Account<'info, Mint>, #[account( mut, seeds = [b"metadata", mint.key().as_ref()], bump, )] pub asset_metadata: Account<'info, AssetMetadata>,- Line 13
use solana_program::program::invoke_signed;: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address. - Line 26
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 33
seeds = [b"metadata", mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 34
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
programs/alloy-tokenization/src/state/pause.rs L1–24 @ e33c954 · MIT · originaluse anchor_lang::prelude::*;use crate::errors::ErrorCode; #[account]pub struct PauseState { pub mint: Pubkey, pub paused: bool, pub paused_by: Option<Pubkey>, pub paused_at: Option<i64>, pub bump: u8,} impl PauseState { pub fn check_not_paused(&self) -> Result<()> { if self.paused { return err!(ErrorCode::ContractPaused); } Ok(()) } pub fn pause(&mut self, authority: Pubkey) -> Result<()> { self.paused = true; self.paused_by = Some(authority); self.paused_at = Some(Clock::get()?.unix_timestamp);- Line 10
pub bump: u8,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 16
return err!(ErrorCode::ContractPaused);: An explicit validation that aborts the instruction with an error. - Line 24
self.paused_at = Some(Clock::get()?.unix_timestamp);: Reads on-chain time from the Clock sysvar.
programs/alloy-tokenization/src/instructions/payments.rs L1–24 @ e33c954 · MIT · originaluse anchor_lang::prelude::*;use crate::{state::*, errors::ErrorCode}; #[derive(Accounts)]pub struct DistributePayment<'info> { #[account(mut)] pub authority: Signer<'info>, pub mint: Account<'info, anchor_spl::token_interface::Mint>, #[account( mut, seeds = [b"metadata", mint.key().as_ref()], bump, )] pub asset_metadata: Account<'info, AssetMetadata>, #[account( seeds = [b"auth", mint.key().as_ref()], bump, constraint = auth_state.has_role(&authority.key(), Role::Admin) || auth_state.has_role(&authority.key(), Role::Issuer) @ ErrorCode::Unauthorized )] pub auth_state: Account<'info, AuthState>,- Line 7
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 13
seeds = [b"metadata", mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 14
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
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.
| Directory | Command | Runs |
|---|---|---|
. | npm run build | anchor build |
. | npm run test | anchor test |
. | npm run lint | eslint . --ext .ts |
. | npm run deploy | anchor deploy |
sdk | npm run build | tsc |
sdk | npm run test | jest |
sdk | npm run lint | eslint src --ext .ts |
sdk/react | npm run build | tsc |
sdk/cli | npm run start | node dist/index.js |
sdk/cli | npm run build | tsc |
. | anchor build | compile programs and generate IDLs |
. | anchor test | local validator + test suite |
. | anchor run test | yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)