From a9a8bf1d06ac335bf5e4b01edd1cfd8f1ad21b1a Mon Sep 17 00:00:00 2001 From: Lulox Date: Mon, 3 Jul 2023 18:17:21 -0300 Subject: [PATCH] Added a ton of code, might be unnecessary --- migration/DOCUMENTATION.md | 104 +++++++++++ migration/EXPAND-README.md | 172 ------------------ migration/README.md | 135 ++------------ packages/nextjs/pages/index.tsx | 94 ++++++---- .../nextjs/pages/sportsbook/ChallengeCard.tsx | 8 +- packages/nextjs/types/SportsbookTypes.ts | 22 ++- 6 files changed, 203 insertions(+), 332 deletions(-) create mode 100644 migration/DOCUMENTATION.md diff --git a/migration/DOCUMENTATION.md b/migration/DOCUMENTATION.md new file mode 100644 index 0000000..fb0716b --- /dev/null +++ b/migration/DOCUMENTATION.md @@ -0,0 +1,104 @@ +### Write Functions + +**function createChallenge(address \_team2, address \_locationProvider) public payable** + +Desafia a otro address `_team2` especificando un segundo address para el proveedor de locación `_locationProvider`. Asimismo, es payable y tiene un monto mínimo para ser llamada la función, que si se supera, queda todo el monto excedente a ese mínimo como monto apostado. + +**function acceptChallenge(uint256 \_challengeId) public payable** + +Acepta el desafío indicando el `_challengeId` del desafio a aceptar, y es payable, requiriendo pagar al menos el mismo monto que ha pagado el equipo desafiante al enviar el desafío. + +**function updateLocationProvider(uint256 \_challengeId, address \_newLocationProvider) public** + +Permite cambiar el address `_newLocationProvider` del desafío especificado `_challengeId` + +**function deleteChallenge(uint256 \_challengeId) public** + +Permite eliminar o rechazar el desafío, y reembolsar a el/los equipos que hayan pagado para participar. + +**function startChallenge(uint256 \_challengeId) public** + +Función sólo llamable por el proveedor de locación, permite dar por empezado el desafío, quedando así imposibilitado de serr eliminado/rechazado, y habilitandolo para ser completado al finalizar el encuentro. + +**function completeChallenge(uint256 \_challengeId,uint8 \_team1Result,uint8 \_team2Result** + +Función que da por concluido el desafío `_challengeId`, específicando el puntaje de cada equipo `_team1Result` y `_team2Result`. Habilita para ser retirado el monto ganado al equipo ganador, así como el monto abonado al proveedor de locación, y mintea un NFT para cada equipo participante con el resultado del partido. + +**function claimPrize() external** + +Permite reclamar todos los tokens que corresponden al wallet por haber empatado o ganado desafíos. + +**function claimLocationProviderWinnings() public** + +Permite reclamar todos los tokens que corresponden al wallet por haber arbitrado desafíos. + +### Read Functions + +**function viewMatchChallenge(uint256 \_id) public view returns (address[3] memory)** +Retorna [team1, team2, locationProvider]; + +**function viewMatchStatus(uint256 \_id) public view returns (MatchStatus)** +Retorna 0, 1, 2, 3 +(pendiente de aceptar, aceptado, iniciado, terminado) + +**function viewUnclaimedPrize() public view returns (uint256)** +Muestra el monto de premios sin reclamar por un equipo + +**function viewLocationProviderWithdrawableAmount()public view returns (uint256)** +Muestra el monto de premios sin reclamar por un proveedor de locación + +**function viewMatchFee() public view returns (uint256)** +Muestra el fee que se compone de lo que se abonará al proveedor de locación y al dueño del contrato Sportsbook. + +**function getAllMatches() public view returns (MatchChallenge[])** +Retorna todos los partidos que fueron creados en el contrato. + +## Functions + +### createChallenge(address \_team2, address \_locationProvider) + +_Create a new match challenge_ + +- Push a MatchChallenge structured item to matchChallenges array. +- Specify an amount to be betted on the outcome of the match +- Specify the location provider + +### acceptChallenge(uint256 \_challengeId) + +_Accept an existing challenge_ + +- Push the MatchAccepted structured item to the acceptedChallenges array. +- Add payment for betting on outcome of the match + +### deleteChallenge(uint256 \_challengeId) + +_Function that deletes existing challenge_ + +- Set challenge on matchChallenges as finished +- Return ETH paid + +### updateChallengedTeam(uint256 \_challengeId, address \_newTeam2) + +_Update an existing challenge's team 2_ + +- Give team1 (proposer) the ability to challenge a different team on the same proposal. + +### updateLocationProvider(uint256 \_challengeId, address \_newLocationProvider) + +_Update an existing challenge's location provider_ + +- Give both teams the ability to update the location provider. +- If a location update is submitted, it must be accepted by the other team + +### functions to view existing challenges... (to be listed) + +### struct MatchChallenge + +_Data structure for a match_ + +- **Team(s)** involved in a match. +- **Location provider** +- If match already been **accepted** by both teams +- If match already **started** according to location provider +- If match already **finished** according to location provider + (after setting **finished** to true, trigger payments for location provider and winner), diff --git a/migration/EXPAND-README.md b/migration/EXPAND-README.md index 90db2f8..6003a8f 100644 --- a/migration/EXPAND-README.md +++ b/migration/EXPAND-README.md @@ -8,28 +8,6 @@ Permite la creacion de desafios deportivos de un equipo a otro, especificando un Al momento de finalización de cada encuentro, se paga el monto apostado al ganador, y un pequeño fee al proveedor de locacion y al dueño del contrato Sportsbook por el servicio. Tambien se mintea un NFT para cada equipo como conmemoración del resultado del encuentro. -## Para usar - -Para usar el cliente, se recomienda usar la version LTS de node 16.13.0 o superior - -``` -cd client -npm install -npm run dev -``` - -## Network - -#### Tesnet: Mumbai - -Por favor, antes de testear el proyecto, fondear su wallet con MATIC MUMBAI. - -#### Faucet: - -https://mumbaifaucet.com/ - -https://faucet.polygon.technology/ - ## Frontend Primero se llega a una landing que explica brevemente la dapp, permite hacer login con wallet, y luego se ingresa a la app. @@ -76,16 +54,6 @@ La interfaz para proveedores de locacion tiene las siguientes funcionalidades. (for big sports betting there's already [other options](https://stake.com/) ) -## Changelog - -- 11/12/2022: Added some concept images made with Canva -- 09/12/2022: Added some Wagmi read indicators,improvements to interface and smart contract -- 08/12/2022: Integrated Wagmi hooks to make buttons interact with the blockchain -- 07/12/2022: Made a simple layout for buttons with ChakraUI -- 06/12/2022: Installed NextJS with TypeScript, RainbowKit, Wagmi and ChakraUI for frontend -- 05/12/2022: Tested smart contract with Hardhat and did some corrections. Expanded README.md -- 04/12/2022: Created README.md and first smart contract (SportsbookBase). Tested on scaffold-eth for a quick frontend - ## Pending ### Now @@ -140,146 +108,6 @@ La interfaz para proveedores de locacion tiene las siguientes funcionalidades. - Explore the option of a platform token ($SPOR) - Automatically generate a wallet key pair for each user that doesn't register with a wallet, with a section to view the private key like [Gala Games](https://app.gala.games/) or [PunkWallet](https://punkwallet.io/). -# Smart Contracts - -1. To run tests, enter `/hardhat` folder with command `cd hardhat` -2. Then, run `yarn` to install dependencies -3. Finally, run `yarn hardhat test` to run the tests - -## Smart contract - -Deploy: https://mumbai.polygonscan.com/address/0x4f1b7e7f61ad6a6efa788e974bbb9e31519ec05c - -### Write Functions - -**function createChallenge(address \_team2, address \_locationProvider) public payable** - -Desafia a otro address `_team2` especificando un segundo address para el proveedor de locación `_locationProvider`. Asimismo, es payable y tiene un monto mínimo para ser llamada la función, que si se supera, queda todo el monto excedente a ese mínimo como monto apostado. - -**function acceptChallenge(uint256 \_challengeId) public payable** - -Acepta el desafío indicando el `_challengeId` del desafio a aceptar, y es payable, requiriendo pagar al menos el mismo monto que ha pagado el equipo desafiante al enviar el desafío. - -**function updateLocationProvider(uint256 \_challengeId, address \_newLocationProvider) public** - -Permite cambiar el address `_newLocationProvider` del desafío especificado `_challengeId` - -**function deleteChallenge(uint256 \_challengeId) public** - -Permite eliminar o rechazar el desafío, y reembolsar a el/los equipos que hayan pagado para participar. - -**function startChallenge(uint256 \_challengeId) public** - -Función sólo llamable por el proveedor de locación, permite dar por empezado el desafío, quedando así imposibilitado de serr eliminado/rechazado, y habilitandolo para ser completado al finalizar el encuentro. - -**function completeChallenge(uint256 \_challengeId,uint8 \_team1Result,uint8 \_team2Result** - -Función que da por concluido el desafío `_challengeId`, específicando el puntaje de cada equipo `_team1Result` y `_team2Result`. Habilita para ser retirado el monto ganado al equipo ganador, así como el monto abonado al proveedor de locación, y mintea un NFT para cada equipo participante con el resultado del partido. - -**function claimPrize() external** - -Permite reclamar todos los tokens que corresponden al wallet por haber empatado o ganado desafíos. - -**function claimLocationProviderWinnings() public** - -Permite reclamar todos los tokens que corresponden al wallet por haber arbitrado desafíos. - -### Read Functions - -**function viewMatchChallenge(uint256 \_id) public view returns (address[3] memory)** -Retorna [team1, team2, locationProvider]; - -**function viewMatchStatus(uint256 \_id) public view returns (MatchStatus)** -Retorna 0, 1, 2, 3 -(pendiente de aceptar, aceptado, iniciado, terminado) - -**function viewUnclaimedPrize() public view returns (uint256)** -Muestra el monto de premios sin reclamar por un equipo - -**function viewLocationProviderWithdrawableAmount()public view returns (uint256)** -Muestra el monto de premios sin reclamar por un proveedor de locación - -**function viewMatchFee() public view returns (uint256)** -Muestra el fee que se compone de lo que se abonará al proveedor de locación y al dueño del contrato Sportsbook. - -**function getAllMatches() public view returns (MatchChallenge[])** -Retorna todos los partidos que fueron creados en el contrato. - -## Functions - -### createChallenge(address \_team2, address \_locationProvider) - -_Create a new match challenge_ - -- Push a MatchChallenge structured item to matchChallenges array. -- Specify an amount to be betted on the outcome of the match -- Specify the location provider - -### acceptChallenge(uint256 \_challengeId) - -_Accept an existing challenge_ - -- Push the MatchAccepted structured item to the acceptedChallenges array. -- Add payment for betting on outcome of the match - -### deleteChallenge(uint256 \_challengeId) - -_Function that deletes existing challenge_ - -- Set challenge on matchChallenges as finished -- Return ETH paid - -### updateChallengedTeam(uint256 \_challengeId, address \_newTeam2) - -_Update an existing challenge's team 2_ - -- Give team1 (proposer) the ability to challenge a different team on the same proposal. - -### updateLocationProvider(uint256 \_challengeId, address \_newLocationProvider) - -_Update an existing challenge's location provider_ - -- Give both teams the ability to update the location provider. -- If a location update is submitted, it must be accepted by the other team - -### functions to view existing challenges... (to be listed) - -### struct MatchChallenge - -_Data structure for a match_ - -- **Team(s)** involved in a match. -- **Location provider** -- If match already been **accepted** by both teams -- If match already **started** according to location provider -- If match already **finished** according to location provider - (after setting **finished** to true, trigger payments for location provider and winner), - -## Frontend - -### Functionalities to add: - -**Displaying existing challenges for connected wallet:** - -- **address and name** of who made the challenge -- **challenge cost** -- **bet amount** (if any) -- **accept** button -- **cancel** button - -**Displaying unstarted challenges by location provider** - -- a button to **start** -- a button to **complete** -- a button to **decline** - -**Profiles** - -- A display for NFTs -- Challenges made, received, and played -- Contact information -- Preferred locations & geographic area for matchmaking. - ## Cosas que nos faltaron: - La interface del Referee, que se encarga de actualizar los estados del partido desde su inicio hasta su finalización. diff --git a/migration/README.md b/migration/README.md index 03c7eff..c2ddd24 100644 --- a/migration/README.md +++ b/migration/README.md @@ -41,150 +41,45 @@ The location provider is also the referee that logs the result of the match, and (for big sports betting there's already [other options](https://stake.com/) ) -# 🏄‍♂️ Quick Start - -Prerequisites: [Node (v18 LTS)](https://nodejs.org/en/download/) plus [Yarn (v1.x)](https://classic.yarnpkg.com/en/docs/install/) and [Git](https://git-scm.com/downloads) - -🚨 If you are using a version < v18 you will need to remove `openssl-legacy-provider` from the `start` script in `package.json` - -> 1️⃣ clone/fork 🏗 scaffold-eth: - -```bash -git clone https://github.com/luloxi/sportsbook-dapp.git -``` - -> 2️⃣ install and start your 👷‍ Hardhat chain: - -```bash -cd sportsbook-dapp -yarn install -yarn chain -``` - -> 3️⃣ in a second terminal window, start your 📱 frontend: - -```bash -cd sportsbook-dapp -yarn start -``` - -> 4️⃣ in a third terminal window, 🛰 deploy your contract: - -```bash -cd sportsbook-dapp -yarn deploy -``` - -- Need more info? Read how to about [scaffold-eth environment here](./ENVIRONMENT.md) -- Don't understand the contract? Read [info about its functions](./FUNCTIONS.md) - -> Challenge: Make the challenge be for a new sport (chess, tennis, golf, etc) - -## Pending changes (original Sportsbook.sol) - -> ⚽ Challenge: pick one and implement it +## Pending changes ### Now -**Frontend** - -- Add a switch to change between team and location provider interface -- Add a list of all existing challenges -- Read current challenge cost from blockchain when accepting - **Smart contracts** -- Allow for createChallenge to not include a challenged address, thus making the challenge acceptable by anyone -- If team2 is not specified in a challenge, make whoever calls acceptChallenge the team2 address -- Specify an amount to be paid to the location provider -- Add updateLocationProvider() function - -### Later - -**Frontend** - -- Add an option to bet when accepting? -- Show location provider address and bet amount (if any) and calculate a total when accepting/declining -- Add a button to select different layouts (for location provider and team options) -- Add a functionality to detect current chain Id and select correct contract address accordingly - -**Smart contract** +> Challenge: Make the challenge be for a new sport (chess, tennis, golf, etc) +- Specify an amount to be paid to the referee - Give both teams the ability to update the challenge with a bet? - Include Date and time of the match ([to be included later](https://soliditytips.com/articles/solidity-dates-time-operations/)) wherever necessary -- Mint a basic NFT when completing the challenge by location provider -- Give both teams the ability to change location (other team has to accept the change for new location provider to be able to start and complete the challenge) - -### Future +- Mint a basic NFT when completing the challenge by referee +- Make the NFT include dynamically the result of the challenge and location provider & teams logos. +- Explore the option of a [feeless payment gateway](https://github.com/lacrypta/gateway) +- Explore the option of a platform token ($SPBK) +- Explore the option of referees to be a contract that contains data on how much it wants to be paid, and days and time available for matches **Frontend** -- Translate address to a name (user/team/location provider) -- Add another layout for user options -- Show user stats and other information - Show existing challenges for connected wallet - Include a search engine for teams when challenging and updating - Integrate mail, Google, Twitter and Facebook login options - Start integrating a social media feature -**Smart contract** +**Profiles** -- Add a small fee when processing payments -- Make the NFT include dynamically the result of the challenge and location provider & teams logos. -- Explore the option of a [feeless payment gateway](https://github.com/lacrypta/gateway) -- Explore the option of location providers to be a contract that contains data on how much it wants to be paid, and days and time available for matches -- Explore the option of a platform token ($SPBK) +- A display for NFTs +- Show user stats (Challenges made, received, and played, etc) +- Contact information +- Preferred locations & geographic area for matchmaking. ### struct MatchChallenge _Data structure for a match_ - **Team(s)** involved in a match. -- **Location provider** +- **Referee** +- **Bet amount** (if any) - If match already been **accepted** by both teams - If match already **started** according to location provider - If match already **finished** according to location provider (after setting **finished** to true, trigger payments for location provider and winner), - -## Frontend (original) - -> ⚽ Challenge: migrate it from `migrations/original/nextjs-frontend` to scaffold-eth - -### Functionalities to add: - -**Displaying existing challenges for connected wallet:** - -- **address and name** of who made the challenge -- **challenge cost** -- **bet amount** (if any) -- **accept** button -- **cancel** button - -**Displaying unstarted challenges by location provider** - -- a button to **start** -- a button to **complete** -- a button to **decline** - -**Profiles** - -- A display for NFTs -- Challenges made, received, and played -- Contact information -- Preferred locations & geographic area for matchmaking. - -> ⚽ Challenge: Implement the frontend similar to concept in `migrations/original/concept`, can take inspiration from `migrations/hackathon/client` with deploy instructions in `migrations/hackathon/README.md` - -## Changelog - -- 25/24/2023: Migrated original contract to scafold-eth, and added a migrations folder with files from original and hackathon versions -- 12/04/2023: Added hackathon repo to this one. Team didn't care to keep developing it further. -- 16/12/2022: Lulox's team submitted [this repo](https://github.com/alejoviola/hackaton-2022-Gol) and won the 1st place! -- 15/12/2022: Entered Think&Dev hackathon as the version in the original branch -- 11/12/2022: Added some concept images made with Canva -- 09/12/2022: Added some Wagmi read indicators,improvements to interface and smart contract -- 08/12/2022: Integrated Wagmi hooks to make buttons interact with the blockchain -- 07/12/2022: Made a simple layout for buttons with ChakraUI -- 06/12/2022: Installed NextJS with TypeScript, RainbowKit, Wagmi and ChakraUI for frontend -- 05/12/2022: Tested smart contract with Hardhat and did some corrections. Expanded README.md -- 04/12/2022: Created README.md and first smart contract (SportsbookBase). Tested on scaffold-eth for a quick frontend diff --git a/packages/nextjs/pages/index.tsx b/packages/nextjs/pages/index.tsx index cfed49e..9b41f84 100644 --- a/packages/nextjs/pages/index.tsx +++ b/packages/nextjs/pages/index.tsx @@ -186,18 +186,26 @@ const Home: NextPage = () => { contractName: "Sportsbook", eventName: "UpdateRefereeRequest", listener: (challengeId, proposingTeam, newReferee) => { - setUpdateRefereeRequestHistory(prev => { + setUpdateRefereeRequestHistory(prevHistory => { const newChallengeId = parseInt(challengeId.toString()); - if (prev.some(challenge => challenge.challengeId === newChallengeId)) { - return prev; + + const updatedHistory = { ...prevHistory }; + + if (!updatedHistory[newChallengeId]) { + updatedHistory[newChallengeId] = { + challengeId: newChallengeId, + properties: [], + }; } - const newChallenge: UpdateRefereeRequestProps = { - challengeId: newChallengeId, + const newChallenge = { proposingTeam, newReferee, }; - return [newChallenge, ...prev]; + + updatedHistory[newChallengeId].properties.push(newChallenge); + + return updatedHistory; }); }, }); @@ -206,18 +214,26 @@ const Home: NextPage = () => { contractName: "Sportsbook", eventName: "UpdateRefereeResponse", listener: (challengeId, newReferee, updateAccepted) => { - setUpdateRefereeResponseHistory(prev => { + setUpdateRefereeResponseHistory(prevHistory => { const newChallengeId = parseInt(challengeId.toString()); - if (prev.some(challenge => challenge.challengeId === newChallengeId)) { - return prev; + + const updatedHistory = { ...prevHistory }; + + if (!updatedHistory[newChallengeId]) { + updatedHistory[newChallengeId] = { + challengeId: newChallengeId, + properties: [], + }; } - const newChallenge: UpdateRefereeResponseProps = { - challengeId: newChallengeId, + const newChallenge = { newReferee, updateAccepted, }; - return [newChallenge, ...prev]; + + updatedHistory[newChallengeId].properties.push(newChallenge); + + return updatedHistory; }); }, }); @@ -286,10 +302,14 @@ const Home: NextPage = () => { useEffect(() => { if (UpdateRefereeRequestHistory) { const mappedHistory = UpdateRefereeRequestHistory.map(event => ({ - challengeId: event.args[0].toString(), - proposingTeam: event.args[1], - newReferee: event.args[2], - })) as UpdateRefereeRequestProps[]; + challengeId: parseInt(event.args[0].toString()), + properties: [ + { + proposingTeam: event.args[1], + newReferee: event.args[2], + }, + ], + })); setUpdateRefereeRequestHistory(mappedHistory); } }, [UpdateRefereeRequestHistory]); @@ -298,9 +318,13 @@ const Home: NextPage = () => { if (UpdateRefereeResponseHistory) { const mappedHistory = UpdateRefereeResponseHistory.map(event => ({ challengeId: event.args[0].toString(), - newReferee: event.args[1], - updateAccepted: event.args[2], - })) as UpdateRefereeResponseProps[]; + properties: [ + { + newReferee: event.args[1], + updateAccepted: event.args[2], + }, + ], + })); setUpdateRefereeResponseHistory(mappedHistory); } }, [UpdateRefereeResponseHistory]); @@ -311,22 +335,30 @@ const Home: NextPage = () => { const challengeStarted = challengeStartedHistory.find(result => result.challengeId === challenge.challengeId); const challengeResult = challengeResultHistory.find(result => result.challengeId === challenge.challengeId); - const updateRefereeRequests = updateRefereeRequestHistory.filter( - request => request.challengeId === challenge.challengeId, - ); - const updateRefereeResponses = updateRefereeResponseHistory.filter( - response => response.challengeId === challenge.challengeId, - ); + const updateRefereeRequests = Array.isArray(updateRefereeRequestHistory) + ? updateRefereeRequestHistory.filter(request => request && request.challengeId === challenge.challengeId) + : []; + const updateRefereeResponses = Array.isArray(updateRefereeResponseHistory) + ? updateRefereeResponseHistory.filter(response => response && response.challengeId === challenge.challengeId) + : []; + + const newestRequest = updateRefereeRequests + .filter(request => request.challengeId === challenge.challengeId) + .map(response => response.properties.find(event => event.newReferee)) + .pop(); - console.log("Update referee request", updateRefereeRequests); + // console.log("Newest request", updateRefereeRequests); - const lastAcceptedResponse = updateRefereeResponses.find(response => response.updateAccepted); - const newestRequest = updateRefereeRequests[updateRefereeRequests.length - 1]; + const lastAcceptedResponse = updateRefereeResponses + .filter(response => response.challengeId === challenge.challengeId) + .map(response => response.properties.find(event => event.updateAccepted)) + .pop(); const eventToShow = - updateRefereeRequests.length > 0 && - updateRefereeResponses.length > 0 && - updateRefereeRequests.length === updateRefereeResponses.length + updateRefereeRequests.filter(request => request.challengeId === challenge.challengeId).length > 0 && + updateRefereeResponses.filter(response => response.challengeId === challenge.challengeId).length > 0 && + updateRefereeRequests.filter(request => request.challengeId === challenge.challengeId).length === + updateRefereeResponses.filter(response => response.challengeId === challenge.challengeId).length ? { response: lastAcceptedResponse ? lastAcceptedResponse : undefined, } diff --git a/packages/nextjs/pages/sportsbook/ChallengeCard.tsx b/packages/nextjs/pages/sportsbook/ChallengeCard.tsx index 6859c66..e6cc150 100644 --- a/packages/nextjs/pages/sportsbook/ChallengeCard.tsx +++ b/packages/nextjs/pages/sportsbook/ChallengeCard.tsx @@ -24,8 +24,6 @@ const ChallengeCard = ({ const { address } = useAccount(); - console.log("UpdateRefereeRequest: ", updateRefereeRequest); - const { writeAsync: acceptChallenge } = useScaffoldContractWrite({ contractName: "Sportsbook", functionName: "acceptChallenge", @@ -282,12 +280,12 @@ const ChallengeCard = ({ {updateRefereeRequest && ( -
+

has proposed

-
+

as a new referee

- {updateRefereeRequest.proposingTeam != address && ( + {updateRefereeRequest?.proposingTeam != address && (