Cooltopia Login and Permission System

7 min readMay 25, 2022

A Solidity authorization and permission system

If you have used Cooltopia, you will know that you only need to authorize your account once via a signed message. After that, you will no longer see any wallet popups asking for permissions. You don't have to pay for any transactions and the whole system runs on Polygon, while you are connected to Ethereum.

It all feels a little counterintuitive and confusing, but the end result is a system that allows users to freely interact with Cooltopia without interruptions or worries about spending too much on transactions.

So how did we achieve this?

Ethereum and Polygon

In designing Cooltopia we wanted to make it really simple for new NFT/crypto users to interact without the need to buy MATIC and swap chains on Metamask. Explaining how to fund a wallet with Eth is hard enough and for first-time users, it's scary.

Luckily Polygon is EVM-compatible.

Among other things, this means we can write Solidity code designed for Ethereum and it will also run on Polygon. This opens a lot of doors

Shared Addresses

Your address on Ethereum is also your address on Polygon. Of course, the tokens held on Ethereum are not mirrored on Polygon.

Think of your address as a house and that house exists in two somewhat parallel universes. Same street and town address and both houses start empty. As you buy furniture in the Ethereum universe it gets added to your Ethereum house, but not your Polygon house and vice versa.

Shared Signatures

The beauty of having a shared address and EVM compatibility is that you can sign a data packet on Ethereum and pass that signature to Polygon and it is still valid.

When you authorize on Cooltopia you are connected to Ethereum and sign the following data:

Note: Always take note of anything you sign with your wallet. Signing unexpected data could result in a wallet hack. For this reason, we make all our signature requests human-readable and not hashed data

Once the data is signed by you, the signature is passed to our Polygon contracts where we run a few security checks to ensure the data is unique and actually signed by you.

We never store any signatures :)

There are a few points to note here:

  • msgHash: We rebuild the expected message to ensure the signature matches
  • _isValidSignature(): The msgHash is checked against your address and the signature to verify that you have requested this connection

If you would like to learn more about signature verifications have a look here:

Let’s look at why we require you to connect in this way.

Access Control

Access Control is exactly as its name suggests. A system to control who has access to certain features and functions. You can think of it as a system to grant users things like moderator and admin permissions.

Cooltopia Roles

Our contracts use three main roles:

  • ADMIN_ROLE: Used to make changes to game settings for the purposes of balancing and maintenance
  • CONTRACT_ROLE: Refers to contracts within our system. Sometimes they need to fire off functions within other system contracts and this role allows for that
  • GAME_ROLE: Used for requests coming directly from users playing the game

Possibly a little confusing without an example so let's look at how the ItemFactory plugs into the system and what happens when a user buys a Pet Chest.

Connecting Contracts

Below is an ItemFactory.sol contract that connects to the SystemChecker.sol contract via the HSystemChecker.sol

Notice that ItemFactory.sol line #17 passes systemCheckerAddress as a parameter to HSystemChecker which it extends.

Looking at HSystemChecker.sol line #14 you can see we have connected to SystemChecker via an interface.

Note HSystemChecker.sol does not extend SystemChecker.sol. This is because we need to access the data that exists within the contract and not just the structure.

After all this connecting, ItemFactory can use the modifiers onlyRole and isUser . With access to these modifiers and the ability to access the data stored in SystemChecker.sol, we are now ready to use the roles that were detailed above and let users buy Pet Chest.

Buying a Pet Chest

Before we follow the flow of buying a chest, a quick recap.

User State:

  • Connected to Ethereum
  • Authorized on Cooltopia
  • Sat on the shop with MILK and ready to buy

Contract State:

  • ItemFactory.sol extends HSystemChecker.sol
  • HSystemChecker.sol has access to SystemChecker.sol
  • ItemFactory.sol can access SystemChecker.sol data via modifiers

Time to buy

A user requests to buy a Pet Chest by interacting with the web UI. The request is passed to our system which in turn fires off the transaction on Polygon (did I mention we pay for all the gas? :))

The transaction hits the buyBox() function and the onlyRole(GAME_ROLE) checks that the transaction call originated from the Cool Cats’ system.

From there the function starts to run and then we hit the _mint() function. If you examine that function you will notice that it fires off the modifier isUser which checks that the user minting the Pet Chest is an authorized user.

If an unauthorized user tried to buy a Pet Chest, it would revert. Otherwise, all is well and the user mints a Pet Chest.

Wait, what about the burning of MILK?

Avoiding Popups

This is where the power of the Cooltopia authorization comes into play. When you authorized you hopefully took the time to read the notification:

You can think of authorizing as logging into a traditional game.

While you are logged into the game and playing, the game doesn't keep asking you to give permission for the action you just asked it to perform. Instead, it does as you requested (given that you have all the appropriate requirements — gold, items, etc). Thereby helping you to stay immersed.

Nice and streamlined for the user, but how do we achieve this?

Note: Cooltopia can only update Cooltopia related tokens. Granting authorization does not allow access to any other tokens in your wallet

When a user buys a Pet Chest the function treasury.burn() is called from within buyBox() on the ItemFactory.sol contract. As you can see on line #28 in the above code snippet, burn() uses the modifier onlyRole(CONTRACT_ROLE) .

When we deployed ItemFactory.sol we granted it the CONTRACT_ROLE which allows it to call treasury.burn() . If you were to call burn() from an address that didn't have the CONTRACT_ROLE , your transaction would revert.

treasury.burn() calls _milkChild.gameBurn() and this is where the users' MILK is burned.

gameBurn() can only be called by a contract with the TREASURY_ROLE . Given to the Treasury.sol contract upon deployment.

gameBurn() is where the anti-popup magic happens.

Remember that logged-in users have already agreed that Cooltopia can update their tokens as part of the gaming experience. With this permission, we transact on the users' behalf — simple yet powerful.

This prevents the user from being shown a permissions popup every time a token needs to be interacted with.

Token Ownership

Every token that a user earns in Cooltopia, goes straight into their wallets and if a user wishes to deauthorize (log out) of the game, they can:

In doing so, Cooltopia will have no ability to update their tokens any longer and the user still retains everything, free to sell and trade on the open market in places like Opensea.

Extending the System

Not only does this system allow for a nice user experience, it also makes contract upgrading extremely easy (with some caveats).

Contracts that produce tokens or store time-sensitive info can not be upgraded without the migration, things like the ItemFactory and MilkChild.

However, all other contracts can easily be decoupled from the system by revoking their CONTRACT_ROLE and ties to dependants. An upgraded contract can then be slotted in.

Community Contracts

The simple use of roles allows anyone to create a contract for Cooltopia and as long as it passes security and balancing audits (internal and public), it can be bolted on and ready to use.

Important Notes

The demo code snippets are taken from the live contracts and those are linked below each snippet.

I haven't explained all the features, there are a number of security features and structures that I won't detail.

As with all my dev blogs, use the code at your own risk.


Where to find me

I can normally be found in the Cool Cats discord channel

Or on Twitter




Im a Found of Cool Cats, general Web 3 consultant with 20 yrs experience as a builder, marketer and company owner in Web 2.