import { CLUSTER_URL } from "@/data/shared"; import { Bets } from "@/idl/bets"; import { AnchorProvider, BN, Program } from "@coral-xyz/anchor"; import { ConnectedSolanaWallet } from "@privy-io/react-auth"; import { Connection, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; import idl from "../idl/bets_idl.json"; import { Bet } from "@/types/Bet"; import { toast } from "sonner"; import { Game } from "@/types/Game"; import { CONFIRMATION_THRESHOLD, FEE_COLLECTOR_PUBKEY } from "./constants"; export const fetchOpenBets = async (wallets: ConnectedSolanaWallet): Promise => { try { if (!wallets) return []; const connection = new Connection(CLUSTER_URL); const wallet = { publicKey: new PublicKey(wallets.address), signTransaction: wallets.signTransaction, signAllTransactions: wallets.signAllTransactions, }; const provider = new AnchorProvider(connection, wallet, { preflightCommitment: CONFIRMATION_THRESHOLD, }); const program = new Program(idl, provider); const [bet_list_pda] = await PublicKey.findProgramAddress( [Buffer.from("bets_list")], program.programId ); // Fetch all open bet accounts const bet_list = await program.account.betsList.fetch(bet_list_pda); // Extract required bet data const formattedBets = await Promise.all( bet_list.bets.map(async (bet) => { const betAcc = await program.account.betVault.fetch(bet); // console.log(betAcc.gameId); return { address: bet.toString(), id: betAcc.gameId, owner: betAcc.owner.toBase58(), owner_id: betAcc.ownerId, joiner: betAcc.joiner ? betAcc.joiner.toBase58() : "Open", joiner_id: betAcc.joinerId, wager: betAcc.wager.toNumber() / LAMPORTS_PER_SOL }; }) ); // console.log(`Got ${formattedBets.length} bets`); return formattedBets; } catch (error) { console.error("Error fetching open bets:", error); } return []; }; export async function getVaultByAddress(wallets: ConnectedSolanaWallet, address: string): Promise { try { if (!wallets) return undefined; const connection = new Connection(CLUSTER_URL); const wallet = { publicKey: new PublicKey(wallets.address), signTransaction: wallets.signTransaction, signAllTransactions: wallets.signAllTransactions, }; const provider = new AnchorProvider(connection, wallet, { preflightCommitment: CONFIRMATION_THRESHOLD, }); const program = new Program(idl, provider); // Extract required bet data const betAcc = await program.account.betVault.fetch(address); // console.log(betAcc.gameId); return { address: address.toString(), id: betAcc.gameId, owner: betAcc.owner.toBase58(), owner_id: betAcc.ownerId, joiner: betAcc.joiner ? betAcc.joiner.toBase58() : "Open", joiner_id: betAcc.joinerId, wager: betAcc.wager.toNumber() / LAMPORTS_PER_SOL }; } catch (error) { console.error("Error fetching open bets:", error); } return undefined; } export async function closeBet(wallets: ConnectedSolanaWallet, betId: string): Promise { try { const connection = new Connection(CLUSTER_URL); const wallet = { publicKey: new PublicKey(wallets.address), signTransaction: wallets.signTransaction, signAllTransactions: wallets.signAllTransactions, }; const provider = new AnchorProvider(connection, wallet, { preflightCommitment: CONFIRMATION_THRESHOLD, }); const program = new Program(idl, provider); const [bet_list_pda] = await PublicKey.findProgramAddress( [Buffer.from("bets_list")], program.programId ); // Fetch the bet list const betList = await program.account.betsList.fetch(bet_list_pda); let chosenBet: PublicKey | null = null; for (const bet of betList.bets) { const betAcc = await program.account.betVault.fetch(bet); if (betAcc.owner.toBase58() === wallets.address && betAcc.gameId.toString() === betId) { chosenBet = bet; break; } } if (!chosenBet) { console.error("Bet not found or not owned by the user"); return ""; } const winner = new PublicKey(wallets.address); // Execute the closeBet transaction const tx = await program.methods .closeBet(winner) .accounts({ betVault: chosenBet, betsList: bet_list_pda, winner: winner, feeWallet: FEE_COLLECTOR_PUBKEY }) .transaction(); tx.feePayer = new PublicKey(wallets.address); tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; // Sign transaction with Privy const signedTx = await wallet.signTransaction(tx); // Send transaction// Replace with correct RPC endpoint const txId = await connection.sendRawTransaction(signedTx.serialize()); console.log(`Transaction: ${tx}`); return txId; } catch (error) { console.error("Error closing bet:", error); } return ""; } export async function createBet(wallets: ConnectedSolanaWallet, uid: string, selectedPrice: number, selectedGame: string, get_account_address: boolean): Promise { const connection = new Connection(CLUSTER_URL); const wallet = { publicKey: new PublicKey(wallets.address), signTransaction: wallets.signTransaction, signAllTransactions: wallets.signAllTransactions, }; const provider = new AnchorProvider(connection, wallet, { preflightCommitment: CONFIRMATION_THRESHOLD, }); const program = new Program(idl, provider); try { toast.loading("Creating bet..."); const nonce = getRandomInt(100000000); const [bet_list_pda] = await PublicKey.findProgramAddress( [Buffer.from("bets_list")], program.programId ); console.log(`bets list : ${bet_list_pda}`); // Create transaction const tx = await program.methods .createBet(new BN(selectedPrice * 1000000000), uid, selectedGame, new BN(nonce)) .accounts({ betsList: bet_list_pda, }) .transaction(); // Get transaction object instead of RPC call tx.feePayer = new PublicKey(wallets.address); tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; // Sign transaction with Privy const signedTx = await wallet.signTransaction(tx); // Send transaction const txId = await connection.sendRawTransaction(signedTx.serialize()); console.log(`Transaction sent: ${txId}`); toast.success("Bet created successfully"); console.log(`Transaction confirmed: ${txId}`); if (get_account_address) { const gameIdBytes = Buffer.from(selectedGame); // selectedGame is a string (game_id) const nonceBytes = new BN(nonce).toArrayLike(Buffer, "le", 8); // _nonce.to_le_bytes() const [betVaultPda] = await PublicKey.findProgramAddress( [ Buffer.from("bet_vault"), new PublicKey(wallets.address).toBuffer(), // payer gameIdBytes, nonceBytes, ], program.programId ); return betVaultPda.toString(); } else { return txId; } } catch (error: unknown) { toast.dismiss(); const errorMessage = String(error); // Converts error to string safely if (errorMessage.includes("already in use")) { toast.error("You can't make 2 bets for the same game."); } else { toast.error("Failed to create bet."); console.error(error); } } return ""; } export async function joinBet(wallets: ConnectedSolanaWallet, uid: string, gameId: string, address: string): Promise { const connection = new Connection(CLUSTER_URL); const wallet = { publicKey: new PublicKey(wallets.address), signTransaction: wallets.signTransaction, signAllTransactions: wallets.signAllTransactions, }; const provider = new AnchorProvider(connection, wallet, { preflightCommitment: CONFIRMATION_THRESHOLD, }); const program = new Program(idl, provider); try { const [bet_list_pda] = await PublicKey.findProgramAddress( [Buffer.from("bets_list")], program.programId ); const connection = new Connection(CLUSTER_URL, CONFIRMATION_THRESHOLD); const betVaultPubkey = new PublicKey(address); console.log(`bets list : ${bet_list_pda}`); // Create transaction const tx = await program.methods .joinBet(uid, gameId) .accounts({ betVault: betVaultPubkey, }) .transaction(); // Get transaction object instead of RPC call tx.feePayer = new PublicKey(wallets.address); tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; // Sign transaction with Privy const signedTx = await wallet.signTransaction(tx); // Send transaction// Replace with correct RPC endpoint const txId = await connection.sendRawTransaction(signedTx.serialize()); console.log(`Transaction ID: ${txId}`); return txId; } catch (error: unknown) { // const errorMessage = String(error); // Converts error to string safely toast.error("Failed to create bet."); console.error(error); } return ""; } function getRandomInt(max: number): number { return Math.floor(Math.random() * max); }