duelfi/src/shared/solana_helpers.ts
2025-04-30 10:20:41 +00:00

300 lines
9.6 KiB
TypeScript

import { CLUSTER_URL, connection, VALIDATOR_URL, VALIDATOR_URL_DEV } 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 { CONFIRMATION_THRESHOLD, FEE_COLLECTOR_PUBKEY } from "./constants";
export async function fetchOpenBets(): Promise<Bet[]> {
const response = await fetch(`${VALIDATOR_URL}fetchBets`);
const data = await response.json();
console.log(`Fetched ${data.length} open bets from validator`);
return data;
}
export async function fetchOpenBetsDev(): Promise<Bet[]> {
const response = await fetch(`${VALIDATOR_URL_DEV}fetchBets`);
const data = await response.json();
console.log(`Fetched ${data.length} open bets from validator`);
return data;
}
// export const fetchOpenBetsSol = async (wallets: ConnectedSolanaWallet): Promise<Bet[]> => {
// try {
// if (!wallets) return [];
// 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<Bets>(idl, provider);
// const [bet_list_pda] = await PublicKey.findProgramAddress(
// [Buffer.from("bets_list")],
// program.programId
// );
// console.log(`bet_list_pda: ${bet_list_pda}`);
// // Fetch all open bet accounts
// const bet_list = await program.account.betsList.fetch(bet_list_pda, CONFIRMATION_THRESHOLD);
// console.log(`Fetched ${bet_list.bets.length} open bets from solana`);
// // 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<Bet | undefined> {
try {
if (!wallets) return undefined;
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<Bets>(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, uid:string, betId: string): Promise<string> {
try {
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<Bets>(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\n${wallets.address}\n${betId}`);
return "";
}
const winner = new PublicKey(wallets.address);
// Execute the closeBet transaction
const tx = await program.methods
.closeBet(winner, uid)
.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<string> {
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<Bets>(idl, provider);
try {
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}`);
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<string> {
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<Bets>(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);
}