Coding a Smart Contract

Chapter 16:

Previous: Beyond Bitcoin: Smart Contracts

Next: The Internet of Blockchains

In the previous chapter we had a glimpse at some smart contract code and had a very brief overview of how it works. The preceding code was generated by ChatGPT and was just meant as a sample for context purposes. The best way to understand how dApps work is to see how a smart contract works so I have written a simple but functional smart contract for us to experiment with. We have already reviewed some code, but nothing beats hands on experience. In this chapter we will actually write a smart contract where we can click buttons and enter wallet addresses and values to observe the effect. Once you have seen for yourself exactly how these on-chain, open, permissionless computer programs function under the hood, it will be much simpler to picture what is happening when we interact with various dApps throughout the rest of the book. It will make you more aware of the real risks of smart contract failure, bugs, malicious code and most interestingly, how awesome this technology is.

We will use the most common programming language for smart contract development, Solidity. You do not need to be a coder, most programming languages, Solidity included, use English-like words and terms along with simple mathematical symbols. These words phrases and symbols are then converted into the complex human-unreadable codes, called bytecode which is executed by the thousands of Ethereum Virtual Machines EVM that run the code on the Ethereum blockchain. Furthermore, the code we will write and execute in this chapter will run on an Ethereum emulator, but EVM code is used on many blockchains, so if you aspire to be a blockchain developer this will get you started coding for Ethereum, Polygon, Binance Smart Chain, Avalanche, a few of the Polkadot parachains, 1 or 2 on Cosmos, Fantom, and more besides.

We will be using the Remix programming tool which you can use straight off the website or you can download a desktop app if you prefer. The Remix tool is a lifesaver for people wanting to learn blockchain programming. As we will see the code in a smart contract is not especially complicated to begin to learn. Certainly, creating sophisticated dApps is complex but the initial code is quite understandable. In my opinion the biggest obstacle for getting started with blockchain programming is the complex technology stack required. Let me explain. You must have an app for writing smart contracts, and app for compiling the code to byte code. An app for deploying the smart contracts to the blockchain emulator as well as the blockchain emulator. You will need an app to help you debug the smart contracts also. In addition, you will need to run/borrow a node that can call your smart contracts on the blockchain emulator. Furthermore, you will need an app for writing the code for the user interface – remember the smart contract is just the logic on the blockchain. This UI code usually takes the form of a JavaScript library and HTML, usually running in a JavaScript based UI framework, perhaps React and they often need a back-end database and the whole thing needs to run on a web server. You will usually then employ a package manager like NPM, which is a text-based command line tool. All the version numbers of all the software will need to be synchronized and compatible or nothing will work. Oh dear!

Remix removes all of this by playing every single role. This enables you to learn, grow confident in all areas while making decisions and learning about the myriad of options to setup your development environment over time.

I won’t go into every tiny detail of the code that would take to long but I will give an overview of every single line so you can see how it all comes together and get a feeling for how much work goes into a sophisticated dApp like AAVE or Uniswap. Furthermore, as mentioned, we will run this dApp on an emulator but it would only take a few extra clicks to deploy it to Ethereum for real. Don’t do this. It will cost a fortune in gas and the dApp is extremely amateur and easily exploitable. I will be releasing Solidity for Beginners book when I get the time, if you want to learn more and get a bit more professional.

First, I thought I would explain exactly what our dApp will do and show you some pictures.

The dApp in action

Our dApp will allow the owner – that’s you – to publish the smart contract to the blockchain while simultaneously adding some ETH into the smart contract. The buttons of the dApp will allow the user to get the wallet address and balance of the owner/deployer, the smart contract itself.

The user will be able to copy and paste a wallet address which the smart contract will store on the blockchain along with a value. When the user clicks the Pay button the smart contract will pay that wallet address the value of ETH previously entered. The payable wallet address and the value can be updated at any time.

In the next image you can see what our dApp will look like when it is first deployed.

dApp when first deployed

At the top you can see the pay button that will trigger a payment of ETH from the smart contract wallet. The saveAddress and setValue buttons respectively will determine where that payment is sent and how much is sent. The contAddress and contFunds buttons will display the address of the contract and how much ETH the contract has. The next two buttons ownerAddress and ownerFunds do the same for the owner/deployers wallet. The userAddress and userFunds buttons do the same for the wallet address that we will enter in the saveAdress field. Initially this value will be empty.

The color coordination of the buttons is red when a payment will be made, orange when gas fees will occur and blue which is a simple query to a node without code execution. The buttons are analogous to the user interface on a website that would use HTML to display buttons to the user and JavaScript to send instructions via a node to the EVMs on the blockchain. Remix handles all of this by creating buttons for us when we code the smart contract so we can learn more quickly.

This next image shows what the user will see when they interact with the blue buttons.

dApp when buttons first interacted

We can see that clicking the buttons yields an output. At the top underlined and labelled 1 then partway down underlined and labelled 1 we can confirm that pressing the contAddress button does yield the output of the contract address. This is also the address where funds can be sent. The next underlining has the value 10. I sent 10 ETH to the contract when I deployed it. You will see how to do this when you deploy your own code soon. Below that is the address of the owner of the smart contract. When you deploy your own code you will see we can verify this. The next underlining is the value 89. Our code will round to the nearest whole ETH. As all the test accounts start with 100 ETH, we sent 10 ETH to the smart contract and spent a small fraction of an ETH on the gas to deploy the contract, we have 89 whole ETH remaining in that address. Below we can see the user address is all zeroes and the user has zero ETH. If we were to enter a wallet address in the saveAddress field then click the button, then enter a value in the setValue field and click the button, then, finally, click the pay button the userAddress and walletFunds would yield positive values. To prove this, let’s write the code, paste it into Remix, and deploy the smart contract along with the funds.

