How to Use BUMO: Creating a ToDo DApp

By: Jesse Abramowitz
Blockchain Developer

Originally posted on BlockX Labs

Hi and welcome back to the fourth and final part of this tutorial series on how to use BUMO. In part 1 we talked about creating accounts, part 2 learned about setting up a local node and sending a transaction, part 3 was creating a simple counter contract and deploying it and finally part 4 will be about creating a slightly more complex contract, a to do contract and connecting it to a front end thus creating a to do DApp.

From knowing how to build a web page to hosting it, realistically, there is a lot that goes into creating a DApp.

Although I can’t take you through it all, you can refer to my earlier blogs which outline how to do everything you need to do that is BUMO related. I will highlight and solve some difficult challenges I had while trying to connect the back end blockchain to the front end. All of the code I use related to BUMO can be viewed on my github.

So first thing is first we need to write the contract. In part 3 I showed you how to do this with a simple counter, this code got a little more complex in writing and calling it.

Just to recap there are three main functions in a BUMO smart contract:

  1. init, which gets called on initialization of the contract,
  2. main, which is your write function
  3. query, which is your read function.

The Contract

For this to do DApp, I wrote a simple contract that stores an array of structs. The struct is a string representing a task and a boolean representing if the task is completed. I wrote a function to loop through the array and compare the name of the task inputted to the tasks in the array and if they are the same name, change the boolean to true. To do this, we can nest both of these in the write function which is the main function and have an if statement to call them. If that does not make sense that is ok let’s look at the code of the contract and how to call both write functions:

function main(input)
{
const toDoStorage = storageLoad(‘toDo’);
let para = JSON.parse(input);
let task = para.params;
let toDo = JSON.parse(toDoStorage);
if (para.method === ‘add’ ) {
toDo.push(task);
}else if (para.method === ‘done’){
let i = 0;
for (i = 0; i < toDo.length; i+=1) {
if (toDo[i].desc === task.desc){
toDo[i].completed = true;
}
}
}
storageStore(‘toDo’, JSON.stringify(toDo));
}

At this point, I am telling the blockchain to look at the input methods when a transaction is sent to it, if it is labeled “add”, add the parameters (task) to the array, if the method is equal to done then grab the parameter labeled desc, loop through the array comparing it to the other items labeled desc and if they are equal set the boolean completed to true. I have put this code on my github.

Next we need to deploy this. Which you can find a script named deployToDo.js to do in my github and its subsequent explanation in part 3. Running this script involves installing their SDK and running a local node for detailed instructions to run this script look at part 3 and part 2.

Let’s look at the parts that make them more complex, remember this is only slightly we are changing one aspect of the transaction script.

sdk.operation.contractInvokeByBUOperation({
//your contract address
contractAddress: ‘buQswKLuqPUSDQjsfqh4CZ9i3NPWevTNuvnk’,
input: JSON.stringify({
method: ‘add’,
params: {
“completed” : false,
“desc” : “this is desc”
}
}),
})

So while building this operation we call

sdk.operation.contractInvokeByBUOperation

We input the method we want which is add and then the params, completed: false and desc : the task we want. Then we send it like a normal transaction.

Next we need to call the second method on the contract. This is pretty much the same thing I did above:

sdk.operation.contractInvokeByBUOperation({
// your contract address
contractAddress: ‘buQswKLuqPUSDQjsfqh4CZ9i3NPWevTNuvnk’, 
input: JSON.stringify({
method: ‘done’,
params: {
“desc” : “this is desc”
}
}),
})

After running this, it should set this is desc from false to true. Fantastic, feel free to run the readToDo.js to check if the transactions are working.

Troubleshooting

I can foresee some common issues that people will run into and some that I personally did, so I will hopefully address some in this section. First of all when deploying your contract, remember my scripts are tied to my personal blockchain. I am running on my vagrant machine so make sure to change that. See part 2 for setting up a local node.

Next, make sure you are using your personal account and contract address as well as proper nonce, part 3 and part 2 go into more detail about this.

Finally, I ran into an error where the fee I set was enough to get the transaction through but not enough to run the function, it was weird and hard to realize until I grabbed the transaction receipt from the blockchain. I thought reproducing the problem and taking a paragraph or two to explain it would be worthwhile.

So to reproduce this error I used the fee limit that was the standard for sending a transaction and I get this error:

So then I set my fee limit for the least amount of the transaction 311000. I get an error code of zero and a hash no problem. However it you get the transaction history you will see this:

You can see it passes the first check but then fails at the second check saying the actual fee should be 1029000.

Just to explain fee limit and gas price: The BUMO blockchain has a virtual machine and each line of code you write takes a certain amount of computational steps to complete. This will be constant as it is always writing the same code. First we set a gas price which is how much we are going to spend for each computational step. Makes sense. Fee limit comes in because we don’t always know or think about how many computational steps something will be, so for example if I write an infinite loop and send it to the BUMO virtual machine then it will continue running it until my wallet is drained and the code will fail. For this reason there is a fee limit which says run this code until this amount of coins are drained, if I go over that amount stop executing the code. I would have lost lost the coins but it would be limited and not drain my whole wallet.

