This commit is contained in:
Sewmina Dilshan 2024-10-26 15:19:21 +05:30
commit ff1856c5bd
24 changed files with 7104 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.anchor
.DS_Store
target
**/*.rs.bk
node_modules
test-ledger
.yarn

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
.anchor
.DS_Store
target
node_modules
dist
build
test-ledger

18
Anchor.toml Normal file
View File

@ -0,0 +1,18 @@
[toolchain]
[features]
resolution = true
skip-lint = false
[programs.localnet]
ticket_store = "8w2kJrGZyPrhd2cN68x16wwjwaAmngZZcgudDz2k9C9Z"
[registry]
url = "https://api.apr.dev"
[provider]
cluster = "Localnet"
wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

2768
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

14
Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[workspace]
members = [
"programs/*"
]
resolver = "2"
[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1

View File

@ -0,0 +1 @@
[204,41,160,16,100,242,215,225,76,59,232,247,60,243,217,249,143,176,141,127,239,162,107,196,0,50,22,142,222,142,226,214,22,131,34,111,37,174,120,75,87,176,235,187,68,171,40,237,50,89,179,221,103,129,165,40,194,207,241,50,245,108,126,136]

12
migrations/deploy.ts Normal file
View File

@ -0,0 +1,12 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@coral-xyz/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

2567
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"license": "ISC",
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@solana/spl-token": "^0.4.9",
"anchor-bankrun": "^0.5.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
}

View File

@ -0,0 +1,22 @@
[package]
name = "ticket_store"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "ticket_store"
[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
[dependencies]
anchor-lang = {version="0.30.1", features=["init-if-needed"]}
anchor-spl= "0.30.1"
solana-program = "=2.0.3"

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,4 @@
use anchor_lang::prelude::*;
#[constant]
pub const ID: &str = "";

View File

@ -0,0 +1,9 @@
use anchor_lang::prelude::*;
#[error_code]
pub enum CustomErrors {
#[msg("Custom error message")]
CustomError,
#[msg("Insufficient funds to purchase a ticket, Recharge your wallet and try again")]
InsufficientFunds
}

View File

@ -0,0 +1,46 @@
use anchor_lang::prelude::*;
use anchor_spl:: token::{Mint, Token, TokenAccount};
use crate::Sales;
#[derive(Accounts)]
pub struct Initialize <'info>{
#[account(mut)]
pub signer: Signer<'info>,
#[account(
init,
payer = signer,
space = 8 + Sales::INIT_SPACE,
seeds= [b"sales", signer.key().as_ref()],
bump
)]
pub sales_account: Account<'info, Sales>,
#[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,
)]
pub signer_token_account: Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
}
pub fn handler(ctx: Context<Initialize>) -> Result<()> {
let sales_account = &mut ctx.accounts.sales_account;
sales_account.owner = ctx.accounts.signer.key();
sales_account.seller_ata = ctx.accounts.signer_token_account.key();
Ok(())
}

View File

@ -0,0 +1,5 @@
pub mod initialize;
pub use initialize::*;
pub mod purchase;
pub use purchase::*;

View File

@ -0,0 +1,67 @@
use anchor_lang::{prelude::*, solana_program::native_token::LAMPORTS_PER_SOL};
use anchor_spl::{associated_token::AssociatedToken, token::{self, Mint, Token, TokenAccount}};
use crate::{error::CustomErrors, Sales};
const TICKET_PRICE:u64 = LAMPORTS_PER_SOL *1;
pub fn purchase_ticket(ctx: Context<PurchaseTickets>, 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);
// Transfer SOL from payer to the sales account owner
anchor_lang::system_program::transfer(
CpiContext::new(
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 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(),
};
let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
token::transfer(cpi_context, amount)?;
Ok(())
}
#[derive(Accounts)]
pub struct PurchaseTickets<'info>{
#[account(mut)]
pub payer:Signer<'info>,
#[account(mut)]
pub owner:Signer<'info>,
#[account(
init_if_needed,
payer = payer,
associated_token::mint =mint,
associated_token::authority = payer
)]
pub buyer_ata:Account<'info, TokenAccount>,
pub mint: Account<'info, Mint>,
#[account(mut, has_one=owner, has_one=seller_ata)]
pub sales: Account<'info, Sales>,
pub seller_ata: Account<'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>,
}

View File

@ -0,0 +1,25 @@
pub mod constants;
pub mod error;
pub mod instructions;
pub mod state;
use anchor_lang::prelude::*;
pub use constants::*;
pub use instructions::*;
pub use state::*;
declare_id!("8w2kJrGZyPrhd2cN68x16wwjwaAmngZZcgudDz2k9C9Z");
#[program]
pub mod ticket_store {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
initialize::handler(ctx)
}
pub fn purchase_tickets(ctx:Context<PurchaseTickets>, amount:u64)->Result<()>{
purchase::purchase_ticket(ctx, amount)
}
}

View File

@ -0,0 +1,2 @@
pub mod sales;
pub use sales::Sales;

View File

@ -0,0 +1,10 @@
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
}

95
tests/ticket_store.ts Normal file
View File

@ -0,0 +1,95 @@
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<TicketStore>;
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
);
// 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);
})
});

62
tests/ticket_store.ts.bkp Normal file
View File

@ -0,0 +1,62 @@
import * as anchor from "@coral-xyz/anchor";
import { Program} from "@coral-xyz/anchor";
import {Connection, Keypair, PublicKey} from "@solana/web3.js";
import { TicketStore } from "../target/types/ticket_store";
import {createMint, getAccount} from "@solana/spl-token";
import { expect } from "chai";
const IDL = require('../target/idl/ticket_store.json');
const programID = new PublicKey("Cy1DGkDcqwL5viNnbiZZgQR4PSzv6rtCDmEXj9bv8cVc");
const ticketMint = Keypair.fromSecretKey(Uint8Array.from([229,133,46,6,15,114,88,149,178,253,116,33,217,111,94,244,105,170,38,6,63,11,240,208,100,188,213,19,214,172,25,104,13,66,62,178,5,210,60,27,205,73,155,208,220,115,21,71,229,90,136,12,171,103,219,77,230,84,214,49,107,123,186,61]));
const connection = new Connection("https://api.devnet.solana.com");
const walletKeyPair = Keypair.fromSecretKey(Uint8Array.from([202,150,67,41,155,133,176,172,9,100,150,190,239,37,69,73,18,16,76,65,164,197,99,134,240,151,112,65,61,122,95,41,9,44,6,237,108,123,86,90,144,27,1,160,101,95,239,35,53,91,195,220,22,214,2,84,132,37,20,236,133,242,104,197]));
describe("ticket_store", () => {
let wallet:anchor.Wallet = new anchor.Wallet(walletKeyPair);;
let provider:anchor.AnchorProvider = new anchor.AnchorProvider(connection,wallet);
anchor.setProvider(provider);
let program:Program<TicketStore>;
let tokenAccount;
before(async()=>{
// console.log("Starting test env");
// // context = await startAnchor("", [{name:"ticket_store",programId: programID}],[]);
// // provider = new BankrunProvider(context);
// wallet = new anchor.Wallet(walletKeyPair);
// provider = new anchor.AnchorProvider(connection,wallet);
// program = new Program<TicketStore>(IDL,provider);
// console.log("Program is init");
program = anchor.workspace.TicketStore as Program<TicketStore>;
console.log("Program is init");
console.log(program.programId);
try{
await createMint(
provider.connection,
wallet.payer,
wallet.publicKey,
wallet.publicKey,
0,
ticketMint
)
console.log("minted tickets");
}catch{
console.log(`tickets are already minted mint:${ticketMint.publicKey}`)
}
})
it("Is initialized!", async () => {
// Add your test here.
console.log(`Initializing ${program.programId.toBase58()}`);
const tx = await program.methods.initialize().accounts({mint:ticketMint.publicKey}).rpc()
console.log("Your transaction signature", tx);
const [sellerAccountAddress] = PublicKey.findProgramAddressSync([Buffer.from("sales"), wallet.publicKey.toBuffer()], programID);
const sellerAccount = await program.account.sales.fetch(sellerAccountAddress);
console.log(sellerAccount);
expect(sellerAccount.owner).equal(wallet.publicKey);
});
});

View File

@ -0,0 +1 @@
[193,21,186,69,186,122,101,19,62,69,196,40,89,235,139,229,232,64,236,94,101,251,158,72,220,80,29,53,239,30,164,182,13,73,178,108,125,105,163,66,6,198,179,202,35,57,74,95,112,72,64,42,124,67,58,144,235,100,185,240,64,71,65,24]

10
tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}

1328
yarn.lock Normal file

File diff suppressed because it is too large Load Diff