Mastering Web3 with Waves Module #6

Ventuary Lab
Mastering Web3 with Waves
11 min readJul 7, 2019

Module #6 Launching your own “Coupon Bazaar” Web 3 dApp

Hey! It’s “Mastering Web3 with Waves” online course. Final module — 6!

6.0 Oracles;
6.1 Fetching account key-value data;
6.2 Signing and Publishing transactions using Waves Keeper;
6.3 Signing several transactions at the same time with W. Keeper;
6.4 Multi-Domain Web 3 dApp;
6.5 Sponsored transactions

Blockchains do not have direct access to the data from the outside world. Blockchains, for example, cannot retrieve the information such as currency exchange rates, weather forecasts or stock market prices directly from the internet.

Oracles are here to help as the interfaces that connect a blockchain with the external world.

Oracle is an agent — a human, group of people or a computer program, that sends data from the outside world to the blockchain (inbound Oracle). Oracles can be used inside of smart contracts.

There are many different types of oracles depending on dApp developers needs and data sources. Basically, it’s any information from the “external” world which was recorded into the blockchain.

Waves platform allows users to issue their own oracle and act as a data carrier, a reliable connection between Web APIs and your dApp. Oracles allow you to write data in the blockchain according to certain rules.

Based on the tool provided, some users will be able to describe their protocols and create Oracles, while other users will be able to easily find already created oracles and build DApp using this protocols.

Link: https://oracles.wavesexplorer.com/

Docs: https://docs.wavesplatform.com/en/waves-oracles/about-waves-oracles.html

Steps to Create an Oracle

  1. Create a Waves account or use method GenerateAddress to get the address along with private and public keys(one address can create one oracle). The provider can also use an existing account and keys.
  2. Install Waves Keeper for easy authorization.
  3. Open Waves Oracles and choose to Create an Oracle. It needs to be authorized by Waves Keeper.
  4. Fill the data provider information, create the specification and send initiating data transaction.
  5. Oracle card created! Now you have a public card of your Oracle, which will tell other users what kind of data and in what form your Oracle will transfer from off chain to the blockchain. Next, start sending a transaction in any convenient format supported by Waves protocol.

Source: https://docs.wavesplatform.com/en/waves-oracles/about-waves-oracles.html

Task: create your own data oracle and put here its key-value data from the oracle’s account

As you may notice: ORACLE is just an account where standardized data was recorded to be used in other dApps or Smart Assets.

Besides of oracles standardization you’re free to add more logic above this oracle account… even make it dApp account with certain logic.

We’re building a decentralized Web3 coupon marketplace — “Coupon Bazaar”. Each coupon — is a digital asset which represents a special discount from suppliers.

What if our suppliers won’t accept their issued coupons? Customers will not like that and they will send reports to some “verifier”-oracle.

This oracle is able to change the supplier’s status to VERIFIED or BLACKLISTED.

So, now our CouponBazaar will reject any new items from BLACKLISTED suppliers.

Please, create two dApps:

1. Oracle-Verifier

2. CouponBazaar using the code from the previous step and try to add a new item.

Put here kv-data from the CouponBazaar dApp:

Hint: Oracle-Verifier hasn’t marked account yet.

Please, create two dApps:

1. Oracle-Verifier

2. CouponBazaar using the code from the previous step. Set status to VERIFIED using Oracle-Verifier for some account. Try to add a new item from VERIFIED account.

Put here kv-data from the CouponBazaar dApp:

We’re building a decentralized Web3 coupon marketplace — “Coupon Bazaar”. Users are searching for goods and services discounts and they can buy them for a small price in the marketplace.

As you can see there is no server with database for our items/coupons. Everything is stored in the blockchain in a key-value dApp account storage.

How to retrieve the items array from the blockchain (account data) to the JavaScript client-side environment?

In previous modules, we’ve already started experimenting with @waves-transaction package.

There are many very useful API functions to interact with the blockchain node. Let’s look at the nodeInteraction:

https://github.com/wavesplatform/waves-transactions/blob/master/src/nodeInteraction.ts

Probably, you’ve already noticed that blockchains have a special value called “height”, which means the number of blocks mined from the first “genesis” block. This value is often been used as a measure of “time” within smart contracts.

In order to fetch the current blockchain height you can use:

export declare const currentHeight: (apiBase: string) => Promise<number>;

In order to fetch all records of the current dApp state you can use:

export declare function accountData(address: string, nodeUrl: string): Promise<Record<string, IDataEntry>>;

It’s also possible to test and experiment with the dApp data in the IDE’s console:

export declare function accountDataByKey(key: string, address: string, nodeUrl: string): Promise<IDataEntry>;

