init
This commit is contained in:
commit
ff1856c5bd
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
**/*.rs.bk
|
||||
node_modules
|
||||
test-ledger
|
||||
.yarn
|
||||
7
.prettierignore
Normal file
7
.prettierignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
test-ledger
|
||||
18
Anchor.toml
Normal file
18
Anchor.toml
Normal 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
2768
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal 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
|
||||
1
bosyxbD8uvYztyQyqg98Fz9WWbRk973h9ZtyVms5FXR.json
Normal file
1
bosyxbD8uvYztyQyqg98Fz9WWbRk973h9ZtyVms5FXR.json
Normal 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
12
migrations/deploy.ts
Normal 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
2567
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
Normal file
22
package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
22
programs/ticket_store/Cargo.toml
Normal file
22
programs/ticket_store/Cargo.toml
Normal 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"
|
||||
2
programs/ticket_store/Xargo.toml
Normal file
2
programs/ticket_store/Xargo.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
||||
4
programs/ticket_store/src/constants.rs
Normal file
4
programs/ticket_store/src/constants.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
#[constant]
|
||||
pub const ID: &str = "";
|
||||
9
programs/ticket_store/src/error.rs
Normal file
9
programs/ticket_store/src/error.rs
Normal 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
|
||||
}
|
||||
46
programs/ticket_store/src/instructions/initialize.rs
Normal file
46
programs/ticket_store/src/instructions/initialize.rs
Normal 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(())
|
||||
}
|
||||
5
programs/ticket_store/src/instructions/mod.rs
Normal file
5
programs/ticket_store/src/instructions/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod initialize;
|
||||
pub use initialize::*;
|
||||
|
||||
pub mod purchase;
|
||||
pub use purchase::*;
|
||||
67
programs/ticket_store/src/instructions/purchase.rs
Normal file
67
programs/ticket_store/src/instructions/purchase.rs
Normal 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>,
|
||||
}
|
||||
25
programs/ticket_store/src/lib.rs
Normal file
25
programs/ticket_store/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
2
programs/ticket_store/src/state/mod.rs
Normal file
2
programs/ticket_store/src/state/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod sales;
|
||||
pub use sales::Sales;
|
||||
10
programs/ticket_store/src/state/sales.rs
Normal file
10
programs/ticket_store/src/state/sales.rs
Normal 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
95
tests/ticket_store.ts
Normal 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
62
tests/ticket_store.ts.bkp
Normal 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);
|
||||
});
|
||||
});
|
||||
1
tsUj8usCK1bY4GYmLDE66tHV167Tby6o5ogkAW4t7FD.json
Normal file
1
tsUj8usCK1bY4GYmLDE66tHV167Tby6o5ogkAW4t7FD.json
Normal 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
10
tsconfig.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user