Sol.College

Full lesson

Bonding_Curve_SOL_contracts

SketchXpress is a Solana-based NFT AMM protocol using bonding curves and Token-Owned Escrows (TOE) for dynamic pricing, direct SOL liquidity, and Tensor marketplace integration.

What it does

From the project README:

[](https://solana.com/) [](https://www.rust-lang.org/) [](https://www.anchor-lang.com/) [](https://www.typescriptlang.org/)
Original
SketchXpress/Bonding_Curve_SOL_contracts
SuperStack fork
Blockchains/Bonding_Curve_SOL_contracts @ 7c60166
Stars
5
Last push (original)
2025-08-19
Languages
TypeScript, Rust, JavaScript, Shell, Dockerfile, CSS
Detected stack
AnchorMetaplexNext.jsweb3.js
Categories
DeFiNFTs & Digital Assets
Licence
Apache-2.0
Homepage
https://sketchxpress.tech

Architecture

149 source files. By extension: .rs ×56, .ts ×53, .tsx ×32, .js ×7, .py ×1.

Top-level directorySource files
nextjs-frontend88
programs55
tests4
migrations1
scripts1

On-chain programs: programs/bonding-curve-system

Dapp: deploying

This fork is queued for its own live dapp on Sol.College, with a Devnet/Mainnet switch. It appears here, at /dapps/bonding-curve-sol-contracts/, 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.

SketchXpress/Bonding_Curve_SOL_contracts · programs/bonding-curve-system/src/instructions/migrate_to_tensor.rs L13–36 @ 7c60166 · Apache-2.0 · original
    pub authority: Signer<'info>,     #[account(        mut,        seeds = [b"bonding-curve-pool", collection_mint.key().as_ref()],        bump = pool.bump,                constraint = pool.config.creator == authority.key() @ ErrorCode::InvalidAuthority    )]    pub pool: Account<'info, BondingCurvePool>,     /// CHECK: This is the collection mint used for pool PDA derivation    pub collection_mint: UncheckedAccount<'info>,     pub system_program: Program<'info, System>,} pub fn migrate_to_tensor(ctx: Context<MigrateToTensor>) -> Result<()> {    // Access pool data directly    let pool = &mut ctx.accounts.pool;     // Verify not already migrated    require!(                !pool.state.is_migrated_to_tensor,        ErrorCode::AlreadyMigrated // Use existing error code if applicable
  1. Line 13 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 17 seeds = [b"bonding-curve-pool", collection_mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 18 bump = pool.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 34 require!(: An explicit validation that aborts the instruction with an error.
SketchXpress/Bonding_Curve_SOL_contracts · programs/bonding-curve-system/src/instructions/sell_nft.rs L121–144 @ 7c60166 · Apache-2.0 · original
     let net_amount_to_seller_calculated = available_lamports        .checked_sub(sell_fee_calculated)        .ok_or(ErrorCode::MathOverflow)?;     let total_payout_amount = sell_fee_calculated        .checked_add(net_amount_to_seller_calculated)        .ok_or(ErrorCode::MathOverflow)?;     if escrow_total_lamports        < total_payout_amount            .checked_add(rent_exempt_minimum)            .ok_or(ErrorCode::MathOverflow)?    {        return err!(ErrorCode::InsufficientEscrowBalance);    }     let final_amount_to_seller_transfer = net_amount_to_seller_calculated        .checked_add(rent_exempt_minimum)        .ok_or(ErrorCode::MathOverflow)?;     let nft_mint_key = ctx.accounts.nft_mint.key();    let escrow_bump = ctx.accounts.escrow.bump;    let seeds = &[
  1. Line 123 .checked_sub(sell_fee_calculated): Uses checked arithmetic so overflow fails instead of wrapping.
  2. Line 135 return err!(ErrorCode::InsufficientEscrowBalance);: An explicit validation that aborts the instruction with an error.
  3. Line 143 let escrow_bump = ctx.accounts.escrow.bump;: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 144 let seeds = &[: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
SketchXpress/Bonding_Curve_SOL_contracts · programs/bonding-curve-system/src/instructions/create_pool.rs L1–24 @ 7c60166 · Apache-2.0 · original
use anchor_lang::prelude::*; use crate::state::BondingCurvePool; #[derive(AnchorSerialize, AnchorDeserialize, Clone)]pub struct CreatePoolArgs {    pub base_price: u64,    pub growth_factor: u64,} #[derive(Accounts)]pub struct CreatePool<'info> {    #[account(mut)]    pub creator: Signer<'info>,        /// CHECK: This is the collection mint for the NFTs    pub collection_mint: UncheckedAccount<'info>,        #[account(        init,        payer = creator,        space = BondingCurvePool::SPACE,        seeds = [b"bonding-curve-pool", collection_mint.key().as_ref()],        bump
  1. Line 14 pub creator: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 20 init,: Creates and pays for a new account. Space and payer must be specified.
  3. Line 23 seeds = [b"bonding-curve-pool", collection_mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 24 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
SketchXpress/Bonding_Curve_SOL_contracts · programs/bonding-curve-system/src/instructions/cancel_bid.rs L49–72 @ 7c60166 · Apache-2.0 · original
    /// CHECK: This account holds the SOL for the bid    #[account(        mut,        seeds = [b"bid-escrow", bid.key().as_ref()],        bump,    )]    pub bid_escrow: UncheckedAccount<'info>,     pub system_program: Program<'info, System>,} pub fn cancel_bid(    ctx: Context<CancelBid>,    args: CancelBidArgs,) -> Result<()> {    let bid_id = args.bid_id;    let current_timestamp = Clock::get()?.unix_timestamp;    let bid = &mut ctx.accounts.bid;    let bid_listing = &mut ctx.accounts.bid_listing;     // Check if bid can be cancelled    require!(        bid.can_cancel(&ctx.accounts.bidder.key()),        ErrorCode::CannotCancelBid
  1. Line 52 seeds = [b"bid-escrow", bid.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  2. Line 53 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  3. Line 65 let current_timestamp = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar.
  4. Line 70 require!(: 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 devcd nextjs-frontend && npm run dev
.npm run startcd nextjs-frontend && npm run start
.npm run buildanchor build && cd nextjs-frontend && npm run build
.npm run testanchor test
.npm run lintcd nextjs-frontend && npm run lint
.npm run deployanchor deploy
nextjs-frontendnpm run devnext dev
nextjs-frontendnpm run startcross-env NODE_ENV=production node server.js
nextjs-frontendnpm run buildnext build
nextjs-frontendnpm run lintnext lint
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testecho 'Tests should be run from the frontend directory with npm test'
.cargo buildRust workspace build
.cargo testRust tests

What you could build from it

Used in lessons