P.S.: If you’re not using JS you’re able to use Node API directly: https://nodes.wavesplatform.com/api-docs/index.html#!/addresses/getData_1

For example, if you’re trying to fetch only specific data from the dApp storage. You are free to define this request in the form of regular expression (RegEx) pattern:

async _accountDataPattern(matches) {
return await axios.get(`addresses/data/${this.dal.dApp}?matches=${matches}`, {
baseURL: this.nodeUrl,
validateStatus
})
.then(process400)
.then(x => x.data);
}

where

this.nodeUrl = process.env.APP_NODE_URL || 'https://testnodes.wavesnodes.com';

and PATTERN can be generated from the list of required keys ([ “key1”, “key2”, …, “key3” ]).

/**
* Get node data by multiple keys
* @param {string[]} keys
* @returns {Promise<null|string | number | boolean>}
*/
async nodeFetchKeys(keys) {
const regexpKeys = keys.map(key => _escapeRegExp(key));
const regexp = new RegExp('^(' + regexpKeys.join('|') + ')$');
const data = await this.nodeFetchPattern(regexp);
return keys.map(key => data[key] || null);
}

In Web3 those who control SEEDs will control all corresponding ACCOUNTS activities: transactions, data, digital assets, interaction with smart contracts. So, SEED must be well protected somehow and, at the same time, easy to use by clients.

Waves Keeper is a browser extension which allows users to manage their accounts (keys) and interact securely and seamlessly with Waves-enabled web services and dApps.

We’ve already installed and configured Keeper in our Web3 application in Module 1.

Waves Keeper could be used for transaction signing and publishing.

WavesKeeper.signAndPublishTransaction(tx).then((data) => {
// published tx result
}).catch((error) => {
//processing errors
});

In order to “sign & publish” an invokeTransaction we have to create a tx object with proper fields:

