289 lines
9.1 KiB
TypeScript
289 lines
9.1 KiB
TypeScript
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<Bet[]> => {
|
|
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<Bets>(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<Bet | undefined> {
|
|
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<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, betId: string): Promise<string> {
|
|
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<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");
|
|
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<string> {
|
|
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<Bets>(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<string> {
|
|
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<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);
|
|
} |