This repository contains an Anchor-based Solana program that creates and manages Raydium CPMM pools via CPI, while adding a thin layer of business logic around:
- Bootstrapping a new CPMM pool (Raydium’s constant‑product AMM) with
wSOL+ a target token. - Custodying LP tokens in a program‑owned vault as collateral for a short, configurable “loan period.”
- Dynamic, size‑tiered service fees in lamports based on the initial SOL contributed.
- Authorized early close / liquidation of the pool position if the loan expires or policy requires it.
- Token‑2022 awareness (uses
transfer_checkedfrom the Token‑2022 interface; works with standard SPL tokens too).
Status: engineering/developer preview. Not audited. Mainnet use is at your own risk.
The program integrates with Raydium’s CPMM program (raydium-cpmm-cpi) using CPI calls to create a pool, mint LP, and withdraw liquidity. On top, it maintains a small set of program accounts and PDAs to track policy and collateral:
| PDA | Seed(s) | Purpose |
|---|---|---|
| Config | "config" |
Global settings: admin keys, service‑fee tiers, pause flag, running counter of vault balance. |
| PoolLoan | "pool_loan", pool_state |
Per‑pool loan metadata: user, pool, LP mint, token mint, initial amounts, start time/duration, repaid flag. |
| Service Vault | "vault" |
Program‑owned token account (ATA) for the asset used as service currency (typically wSOL). |
| Service LP | "lp_token", pool_state |
Program‑owned ATA for the Raydium LP mint used to temporarily custody LP as collateral. |
Raydium CPMM PDAs (e.g.
POOL_SEED,POOL_VAULT_SEED,POOL_LP_MINT_SEED,OBSERVATION_SEED) are derived inside CPI calls and are not owned by this program.
Config stores fixed lamport amounts for the following SOL tiers: 2, 5, 10, 20, 50, 100, 200. When bootstrapping a pool with a given wSOL amount, the program charges the corresponding fixed fee and moves it to the Service Vault upfront.
- Create liquidity (Raydium CPI): user provides
wSOL+ token; pool is created and LP is minted. - LP custody: all minted LP is transferred to Service LP (program PDA) as collateral using
send_lp_tokens. - User close (before expiry): user can call
remove_liquiditywithin the loan window; the program returns LP (PDA‑signed), withdraws from the pool, and enforces repayment of thewSOLprincipal back to Service Vault. The user keeps the counter‑asset received from the withdrawal. - Liquidation (after expiry / by operator): an authorized operator (
admin,syncer, orverifier) can callliquidate_loan; the program unwinds liquidity, settles balances, marks the loan repaid, emits an event, and updatesConfig.amountaccordingly.
- smartv21 (devnet):
94f9T2tk9gfUhvvAP4WUMbQNj99M6t7T1xDiX73dEfvJ
(Configured inAnchor.toml→[programs.devnet])
Raydium CPMM program IDs differ by cluster. In tests we use the mainnet CPMM id
CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C(a devnet id is commented). Adjust for your environment.
Below is a concise reference of public entrypoints defined by the program.
Creates the global Config PDA, sets the admin to the signer, stores the syncer and verifier, writes default fee tiers, and initializes the Service Vault (program‑owned ATA) for the chosen token_mint (typically wSOL).
Key accounts (abridged):
admin(signer) – becomesconfig.adminconfig(PDA"config") – createdtoken_mint– mint used for the Service Vault (commonlySo111...forwSOL)service_vault(PDA"vault") – ATA with authority =configtoken_program(Token‑2022 interface) /system_program
Default fees (lamports):
2→200_000_000, 5→250_000_000, 10→300_000_000, 20→400_000_000, 50→1_000_000_000, 100→2_500_000_000, 200→5_000_000_000
Admin‑only. Updates one of the fee tiers where index ∈ {2,5,10,20,50,100,200}.
Creates a Raydium CPMM pool via CPI and records a PoolLoan row. Supports either ordering of wSOL (So111...) as token_0 or token_1. Enforces:
- If
wSOLis provided, its amount must equal one of 2/5/10/20/50/100/200 SOL (in lamports). - For the counter token: total supply must equal
init_amount_*and both mint and freeze authorities must be revoked.
Also charges the dynamic service fee to Service Vault upfront and stores loan timing:
loan_start_time = Clock::get().unix_timestamploan_duration(e.g.,60*60*24for 1 day)
Key accounts (abridged):
config(PDA"config")pool_loan(PDA"pool_loan", seed includespool_state) – createdservice_vault(PDA"vault")- Raydium CPMM accounts:
cp_swap_program,pool_state,lp_mint,token_0_vault,token_1_vault,observation_state,create_pool_fee - User token accounts for the two mints and the LP mint
Moves all LP minted to the user into the Service LP PDA for safekeeping (collateral).
Key accounts (abridged):
pool_loan(PDA"pool_loan")service_token_lp(PDA"lp_token", ATA forlp_mintowned by program)owner(signer) +owner_lp_tokenpool_state,lp_mint,token_program
User path (before expiry). Returns the specified LP amount from Service LP back to the user (PDA‑signed), executes Raydium withdraw via CPI, and then enforces repayment of the wSOL principal by transferring it back to Service Vault. Marks loan repaid when conditions are met.
Key constraints:
- Caller must be
pool_loan.user. - Must be within
loan_start_time + loan_duration. - Fails if
pool_loan.is_repaidis alreadytrue.
Operator path (after expiry). Authorized caller (config.admin / config.syncer / config.verifier) returns LP (PDA‑signed), unwinds via Raydium withdraw, settles balances (including wSOL back to Service Vault), marks as repaid, and emits:
#[event]
pub struct LoanLiquidatedEvent {
pub pool: Pubkey,
pub user: Pubkey,
pub liquidator: Pubkey,
pub amount: u64,
pub timestamp: i64,
}Admin‑only helper to fund / empty the program’s Service Vault ("vault" PDA). Uses Token‑2022 transfer_checked and updates config.amount as a running balance.
ProgramPausedInvalidFeeIndexInsufficientBalanceInsufficientTokenBalance(counter‑asset supply must match expected at pool creation)InvalidDurationInvalidInitSolAmount(must be one of 2/5/10/20/50/100/200 SOL)UnauthorizedLoanExpired/LoanNotExpiredInvalidFee,InvalidTreasuryMintAuthorityNotRevoked/FreezeAuthorityNotRevoked
See programs/smartv21/src/error.rs for the full list.
- Rust & Solana toolchain
- Anchor 0.29.0 (see
Cargo.toml/ program usesanchor-lang = "=0.29.0") - Node 18+ and yarn (for tests)
- A devnet wallet with SOL for fees
Do not commit private RPC keys. In Anchor.toml, set your own cluster & wallet, e.g.:
[provider]
cluster = "https://api.devnet.solana.com"
wallet = "~/.config/solana/id.json"anchor buildyarn install
anchor test
# or, as configured:
yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/smartv21.tsThe test suite demonstrates a full flow:
- Program
initializeand Service Vault setup. - Creating a Raydium CPMM pool with
wSOL+ token. - Custodying LP to Service LP via
send_lp_tokens. - Withdrawing liquidity via
remove_liquidity(user) and vialiquidate_loan(operator).
Raydium CPMM program id is configurable in
tests/config.ts. Use the correct id for your cluster.
- Counter token mint authorities must be revoked prior to pool creation:
mint_authority.is_none()andfreeze_authority.is_none()are enforced.
- Allowed SOL sizes: 2/5/10/20/50/100/200 SOL (lamports).
- The program is Token‑2022 aware and uses
transfer_checked. Standard SPL tokens are supported as well. - The Service Vault is an ATA controlled by the Config PDA. All movements from it require PDA signing.
Config.amountis a running balance field for the Service Vault (for accounting/telemetry).- Program emits
LoanLiquidatedEventon liquidation for off-chain indexing.
programs/smartv21/
├─ src/
│ ├─ lib.rs # entrypoints and instruction routing
│ ├─ constants.rs # PDA seed constants
│ ├─ state/mod.rs # Config, PoolLoan
│ ├─ error.rs # custom errors
│ ├─ event.rs # events
│ └─ instructions/
│ ├─ initialize.rs # initialize, update_service_fee, create_liquidity_pool, send_lp_tokens
│ ├─ manage.rs # deposit, withdraw (Service Vault)
│ ├─ withdraw_pool.rs # remove_liquidity (user path)
│ └─ liquidate_loan.rs# liquidate_loan (operator path)
tests/
├─ smartv21.ts # end‑to‑end tests
└─ utils/ # PDA helpers, instruction wrappers
This code has not undergone a formal audit. It interacts with external programs (Raydium CPMM, Token‑2022). Use on mainnet at your own risk. Review constraints, authority checks, and fee logic for your specific use case.
Add a license file (e.g., LICENSE) of your choice. If you’re unsure, MIT is a common choice for open‑source Solana programs.
- Raydium CPMM CPI interfaces
- Anchor framework by Coral