diff --git a/Anchor.toml b/Anchor.toml index 4eb6419..53161a7 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -5,7 +5,7 @@ resolution = true skip-lint = false [programs.localnet] -ticket_store = "6T3kQi1i8fHpeqWYcBDrxqv7TBSqW63YasajrnuLZRsf" +ticket_store = "BX8z8nGWybFRW3rs6Novhp7oERFqek1QqfESXRB1GPWr" [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 7769fea..2ea7cae 100644 --- a/programs/ticket_store/src/instructions/add_seller.rs +++ b/programs/ticket_store/src/instructions/add_seller.rs @@ -47,7 +47,7 @@ pub fn handler(ctx: Context) -> Result<()> { 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()); diff --git a/programs/ticket_store/src/instructions/purchase.rs b/programs/ticket_store/src/instructions/purchase.rs index ba72ce3..38ef989 100644 --- a/programs/ticket_store/src/instructions/purchase.rs +++ b/programs/ticket_store/src/instructions/purchase.rs @@ -17,19 +17,37 @@ pub fn purchase_ticket(ctx: Context, amount:u64)->Result<()>{ 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(), + 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(); + + let seeds = &[ + b"ticket_seller".as_ref(), + owner_key.as_ref(), + mint_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: sales_account.to_account_info(), + authority: ctx.accounts.sales.to_account_info(), }; - let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts); + + let cpi_context = CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + cpi_accounts, + &signer_seeds + ); token::transfer(cpi_context, amount)?; Ok(()) } @@ -42,7 +60,7 @@ pub struct PurchaseTickets<'info>{ pub payer:Signer<'info>, #[account(mut)] - pub owner:Signer<'info>, + pub owner:SystemAccount<'info>, #[account( init_if_needed, @@ -53,9 +71,21 @@ pub struct PurchaseTickets<'info>{ pub buyer_ata:Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, - #[account(mut, has_one=owner, has_one=seller_ata)] + #[account( + mut, + has_one=seller_ata, + seeds= [b"sales", owner.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, + )] pub seller_ata: Account<'info, TokenAccount>, // /// CHECK: diff --git a/programs/ticket_store/src/lib.rs b/programs/ticket_store/src/lib.rs index c038bf8..59d2ffd 100644 --- a/programs/ticket_store/src/lib.rs +++ b/programs/ticket_store/src/lib.rs @@ -9,7 +9,7 @@ pub use constants::*; pub use instructions::*; pub use state::*; -declare_id!("5kgwVNdKDndFYNkUhZ5TvkNXxEJngMSczfS61qNALhzJ"); +declare_id!("BX8z8nGWybFRW3rs6Novhp7oERFqek1QqfESXRB1GPWr"); #[program] pub mod ticket_store { diff --git a/programs/ticket_store/src/state/sales.rs b/programs/ticket_store/src/state/sales.rs index d48b075..648aec3 100644 --- a/programs/ticket_store/src/state/sales.rs +++ b/programs/ticket_store/src/state/sales.rs @@ -6,5 +6,6 @@ pub struct Sales{ pub owner: Pubkey, pub seller_ata: Pubkey, pub sales_count:u64, - pub last_buyer: Pubkey + pub last_buyer: Pubkey, + pub bump:u8 } \ No newline at end of file diff --git a/tests/ticket_store.ts b/tests/ticket_store.ts index c39e457..0ee98be 100644 --- a/tests/ticket_store.ts +++ b/tests/ticket_store.ts @@ -55,6 +55,7 @@ describe("TicketStore - Sales Initialization", () => { program.programId ); + await program.methods.initialize().rpc(); // Call the initialize function await program.methods .initialize() diff --git a/tests/ticket_store.ts.working.bkp b/tests/ticket_store.ts.working.bkp new file mode 100644 index 0000000..0ee98be --- /dev/null +++ b/tests/ticket_store.ts.working.bkp @@ -0,0 +1,96 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram } from "@solana/web3.js"; +import { TicketStore } from "../target/types/ticket_store"; +import { createMint, createAccount, getAccount, TOKEN_PROGRAM_ID, mintTo } from "@solana/spl-token"; +import { expect } from "chai"; + +describe("TicketStore - Sales Initialization", () => { + + const signer = Keypair.generate(); + const buyer = Keypair.generate(); + + // Set up the Anchor provider + const provider = new anchor.AnchorProvider(anchor.AnchorProvider.env().connection, new anchor.Wallet(signer)); + anchor.setProvider(provider); + + // Get the program using its IDL and TypeScript type + const program = anchor.workspace.TicketStore as Program; + + + let salesAccountPda = null; + let signerTokenAccountPda = null; + let mintAccount = null; + + before(async()=>{ + const connection = provider.connection; + + }) + + it("Initializes the sales account and token account", async () => { + console.log(program.programId); + // Request an airdrop to fund the signer + const connection = provider.connection; + await connection.confirmTransaction( + await connection.requestAirdrop(signer.publicKey, anchor.web3.LAMPORTS_PER_SOL * 100) + ); + + // Create a new mint account for tokens + mintAccount = await createMint( + provider.connection, + signer, // Fee payer + signer.publicKey, // Mint authority + null, // Freeze authority + 9 // Decimals + ); + + // Derive PDAs for the sales and token accounts + [salesAccountPda] = await PublicKey.findProgramAddress( + [Buffer.from("sales"), signer.publicKey.toBuffer()], + program.programId + ); + + [signerTokenAccountPda] = await PublicKey.findProgramAddress( + [Buffer.from("ticket_seller"), signer.publicKey.toBuffer(), mintAccount.toBuffer()], + program.programId + ); + + await program.methods.initialize().rpc(); + // Call the initialize function + await program.methods + .initialize() + .accounts({ + mint: mintAccount, + }) + .signers([signer]) + .rpc(); + + // Fetch account data to verify initialization + const salesAccount = await program.account.sales.fetch(salesAccountPda); + const signerTokenAccount = await getAccount(provider.connection, signerTokenAccountPda); + console.log(salesAccount); + await mintTo(provider.connection, signer, mintAccount, signerTokenAccountPda, signer, LAMPORTS_PER_SOL); + // Assertions + expect(salesAccount.owner.toBase58()).to.equal(signer.publicKey.toBase58()); + expect(salesAccount.sellerAta.toBase58()).to.equal(signerTokenAccountPda.toBase58()); + expect(signerTokenAccount.mint.toBase58()).to.equal(mintAccount.toBase58()); + expect(signerTokenAccount.owner.toBase58()).to.equal(salesAccountPda.toBase58()); + }); + + it("Purchases a ticket", async()=>{ + const provider = new anchor.AnchorProvider(anchor.AnchorProvider.env().connection, new anchor.Wallet(buyer)); + anchor.setProvider(provider); + console.log(`requesting airdrop to ${buyer.publicKey}`); + const airdropTx = await provider.connection.requestAirdrop(buyer.publicKey, LAMPORTS_PER_SOL * 100); + await provider.connection.confirmTransaction(airdropTx); + + console.log(await provider.connection.getBalance(buyer.publicKey)); + + await program.methods.purchaseTickets(new anchor.BN(1)).accounts( + {signer:buyer.publicKey,sales: salesAccountPda,owner:signer.publicKey,mint:mintAccount, sellerAta:signerTokenAccountPda} + ).rpc(); + + const salesAccount = await program.account.sales.fetch(salesAccountPda); + console.log(salesAccount); + }) +});