Developer Christophe Verdot shares his experiences of recently taking the online course ‘Mastering Web 3.0 with Waves’.



What is your background? What motivated you to take the course?

I’ve been a web developer for about 15 years, working mostly as a freelancer.

Working on the development of sustainable registry web application for emerging countries, financed by a bank group, I was once asked about the possibility of implementing blockchain certification into the registry. At that time, I didn’t know much about blockchain certification even though I had been into crypto for a while, mostly on the investor side.

We didn’t push through with the implementation of that feature on that specific application but the idea that institutions and banks were asking about implementing such technologies in their applications made me do a lot of study and research into the topic and then start the Signature Chain project.

I developed a beta version that is already live on Mainnet. At that time [Waves programming language] Ride wasn’t ready yet, so I developed it in the simplest way, using transfer transactions with attached JSON. But the main goal has always been to deliver more advanced features once Ride was available. And this was the main reason for me to take the course, since the next planned step for the project is turning it into a decentralised application (dApp).

What aspects of the course did you find the easiest and most difficult?

The easiest aspect was that we had plenty of time to do it, that it was about just learning rather than competing to be the best. It was also well explained, with simple but explicit illustrations which helped a lot to visualise and understand different topics.

In the challenges, we were pushed to think on our own and sometimes do some research, which is the best way to learn and get a stronger understanding of concepts from a lesson.

Several times, I didn’t have a clear understanding from the lecture part until I got into the code and went through the challenge. No ‘copy/paste’ was allowed, which forced us to write all the challenge code, and that was another important point in reinforcing understanding.

The most difficult part was that sometimes multiple-choice questions in the challenge part were not that clear. I mean, my English isn’t excellent, and the questions were written by a non-native English speaker, so there was a gap in understanding sometimes.

Probably, I would have liked to learn more from the oracle and NFT sections. But, in any case, this course mainly aims to hook developers in, and you definitely need to spend time playing around with it and practising later to really get a full understanding of every aspect and detail.

Could you discuss in detail the solution you worked on during the course – ‘Coupon Bazaar’? Could you also provide examples of code?

We worked on ‘Coupon Bazaar’, which is basically a marketplace where people buy and sell discount coupons for goods and services at a low price. Each coupon was represented by a digital asset, which also represented a special discount offered by suppliers.



Work was required on several components of the application. First, it had to allow suppliers to register and manage their items (coupons). Then, a purchase confirmation feature needed to be created, as well as a feature enabling customers to search for and purchase coupons.



Several extra features were also added during the course, including a voting system and a feature for verification and blacklisting of suppliers.

We first went through differences between Smart Assets, Smart Accounts and dApp accounts, and the fundamentals of working with verifier functions. Verifier functions allow changing the default behavior of an account. By default, they check signatures of transactions, but the verifier function allows you to set another ‘rule’.

