Skip to content

Commit

Permalink
c1
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjaqbek committed Jul 25, 2024
1 parent b2f8fe3 commit d3918a5
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/LeaderboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,11 @@ const LeaderboardPage: React.FC<LeaderboardPageProps> = ({ elapsedTime, onClose,
{showSaveScoreWindow && (
<SaveScoreWindowContainer>
<SaveScoreWindowContent>
<p>Connect your wallet to set user Id and save your score:</p>
<p>Connect your wallet to play OnChain:</p>
<TonConnectButton />
<StyledButton onClick={handleSaveScoreConfirm} style={{ marginTop: '10px' }}>Save Score</StyledButton>
<StyledButtonSecondary onClick={() =>
console.log("OnChain Race clicked")} style={{ marginTop: '10px' }} disabled={!rawAddress}> OnChain Race </StyledButtonSecondary>
<StyledButton onClick={handleSaveScoreConfirm} style={{ marginTop: '10px' }}>Save Score</StyledButton><br></br>
<StyledButton onClick={handleCloseSaveScoreWindow} style={{ marginTop: '10px' }}>Close</StyledButton>
</SaveScoreWindowContent>
</SaveScoreWindowContainer>
Expand Down
154 changes: 154 additions & 0 deletions src/contracts/race.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
const int OP_JOIN_RACE = 1;
const int OP_SUBMIT_TIME = 2;
const int OP_FINALIZE_RACE = 3;
const int OP_DISTRIBUTE_PRIZE = 5;
const int OP_REFUND_ENTRY_FEE = 6;
const int OP_WITHDRAW_FUNDS = 7;

() recv_internal(int my_balance, cell in_msg, slice in_msg_body) impure {
slice cs = in_msg_body;
int op = cs~load_uint(32);

;; Contract state variables
int race_start_time;
map participants; ;; Map to store participant addresses, join times, and their race times
slice owner_address;
bool race_finalized = false;
int total_entry_fees = 0; ;; Track total entry fees collected
int participant_entry_fee = 1000000000; ;; 1 TON = 1000000000 nanoton

;; Load contract state from persistent storage
slice data = get_data();
if (data.slice_refs() > 0) {
(race_start_time, participants, owner_address, race_finalized, total_entry_fees) = data~load_tuple();
}

;; Join Race
if (op == OP_JOIN_RACE) {
slice participant_address = cs~load_msg_addr();
int current_time = now();

if (race_start_time == 0) {
race_start_time = current_time;
}

tuple participant_info = (current_time, []);
participants = participants.set(participant_address, participant_info);

total_entry_fees += participant_entry_fee; ;; Add entry fee to total
send_raw_message(build_internal_message(participant_address, participant_entry_fee, false, null()), 128);
}

;; Submit Time
if (op == OP_SUBMIT_TIME) {
slice participant_address = cs~load_msg_addr();
int time = cs~load_uint(64);
int current_time = now();

tuple participant_info = participants[participant_address];
int join_time = participant_info.get(0);
cell times = participant_info.get(1);

if (current_time <= join_time + 20 * 60) { ;; 20 minutes after join time
if (times.size() < 3) {
times = times.push(time);
participants = participants.set(participant_address, (join_time, times));
} else {
throw(101); ;; "You can only submit up to 3 times."
}

send_raw_message(build_internal_message(participant_address, 0.002 ton, false, null()), 128);
} else {
throw(102); ;; "Time to submit results has expired."
}
}

;; Finalize Race
if (op == OP_FINALIZE_RACE) {
int current_time = now();
int finalization_time = race_start_time + 3600; ;; 1 hour after race start

if (current_time > finalization_time) {
race_finalized = true;
} else {
throw(103); ;; "Race finalization time has not ended yet."
}
}

;; Distribute Prize
if (op == OP_DISTRIBUTE_PRIZE) {
if (!race_finalized) {
throw(104); ;; "Race has not been finalized yet."
}

slice participant_address = cs~load_msg_addr();
slice winner_address;
int best_time = 9223372036854775807; ;; Maximum 64-bit signed integer value
int winner_entry_fee = 0;

participants.each { (slice addr, tuple participant_info) =>
cell times = participant_info.get(1);
int best_participant_time = times.min();
if (best_participant_time < best_time) {
best_time = best_participant_time;
winner_address = addr;
winner_entry_fee = participant_entry_fee;
}
}

if (participant_address == winner_address) {
;; Calculate the prize: 50% of winner's entry fee + 95% of others' entry fees
int prize_amount = (winner_entry_fee / 2) + ((95 * (total_entry_fees - winner_entry_fee)) / 100);

;; Add a small portion (gas fee) to be left in the contract
int gas_fee = 2000000;
int final_amount = prize_amount - gas_fee;

send_raw_message(build_internal_message(winner_address, final_amount, false, null()), 128);
} else {
throw(105); ;; "Only the winner can claim the prize."
}
}

;; Refund Entry Fee
if (op == OP_REFUND_ENTRY_FEE) {
slice participant_address = cs~load_msg_addr();
int current_time = now();
int join_end_time = race_start_time + 40 * 60; ;; 40 minutes after race start

if (current_time > join_end_time && participants.size() == 1) {
;; Return entry fee (1 TON)
int entry_fee = participant_entry_fee;
send_raw_message(build_internal_message(participant_address, entry_fee, false, null()), 128);
} else {
throw(106); ;; "Refund is not allowed."
}
}

;; Withdraw Funds by Owner
if (op == OP_WITHDRAW_FUNDS) {
slice sender_address = in_msg~load_msg_addr();
if (sender_address != owner_address) {
throw(107); ;; "Only the owner can withdraw funds."
}

;; Subtract the gas fee from the current balance to calculate withdrawal amount
int gas_fee = 2000000;
int withdrawal_amount = my_balance - gas_fee;

;; Ensure the withdrawal amount is non-negative
if (withdrawal_amount < 0) {
throw(108); ;; "Insufficient balance for withdrawal."
}

;; Send the remaining balance to the owner
send_raw_message(build_internal_message(owner_address, withdrawal_amount, false, null()), 128);
}

;; Save contract state
set_data(store_tuple((race_start_time, participants, owner_address, race_finalized, total_entry_fees)));
}

() recv_external(int my_balance, cell in_msg, slice in_msg_body) impure {
throw(100); ;; External messages not supported
}
68 changes: 68 additions & 0 deletions src/hooks/useRace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useState, useEffect } from "react";
import Race from "../contracts/race";
import { useTonClient } from "./useTonClient";
import { useAsyncInitialize } from "./useAsyncInitialize";
import { useTonConnect } from "./useTonConnect";
import { Address, OpenedContract } from "ton-core";
import { useQuery } from "@tanstack/react-query";
import { CHAIN } from "@tonconnect/protocol";

export function useRaceContract() {
const { client } = useTonClient();
const { sender, network } = useTonConnect();

const raceContract = useAsyncInitialize(async () => {
if (!client) return;
const owner = Address.parse("YourOwnerAddressHere"); // Replace with the owner's address
const contract = new Race(
Address.parse(
network === CHAIN.MAINNET
? "EQBpRaceMainNetAddressHere" // Replace with mainnet address
: "EQBraceTestNetAddressHere" // Replace with testnet address
),
owner
);
return client.open(contract) as OpenedContract<Race>;
}, [client]);

const { data: raceStartTime, refetch: refetchRaceStartTime } = useQuery(
["raceStartTime"],
async () => {
if (!raceContract) return null;
return raceContract.getRaceStartTime();
},
{ refetchInterval: 3000 }
);

const { data: participants, refetch: refetchParticipants } = useQuery(
["participants"],
async () => {
if (!raceContract) return null;
// Fetch and return participants (you may need to implement this method in Race contract)
},
{ refetchInterval: 3000 }
);

const { data: winner, refetch: refetchWinner } = useQuery(
["winner"],
async () => {
if (!raceContract) return null;
return raceContract.getWinner();
},
{ refetchInterval: 3000 }
);

return {
raceStartTime,
participants,
winner,
joinRace: () => raceContract?.joinRace(sender),
submitTime: (time: number) => raceContract?.submitTime(sender, time),
finalizeRace: () => raceContract?.finalizeRace(sender),
distributePrize: () => raceContract?.distributePrize(sender),
refundEntryFee: () => raceContract?.refundEntryFee(sender),
refetchRaceStartTime,
refetchParticipants,
refetchWinner,
};
}

0 comments on commit d3918a5

Please sign in to comment.