let tx = {
type: 16,
data: {
fee: {
assetId: "WAVES",
tokens: "0.005"
},
dApp: "3NBB3iv7YDRsD8xxxxxxV5eTcsfqh3j2mvF",
call:{
function:"additem",
args:[
{type: "string", value: itemId},
{type: "integer", value: 2},
]},
payment: [{tokens: 1, asset: "WAVES"}]
}

ATTENTION:

MoneyLike syntax for PAYMENT parameter could look as:

{ tokens: 1, assetId: “WAVES” } or

{ coins: 100000000, assetId: “WAVES” } or

{ amount: 100000000, assetId: null }

In both messages, the same price of 1 WAVES is indicated. You can easily convert coinsinto tokensand back, if you know in what asset the price is indicated and you have received its precision tokens = coins / (10 ** precision). If the field contains other types than MoneyLike, for instance, string/MoneyLike , the sum is indicated as a number in coins.

To know more about Waves Keeper API read the documentation:

https://docs.wavesplatform.com/en/waves-api-and-sdk/waves-keeper-api.html

On browser pages that operate under the HTTP/HTTPS (not worked local pages with file:// protocol) with Waves Keeper extension installed, Waves Keeper global object becomes available, featuring the following methods:

  • auth
  • publicState
  • signAndPublishCancelOrder
  • signAndPublishOrder
  • signAndPublishTransaction
  • signCancelOrder
  • signOrder
  • signTransaction
  • signRequest
  • signTransactionPackage
  • on

All methods, except for “on” operate asynchronously and return promises.Please, open nodeInteratcion module source code. Put here function, which is used to publish a transaction to the blockchain.

In this online course, we worked mostly in IDE and signatures for transactions were generated from SEED phrases variables directly.

const accountCustomerSeed = "genuine portion citizens waiting space merry solar above grow task lunar blanket"

In Web3 those who control SEEDs will control all corresponding ACCOUNTS activities: transactions, data, digital assets, interaction with smart contracts. So, SEED must be well protected somehow and, at the same time, easy to use by clients.

Waves Keeper is a browser extension which allows users to manage their accounts (keys) and interact securely and seamlessly with Waves-enabled web services and dApps.

We’ve already installed and configured Keeper in our Web3 application in Module 1.

Waves Keeper could be used for transaction signing and publishing.

WavesKeeper.signAndPublishTransaction(tx).then((data) => {
// published tx result
}).catch((error) => {
//processing errors
});

In order to “sign & publish” an invokeTransaction we have to create a tx object with proper fields:

let tx = {
type: 16,
data: {
fee: {
assetId: "WAVES",
tokens: "0.005"
},
dApp: "3NBB3iv7YDRsD8xxxxxxV5eTcsfqh3j2mvF",
call:{
function:"additem",
args:[
{type: "string", value: itemId},
{type: "integer", value: 2},
]},
payment: [{tokens: 1, asset: "WAVES"}]
}

ATTENTION:

MoneyLike syntax for PAYMENT parameter could look as:

{ tokens: 1, assetId: “WAVES” } or

{ coins: 100000000, assetId: “WAVES” } or

{ amount: 100000000, assetId: null }

In both messages, the same price of 1 WAVES is indicated. You can easily convert coinsinto tokensand back, if you know in what asset the price is indicated and you have received its precision tokens = coins / (10 ** precision). If the field contains other types than MoneyLike, for instance, string/MoneyLike , the sum is indicated as a number in coins.

To know more about Waves Keeper API read the documentation:

https://docs.wavesplatform.com/en/waves-api-and-sdk/waves-keeper-api.html

On browser pages that operate under the HTTP/HTTPS (not worked local pages with file:// protocol) with Waves Keeper extension installed, Waves Keeper global object becomes available, featuring the following methods:

  • auth
  • publicState
  • signAndPublishCancelOrder
  • signAndPublishOrder
  • signAndPublishTransaction
  • signCancelOrder
  • signOrder
  • signTransaction
  • signRequest
  • signTransactionPackage
  • on

All methods, except for “on” operate asynchronously and return promises.

Sometimes we have situations where:

1. we have to sign several transactions and publish them at the same time later

It is a case for supplier transactions pair: (1) Issue new tokens — coupons; (2) Add new item invoke Tx — sending coupons to the dApp account.

2. we have to sign a batch of transactions at the same time, but we’ll publish them separately depending on certain conditions (triggers)

It is a case for customers to vote “for” or “against” the supplier: (1) commit tx, (2) reveal tx

Transactions which have been signed, but not published yet are valid for publishing within approx. 1.5 hours.

To sign a batch of transactions without publishing we’ll use:

let txIssueSigned = await WavesKeeper.signTransaction(txIssue);let txAddItemSigned = await WavesKeeper.signTransaction(txAddItem);

and then call synchronously call (broadcast):

let resultIssue = await nodeInteraction.broadcast(JSON.parse(txIssueSigned), nodeUrl);let resultAddItem = await nodeInteraction.broadcast(JSON.parse(txAddItemSigned), nodeUrl);

ATTENTION: 1 — JSON.parse(…) is required here. 2 — you are able to generate tx.id before signing or publishing it (see docs).

or

let txCommitSigned = await WavesKeeper.signTransaction(txCommit);let txRevealSigned = await WavesKeeper.signTransaction(txReveal);

and then call publish commit tx. Save “reveal tx” into the browser’s localStorage. Extract “reveal tx” from localStorage and broadcast it to the blockchain after all voters will have finished their commit steps.

Now you are able to develop any “pipelines” logic with transactions in the browser JS code!

The last thing with Keeper we gonna discuss in this module: How to recognize users who have already registered in your dApp?

As an example, let’s look at the supplier’s profile page on CouponBazaar.

To do that we’ll use:

WavesKeeper.publicState()
then(state => {
console.log(state); //displaying the result in the console
/*...processing data */
}).catch(error => {
console.error(error); // displaying the result in the console
/*...processing errors */
})

An address of the current user is placed in: state.account.address

The traditional web services have “server” and “client” parts. When a user opens the page in his browser the server responses a Web Page static content, HTML, assets (like images and fonts) and JavaScript code.

When a user interacts with UI components of the web page, for example, buttons and forms, the JavaScript code creates new requests to the server to get more data to display or write some data to the server’s database.

After processing this request server returns a response as a JSON key-value data format. Browser is using this data to change a client application state and view.

The Web3 works in a bit different way. Of course, the Web3 app also requires the HTML plus JavaScript code to run it in the browser. So, to do that, the server with the client application code is still needed.

When a user interacts with client application the “Read” request is created. This request is being processed by node application from Distributed Ledger — Blockchain Network. The main difference here is that this data can be read by anyone. It’s public for popular opened distributed ledgers.

The most important moment is: how to write a piece of new information into the blockchain network. Here we have a transaction term and digital signature.

All updates in the blockchain network must be implemented by transaction submit which requires a digital signature from transaction initiator.

As you can see, the only reason to have a server is — just hosting JavaScript+HTML of the browser application.

So, it means that we are able to use one dApp account as a “backend” side for many different websites, with different domains, JS+HTML code, and assets like images, gifs, etc.

We wish you good luck with your amazing dApps!

Enjoy Web3!

Congratulations, you finished the course “Mastering Web3 with Waves”.

--

--

Ventuary Lab
Mastering Web3 with Waves

DLT, Web3.0, Blockchain, Decentralization, Startups, Waves Platform