Please read Purpose of this Repository π€ (1 min read)
A fighting game on the browser using NFT technology that can be played in EVM compatible blockchains.
(completely decentralized version might come...)
- Purpose of this Repository π€
- App So Far Demo Video: πΉ
- About the App βΉοΈ
- Potential Misuse of Power π’
- A Trustless Fully Decentralized Scenario πΌ
- Self-Audit state π₯
- Packages & Technologies Utilized π§°
- Brief System Overview βοΈ
- Project Structure π
- Capabilities Of Deployed App π
- Instructions for Local Usage βΉοΈπ
- Special Thanks π
- TODO π§
- License π
Learn and gain experience in developing a robust full-stack web application that combines traditional client-server-database
mechanics and cutting-edge interactions with EVM-compatible blockchains and
decentralized networks like IPFS
.
-
General Insights π
Systems Design
: Divide and Conquer, Separation of Responsibilities, Don't Repeat Yourself...Client-Server communication
: request-response, SSE...Database state management
: CRUD operations, clean-up functions...Blockchain and IPFS interaction
: with server and client.Smart Contracts' System Design
-
Backend Development π»:
- Client-Server
API development
: routing, controllers, and services. User auth methods
in Web2 and Web3 : signature-based-auth and token auth.Error handling, logging and job scheduling
.MongoDB
.System testing
using frameworks like Mocha, Chai, and Supertest.
- Client-Server
-
Frontend Development π¨:
Next.js
for buildingReact
apps.UI/UX design
for web3.
At this stage, I am content to leave the project in its current state. I've achieved significant learning through practical experience and don't foresee additional learning benefits from further development. However, in the future, I might develop this project for fun. π
Essentially, this application is a fighting game where players can battle against each other using NFTs and bet their cryptocurrencies on these fights. It includes a "change stats" feature, allowing you to adjust your NFT's stats in a fully trustless, decentralized manner.
Click on the image to watch the video on Youtube
.
The fighting and betting system isn't completely trustless, as it requires a trusted backend to execute off-chain computations when battles occur. However, it's designed in a way that makes any backend misbehavior easy to detect, enhancing the overall trustworthiness of the app's betting system.
One point of concern is the declaration of the winner. The backend could potentially declare any winner it prefers, regardless of the actual battle outcome. As the system stands, it's impossible to fully verify via computations alone if the backend is being dishonest. A more resource-intensive system on-chain that resolves this issue could be implemented, as explained below.
So the backend can potentially steal your current bet in an active fight if desired.
The second point of concern is in the startFight()
function, specifically in the _bets
variable. You have to trust that the backend will give input permissions with the correct bets and not with higher ones. The backend can potentially cooperate with a party player to give permissions, saying your bet was higher than the one you deposited, which will result in you losing your tickets.
Basically, the decentralized scenario consists of contracts that implement:
- Decentralized matchmaking.
- Decentralized activation of events on the blockchain.
- Decentralized fight state management.
All this is already possible; I just haven't programmed it yet. The disadvantage is that it results in a more expensive game to play due to the need for more transactions and on-chain computations. The following solution just focuses on making it truly decentralized and as cheap as possible, but always prioritizing fair play and trustless decentralized gameplay.
This is how it would be done though:
You could even decentralize this app more without even requiring a backend to execute the matchmaking by doing the following:
Using the InputControl
contract, you can allow players to give each other permissions to call startFight()
with a previously agreed value, so now no bets would be faked.
If you mix this with the fight's states management on-chain described above, there would not even be a need for a trusted backend or a backend at all.
This feature would be needed in order for fights, turns, or matchmaking not to potentially last forever.
This feature idea has already been explored and implemented by the Chainlink team with their Time Based Automation.
To facilitate trustless fights, we'd need to introduce additional variables and functions to the FightsManager
contract.
These would include:
- Mapping of the player's address to the last move executed. This variable should only be alterable by the same player, with moves potentially represented by integers.
- Mapping of the
fightId
to the last fight state. This would be a unique hashed representation of the fight state after each turn.
Once the next move is updated, the next fight's state should be predictable by both clients. For the clients to agree on the next state, the game logic must be open-source.
There must also be a function that validates the next fight states. If different fight states are calculated by clients, this function could be called to figure out the right one and punish only the players who actually didn't behave correctly.
π Note βΉοΈ: For game mechanics with luck-based moves, VRF Chainlink contracts should be used to determine the random luck-based result. So users can validate the next fight state, even if the process is deterministic. Now they can use a verified random value for the deterministically computed next state.
Hardhat localhost network π‘ :
- Manual testing π’
- High Manual Tests' Coverage π’
- Slither syntax analysis π’
- Contracts Fuzz & Invariant testing βπ΅οΈ
Any testnet or mainnet π΄ ββββ
For the complete list of dependencies, please check the package.json
file in the respective front-end and back-end directories.
Back-end dependencies π»
ethers
: Ethereum blockchain and smart contracts JavaScript API.@chainlink/contracts
: VRF (Verifiable Random Function) Chainlink.@openzeppelin/contracts
: Secure open source smart contracts.agenda
: For lightweight job scheduling.express
: Web server framework.mongoose
: For MongoDB object modeling.jsonwebtoken
: For JSON Web Token sign and verify.express-sse
: Server-side events management in express framework.cors
: For enabling CORS in Express.dotenv
: Management of environment variables.express-winston
: Server-side logging.
Front-end dependencies π¨
@web3uikit/core
: UI components for DApps.ethers
: Ethereum blockchain and smart contracts JavaScript API.moralis
: For fast blockchain development.react
: For the UI.
The front end handles API calls to the backend and also contains blockchain interaction code executed through a MetaMask provider.
The backend contains all the services, controllers, and route structures necessary for the request-response communication cycle. Additionally, it uses Server-Side Events (SSE) to manage matchmaking and fight mechanics services. Moreover, it can interact with the blockchain via Alchemy as a provider.
And don't forget to mention the front end and backend are designed to be hosted on different servers, therefore utilizing CORS.
Here is the overall structure of the project. It's divided into two main parts: game
(back-end) and website-game
(front-end).
Each directory is further structured to organize files based on their functionality. Not all files are listed here, only the most relevant ones for a high-level overview. For more detailed information, please navigate to the respective directories.
This structure allows for modular development, easy maintainability, and scalability of the project.
.
βββ game (Contains the server-side logic, blockchain scripts, contracts and tests)
β βββ README.md
β βββ src
β β βββ blockchainScripts (Scripts directly interacting with the blockchain)
β β βββ contracts (Smart Contracts written in Solidity)
β β βββ controllers (Controllers to manage route handling)
β β βββ database (Mongoose models and DB connection settings)
β β βββ logs (Different log files and the logger script)
β β βββ middleware (Middleware for Express.js, JWT AUTH and SSE management)
β β βββ routes (Routes for Express.js)
β β βββ serverConfig (Server configuration file)
β β βββ services (Services to handle business logic)
β β βββ tasks (Background tasks setup)
β β βββ tests (Unit and integration tests for the blockchain scripts and services)
β β βββ utils (Utility scripts used across the project)
β βββ hardhat.config.js
β βββ helper-hardhat-config.js
β βββ nodemon.json
β βββ package.json
β βββ yarn.lock
βββ LICENSE
βββ package.json
βββ printChildrenDirectories.sh
βββ readme-images (images used in the readmes)
βββ README.md
βββ TODO.md
βββ website-game (Contains the client-side logic, UI components, and styles)
β βββ components (React components for UI)
β βββ constants (JSON and index files with constant values)
β βββ next.config.js
β βββ package.json
β βββ pages (React pages)
β βββ public (Static files served by Next.js)
β βββ README.md
β βββ styles (CSS and SCSS style files)
β βββ utils (Utility scripts used across the project)
β βββ yarn.lock
βββ yarn.lock
As a personal portfolio project, the public website and backend are limited to supporting only 4 authenticated users playing or using backend services simultaneously. This restriction allows me to host the project for free. (:D)
To run the application locally, follow these steps:
- Download the code.
- Create an
.env
file and make sure you have all the API keys and variables set. Check them here .env.example - Start a Hardhat node.
- Mint some NFTs using the provided minting script. (blockchainScripts/00-useScript.js)
- Launch the website on two different browsers or in sessions that don't share cookies. Recommended
Brave
,Chrome
,Firefox
. - Connect MetaMask to the local Hardhat network using the first two default addresses, one for each browser.
- Run the server file.
- Start the MongoDB database.
Thanks to @aonsager for allowing me to use his images as long as I don't create a commercial product out of them.
Creator of => https://pokemon.alexonsager.net/
Source of the pokemon fusions images used in this project.
- Apply fuzz testing and symbolic analysis in smart contracts.
- Try to improve manual smart contract tests.
- Improve routes security type checking in backend.
- Finish and test frontend, API and backend services.
- Improve READMEs: intructions for local usage.
- Try to improve tests' readability.
- Create the completely decentralized trustless version of BuddyFighters!
MIT
GitHub @CarlosAlegreUr