A-Z of building dApps on ICE — Part 2: Smart Contracts
This is the part 2 of the A-Z of building dApps on ICE series. This article shows how to write your first smart contract using Solidity language and deploy it to the Arctic testnet (since Frost is deprecated). This tutorial assumes you have a gone through Part 1 of the series and have set up the required environment and tools.
What you will learn
- What smart contracts are and how they work.
- Write a simple smart contract.
- Create your own token.
- Deploy Smart contracts with Remix IDE.
What are Smart Contracts?
Smart contracts are programs stored on a blockchain that run when invoked through qualified external accounts. They may be used to automate changes to stored variables which represent things as token balances or system parameters. Each invocation of the contract is executed without expenditure of time by any intermediary and is stored on the blockchain as a transaction that can be verified by all participants.
How Smart Contracts work ?
Smart contracts operate by executing basic conditional expressions expressed as code on a blockchain. When the terms of the agreement are satisfied, smart contracts are immediately executed. These actions could include releasing funds to appropriate parties, registering a vehicle, sending notifications, or issuing a ticket. When the transaction is completed, the blockchain is updated. Once executed, the transaction cannot be changed and can, in most cases, be verified by all participants.
There can be as many criteria as needed within a smart contract to convince the parties that the work will be executed correctly. Participants must identify how transactions and their data are represented on the blockchain, agree on the conditional rules that govern those transactions, investigate any conceivable exceptions, and develop a structure for resolving disputes in order to establish the terms.
Benefits of smart contracts
Autonomy and savings
Because smart contracts do not require brokers or other middlemen to confirm the agreement, they reduce the possibility of third-party manipulation. Furthermore, the lack of a middleman in smart contracts leads in cost reductions.
Speed, efficiency and accuracy
When a condition is satisfied, the contract is instantly executed.
Because smart contracts are digital and automated, there is no paperwork to handle and no time wasted correcting errors that frequently occur when filling out forms manually.
Clear Communication
Everything is clear due to the necessity for precision in contract specification. There can be no space for misunderstanding or miscommunication.
As a result, smart contracts have the potential to significantly reduce the amount of efficiency wasted due to communication gaps.
Trust and transparency
There is no need to question if information has been manipulated for personal gain because there is no third party engaged and encrypted records of transactions are transmitted between participants.
Security
Blockchain transaction records are encrypted, making them extremely difficult to hack. Furthermore, because each record on a distributed ledger is linked to the preceding and subsequent entries, hackers would have to modify the entire chain to change a single record.
A Simple Smart Contract
We will start with a simple Greeter contract that greets the user. It also allows changing the greeting message, so that users can be greeted differently.
pragma solidity >=0.4.16 <=0.8.12;contract Greeter { string greetMsg = “Hello there!”; function setGreetMsg(string memory _greetMsg) public {
greetMsg = _greetMsg;
} function greet() public view returns (string memory) {
return greetMsg;
}}
The first line is a Version Pragma. Pragmas are directives that specify how the solidity compiler should treat the source code. The version in our contract specifies that our code is written for Solidity version between 0.4.16 and 0.8.12 (inclusive).
Then we go on defining our Greeter contract. This contract has one string variable greetMsg and two functions setGreetMsg() and greet().
We have initialized the greetMsg variable to be “Hello there”. So if we call the greet() function defined above, this is the message that the user will be greeted with initially.
Then we have defined a function setGreetMsg() that takes a string as an argument and sets our contract variable to this string. This is a setter method since it sets/updates our contract variable. The function argument is of type string memory, which indicates that the argument variable resides on memory as a reference, rather than on storage. The public keyword before the beginning of the function body is called access modifier, and allows this method to be accessed from both inside and outside the contract.
Finally we have a read-only function greet(), that greets the user with the message set on the greetMsg variable. The view keyword tells the compiler that this method will never modify the state of the contract i.e. it’s variables. returns (string memory) indicates that this method returns a reference/pointer to a string to the function caller.
You can find more about solidity smart contracts here.
Creating a Token
Let us create our own token now. We’ll try to keep the Token as simple as possible with basic functionality and without using any external libraries like OpenZeppelin contracts.
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.4.16 <=0.8.12;contract MyToken {
address public admin;
mapping (address => uint256) balances; event Transfer(address from, address to, uint256 amount);
event Mint(address to, uint256 amount); constructor() {
admin = msg.sender;
} function balanceOf(address account) public view returns (uint256) {
return balances[account];
} function mintTo(address recipient, uint256 amount) public {
require(msg.sender == admin, "Unauthorized");
balances[recipient] += amount;
emit Mint(recipient, amount);
} function transfer(address recipient, uint256 amount) public {
require(amount < balances[msg.sender], "Insufficient balance");
balances[msg.sender] -= amount;
balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
}
}
This example is inspired from SubCurrency contract example on the Solidity docs.
The line address public admin; declares a state variable of type address and makes it publicly accessible. The address data type in Solidity is a 20 byte value and represents a wallet or a contract address (eg: 0xa994f5ea0ba39494ce839613fffba74279579268).
The next line, mapping (address => uint256) public balances; creates a publicly accessible mapping data type. Mappings in Solidity can be viewed as hash tables. In this case, you can map a wallet/contract address to a uint256 (32 byte integer). Each of this map will signify the token balance held by that address.
The next two lines declare events. Events in solidity are used to indicate that something interesting has occurred in the contract. We will emit these two events later in the code whenever successful minting and transfer of tokens take place. You can use client side APIs like web3.js, ethers.js, etc to subscribe to these events. More info here.
Next, we define a constructor for our contract. This constructor gets invoked whenever a contract code is deployed on a blockchain network. For this contract, we are setting the value of the admin variable to msg.sender, which references to the account that deploys this contract.
The balanceOf() function is a read-only function that gives the token balance of the specified account.
The mintTo() function mints (creates) the specified amount of token out of thin air and makes those tokens available to the specified account address. However, we don’t want anyone to mint any amount of tokens in a random fashion, and degrade our token. The line require(msg.sender == admin, “Unauthorized”); checks just that. It ensures that only an admin can call this function, or else the whole transaction is reverted and an error with the “Unauthorized” message that we specified, is thrown. In the last two lines of this function, we increase the balance of the recipient by specified amount and emit an event to indicate that an account has been minted a certain amount of tokens by the admin.
Finally we define the transfer() function to transfer specified amount of tokens from the account that initiated this call, to the specified account. First we make a check to ensure that the calling account has enough funds to make the transfer, or else we throw an error with message “Insufficient balance” and revert the whole transaction. We, then decrease the specified amount from the calling account, and add that balance to the specified recipient account. Finally, we emit the Transfer event, specifying that the specified amount has been transferred from the calling account to recipient account.
Now that we can understand the concept of smart contracts and the Solidity language, let us try to deploy the smart contract and interact with it. For that we will be utilizing Remix IDE.
Deploying smart contract using Remix
Remix IDE is an open source web and desktop application. It provides a fast development cycle and has a rich set of plugins with intuitive GUIs. Remix is used for the entire journey of contract development as well as act as a playground for learning and teaching EVM based blockchains.
It is one of the most popular Ethereum programming environments for smart contracts. Since ICE is EVM compatible, Remix can be directly used to deploy smart contracts on ICE testnet or ICE development node.
You will first need to configure your metamask with Arctic testnet network. Details can be found here.
Then you will need some testnet tokens to deploy your contract which you can get from a faucet. Details can be found here.
Now we are all set to deploy our sub-currency smart contract to ICE’s Arctic testnet using Remix IDE.
Open Remix IDE
Head over to https://remix.ethereum.org to open Remix IDE in your browser.
- Create a new workspace and then create a new file in this workspace named MyToken.sol
- Paste the sub-currency contract into MyToken.sol file.
Compile your contract
- Navigate to the solidity compiler through the left navigation tab.
- Click on Compile MyToken.sol button to compile your contract.
- It will generate the contract’s bytecode and the Application Binary Interface (ABI).
Deploy your contract
- Navigate to the Deploy and run transaction tab from left side navigation
- Change the environment to injected web3 from Environment dropdown. This will invoke the metamask extension in your browser. Make sure your metamask is configured to ICE testnode (configure metamask).
- Select the account from metamask and confirm the connection
- From contracts dropdown select MyToken.sol and click on Deploy button
- Next metamask will pop up to ask to confirmation. confirm it and wait some time till it shows the transaction details in the console.
Congratulations! You have deployed your smart contract to the Arctic testnet using Remix IDE.
More information about deploying a contract and interacting with it can be found here.
Conclusion
In this second part of the development on ICE series, we got an idea about smart contract and its working. We also wrote a simple Greeter contract and created our own Token. In the end, we deployed the latter to our Arctic testnet using Remix IDE.
In the next part, we will learn about building and deploying a decentralized voting application using Solidity and React JS on the Arctic testnet.