This commit is contained in:
Sewmina 2025-04-12 07:11:12 +00:00
parent 97304366cb
commit d41d3a13c0
10 changed files with 162 additions and 63 deletions

View File

@ -87,7 +87,7 @@ export default function Header() {
</div>
{/* Mobile-only view (Logo & Login) */}
<div className="flex md:hidden items-center scale-80 ">
<div className="flex md:hidden items-center ">
<PrivyButton></PrivyButton>
<button

View File

@ -59,12 +59,14 @@ export default function HeroSection() {
const wallet = wallets.find((_wallet) => _wallet.type === "solana") || wallets[0];
setSolWallet(wallet);
const fetchedBets = await fetchOpenBets(wallet);
const fetchedBets = await fetchOpenBets();
const filteredBets = fetchedBets.filter((bet) => !(bet.owner_id && bet.joiner_id));
const filledBets = fetchedBets.filter((bet) => bet.owner_id && bet.joiner_id);
let activeBet = filledBets.find((bet) => bet.owner_id === user?.id || bet.joiner_id === user?.id);
if(activeBet){
await new Promise(resolve => setTimeout(resolve, 2000));
const betHistoryResponse = await fetch(`${API_URL}get_game_completed.php?address=${activeBet.address}`);
const betHistory = await betHistoryResponse.text();
@ -114,7 +116,7 @@ export default function HeroSection() {
// Step 3: Poll until vault account is found
const pollUntilFound = async (
maxRetries = 10,
delayMs = 3000
delayMs = 10000
): Promise<Bet | undefined> => {
for (let i:number = 0; i < maxRetries; i++) {
const vault = await getVaultByAddress(solWallet, tx);
@ -150,7 +152,7 @@ export default function HeroSection() {
try {
const pollForRematchAddress = async (
maxRetries = 10,
delayMs = 3000
delayMs = 10000
): Promise<string | null> => {
for (let i = 0; i < maxRetries; i++) {
console.log(`Polling rematch address... (${i + 1}/${maxRetries})`);
@ -178,7 +180,7 @@ export default function HeroSection() {
const pollForVault = async (
address: string,
maxRetries = 10,
delayMs = 3000
delayMs = 10000
): Promise<Bet | undefined> => {
for (let i:number = 0; i < maxRetries; i++) {
console.log(`Polling vault for address ${address}... (${i + 1}/${maxRetries})`);
@ -212,7 +214,7 @@ export default function HeroSection() {
if (!ready) return;
updateBets();
const interval = setInterval(updateBets, 3500);
const interval = setInterval(updateBets, 3000);
return () => clearInterval(interval);
}, [ready]);

View File

@ -82,7 +82,7 @@ export default function YourGames({ bets }: GameModalProps) {
setMyBets(enrichedBets);
setLoading(false);
console.log(`Got ${bets.length} bets`);
console.log(`Got ${bets.length} bets, enriched to ${enrichedBets.length}`);
};
useEffect(() => {
@ -103,6 +103,8 @@ export default function YourGames({ bets }: GameModalProps) {
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{myBets.map((bet) => {
console.log(`Bet ${bet}`);
const game = games.find((g) => g.id === bet.id);
if (!game) return null;
@ -135,7 +137,7 @@ export default function YourGames({ bets }: GameModalProps) {
<div className="flex justify-between text-xs font-mono py-1">
<p className="text-white">{bet.wager} SOL</p>
<p className="text-white">{(bet.wager * 2 * WAGER_PRIZE_MULT).toFixed(2)} SOL</p>
<p className="text-white">{(bet.wager * 2 * WAGER_PRIZE_MULT).toFixed(3)} SOL</p>
</div>
{bet.ownerProfile && (
@ -219,7 +221,7 @@ export default function YourGames({ bets }: GameModalProps) {
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Prize:</span>
<span className="font-bold">{(selectedBet.wager * 2 * WAGER_PRIZE_MULT).toFixed(2)} SOL</span>
<span className="font-bold">{(selectedBet.wager * 2 * WAGER_PRIZE_MULT).toFixed(3)} SOL</span>
</div>
</div>
</div>

View File

@ -67,7 +67,7 @@ export default function PrivyButton() {
setSolBalance((balance / 1e9).toFixed(2));
} catch (error) {
console.error("Failed to get balance:", error);
setSolBalance("Error");
setSolBalance("0");
}
}
};
@ -75,7 +75,7 @@ export default function PrivyButton() {
useEffect(() => {
const intervalId = setInterval(() => {
fetchSolBalance();
}, 5000); // 5000 milliseconds = 5 seconds
}, 15000); // 5000 milliseconds = 5 seconds
// Cleanup function to clear the interval on unmount or when `ready` changes
return () => clearInterval(intervalId);

View File

@ -87,6 +87,7 @@ export default function YourGames({bets}:GameModalProps) {
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{myBets.map((bet) => {
console.log(`Bet id:${bet.id}`);
const game = games.find((g) => g.id === bet.id);
if (!game) return null;

View File

@ -1,9 +1,11 @@
import { Connection } from "@solana/web3.js";
export const CLUSTER_URL = "https://api.devnet.solana.com";
export const EXPLORER_ADDRESS_TEMPLATE = "https://explorer.solana.com/address/{address}?cluster=devnet";
export const EXPLORER_TX_TEMPLATE = "https://explorer.solana.com/tx/{address}?cluster=devnet";
// Replace this URL with your dedicated RPC endpoint
// You can get one from providers like QuickNode, Alchemy, Helius, or GenesysGo
export const CLUSTER_URL = "https://tiniest-cold-darkness.solana-mainnet.quiknode.pro/72332d636ff78d498b880bd8fdc3eb646c827da8/";
export const EXPLORER_ADDRESS_TEMPLATE = "https://explorer.solana.com/address/{address}";
export const EXPLORER_TX_TEMPLATE = "https://explorer.solana.com/tx/{address}";
export const connection = new Connection(CLUSTER_URL);
export const API_URL = "https://api.duelfi.io/v1/";
export const VALIDATOR_URL = "https://validator.duelfi.io/";

View File

@ -5,7 +5,7 @@
* IDL can be found at `target/idl/bets.json`.
*/
export type Bets = {
"address": "JAf3ZkQ469okXAzA6BKJeKBb9ZkCtZanULaUsapskoyn",
"address": "Haj94DF925qNRgcoRwQfNsVLKgSmFhG4bjgtvusMkkpD",
"metadata": {
"name": "bets",
"version": "0.1.0",
@ -223,6 +223,48 @@ export type Bets = {
"type": "string"
}
]
},
{
"name": "refundBet",
"discriminator": [
209,
182,
226,
96,
55,
121,
83,
183
],
"accounts": [
{
"name": "betsList",
"writable": true
},
{
"name": "betVault",
"writable": true
},
{
"name": "owner",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "systemProgram",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "owner",
"type": "pubkey"
}
]
}
],
"accounts": [
@ -312,7 +354,7 @@ export type Bets = {
{
"name": "feeCollector",
"type": "string",
"value": "\"cocD4r4yNpHxPq7CzUebxEMyLki3X4d2Y3HcTX5ptUc\""
"value": "\"9esrj2X33pr5og6fdkDMjaW6fdnnb9hT1cWshamxTdL4\""
},
{
"name": "seed",

View File

@ -1,5 +1,5 @@
{
"address": "JAf3ZkQ469okXAzA6BKJeKBb9ZkCtZanULaUsapskoyn",
"address": "Haj94DF925qNRgcoRwQfNsVLKgSmFhG4bjgtvusMkkpD",
"metadata": {
"name": "bets",
"version": "0.1.0",
@ -217,6 +217,48 @@
"type": "string"
}
]
},
{
"name": "refund_bet",
"discriminator": [
209,
182,
226,
96,
55,
121,
83,
183
],
"accounts": [
{
"name": "bets_list",
"writable": true
},
{
"name": "bet_vault",
"writable": true
},
{
"name": "owner",
"writable": true
},
{
"name": "payer",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
}
],
"args": [
{
"name": "owner",
"type": "pubkey"
}
]
}
],
"accounts": [
@ -306,7 +348,7 @@
{
"name": "FEE_COLLECTOR",
"type": "string",
"value": "\"cocD4r4yNpHxPq7CzUebxEMyLki3X4d2Y3HcTX5ptUc\""
"value": "\"9esrj2X33pr5og6fdkDMjaW6fdnnb9hT1cWshamxTdL4\""
},
{
"name": "SEED",
@ -314,4 +356,4 @@
"value": "\"anchor\""
}
]
}
}

View File

@ -1,6 +1,6 @@
import { Commitment, PublicKey } from "@solana/web3.js";
export const FEE_COLLECTOR_PUBKEY = new PublicKey("cocD4r4yNpHxPq7CzUebxEMyLki3X4d2Y3HcTX5ptUc");
export const FEE_COLLECTOR_PUBKEY = new PublicKey("9esrj2X33pr5og6fdkDMjaW6fdnnb9hT1cWshamxTdL4");
export const WAGER_PRIZE_MULT = 0.95;
export const CONFIRMATION_THRESHOLD:Commitment = 'finalized';

View File

@ -1,4 +1,4 @@
import { CLUSTER_URL, connection } from "@/data/shared";
import { CLUSTER_URL, connection, VALIDATOR_URL } from "@/data/shared";
import { Bets } from "@/idl/bets";
import { AnchorProvider, BN, Program } from "@coral-xyz/anchor";
import { ConnectedSolanaWallet } from "@privy-io/react-auth";
@ -8,52 +8,60 @@ import { Bet } from "@/types/Bet";
import { toast } from "sonner";
import { CONFIRMATION_THRESHOLD, FEE_COLLECTOR_PUBKEY } from "./constants";
export const fetchOpenBets = async (wallets: ConnectedSolanaWallet): Promise<Bet[]> => {
try {
if (!wallets) return [];
export async function fetchOpenBets(): Promise<Bet[]> {
const wallet = {
publicKey: new PublicKey(wallets.address),
signTransaction: wallets.signTransaction,
signAllTransactions: wallets.signAllTransactions,
};
const provider = new AnchorProvider(connection, wallet, {
preflightCommitment: CONFIRMATION_THRESHOLD,
});
const response = await fetch(`${VALIDATOR_URL}fetchBets`);
const data = await response.json();
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, CONFIRMATION_THRESHOLD);
return data;
}
// 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
};
})
);
// export const fetchOpenBets = async (wallets: ConnectedSolanaWallet): Promise<Bet[]> => {
// try {
// if (!wallets) return [];
// console.log(`Got ${formattedBets.length} bets`);
// const wallet = {
// publicKey: new PublicKey(wallets.address),
// signTransaction: wallets.signTransaction,
// signAllTransactions: wallets.signAllTransactions,
// };
// const provider = new AnchorProvider(connection, wallet, {
// preflightCommitment: CONFIRMATION_THRESHOLD,
// });
return formattedBets;
} catch (error) {
console.error("Error fetching open bets:", error);
}
return [];
};
// 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, CONFIRMATION_THRESHOLD);
// // 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 {