A professional Solana (Anchor) staking dApp using SPL tokens. Users stake SPL tokens and claim rewards. Configuration is stored in a PDA; reward mint authority is owned by the program PDA.
- Overview
- Quick Start
- Devnet Setup
- Requirements
- Configuration & Addresses
- Anchor Scripts
- SPL Token CLI (ATAs & Minting)
- Frontend Integration
- Build, Deploy, Initialize
- Testing (localnet/devnet)
- Logs & Verification
- Troubleshooting
solana config set -u devnetspl-token create-token --decimals 9(create your stake mint)spl-token create-token --decimals 9(create your reward mint)- Update
config.tswith<YOUR_STAKE_MINT>and<YOUR_REWARD_MINT> spl-token create-account <YOUR_STAKE_MINT> --url https://api.devnet.solana.comspl-token create-account <YOUR_REWARD_MINT> --url https://api.devnet.solana.comanchor run initanchor run token-info
solana-cli>= 1.18anchor>= 0.29node>= 18,npm>= 9ts-nodeinstalled (dev dependency)- TypeScript
tsconfig.jsonset totarget: es2020andlib: ["es2020"]
- StakeFlow lets users deposit stake tokens and later claim rewards.
- Uses two SPL mints:
stakeMint(user stake) andrewardMint(rewards). - PDAs manage config, stake vault, penalty vault, and reward mint authority.
- Install
solana-cliandspl-token. - Follow these steps to use your own addresses:
- Step 1: Configure devnet:
solana config set -u devnetsolana airdrop 2solana balance
- Step 2: Create your SPL tokens (9 decimals):
- Create stake mint:
spl-token create-token --decimals 9 - Create reward mint:
spl-token create-token --decimals 9 - Copy both mint addresses for the next step.
- Create stake mint:
- Step 3: Replace addresses in
config.ts:- Set
stakeMintAddress=<YOUR_STAKE_MINT> - Set
rewardMintAddress=<YOUR_REWARD_MINT>
- Set
- Step 4: Create your ATAs on devnet:
- Stake ATA:
spl-token create-account <YOUR_STAKE_MINT> --url https://api.devnet.solana.com - Reward ATA:
spl-token create-account <YOUR_REWARD_MINT> --url https://api.devnet.solana.com
- Stake ATA:
- Step 5: Mint stake tokens (only if you are the stake mint authority):
spl-token mint <YOUR_STAKE_MINT> 10 <YOUR_STAKE_ATA> --url https://api.devnet.solana.com
- Step 6: Initialize on-chain state:
anchor run init(migrates reward mint authority to the program PDA)
- Step 7: Inspect and verify:
anchor run token-info(mints info and your ATAs)- Use Solscan for transaction signatures (
?cluster=devnet)
- Step 1: Configure devnet:
- Project config:
config.ts(APR, lock duration, early-unstake penalty). - IDL:
target/idl/stake_flow.json - Types:
target/types/stake_flow.ts - Current devnet mints:
stakeMint:BeyV4AuCPvchhJc7NXSaAa2ECbPVkj39wy9CY7fu8opDrewardMint:GQCW1M9szh426zC5a51BLZbPhvXoPnMKCeRWepyCziK3
- Program ID prints during
anchor run initand is stored undertarget/deploy/stake_flow-keypair.json.
- Token info:
anchor run token-info- Prints mint authority, freeze authority, decimals, supply.
- Shows your associated token accounts (ATAs) and balances; prints creation commands when missing.
- Initialization:
anchor run init- Derives PDAs (
config,stake_vault,penalty_vault,reward_mint_authority). - Calls
initializeConfig(aprBps, minLockDuration, earlyUnstakePenaltyBps). - Migrates
rewardMintauthority to the PDA.
- Derives PDAs (
Create your ATAs on devnet and mint stake tokens if you are the mint authority.
-
Create ATAs (examples from this project):
- Stake ATA:
spl-token create-account BeyV4AuCPvchhJc7NXSaAa2ECbPVkj39wy9CY7fu8opD --url https://api.devnet.solana.com- Example output:
Creating account 7rkgU6o3nUzVcYp7YrzKiYzrr84YbezmdTP98G6TcgUFand a signature.
- Example output:
- Reward ATA:
spl-token create-account GQCW1M9szh426zC5a51BLZbPhvXoPnMKCeRWepyCziK3 --url https://api.devnet.solana.com- Example output:
Creating account B5DNNuHoTEiRDpusgi9mJne9JkPxN4j35apSDGTHUfekand a signature.
- Example output:
- Stake ATA:
-
Mint stake tokens (if your wallet is the stake mint authority):
spl-token mint BeyV4AuCPvchhJc7NXSaAa2ECbPVkj39wy9CY7fu8opD 10 7rkgU6o3nUzVcYp7YrzKiYzrr84YbezmdTP98G6TcgUF --url https://api.devnet.solana.com- Example output:
Minting 10 tokensand signature4kHNVb9YeHd1de8R3325Qodcy28y12aTjaE7AsDEpTGhEXFpb6En9ULN5MFxzyw6qPqrwhE6rsnA4xpmi6nzSYQb.
-
Query balances and supply:
- List accounts:
spl-token accounts --owner <YOUR_WALLET> --url https://api.devnet.solana.com - Mint supply:
spl-token supply <MINT> --url https://api.devnet.solana.com - ATA balance:
spl-token balance <ATA> --url https://api.devnet.solana.com
- List accounts:
-
Authority notes:
rewardMintis owned by the program PDA; you cannot mint via CLI. Rewards must be issued by the program instruction.stakeMintcan be minted via CLI if your wallet is itsmintAuthority.
- Use
@coral-xyz/anchorwith thestake_flowIDL and program ID. - Respect types: pass
i64asanchor.BN(e.g.,min_lock_duration) andu16as numbers. - Ensure the wallet has a stake ATA with balance before calling
stake. - Rewards are minted by the program (signed by the
reward_mint_authorityPDA).
Notes:
- Path to IDL may differ in your frontend; ensure bundler can load it.
- Derive exact accounts from
target/idl/stake_flow.json. - Use
new BN(...)foru64/i64amounts and durations.
- Set provider in
Anchor.toml:[provider] cluster = "devnet",wallet = "~/.config/solana/id.json"(or your path).
- Deploy on devnet:
anchor buildanchor deploy
- Initialize on-chain state:
anchor run init- Re-running may show
account already in useforconfigPDA; expected if already initialized.
- Recommended: run tests on localnet to avoid external rate limits.
- In
Anchor.toml, usecluster = "localnet"and a validwallet. - Run:
anchor test(creates local mints, PDAs, and accounts).
- In
- Devnet tests:
- Align tests with
config.tsmints and remove localcreateMintcalls. - Be aware of faucet/RPC rate limits; retry if needed.
- Align tests with
- Program logs:
.anchor/program-logs/orsolana logs. - Inspect transactions:
https://solscan.io/tx/<SIGNATURE>?cluster=devnet. - Use
anchor run token-infoto verify mint authorities, decimals, supply and ATAs.
TypeError: src.toTwos is not a function: Ani64was passed as a number; usenew anchor.BN(...).Allocate: account already in use: Config PDA already exists; harmless when re-initializing.bigint: Failed to load bindings: Benign warning frombn.js; pure JS path is used.- Older
spl-tokenversions may lackmint-info/account-info; usesupply,accounts,balance.