{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
letownerPublicKey = base58'H8ndsHjBha6oJBQQx33zqbP5wi8sQP7hwgjzWUv3q95M'
@Verifier(tx)
funcverify() = {
    matchtx {
        cases: SetScriptTransaction=>sigVerify(tx.bodyBytes, tx.proofs[0], ownerPublicKey)
        cased: DataTransaction=>true
        case_ =>false
    }
}

Then we started adding items. We used one of the most important features for dApp development that allows any type of data to be recorded onto the blockchain as key-value pairs, the data transaction. We combined it with a new transaction, invokeScript, used to call a callable function into a dApp from the outside.

One type of data transaction we used in the course was for suppliers to add items to the marketplace:

letdatajson = {
    "title":        "t-shirt with , vote 1",
    "coupon_price": 10000000,
    "old_price":    1000000000,
    "new_price":    100000000,
    "address":      "Universe",
    "description":  "I want you to make love, not war, i know you've heard it before",
    "image":        "https://bit.ly/2EXTghg"
}
it('add item', asyncfunction(){
    letts = invokeScript({
       dApp: dappAddress,
           call:{
               function:"addItem",
               args:[
                    { type:"string", value: datajson.title },
                    { type:"integer", value: datajson.coupon_price },
                    { type:"string", value: JSON.stringify(datajson) }
               ]},
               payment: []
           }, accountSupplierSeed)
    lettx = awaitbroadcast(ts)
    awaitwaitForTx(tx.id)
})

To process that data transaction through the addItem function and later develop a purchase and other options, we went through the callable function, able to be called by users from the outside. As a result, it can perform various tasks, such as initiating transfer of funds, writing or updating data in the dApp’s data storage, etc.

Here is an example of a callable function. It is used for the addItem function:

@Callable(i)
funcaddItem(title: String, price: Int, data: String) = {
    letsupplierAddress = toBase58String(i.caller.bytes)
    letitem = getKeyItem(supplierAddress, title)
    if( price <= 0) thenthrow("purchase amount cannot be less than item price")
    elseif( getValueItemSupplier(item) !=NONE ) thenthrow("an item is already exist")
    else{
        WriteSet([
           DataEntry(getKeyItemSupplier(item), supplierAddress),
           DataEntry(getKeyItemPrice(item), price),
           DataEntry(getKeyItemData(item), data)
        ])
    }
}

We later worked on a voting system to allow users to vote for promotion or removal of certain products. It used a ‘Commit-Reveal’ scheme to avoid outside influence during the voting process.
The commit step was used to collect encrypted votes with a hash function and salt.
The reveal step is for collecting decrypted votes and comparing their hashes.

Here is an example of a callable function used for this feature:

@Callable(i)
funcvoteCommit(item: String, hash: String) = {
    letuser = toBase58String(i.caller.bytes)
    letcommits = getValueCommitsCount(item)
    letstatus = getValueItemStatus(item)
    if( commits >=VOTERS) thenthrow("reached max num of voters")
    elseif(getValueCommit(item, user) !=NONE) thenthrow("user has already participated")
    elseif(getKeyItemSupplier(item) ==NONE) thenthrow("item does not exist")
    elseif(status !=NONE && status !=VOTING) thenthrow("voting is not possible")
    else{
        WriteSet([
           DataEntry(getKeyCommit(item, user), hash),
           DataEntry(getKeyCommitsCount(item), commits +1),
           DataEntry(getKeyItemStatus(item),if(commits ==VOTERS) thenREVEAL elseVOTING)
        ])
    }
}

What else did you learn from the course?

The course also touched upon tokenisation and non-fungible tokens (NFTs) – tokens that represent something unique and are therefore not interchangeable.

The last class focused on the use of oracles. Since a blockchain cannot access data from the outside world, we need to use oracles that send real-world data to the blockchain.

In the scope of our marketplace, we used an oracle to verify and, if necessary, blacklist a supplier that, for instance, didn't accept a previously sold coupon.

Here is a code example:

funcgetExtValueItemWhiteListStatus(item:String) = {
    item +"_verifier_status"
}
 
letverifier = "3Mx9qgMyMhHt7WUZr6PsaXNfmydxMG7YMxv"
letVERIFIED = "verified"
letBLACKLISTED = "blacklist"
@Callable(i)
funcsetstatus(supplier: String, status: String) = {
    letaccount = toBase58String(i.caller.bytes)
    if( account !=verifier ) thenthrow("only oracle verifier are able to manage whitelist")
    elseif( status !=VERIFIED && status !=BLACKLISTED) thenthrow("wrong status")
    else{
        WriteSet([
           DataEntry(getExtValueItemWhiteListStatus(supplier), status)
        ])
    }
}

What part(s) of the course did you find most useful?

The most useful part was the challenges. They made the lectures clearer and solidified my newly-acquired knowledge by trial and error. Practising with the IDE, explorer and oracles was really useful.

How do you plan to use what you learned from the course?

From the beginning, this course was supposed to help bring my project to the next level. So, the idea is now to rewrite sign-web.app on Ride. The current version already offers working features for document certification, but it will definitely be enhanced with a future Ride version. It will allow more flexibility and clarity and more features, including email certification, multi-party agreement, etc.

The course was also very mind-opening, and many ideas started to pop up in my head. I'm sure that more will come out of it in the future.

Комментарии (0)