-
Notifications
You must be signed in to change notification settings - Fork 475
Create MajsaiSol #442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
istvanmajsai70-pixel
wants to merge
1
commit into
solana-developers:main
Choose a base branch
from
istvanmajsai70-pixel:patch-1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Create MajsaiSol #442
istvanmajsai70-pixel
wants to merge
1
commit into
solana-developers:main
from
istvanmajsai70-pixel:patch-1
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use anchor_lang::prelude::*; use anchor_spl::token::{self, Mint, TokenAccount, Token, MintTo, Transfer, CloseAccount};
declare_id!("ReplaceWithYourProgramId1111111111111111111111");
// Simple Anchor program that: // - creates an SPL mint with a fixed total supply (100_000_000_000 tokens, decimals = 6) // - holds a token reserve owned by the program // - allows users to buy tokens by sending SOL and selling tokens back for SOL // - uses a fixed price (tokens per 1 SOL) and a small fee (in basis points)
// NOTE: // - This is an educational example. Do NOT use in production without audit. // - We use decimals = 6 so total_supply fits into u64 when scaled. // - The program uses CPI to the SPL Token program.
#[program] pub mod anchor_spl_token_exchange { use super::*;
pub fn initialize(
ctx: Context<Initialize>,
price_tokens_per_sol: u64,
fee_bps: u16,
) -> Result<()> {
// price_tokens_per_sol: how many token base-units (i.e. including decimals) equals 1 SOL
// fee_bps: fee in basis points (10000 bps = 100%)
let state = &mut ctx.accounts.state;
state.mint = ctx.accounts.mint.key();
state.reserve = ctx.accounts.reserve.key();
state.authority = ctx.accounts.authority.key();
state.price_tokens_per_sol = price_tokens_per_sol;
state.fee_bps = fee_bps;
// Mint the total supply to the reserve token account (program-owned reserve)
let cpi_accounts = MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.reserve.to_account_info(),
authority: ctx.accounts.mint_authority.to_account_info(),
};
// mint_authority is a PDA we derived; sign with seeds
let mint_authority_seeds: &[&[u8]] = &[
b"mint_authority",
ctx.program_id.as_ref(),
];
let signer = &[&mint_authority_seeds[..]];
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
// 100_000_000_000 tokens with decimals = 6 -> scaled supply = 100_000_000_000 * 10^6
let total_supply: u64 = 100_000_000_000u64.checked_mul(10u64.pow(ctx.accounts.mint.decimals as u32)).unwrap();
token::mint_to(cpi_ctx, total_supply)?;
Ok(())
}
// Buy tokens: user attaches SOL (lamports) and receives tokens at fixed price
pub fn buy(ctx: Context<Buy>, min_tokens_out: u64) -> Result<()> {
let state = &ctx.accounts.state;
// amount of lamports sent in this instruction must be transfered by the client
let lamports_sent = ctx.accounts.payer.to_account_info().lamports()
.checked_sub(ctx.accounts.payer_starting_balance)
.unwrap_or(0);
require!(lamports_sent > 0, ExchangeError::NoLamportsSent);
// tokens per 1 SOL (1 SOL = 1_000_000_000 lamports)
let tokens_per_lamport = state.price_tokens_per_sol.checked_div(1_000_000_000u64).ok_or(ExchangeError::InvalidPrice)?;
let tokens_out = lamports_sent.checked_mul(tokens_per_lamport).ok_or(ExchangeError::MathOverflow)?;
// apply fee
let fee = tokens_out.checked_mul(state.fee_bps as u64).ok_or(ExchangeError::MathOverflow)?
.checked_div(10000).ok_or(ExchangeError::MathOverflow)?;
let tokens_to_user = tokens_out.checked_sub(fee).ok_or(ExchangeError::MathOverflow)?;
require!(tokens_to_user >= min_tokens_out, ExchangeError::SlippageExceeded);
// Transfer tokens from reserve -> user_token_account (program signed)
let cpi_accounts = Transfer {
from: ctx.accounts.reserve.to_account_info(),
to: ctx.accounts.user_token.to_account_info(),
authority: ctx.accounts.reserve_authority.to_account_info(),
};
let seeds = &[b"reserve_authority", ctx.program_id.as_ref()];
let signer = &[&seeds[..]];
let cpi_ctx = CpiContext::new_with_signer(ctx.accounts.token_program.to_account_info(), cpi_accounts, signer);
token::transfer(cpi_ctx, tokens_to_user)?;
Ok(())
}
// Sell tokens: user transfers tokens to reserve, program sends SOL back at fixed price
pub fn sell(ctx: Context<Sell>, tokens_in: u64, min_lamports_out: u64) -> Result<()> {
let state = &mut ctx.accounts.state;
require!(tokens_in > 0, ExchangeError::InvalidAmount);
// compute lamports out = tokens_in / tokens_per_lamport
let tokens_per_lamport = state.price_tokens_per_sol.checked_div(1_000_000_000u64).ok_or(ExchangeError::InvalidPrice)?;
require!(tokens_per_lamport > 0, ExchangeError::InvalidPrice);
let lamports_out = tokens_in.checked_div(tokens_per_lamport).ok_or(ExchangeError::MathOverflow)?;
// apply fee on tokens (fee taken to reserve, so user receives lamports based on tokens after fee)
let fee = tokens_in.checked_mul(state.fee_bps as u64).ok_or(ExchangeError::MathOverflow)?
.checked_div(10000).ok_or(ExchangeError::MathOverflow)?;
let tokens_after_fee = tokens_in.checked_sub(fee).ok_or(ExchangeError::MathOverflow)?;
let lamports_to_user = tokens_after_fee.checked_div(tokens_per_lamport).ok_or(ExchangeError::MathOverflow)?;
require!(lamports_to_user >= min_lamports_out, ExchangeError::SlippageExceeded);
// Transfer tokens from user -> reserve (signed by user)
let cpi_accounts = Transfer {
from: ctx.accounts.user_token.to_account_info(),
to: ctx.accounts.reserve.to_account_info(),
authority: ctx.accounts.user_authority.to_account_info(),
};
let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
token::transfer(cpi_ctx, tokens_in)?;
// Send lamports from program's vault (the state account) to user
**ctx.accounts.state.to_account_info().try_borrow_mut_lamports()? -= lamports_to_user;
**ctx.accounts.user_authority.to_account_info().try_borrow_mut_lamports()? += lamports_to_user;
Ok(())
}
}
// ------------------- Accounts & State -------------------
#[account] pub struct State { pub mint: Pubkey, pub reserve: Pubkey, pub authority: Pubkey, pub price_tokens_per_sol: u64, pub fee_bps: u16, }
#[derive(Accounts)] #[instruction(price_tokens_per_sol: u64, fee_bps: u16)] pub struct Initialize<'info> { #[account(init, payer = authority, space = 8 + 32*3 + 8 + 2)] pub state: Account<'info, State>,
#[account(
init,
payer = authority,
mint::decimals = 6,
mint::authority = mint_authority,
)]
pub mint: Account<'info, Mint>,
/// CHECK: PDA that will be mint authority
#[account(seeds = [b"mint_authority", program_id.as_ref()], bump)]
pub mint_authority: UncheckedAccount<'info>,
#[account(
init,
payer = authority,
token::mint = mint,
token::authority = reserve_authority,
)]
pub reserve: Account<'info, TokenAccount>,
/// CHECK: PDA that will be reserve authority for transferring tokens
#[account(seeds = [b"reserve_authority", program_id.as_ref()], bump)]
pub reserve_authority: UncheckedAccount<'info>,
#[account(mut)]
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)] pub struct Buy<'info> { #[account(mut)] pub state: Account<'info, State>,
/// CHECK: reserve authority PDA
#[account(seeds = [b"reserve_authority", program_id.as_ref()], bump)]
pub reserve_authority: UncheckedAccount<'info>,
#[account(mut, token::mint = state.mint, token::authority = reserve_authority)]
pub reserve: Account<'info, TokenAccount>,
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut, token::mint = state.mint, token::authority = payer)]
pub user_token: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
// helper to compute lamports sent by client
#[account(mut)]
pub payer_starting_balance: AccountInfo<'info>,
}
#[derive(Accounts)] pub struct Sell<'info> { #[account(mut)] pub state: Account<'info, State>,
#[account(mut, token::mint = state.mint)]
pub reserve: Account<'info, TokenAccount>,
#[account(mut)]
pub user_authority: Signer<'info>,
#[account(mut, token::mint = state.mint, token::authority = user_authority)]
pub user_token: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
// ------------------- Errors -------------------
#[error_code] pub enum ExchangeError { #[msg("No lamports were sent to buy tokens")] NoLamportsSent, #[msg("Invalid price configuration")] InvalidPrice, #[msg("Math overflow")] MathOverflow, #[msg("Slippage exceeded")] SlippageExceeded, #[msg("Invalid amount")] InvalidAmount, }
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
use anchor_lang::prelude::*; use anchor_spl::token::{self, Mint, TokenAccount, Token, MintTo, Transfer, CloseAccount};
declare_id!("ReplaceWithYourProgramId1111111111111111111111");
// Simple Anchor program that: // - creates an SPL mint with a fixed total supply (100_000_000_000 tokens, decimals = 6) // - holds a token reserve owned by the program // - allows users to buy tokens by sending SOL and selling tokens back for SOL // - uses a fixed price (tokens per 1 SOL) and a small fee (in basis points)
// NOTE: // - This is an educational example. Do NOT use in production without audit. // - We use decimals = 6 so total_supply fits into u64 when scaled. // - The program uses CPI to the SPL Token program.
#[program] pub mod anchor_spl_token_exchange { use super::*;
pub fn initialize(
ctx: Context,
price_tokens_per_sol: u64,
fee_bps: u16,
) -> Result<()> {
// price_tokens_per_sol: how many token base-units (i.e. including decimals) equals 1 SOL
// fee_bps: fee in basis points (10000 bps = 100%)
}
// Buy tokens: user attaches SOL (lamports) and receives tokens at fixed price pub fn buy(ctx: Context, min_tokens_out: u64) -> Result<()> {
let state = &ctx.accounts.state;
}
// Sell tokens: user transfers tokens to reserve, program sends SOL back at fixed price pub fn sell(ctx: Context, tokens_in: u64, min_lamports_out: u64) -> Result<()> {
let state = &mut ctx.accounts.state;
}
}
// ------------------- Accounts & State -------------------
#[account] pub struct State { pub mint: Pubkey, pub reserve: Pubkey, pub authority: Pubkey, pub price_tokens_per_sol: u64, pub fee_bps: u16, }
#[derive(Accounts)] #[instruction(price_tokens_per_sol: u64, fee_bps: u16)] pub struct Initialize<'info> { #[account(init, payer = authority, space = 8 + 32*3 + 8 + 2)] pub state: Account<'info, State>,
#[account(
init,
payer = authority,
mint::decimals = 6,
mint::authority = mint_authority,
)]
pub mint: Account<'info, Mint>,
/// CHECK: PDA that will be mint authority
#[account(seeds = [b"mint_authority", program_id.as_ref()], bump)] pub mint_authority: UncheckedAccount<'info>,
#[account(
init,
payer = authority,
token::mint = mint,
token::authority = reserve_authority,
)]
pub reserve: Account<'info, TokenAccount>,
/// CHECK: PDA that will be reserve authority for transferring tokens #[account(seeds = [b"reserve_authority", program_id.as_ref()], bump)] pub reserve_authority: UncheckedAccount<'info>,
#[account(mut)]
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)] pub struct Buy<'info> { #[account(mut)] pub state: Account<'info, State>,
/// CHECK: reserve authority PDA
#[account(seeds = [b"reserve_authority", program_id.as_ref()], bump)] pub reserve_authority: UncheckedAccount<'info>,
#[account(mut, token::mint = state.mint, token::authority = reserve_authority)] pub reserve: Account<'info, TokenAccount>,
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut, token::mint = state.mint, token::authority = payer)] pub user_token: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
// helper to compute lamports sent by client
#[account(mut)]
pub payer_starting_balance: AccountInfo<'info>,
}
#[derive(Accounts)] pub struct Sell<'info> { #[account(mut)] pub state: Account<'info, State>,
#[account(mut, token::mint = state.mint)]
pub reserve: Account<'info, TokenAccount>,
#[account(mut)]
pub user_authority: Signer<'info>,
#[account(mut, token::mint = state.mint, token::authority = user_authority)] pub user_token: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
// ------------------- Errors -------------------
#[error_code] pub enum ExchangeError { #[msg("No lamports were sent to buy tokens")] NoLamportsSent, #[msg("Invalid price configuration")] InvalidPrice, #[msg("Math overflow")] MathOverflow, #[msg("Slippage exceeded")] SlippageExceeded, #[msg("Invalid amount")] InvalidAmount, }