NFT implementation

The GFC Fighter NFTs will be implemented using ERC-721 standard, we chose this standard because all our Fighters will all be 1/1 unique token with unique combination of traits. The weapons will be implemented using ERC-1155 standard allowing multiple copies of the same weapons to co-exist and the weapon to be fungible (i.e. to wear out/ to be fixed).

Our contracts will be deployed on Ethereum main network because it is the most widely recognised blockchain for NFTs and collection display is supported by most of the mainstream crypto wallets. This helps us to provide better user experience and it is also more friendly to players who are relatively new to the NFT space.

The entry requirement for certain game modes is that the player owns at least 1 GFC token at the time of battle. That can be verified by the contract using the Ownable class, our contract maps the ownership of all the GFC tokens. Once we verify this specific wallet is qualified to participate in the battle, we then pull out the information of all the NFTs this wallet owns, and pick out the tokens in our partnering collections. The player can then choose which “fighter” they want to use from all the available tokens.

Fighter Stats & Battle Calculations

There are two different types of users we would like to target for GFC, first are collectors who focus on how rare the token is and what traits it has, and second, gamers who focus on how well the fighters can perform in battle. As a result, we made the stats of the fighter hidden and not visible to the token owners, this means gamers will have to battle to find out how strong their fighters are and how well it performs against certain race/weapon/armor.

This is similar to ZED RUN, which is a horse racing game where players can enter their horses (i.e. the ZED RUN NFTs) into races with each other, their token’s stats are completely hidden, owners are able to see the parent of the horse, the colour and other trait but not the stats of the house, and logic on how to determine the outcome of each race is remain unpublished.

We took a similar approach to this, but we threw in another factor which is players’ skill level, GFC battling system will partially depend on the stats of the token but also how well the player performs.

The most important parts of the game such as all the fighters, weapons and their respective traits are stored on-chain however the team have decided that the stats of the fighters are to be stored on an off-chain database which offer several advantages listed below.

1. Fluid game-play experience

Since our game is a real-time fighting game with player input, it is much more complex and require a lot more processing on the backend to run than for example turned based games such as Axie Infinity or completely stimulated games like ZED RUN. This means that running the entire game on-chain is infeasible without significant lag and impact to gameplay experience, here we are referring to the blockchain trilemma of decentralization, scalability and security trade off.

As game developers we would like to provide better gaming experience and therefore we trade off decentralization for speed. Gaming logic and calculations happen off-chain and are then uploaded to record the result. This allows the game to be fluid and waiting time for transactions to complete is minimal, since data sync will be done with asynchronous operations.

2. Flexibility

Storing the battle calculation and logic off-chain also gives us a lot more flexibility for future updates so that we can continue to improve the game over the long term.

3. Cross-IP fairness/balance

Another reason why we kept the stats hidden is that we allow cross-collection battle, if the Galaxy Fighter tokens have their stats visible but the other collection do not then it creates an imbalance of information between GFC token owners and partnering collection token owners, so we decided to keep all of it hidden for fairness.

4. Gas fees reduction

Finally, if each battle were on-chain then it would create a unique transaction on-chain each time a battle happens, and due to the complexity of the game logic the gas fee spent on starting each battle will be very expensive and is simply not practical.

Smart Contract Optimization

Our developers spent significant time optimizing the contract and applied gas reducing best practices that have been proved valid by previous projects like the Cool Cats.

For example, we ordered the functions in our contract in such a way the frequently used functions are on top and therefore reduced gas when those functions are called.

In our mint function, this is written:

uint256 currentSupply = totalSupply();
require(
    currentSupply + numFighter < MAX_FIGHTER - _reserved, 
    'All fighters have been summoned'
);

Before optimising, it was this:

require(
    totalSupply().add(numFighter) < MAX_FIGHTER.sub(_reserved);, 
    'All fighters have been summoned'
);

In the pre-optimised version, totalSupply() was called in the validation and SafeMath operations(add(), sub())) were called.

Because we know that the total supply of the fighters is 9,994 which is far from the limit of usigned integer 256, and maximum mint number per transaction is 10, we know that overflow problem will not happen here and therefore we can use a simple “+” sign to perform addition instead of SafeMath.add(). Similar logic applies to where SafeMath.sub() was called, it has been optimised to just a simple “-” sign.

Last updated