If you are working on test or local nets and don’t care you can set the fee limit really high however, on the mainnet you should set the fee limit as low as you possibly can while still completing the transaction.

Front End

Now that the contract is done we need to connect it to a front end. The github code for what I did is here. It wasn’t too complex after all the leg work we did in the last 3 tutorials…but there were some difficult aspects so let’s look at the DApp then I will write a section on each of these issues and how I solved them.

This is a simple layout, I kept the react symbol in…

…Mostly because I think it looks really cool. Then two buttons and a text field.

So for a To do DApp, the first thing is we need a task so let’s add one.

Hmmm I wonder what I have to do today…

Oh right yes of course I do have to do that. So input it and then click create task button. I have the console open and I log a bunch of events. We pretty much are running the writeToDo.js script in the background.

Things to notice : first, we can see our operation and we can see under our params we have our input as well as completed is false. Second, You can see in our console, 156, that is me grabbing my nonce. (I will explain that later on in the article.)

That giant block of numbers? I promise I will explain what that is.

Finally, we get to an error code of 0 and a transaction hash.

We wait a few seconds for this to be mined and then click to update list.

Great, now we get our list from the blockchain. We are currently running the readToDo.js script in the background. We see all the entries in the array we created. The desc which is the task and then either an x — for false or a ✓ — for true as well we see a button Done?

Let’s click it:

Now we run the changeDone.js script and grab the desc of the button we clicked as the param input. We send that transaction off to the blockchain and next time we update the list:

Our task has now been completed. Which really means the false was changed to true and now a check is rendered instead of an x.

I promised I was going to write a section on some challenges that need to be solved to use BUMO with a DApp…but I just checked my to do list and apparently I am done writing this blog. I mean it doesn’t seem like it, but this is on the blockchain so it must be true.

Troubleshooting

Nonce

In my scripts, I made you change your nonce manually each time. Now at first I did this because it was easy, then I considered automating it. Then I thought about it and remembered back to how confusing a concept of nonce was to some of the developers around me. Hopefully by manually changing the nonce, you now realize exactly what it is and by knowing this you can do some really cool things. However, when dealing with a DApp we are going to have to fix this.

This is slightly more difficult than it sounds and I will explain why in a minute. If you look at my script we can get the nonce like this.

const accountInfo = await sdk.account.getNonce(‘buQgvdDfUjmK56K73ba8kqnE1d8azzCRYM9G’);
if (accountInfo.errorCode !== 0) {
console.log(accountInfo);
return;
}

But this isn’t enough because for some reason BUMO adds one to the nonce that it returns from its blockchain. If it returns 2 the nonce you need is 3…it also returns it as a string and it needs to be inputted as a string. We need to convert it to an integer, add 1 and then convert it back to a string.

I did it like this:

let nonc = Number(accountInfo.result.nonce) + 1;
let nonce = nonc.toString();
console.log(nonce);

BUMO did it on their examples like this:

const BigNumber = require(‘bignumber.js’);
nonce = new BigNumber(nonce).plus(1).toString(10);

You can choose your method. Then you can put nonce in your blob and you should be good to go.

Enabling CORS

The node we set up and the test node they are running (as of the day I am writing it) are not CORS enabled. In the past blockchains that I have worked on have allowed for this but unfortunately BUMO does not.

CORS stands for Cross Origin Resource Sharing which means that it doesn’t allow outside API calls to a node from different web pages. In essence, our calls to our node get blocked when they come from our DApp. You can read more about CORS on Mozilla.

To overcome this while testing and working locally, we can easily disable CORS for our node. If you go to the chrome store and get the extension Allow-Control-Allow-Origin.

When we add our host path /* this should allow our node to accept and send messages back to our DApp.

To solve this in a production environment, you would have to set up a proxy that is CORS enabled. Your DApp would make calls to the proxy and your proxy would then make calls to the DApp.

Converting the ByteCode to Hex data

This was an interesting issue that was happening. And it relates to the blob of numbers I promised I would talk about later. Specifically this one:

What is happening here is that it is easier to pass this around then hex data so the browser converts it. So we need to write some code to convert it back:

convertToHex = (blob) => {
const buffer = blob.split(‘,’).map(e => Number(e))
return Array.prototype.map.call(new Uint8Array(buffer), x => (‘00’ + x.toString(16)).slice(-2)).join(‘’);
}

I got this function from stackoverflow to do that and I call it after the blob is created, convert that blob and use the hex version of the blob for the rest of the script.

And that’s it!

If you have made it this far please build something and give me a percent of the royalties, realistically that is the least you can do.

Jokes, cheers!, go forth and Buidl.

Do you have any questions about BUMO and the BUMO ecosystem? Let us know in the comments or directly to BUMO through their GitHub, Twitter, Weibo, and Telegram.