diff --git a/Anchor.toml b/Anchor.toml index 53161a7..0af1325 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -5,7 +5,7 @@ resolution = true skip-lint = false [programs.localnet] -ticket_store = "BX8z8nGWybFRW3rs6Novhp7oERFqek1QqfESXRB1GPWr" +ticket_store = "GtT61qMWBYLa7X1WJfEehwB45gzDWwioM2ADAZ1Tcjve" [registry] url = "https://api.apr.dev" diff --git a/programs/ticket_store/src/instructions/add_seller.rs b/programs/ticket_store/src/instructions/add_seller.rs index 2ea7cae..060940d 100644 --- a/programs/ticket_store/src/instructions/add_seller.rs +++ b/programs/ticket_store/src/instructions/add_seller.rs @@ -1,32 +1,45 @@ use anchor_lang::prelude::*; -use anchor_spl:: token::{Mint, Token, TokenAccount}; - +use anchor_spl::{ + associated_token::AssociatedToken, token_interface::{Mint, TokenAccount, TokenInterface, close_account, transfer_checked, CloseAccount, TransferChecked} +}; use crate::{Sales, SellersRegistry, SELLERS_REGISTRY_SEED}; +use super::transfer_tokens; + #[derive(Accounts)] pub struct AddSeller <'info>{ #[account(mut)] - pub signer: Signer<'info>, + pub seller: Signer<'info>, #[account( init, - payer = signer, + payer = seller, space = 8 + Sales::INIT_SPACE, - seeds= [b"sales", signer.key().as_ref()], + seeds= [b"sales", seller.key().as_ref()], bump )] - pub sales_account: Account<'info, Sales>, + pub sales: Account<'info, Sales>, + + #[account(mint::token_program = token_program)] + pub mint: InterfaceAccount<'info, Mint>, + + #[account( + mut, + associated_token::mint=mint, + associated_token::authority=seller, + associated_token::token_program = token_program + )] + pub seller_token_account: InterfaceAccount<'info, TokenAccount>, #[account( init, - payer = signer, - seeds = [b"ticket_seller".as_ref(), signer.key().as_ref(), mint.key().as_ref()], - bump, - token::mint = mint, - token::authority = sales_account, + payer =seller, + associated_token::mint=mint, + associated_token::authority = sales, + associated_token::token_program= token_program )] - pub signer_token_account: Account<'info, TokenAccount>, + pub vault: InterfaceAccount<'info, TokenAccount>, #[account( mut, @@ -35,21 +48,33 @@ pub struct AddSeller <'info>{ )] pub sellers_registry: Account<'info, SellersRegistry>, - pub mint: Account<'info, Mint>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, + pub token_program: Interface<'info, TokenInterface>, + pub associated_token_program: Program<'info, AssociatedToken> } -pub fn handler(ctx: Context) -> Result<()> { - let sales_account = &mut ctx.accounts.sales_account; +pub fn send_tickets_to_vault(ctx: &Context, tickets_count: u64) -> Result<()>{ + transfer_tokens( + &ctx.accounts.seller_token_account, + &ctx.accounts.vault, + &tickets_count, + &ctx.accounts.mint, + &ctx.accounts.seller, + &ctx.accounts.token_program + ) +} - sales_account.owner = ctx.accounts.signer.key(); - sales_account.seller_ata = ctx.accounts.signer_token_account.key(); - sales_account.bump = ctx.bumps.sales_account; - ctx.accounts.sellers_registry.sales_pdas.push(sales_account.key()); +pub fn save_sales_account(ctx: Context) -> Result<()> { + ctx.accounts.sales.set_inner( + Sales{ + seller: ctx.accounts.seller.key(), + mint: ctx.accounts.mint.key(), + bump: ctx.bumps.sales + } + ); + ctx.accounts.sellers_registry.sales_pdas.push(ctx.accounts.sales.key()); Ok(()) } diff --git a/programs/ticket_store/src/instructions/mod.rs b/programs/ticket_store/src/instructions/mod.rs index 74c4fd6..ffa734f 100644 --- a/programs/ticket_store/src/instructions/mod.rs +++ b/programs/ticket_store/src/instructions/mod.rs @@ -5,4 +5,7 @@ pub mod add_seller; pub use add_seller::*; pub mod purchase; -pub use purchase::*; \ No newline at end of file +pub use purchase::*; + +pub mod shared; +pub use shared::*; \ No newline at end of file diff --git a/programs/ticket_store/src/instructions/purchase.rs b/programs/ticket_store/src/instructions/purchase.rs index 38ef989..acb3b8e 100644 --- a/programs/ticket_store/src/instructions/purchase.rs +++ b/programs/ticket_store/src/instructions/purchase.rs @@ -1,55 +1,58 @@ -use anchor_lang::{prelude::*, solana_program::native_token::LAMPORTS_PER_SOL}; -use anchor_spl::{associated_token::AssociatedToken, token::{self, Mint, Token, TokenAccount}}; +use anchor_lang::{prelude::*, solana_program::{native_token::LAMPORTS_PER_SOL, system_instruction}}; +use anchor_spl::{ + associated_token::AssociatedToken, token_interface::{Mint, TokenAccount, TokenInterface, close_account, transfer_checked, CloseAccount, TransferChecked} +}; use crate::{error::CustomErrors, Sales}; -const TICKET_PRICE:u64 = LAMPORTS_PER_SOL *1; -pub fn purchase_ticket(ctx: Context, amount:u64)->Result<()>{ - let total_price = amount * TICKET_PRICE; - let sales_account = &mut ctx.accounts.sales; - // msg!(&ctx.accounts.payer.lamports().to_string()); - // require!(ctx.accounts.payer.lamports() >= total_price,CustomErrors::InsufficientFunds); +use super::transfer_tokens; - // Transfer SOL from payer to the sales account owner - anchor_lang::system_program::transfer( - CpiContext::new( +pub fn pay_seller(ctx:&Context, amount:u64)->Result<()>{ + let from_account = &ctx.accounts.buyer; + let to_account = &ctx.accounts.seller; + + let transfer_instruction = system_instruction::transfer( + from_account.key, + to_account.key, + amount); + + anchor_lang::solana_program::program::invoke_signed( + &transfer_instruction, + &[ + from_account.to_account_info(), + to_account.to_account_info(), ctx.accounts.system_program.to_account_info(), - anchor_lang::system_program::Transfer { - from: ctx.accounts.payer.to_account_info(), - to: ctx.accounts.owner.to_account_info() - }, - ), - total_price, + ], + &[], )?; - // Transfer tokens from seller_ata to buyer_ata - - let owner_key = ctx.accounts.owner.key(); - let mint_key = ctx.accounts.mint.key(); + Ok(()) +} +pub fn handover_tickets(ctx:Context, amount:u64)->Result<()>{ let seeds = &[ - b"ticket_seller".as_ref(), - owner_key.as_ref(), - mint_key.as_ref(), - &[ctx.accounts.sales.bump], - ]; + b"sales", + ctx.accounts.seller.to_account_info().key.as_ref(), + &[ctx.accounts.sales.bump] + ]; let signer_seeds = [&seeds[..]]; - let cpi_accounts = anchor_spl::token::Transfer { - from: ctx.accounts.seller_ata.to_account_info(), - to: ctx.accounts.buyer_ata.to_account_info(), - authority: ctx.accounts.sales.to_account_info(), + let accounts = TransferChecked{ + from: ctx.accounts.vault.to_account_info(), + to: ctx.accounts.buyer_token_account.to_account_info(), + mint: ctx.accounts.mint.to_account_info(), + authority: ctx.accounts.sales.to_account_info() }; let cpi_context = CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - cpi_accounts, + ctx.accounts.token_program.to_account_info(), + accounts, &signer_seeds ); - token::transfer(cpi_context, amount)?; - Ok(()) + + transfer_checked(cpi_context, amount, ctx.accounts.mint.decimals) } @@ -57,41 +60,44 @@ pub fn purchase_ticket(ctx: Context, amount:u64)->Result<()>{ pub struct PurchaseTickets<'info>{ #[account(mut)] - pub payer:Signer<'info>, + pub buyer:Signer<'info>, #[account(mut)] - pub owner:SystemAccount<'info>, + pub seller:SystemAccount<'info>, + + pub mint: InterfaceAccount<'info, Mint>, #[account( init_if_needed, - payer = payer, - associated_token::mint =mint, - associated_token::authority = payer + payer= buyer, + associated_token::mint = mint, + associated_token::authority = buyer, + associated_token::token_program = token_program )] - pub buyer_ata:Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, + pub buyer_token_account: Box>, + + #[account(mut)] + pub seller_account:SystemAccount<'info>, #[account( mut, - has_one=seller_ata, - seeds= [b"sales", owner.key().as_ref()], + has_one=seller, + has_one = mint, + seeds= [b"sales", seller.key().as_ref()], bump = sales.bump )] pub sales: Account<'info, Sales>, - #[account( mut, - seeds = [b"ticket_seller".as_ref(), owner.key().as_ref(), mint.key().as_ref()], - bump, - token::mint = mint, - token::authority = sales, + associated_token::mint= mint, + associated_token::authority = sales, + associated_token::token_program = token_program )] - pub seller_ata: Account<'info, TokenAccount>, - + vault: InterfaceAccount<'info, TokenAccount>, // /// CHECK: // pub owner: AccountInfo<'info>, pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, + pub token_program: Interface<'info, TokenInterface>, + pub associated_token_program: Program<'info, AssociatedToken> } \ No newline at end of file diff --git a/programs/ticket_store/src/instructions/shared.rs b/programs/ticket_store/src/instructions/shared.rs new file mode 100644 index 0000000..7f68b53 --- /dev/null +++ b/programs/ticket_store/src/instructions/shared.rs @@ -0,0 +1,25 @@ +use anchor_lang::prelude::*; +use anchor_spl::token_interface::{TokenAccount, TokenInterface, TransferChecked, Mint, transfer_checked}; + +pub fn transfer_tokens<'info> ( + from: &InterfaceAccount<'info,TokenAccount>, + to: &InterfaceAccount<'info,TokenAccount>, + amount: &u64, + mint: &InterfaceAccount<'info, Mint>, + authority: &Signer<'info>, + token_program: &Interface<'info, TokenInterface> +)-> Result<()>{ + + let transfer_accounts_options = TransferChecked{ + from: from.to_account_info(), + mint: mint.to_account_info(), + to: to.to_account_info(), + authority: authority.to_account_info(), + }; + + let cpi_context = CpiContext::new(token_program.to_account_info(), transfer_accounts_options); + + transfer_checked(cpi_context, *amount, mint.decimals)?; + + Ok(()) +} \ No newline at end of file diff --git a/programs/ticket_store/src/lib.rs b/programs/ticket_store/src/lib.rs index 59d2ffd..3b20108 100644 --- a/programs/ticket_store/src/lib.rs +++ b/programs/ticket_store/src/lib.rs @@ -3,14 +3,13 @@ pub mod error; pub mod instructions; pub mod state; -use anchor_lang::prelude::*; +use anchor_lang::{prelude::*, solana_program::native_token::LAMPORTS_PER_SOL}; pub use constants::*; pub use instructions::*; pub use state::*; -declare_id!("BX8z8nGWybFRW3rs6Novhp7oERFqek1QqfESXRB1GPWr"); - +declare_id!("GtT61qMWBYLa7X1WJfEehwB45gzDWwioM2ADAZ1Tcjve"); #[program] pub mod ticket_store { use super::*; @@ -19,11 +18,14 @@ pub mod ticket_store { initialize::handler(ctx) } - pub fn add_seller(ctx: Context) -> Result<()> { - add_seller::handler(ctx) + pub fn add_seller(ctx: Context, tickets_count:u64) -> Result<()> { + add_seller::send_tickets_to_vault(&ctx, tickets_count)?; + add_seller::save_sales_account(ctx) } + pub fn purchase_tickets(ctx:Context, amount:u64)->Result<()>{ - purchase::purchase_ticket(ctx, amount) + purchase::pay_seller(&ctx, amount)?; + purchase::handover_tickets(ctx, amount) } } diff --git a/programs/ticket_store/src/state/sales.rs b/programs/ticket_store/src/state/sales.rs index 648aec3..44ac2cf 100644 --- a/programs/ticket_store/src/state/sales.rs +++ b/programs/ticket_store/src/state/sales.rs @@ -3,9 +3,7 @@ use anchor_lang::prelude::*; #[account] #[derive(InitSpace)] pub struct Sales{ - pub owner: Pubkey, - pub seller_ata: Pubkey, - pub sales_count:u64, - pub last_buyer: Pubkey, + pub seller: Pubkey, + pub mint : Pubkey, pub bump:u8 } \ No newline at end of file