Creating and Deploying Smart Contracts for Loom’s BaseChain
Introduction
In this article, you are going to learn about Loom Network, it's blockchain and its benefits. Further, you can follow the process on how to create a smart contract for a mini-game from scratch and deploy it for Loom’s BaseChain. If you feel really fancy, you are able to use this smart contract in your own Unity game. Learn How to create a Loom Game in Unity.
Loom Network
The Production-ready, Multichain Interop Platform for Serious Dapp Developers
Loom Network provides developers the scalability and usability they need to build high-performance user-facing dapps today.
With seamless integrations to Bitcoin, Ethereum, Binance Chain, and all major blockchains, deploying once to Loom lets you future-proof your dapp by reaching the largest number of users possible. — https://loomx.io/
Loom Network provides a 2nd-Layer solution to Ethereum providing these main benefits:
- 1–3 second confirmation times
- No gas fees for users
- Avoid mainnet transaction congestion
- Fully compatible with MetaMask and other major Ethereum wallets
Creating a Smart Contract
If you are a beginner in terms of creating smart contracts written in Solidity, I highly recommend Remix IDE to develop and play around with your smart contracts. It enables to easily test some functionality already. Remix also creates the contract ABI and the bytecode for any smart contract — these you will need later on to integrate into your game client.
Also, you should look into Loom’s Solidity School CryptoZombies. 😉
For advanced coders or experts, I recommend using Truffle. You can also follow this guide by Pranshu Rastogi to create and deploy smart contracts for Loom using Truffle.
The smart contract we are going to create is for a small prototype singleplayer game created with the Game Engine Unity where the player is able to collect coins. These coins are actually created and saved on the Loom blockchain for free!
Step by Step
Let’s create this prototype game smart contract step by step.
First of all, let’s create a general smart contract
pragma solidity >=0.5.0 <0.6.0;contract CoinCollector {}
Notice that we defined the range for the compiler version. It has to be between version 5.0 and 6.0. This is recommended since a lower version may include security issues and a higher version may break the contract code. Our contract is called CoinCollector.
Let’s include a win condition, the number of coins a player needs to collect for winning the game.
pragma solidity >=0.5.0 <0.6.0;contract CoinCollector {
uint256 private winCondition;
constructor(uint256 _winCondition) public {
winCondition = _winCondition;
}
}
Notice that we created a global variable “winCondition”, which is of the type uint256 (Positive natural numbers). Further, we included the constructor. The constructor is a special function called once the smart contract is deployed. Here we define our actual win condition in the parameter. Let’s make it 10. For better overview let the parameter always start with an underscore, so you are able to differentiate between parameters and global variables.
Let’s include our functionalities.
pragma solidity >=0.5.0 <0.6.0;contract CoinCollector {
uint256 private winCondition;
mapping (address => uint256) private coinCount;
constructor(uint256 _winCondition) public {
winCondition = _winCondition;
} function startGame() external {
coinCount[msg.sender] = 0;
} function collectCoin() external {
coinCount[msg.sender] += 1;
} function getCoinCount() external view returns (uint256) {
return coinCount[msg.sender];
}}
Notice that besides the constructor we now created three more functions. We want to be able to start a game, collect coins and always receive the information on how many coins have been collected already. We provided each function with visibility modifiers. Possible modifiers are public, private, internal and external.
- Public functions can be called by anyone
- Private means that the function can only be called from inside the contract
- Internal allows contracts that inherit from the parent contract to use that function
- External functions are part of the contract interface, which means they can be called from other contracts and via transactions
That said, keep your functions private or internal unless there is a need for outside interaction. Further, our getCoinCount function has the “view” keyword, which is optional but specifies we are not changing any storage variables within this function. We only “view” some data, e.g. our coins. :)
To keep track of how many coins are collected, we added the mapping coinCount. When the game starts the value for the player is reset and while playing each coin collection will increase the value by 1. Our mapping takes an address (the current player) and “points” towards the value, which is the count for collected coins. Therefore we are able to save the current state for each player within coinCount.
Let’s add our last code pieces: Events. Events are most important for our client. It enables us to notice what’s going on. For example, we want to know when a player starts and wins a game or when a coin is collected.
pragma solidity >=0.5.0 <0.6.0;contract CoinCollector {
uint256 private winCondition;
mapping (address => uint256) private coinCount; event onStartGame(address _player);
event onCoinCollected(address _player, uint256 _coinCount);
event onEndGame(address _player);
constructor(uint256 _winCondition) public {
winCondition = _winCondition;
} function startGame() external {
coinCount[msg.sender] = 0;
emit onStartGame(msg.sender);
} function collectCoin() external {
coinCount[msg.sender] += 1;
emit onCoinCollected(msg.sender, coinCount[msg.sender]);
if (coinCount[msg.sender] == winCondition){
emit onEndGame(msg.sender);
}
} function getCoinCount() external view returns (uint256) {
return coinCount[msg.sender];
}}
Notice we added the possible events at the top where our storage variables are. Each event defines, which information it requires. In our collectCoin function, we added an if-statement to always check if the player who just collects a coin met the win condition. If so, the end game event is emitted and will be noticed by the client. Each time we refer to “msg.sender” in our code it refers to the address of the incoming transaction, the player.
Well done. The base contract is finished. Feel free to play around and add additional functionality, e.g. the overall coins collected are saved, different coin types are collected, or integrate mechanics to make it a multiplayer game. Be creative!
Deploying your Smart Contract
First of all, we need to install the Loom SDK with the following command:
curl https://raw.githubusercontent.com/loomnetwork/loom-sdk-documentation/master/scripts/get_loom.sh | sh
Next, create a private key that is required to deploy the smart contract with the following command:
./loom genkey -k priv_key -a pub_key
Now, we are creating a binary file for our contract.
solc --bin --overwrite -o . CoinCollector.sol
And use this binary file to deploy our smart contract to the Loom Testnet (It’s totally free!) We specify the private key “-k” the binary file “-b” the chain url “-u” and the chain id “--chain”.
./loom deploy -k priv_key -b CoinCollector.bin -u http://extdev-plasma-us1.dappchains.com:80 --chain “extdev-plasma-us1”
The result should look something like this, where at the top is the contract address followed by our bytecode.
New contract deployed with address: extdev-plasma-us1:0xfC4ce0E01e8fdd309af77564bF5bE8b7dE79E3e9Runtime bytecode: [96 128 96 64 82 52 128 21 97 0 16 87 96 0 128 253 91 80 96 4 54 16 97 0 94 87 96 0 53 124 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 144 4 128 99 171 126 56 160 20 97 0 99 87 128 99 202 83 16 172 20 97 0 129 87 128 99 214 90 181 242 20 97 0 139 87 91 96 0 128 253 91 97 0 107 97 0 149 86 91 96 64 81 128 130 129 82 96 32 1 145 80 80 96 64 81 128 145 3 144 243 91 97 0 137 97 0 220 86 91 0 91 97 0 147 97 2 130 86 91 0 91 96 0 96 1 96 0 51 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 144 129 82 96 32 1 96 0 32 84 144 80 144 86 91 96 1 128 96 0 51 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 144 129 82 96 32 1 96 0 32 96 0 130 130 84 1 146 80 80 129 144 85 80 127 191 239 136 214 251 22 149 67 110 98 57 38 222 62 243 239 224 63 35 239 35 206 36 19 14 22 231 220 54 231 86 87 51 96 1 96 0 51 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 144 129 82 96 32 1 96 0 32 84 96 64 81 128 131 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 130 129 82 96 32 1 146 80 80 80 96 64 81 128 145 3 144 161 96 0 84 96 1 96 0 51 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 144 129 82 96 32 1 96 0 32 84 20 21 97 2 128 87 127 241 146 45 227 151 135 44 67 217 208 185 165 167 164 56 126 82 138 167 86 234 251 122 48 149 77 42 177 197 150 123 193 51 96 64 81 128 130 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 145 80 80 96 64 81 128 145 3 144 161 91 86 91 96 0 96 1 96 0 51 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 144 129 82 96 32 1 96 0 32 129 144 85 80 127 164 24 168 129 11 81 212 86 200 190 242 122 103 50 143 121 1 217 23 209 155 38 140 190 174 131 119 90 81 239 236 96 51 96 64 81 128 130 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 115 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 22 129 82 96 32 1 145 80 80 96 64 81 128 145 3 144 161 86 254 161 101 98 122 122 114 48 88 32 37 59 125 14 80 36 176 248 220 94 188 101 217 194 206 222 90 41 16 251 207 104 252 40 65 194 155 70 208 254 117 97 0 41]
Transaction receipt: e9810a90a1cecebce9c5d2b19f152ea52029d574f9ae1cd08cce30d4b3af65f4
Notice the deployed contract address is “0xfC4ce0E01e8fdd309af77564bF5bE8b7dE79E3e9”. You are now able to find it in the Loom Block Explorer. The contract address is needed to add it to your game client.
Well done! Still some energy? Go and create the mini-game in Unity, which will use your smart contract right away! Learn How to create a Loom Game in Unity.
Who are we?
Our vision at Calystral is to unleash the potential of gamers. We are focusing on building games where the time and effort put in is rewarded with real value. Enhancing the players’ experience and empowering gamers to achieve more. In the process, we strive to overcome technical limitations of Blockchain Technology and use its benefits to lift games to the next level. We would love to share these solutions with the community and other developers. Towards a better future of Gaming!