How to build a Web App using Blockchain — Part 4
In the previous articles, we saw together how to build all our Web App component, here we will work on our dApp and create our Smart Contract using the RIDE language on Waves Blockchain. If you didn’t read the part 1, please start there.
PART 1
a) Define the project
b) Why use Blockchain?
c) Initiate the project
PART 2
d) Create the HTML for your INDEX views
e) Create a GetData and Settings Classes
f) Create the Index javascript logics
PART 3
g) Create the HTML for your ADMIN views
h) Create the Admin javascript logic
i) Integrate Waves Signer
PART 4
j) Create the Smart Contract
k) Deploy the Smart Contract
l) Conclustion
J) CREATE YOUR SMART CONTRACT
To get the full code of the Smart Contract click here.
On Waves Blockchain, there is 3 types of Smart Contract:
The Smart Account
This is a Smart Contract attached to a Waves Account and the script logic of this contract will check every outgoing transaction (not incoming one)
The Smart Asset
This is a Smart Contract attached to a Waves Asset and the script will process every outgoing transactions that are using this asset.
The dApp
This is a Smart Contract attached to a Waves Account, similar to the Smart Account but the script logic will also includes @Callable function that you will be able to call from outside using the invokeScript transaction
In our project, we use a dApp account, this account will have two Callable functions:
- updateInfos()
To update the title and description our our page - addUpdateCase()
To add or update entry on our data storage
To create your dApp go to ide.wavesplatform.com, this is a convenient online ide for Waves Protocol, a very easy way to start for front end web developers.
From there click on the + on the bottom left:
It will create a dApp Smart Contract template as follow:
You see the 3 first lines these are directives needed to identify the version and type of the contract, don’t touch it. You can remove the 2 commented lines at the bottom (we wont see the Verifier today).
THE “UPDATEINFOS” FUNCTION
We are left with a @Callable function named foo in this new file, all function we want to be able to call from outside of our contract needs to start with a @Callable(i), the i is a global variable accessible inside the function and giving access to the transaction fields, you could call it differently if you wanted.
Be aware that local function, the one you would not allow to be called from outside, should be declared before all the @Callable one’s and without the @Callable on top of it.
Function in RIDE are declared with the key word “func” followed by the name of the function. Please change the function name from “foo” to “updateInfos”, and include two parameters, name and intro:
Make sure to give types to the parameters, here both are :String.
From there we want get the address of the account calling this function, to do that we will declare a new ownerAddress variable with key word “let” and use the global variable “i” to access the caller address with “caller” key word:
let ownerAddress = i.caller
Now that we have the address of the calling account, the title and the description as well, we will write these data on the data storage of the dApp account.
In the current empty WriteSet([]) add the two following DataEntry:
Each DataEntry line will write (or update if the key already exist) a new key/value entry in the dApp data storage, the parameter here are the key (a string) and the value (string, boolean, integer or byteVector) ex: DataEntry(key, value)
When we retrieved our address with i.caller we received the address in byteVector format, key in our data storage needs to be in string format, this is why we use toString() method to convert the address into string.
And thats it, our first @Callable function to update the general infos of the page of the calling account is ready! Was quite easy no?
THE “ADDUPDATECASE” FUNCTION
This function will add just a bit more logics, as you can see, it have a bit more parameter, some as :String and others as :Int, here the full code then we will discuss it:
Same as for the previous function, we declare the ownerAddress.
We also declare a counterNum variable to get the last identifiant used for this calling account, for that we are using what we call a Pattern Matching with the key word “match” followed by a “getter” method.
Here, this.getInteger(address+”_counterNum”) is looking into the data Storage of the dApp for an entry with the key “{address}_counterNum” and that is an Integer, in RIDE dApp Smart Contract, the key word “this” is a reference to the address of the current dApp account, you could also use it that way:
getInteger(this, ownerAddress + "_counterNum")
if you want get data from another account, you would use it as follow:
getInteger("3nHuJg...", ownerAddress + "_counterNum")
Back to our code, if the getInteger() “match”, meaning if the entry exist and is an integer (case c: Int => c) then we attribute the result value to the currentCounterNum variable, if not, then we set it to 0.
Similarly, we will use a match to define the patient ID, we check if an entry with the “identifiant” parameter exist, remember if its 0 it’s a new entry, else its an update.
If an entry exist on the data storage with the key {ownerAddress}_patient_{identifiant}_identifiant” and it’s an Int then we return it to patientID and it’s an update else the patientID is currentCounterNum +1 which is then a new entry with a new auto incremented ID.
After that we build a json with all the data to store it as well on the data storage, this will be usefull to access all data through a unique key request in our Web App. Remember each invoke call send several parameters for a unique case/patient.
We also want to keep an entry per parameter to use the data storage as an open API where you could get all entrie per location only, age only and so on.
Next step, we needs to define how we will update the value of the {userAddress}_counterNum key, ether with the last know identifiant (if we are updating an entry) or the new identifiant (if create a new entry), for that we do:
let updateCounter = if(identifiant == 0) then currentCounterNum + 1 else currentCounterNum
If identifiant parameter was 0 then it was a new entry, meaning we increment the counter of entry for this address by +1, else, the counterNum stay the same.
I’ll use this last piece of code to point out that in RIDE, every if statement needs a then and a else branch, both are mandatory.
We are alsmot there :) Next, we have an if statement again, so? so there will be a then and else right? This one is to check the status parameter, we only allow 3 possible status, 1,2 or 3 (in our project, 1 = confirmed, 2 = recovered and 3 = deceased)
if(status != 1 && status != 2 && status != 3 ) then throw("Wrong status")else# code continue here...
By the way, comments in RIDE only work single line and start with #.
And here we are, now at the last part of our Smart Contract! and its very easy because we will do the same as for the updateInfos @Callable, remember the WriteSet?
So we want write all our data on the storage, and remember, we needs 1 DataEntry(key,value) per data, meaning:
That’s it, our dApp Smart Contract is finished!
K) DEPLOY YOUR SMART CONTRACT
We still have a couple of things to do, first of all, we needs to deploy this Smart Contract on a Waves Account, from the online ide, go to the top right and create a new account:
Select it and copy the Address, go to the wavesexplorer faucet to get some free waves token for testnet:
Enter your address, click that your are not a robot (unless you are one :p) and click “Request 10 WAVES”.
Once you see these waves on your IDE Account, click on “console” at the bottom left and write deploy()
Push enter and the Smart Contract will deploy on the newly created account, this account will then become the dApp and the address of this account is the address you will use in your settings.js for this.dappAddress.
If you see the link to the transaction in wavesexplorer then everything when well!
You can now go back to our Web Application, in the settings.js file and change the this.dappAddress value to this new dApp address, save it, make sure webpack is still compiling and nodemon restarting.
Then go to localhost:3000/admin, connect with waves signer and you will see an empty form, start entering data again, the title and description then a couple of entries and after that, go back to localhost:3000 and you should see these newly added data.
L) CONCLUSION
It was a bit long i know but i wanted to show you a complete real potential Blockchain use case application built from scratch with the front end, back end, the smart contract and everything integrated together.
I didn’t go as deep as i wanted in some parts because it was already quite long but the idea was to help you realise that finally, its not much different than building a website or a regular web app, if you look back over the 4 parts, 1 out of 4 was focusing on the Smart Contract, everything else was regular html, css, javascript, routing, api calls etc. Everything a front end developer know already.
Of course we could improve a lot from here, for example, a way to add entries in mass or even import a cvs of entries, display the waves balance in the admin page etc.
Going back to the Smart Contract, how does RIDE feel? It’s quite easy to understand right? There is of course a lot more to see and do with it but at least now, you know that it is not that complicated after all!
Here a couple of interesting tools to use that i’ll list here even if i wasnt able to include them in the process:
- Waves Dapp this one will allow you to test your smart contract very conveniently through a very simple UI and Waves Keeper
- Waves Keeper this is an alternative to Waves Signer the one we used in our project, the difference is Keeper is a browser extension.
- Waves Documentation of course the official documentation is your friend and very helpfull, you’ll find everything there! If not, ask on telegram
- Waves Explorer we only used it for the faucet but this is a very convenient tools to explorer account, blocks, transaction, dapp storage etc
- Waves Oracle to create oracle for your project
I hope this will help some of you and if you have any question don’t hesitate to ask in comments or on the Waves Developers telegram channel and if you build something on Waves, don’t hesitate to share it with us, we love to see what others are doing!
The STEP 4 code:
https://github.com/christopheSeeka/covstats-tuto/tree/step4
The complete web app code:
https://github.com/christopheSeeka/cov-stats
The webapp example on TESTNET:
https://covtest.sign-web.app/
The webapp on MAINNET:
https://cov-stats.sign-web.app/