A few days ago, I discovered an online trading card game called Gods Unchained, which is backed by Coinbase. You can buy cards on their website and battle against other players using your cards. Cards are implemented as non-fungible tokens (ERC-721) so you can buy, sell and trade in a marketplace like Rare Bits. I dreamed about online trading card games like this when I was a kid, where it has properties of physical cards such as being able to buy, sell and trade your cards without the card issuer supporting such features and other businesses being able to freely build something on top of the card game e.g. hosting a tournament.
The cards have different levels of rarity: common, epic, legendary and mythic. Mythic cards are extremely rare. There are only two mythic cards that people can draw from card packs and one mythic card called “Hyperion” that was available via a auction hosted by the company, which had a mind-blowing final bid of 146.279 ETH.
The Titan of Light: Hyperion
At the time of this writing, none of the two mythic cards have been drawn yet, so there are still chances of winning these mythic cards by purchasing card packs. However, there’s not much information on the website apart from how many mythic cards are yet to be found. I wanted to know what are the chances of finding a mythic card and if the chances are different depending on the type of card packs that you buy. If this were a traditional online trading card game, I would have stopped here, but since it’s implemented as smart contracts on Ethereum, you can actually read the contracts’ code and find the information.
The rest of this post is my journey of learning the Gods Unchained smart contract implementation and discovering information that is not available on their website.
The first thing I noticed when I purchased a Rare card pack is that I sent ETH to the following address: 0x0777F76D195795268388789343068e4fCd286919. I browsed the address on Etherscan and found out that it’s a contract called RarePackFour. I called a function called “purchase” on the contract with the # of packs I purchased as an argument.
A call to the purchase function on the RarePackFour contract via Etherscan.
The Etherscan page also offers human readable code of the contract. I found implementation of the “purchase” function:
The function creates an instance of Purchase, which just records the user who made the purchase, # of packs, the current block number and randomness set as 0. And then, it emits an event called “PacksPurchased”. Interestingly, the function records a proof of purchase but doesn’t actually draw cards…so I further explored the code and found this function called “callback”:
This function assigns randomness to a given purchase. The only source of randomness that is not predictable at the time of purchase is the blockhash of the block that included the purchase transaction. Since smart contracts can only access the blockhash of previous blocks, this callback function needs to be called in the subsequent blocks after the purchase transaction.
As I monitor incoming transactions to the smart contract, I noticed that this callback is called by someone else soon after my purchase is made. According to a Gods Unchained’s developer, their backend server listens to the “PacksPurchased” event and calls the callback function with a given purchase ID in the event. It’s worth noting that anyone can call the callback function, so you can call it if they don’t, hence there are no trust issues here.
Once randomness is assigned, it’s fair to assume that your cards are determined, so I went back to the smart contract and found a function called “predictPacks” that uses the generated randomness to determine the rarity of cards. Because Rare pack guarantees one of the cards to be at least rare, the function uses two types of functions to derive rarity: “_getCommonPlusRarity” and “_getRarePlusRarity”, where the latter is used to achieve the guarantee.
Now I finally found out that there’s a 1 in 1,000,000 chance of winning a mythic card per card draw, which means roughly 1 in 200,000 per pack purchases.
Both functions offer the same chance of winning a mythic, however, _getRarePlusRarity offers a slightly higher chance of winning epic or legendary cards. Below these functions, I also found functions that seem to be used to achieve similar guarantees for other types of card packs:
This shows that regardless of the card pack types, you have the same chance of winning a mythic card, so if you are only after a mythic card, it’s most cost efficient to buy the cheapest packs.
Now all of my questions are answered!
But I realized something interesting — the cards are neither represented as ERC-721 tokens nor transferred to my account. All I have is a proof of purchase and assigned randomness that can derive my cards, but I can’t sell or trade them yet. Then I found this function called “claim” which is very similar to predictPacks but it has an extra call to migration.createCard(). The migration contract was passed as an argument to this RarePackFour contract’s constructor. I was able to find the migration contract using Etherscan and confirmed it is a ERC-721 token contract. So who calls this function? I wasn’t able to find the information so I went to Discord chat room:
People in the chat room are very friendly and helpful.
It turns out that they separated the transaction that redeems your cards from transactions that make the purchase and assign randomness. This has a few advantages:
The claim function can be called by anyone, so I called the function with my purchase ID and verified that five ERC-721 tokens are now owned by my account. It also turns out that they provide a page that allows you to call the claim function on their website.
This concluded my first experience exploring the first blockchain-based eSport’s smart contracts. The fact that I was able to do this made me more convinced that blockchain changes the power balance between companies and users by giving users more power.
I was exploring smart contracts for other pack types and found the following code in LegendaryPackFour contract.
I discovered that Legendary and Shiny legendary packs not only guarantee at least one legendary card, but also guarantee another card of at least rare rarity. This is not documented in the pack descriptions on the website.
The Lengendary pack description on the Gods Unchained website.
There is certainly more to explore in the contracts, so I encourage you to take a look at them. I would love to get your thoughts — tweet your findings or feedback to @kn.
Thanks to Fatima Sabar for feedback on this post.