Writing a smart contract

Visit https://remix.ethereum.org/ and observe the UI shown next.

Remix UI

It is true there is so much to learn to become proficient. But Remix is an amazing tool because it allows a complete beginner to take their first steps and slowly increase their knowledge over time.

In the middle panel of the UI find and click the New Fille button. In the resulting text entry on the left type test.sol – .sol is the file extension used in the Solidity language. Press the Enter key to create the file and open it in Remix. You will see a blank page ready for our input.

test.sol blank page for input

I will go through the code from top to bottom. You can copy and paste or type it all at once or as we go through it a piece at a time. Here is the code in its entirety if you want to copy and paste it into test.sol in Remix or just look it over before we discuss it. If it any time it is not clear where the code goes or where the curly brackets {} start or end, then just delete everything and copy-paste the entirety of the code below. My suggestion is not to try and run it until we have fully discussed it. At a few stages as we work through the explanation, I will suggest points where we can execute the code and examine the output.

The entire code listing of test.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract Test {

    address payable public userAddress;

    string name;

    uint256 amount = 1;

    address private owner;

    address private thisContract;

     constructor() payable{       

        thisContract = address(this);       

        owner=msg.sender;

    }

    receive() external payable{}

    function saveAddress(address payable ad) public {

        userAddress = ad;       

    }       

    function setValue(uint256 am) public {

        amount = am;       

    }

    function pay() public payable {     

        userAddress.transfer(amount * 1000000000000000000);

        //1 ETH = 1,000,000,000,000,000,000

    }

    function walletFunds() public view returns (uint) {

        return userAddress.balance / 1000000000000000000;

    }

    function contAddress() public view returns (address) {

        return thisContract;

    }

    function contFunds() public view returns (uint) {

        return thisContract.balance / 1000000000000000000;

    }

    function ownerAddress() public view returns (address) {

        return owner;

    }

    function ownerFunds() public view returns (uint) {

        return owner.balance / 1000000000000000000;

    }}

Explaining the code

Section 1: Contract Declaration and Constructor

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract Test {

    // State variables

    address payable public userAddress;

    string name;

    uint256 amount = 1;

    address private owner;

    address private thisContract;

    // Constructor

    constructor() payable {

        // Set the contract’s address and owner during deployment

        thisContract = address(this);

        owner = msg.sender;

    }

    //…

}

Explanation:

Contract Declaration: The contract is declared with the name Test.

State Variables: Various state variables are defined, including userAddress to store an address, name for a string (unused in this example), amount for a default value, and owner and thisContract as private addresses.

Constructor: The constructor function is executed only once during contract deployment. It initializes the contract’s state variables, setting thisContract to the contract’s address and owner to the sender’s address.

Section 2: Receive Function

    //…

    // Receive function to accept Ether

    receive() external payable {}

    //…

}

Explanation:

Receive Function: The receive function is a special function that is automatically called when the contract receives Ether without a specific function call. In this case, it allows the contract to accept Ether transactions.

Section 3: Setter Functions

    //…

    // Set user address

    function saveAddress(address payable ad) public {

        userAddress = ad;

    }

    // Set value function

    function setValue(uint256 am) public {

        amount = am;

    }

    //…

}

Explanation:

Save Address Function: saveAddress allows setting the userAddress variable to a provided payable address.

Set Value Function: setValue sets the amount variable to a specified value.

Section 4: Payment Function

    //…

    // Payment function

    function pay() public payable {

        userAddress.transfer(amount * 1 ether);

    }

    //…

}

Explanation:

Payment Function: The pay function sends Ether from the contract to the specified userAddress. It uses the transfer function and multiplies the amount by 1 ether to convert it from Ether to Wei.

Section 5: Getter Functions

    //…

    // Get user’s wallet balance

    function walletFunds() public view returns (uint) {

        return userAddress.balance / 1 ether;

    }

    // Get contract address

    function contAddress() public view returns (address) {

        return thisContract;

    }

    // Get contract balance

    function contFunds() public view returns (uint) {

        return thisContract.balance / 1 ether;

    }

    // Get owner’s address

    function ownerAddress() public view returns (address) {

        return owner;

    }

    // Get owner’s balance

    function ownerFunds() public view returns (uint) {

        return owner.balance / 1 ether;

    }

}

Explanation:

Getter Functions: These functions allow querying various information about the contract and its state, such as the user’s wallet balance, the contract address, the contract balance, the owner’s address, and the owner’s balance. The balances are converted from Wei to Ether for better readability.

Summary

There was so much about Remix and the Solidity language which I brushed over. For example using the Featured Plugins panel on the main UI you can quickly generate template code to create a smart contract to mint your very own token or your own NFT smart contract. It’s not all that hard either but it does take many pages of discussion. I have said that I am writing a book Solidity for Beginners and in a later edition of this book I will probably go a little deeper with programming too. Now we are well placed to start interacting with other peoples dApps with better knowledge than most about what goes on behind the scenes.

Previous: Beyond Bitcoin: Smart Contracts

Next: The Internet of Blockchains