Sol.College

Full lesson

featherlabs

New-gen onchain stateless asset protocol for ownership, delegation, and rentability of digital assets at scale.

What it does

From the project README:

Feather Assets is a unified, flexible, and stateless asset protocol for ownership, delegation, and rentability on the Solana blockchain.
Original
featherlabsxyz/featherlabs
SuperStack fork
Blockchains/featherlabs @ 5f12cef
Stars
9
Last push (original)
2024-10-09
Languages
TypeScript, Rust, JavaScript, CSS
Detected stack
AnchorMetaplexNext.jsweb3.js
Categories
NFTs & Digital Assets
Licence
Apache-2.0
Homepage
https://featherlabs.xyz

Architecture

64 source files. By extension: .rs ×25, .ts ×22, .tsx ×16, .js ×1.

Top-level directorySource files
programs25
apps19
clients18
migrations1
tests1

On-chain programs: programs/feather_assets

Dapp: deploying

This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/featherlabs/, once it is deployed and checked on devnet. Until then, 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.

featherlabsxyz/featherlabs · programs/feather_assets/src/processor/group/create_group.rs L61–84 @ 5f12cef · Apache-2.0 · original
                .push(group_data.output_compressed_account(&crate::ID, remaining_accounts)?);            msg!(                "Group Metadata Compressed Account: {:?}",                group_data_address            );        }        None => {            group.has_metadata = false;        }    };    output_compressed_accounts.insert(        0,        group            .output_compressed_account(&crate::ID, remaining_accounts)?            .unwrap(),    );    let bump = Pubkey::find_program_address(&[CPI_AUTHORITY_PDA_SEED], invoking_program).1;    let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];    let instruction = InstructionDataInvokeCpi {        proof: Some(lrp.proof),        new_address_params,        relay_fee: None,        input_compressed_accounts_with_merkle_context: Vec::new(),        output_compressed_accounts,
  1. Line 62 msg!(: Logs to the program log, which is visible in explorers and the test output.
  2. Line 77 let bump = Pubkey::find_program_address(&[CPI_AUTHORITY_PDA_SEED], invoking_program).1;: Derives a PDA from seeds and the program ID and returns the address with its canonical bump.
  3. Line 77 let bump = Pubkey::find_program_address(&[CPI_AUTHORITY_PDA_SEED], invoking_program).1;: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 78 let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
featherlabsxyz/featherlabs · programs/feather_assets/src/processor/asset/create_member_asset.rs L97–120 @ 5f12cef · Apache-2.0 · original
            // acc.privilege_attributes = Vec::new();            let compressed = acc.output_compressed_account(&crate::ID, remaining_accounts)?;            output_compressed_accounts.push(compressed);            msg!("Asset Metadata Compressed Account: {:?}", address);        }        None => asset.has_metadata = false,    }    match args.royalties_initializable {        true => asset.royalty_state = RoyaltyState::Unintialized,        false => asset.royalty_state = RoyaltyState::Disabled,    }    output_compressed_accounts.insert(        1,        asset            .output_compressed_account(&crate::ID, remaining_accounts)?            .unwrap(),    );    let bump = Pubkey::find_program_address(        &[CPI_AUTHORITY_PDA_SEED],        ctx.accounts.get_invoking_program().key,    )    .1;    let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];    let instruction = InstructionDataInvokeCpi {
  1. Line 100 msg!("Asset Metadata Compressed Account: {:?}", address);: Logs to the program log, which is visible in explorers and the test output.
  2. Line 114 let bump = Pubkey::find_program_address(: Derives a PDA from seeds and the program ID and returns the address with its canonical bump.
  3. Line 114 let bump = Pubkey::find_program_address(: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 119 let signer_seeds = [CPI_AUTHORITY_PDA_SEED, &[bump]];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
featherlabsxyz/featherlabs · programs/feather_assets/src/processor/asset/add_royalties_asset.rs L49–72 @ 5f12cef · Apache-2.0 · original
        &asset_royalty.new_address_params().unwrap().seed,        &address_merkle_context,    ));    msg!("Asset Royalty Compressed Account: {:?}", address);    ctx.verify(lrp.proof)?;    Ok(())}#[light_accounts]#[instruction(asset_derivation_key: Pubkey, asset_address: Pubkey)]pub struct AddRoyaltiesToAsset<'info> {    #[account(mut)]    #[fee_payer]    pub authority: Signer<'info>,    #[self_program]    pub self_program: Program<'info, crate::program::FeatherAssets>,    /// CHECK: Checked in light-system-program.    #[authority]    pub cpi_signer: AccountInfo<'info>,    #[light_account(mut, seeds = [asset_derivation_key.to_bytes().as_ref()]        constraint = asset.owner == authority.key() @ FeatherErrorCode::InvalidAssetSigner    )]    pub asset: LightAccount<AssetV1>,    #[light_account(        init,
  1. Line 52 msg!("Asset Royalty Compressed Account: {:?}", address);: Logs to the program log, which is visible in explorers and the test output.
  2. Line 61 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  3. Line 67 #[light_account(mut, seeds = [asset_derivation_key.to_bytes().as_ref()]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 72 init,: Creates and pays for a new account. Space and payer must be specified.
featherlabsxyz/featherlabs · programs/feather_assets/src/processor/group/transfer_group_authority.rs L25–48 @ 5f12cef · Apache-2.0 · original
        &address_merkle_context,    ));    msg!("Group Compressed Account: {:?}", group_address);    ctx.verify(lrp.proof)?;    Ok(())}#[light_accounts]#[instruction(derivation_key: Pubkey)]pub struct TransferGroupAuthority<'info> {    #[account(mut)]    #[fee_payer]    pub authority: Signer<'info>,    /// CHECK: Its safe    pub new_authority: AccountInfo<'info>,    #[self_program]    pub self_program: Program<'info, crate::program::FeatherAssets>,    /// CHECK: Checked in light-system-program.    #[authority]    pub cpi_signer: AccountInfo<'info>,    #[light_account(        mut,        seeds = [derivation_key.to_bytes().as_ref()]        constraint = authority.key() == group.owner @ FeatherErrorCode::InvalidGroupSigner    )]
  1. Line 27 msg!("Group Compressed Account: {:?}", group_address);: Logs to the program log, which is visible in explorers and the test output.
  2. Line 36 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  3. Line 46 seeds = [derivation_key.to_bytes().as_ref()]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.

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 lintprettier */*.js "*/**/*{.js,.ts}" --check
clients/jsnpm run testecho "Error: no test specified" && exit 1
apps/webnpm run devnext dev
apps/webnpm run startnext start
apps/webnpm run buildnext build
apps/webnpm run lintnext lint
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testpnpm ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts
.cargo buildRust workspace build
.cargo testRust tests

What you could build from it