Using the client WASMs

The client WASMs convert queries and query results between JSON and binary form. Usage:

  • Load the client WASMs during app initialization
  • Use the client WASMs to convert 1 or more queries to binary
  • Combine the binary queries into a single binary
  • POST the query binary to http(s)://server.name:port/wasmql/v1/query. The response is a binary.
  • Split the binary response into multiple responses
  • Use the client WASMs to convert them to JSON

Different client WASMs handle different kinds of queries. e.g. chain-client.wasm can handle queries about accounts and token-client.wasm can handle queries about tokens.

HistoryTools.js

HistoryTools.js helps browser apps and nodejs servers use client WASMs.

Using in web browsers

This code snippet uses HistoryTools.js to load some client WASMs:

<script src=".../path/to/HistoryTools.js"></script>

<script>
    const encoder = new TextEncoder('utf8');
    const decoder = new TextDecoder('utf8');

    function prettyPrint(title, json) {
        console.log('\n' + title + '\n====================');
        console.log(JSON.stringify(JSON.parse(json), null, 4));
    }

    (async () => {
        try {
            const chainClientWasm = await HistoryTools.createClientWasm({
                mod: await WebAssembly.compileStreaming(fetch('.../path/to/chain-client.wasm')),
                encoder, decoder
            });

            const tokenClientWasm = await HistoryTools.createClientWasm({
                mod: await WebAssembly.compileStreaming(fetch('.../path/to/token-client.wasm')),
                encoder, decoder
            });

            // ... insert code examples from below here ...

        } catch (e) {
            console.log(e);
        }
    })()
</script>

Using with nodejs

This code snippet uses HistoryTools.js to load some client WASMs:

const HistoryTools = require('.../path/to/HistoryTools.js');
const fs = require('fs');
const { TextDecoder, TextEncoder } = require('util');
const fetch = require('node-fetch');
const encoder = new TextEncoder('utf8');
const decoder = new TextDecoder('utf8');

function prettyPrint(title, json) {
    console.log('\n' + title + '\n====================');
    console.log(JSON.stringify(JSON.parse(json), null, 4));
}

(async () => {
    try {
        const chainClientWasm = await HistoryTools.createClientWasm({
            mod: new WebAssembly.Module(fs.readFileSync('.../path/to/chain-client.wasm')),
            encoder, decoder
        });

        const tokenClientWasm = await HistoryTools.createClientWasm({
            mod: new WebAssembly.Module(fs.readFileSync('.../path/to/token-client.wasm')),
            encoder, decoder
        });

        // ... insert code examples from below here ...

    } catch (e) {
        console.log(e);
    }
})()

Examining schemas

The describeQueryRequest and describeQueryResponse methods return JSON schemas:

prettyPrint('Token Request Schema', tokenClientWasm.describeQueryRequest());
prettyPrint('Token Response Schema', tokenClientWasm.describeQueryResponse());

Creating query requests

The createQueryRequest method converts query requests from JSON to binary:

// Create a request: generate TAPOS fields needed for a transaction
const request0 = chainClientWasm.createQueryRequest(JSON.stringify(
    ['tapos', {
        ref_block: ['head', -10],    // 10 blocks behind head
        expire_seconds: 60,          // Expire 60 seconds after that block
    }]
));

// Create a request: get a range of accounts
const request1 = chainClientWasm.createQueryRequest(JSON.stringify(
    ['account', {
        snapshot_block: ['head', 0],
        first: 'eosio',
        last: 'eosio.zzzzzz',
        max_results: 100,
        include_code: false,
        include_abi: false,
    }]
));

// Create a request: get tokens (from multiple contracts) owned by `eosio`
const request2 = tokenClientWasm.createQueryRequest(JSON.stringify(
    ['bal.mult.tok', {
        snapshot_block: ['head', 0],
        account: 'eosio',
        first_key: { sym: '', code: '' },
        last_key: { sym: 'ZZZZZZZ', code: 'zzzzzzzzzzzzj' },
        max_results: 10,
    }]
));

Combining the request, sending it, and splitting the response.

The combineRequests function combines multiple requests into a single wasm-ql request. The splitResponses function splits up multiple replies from a single binary. Both functions are required even when there's only 1 request.

// Query wasm-ql server
const queryReply = await fetch('http(s)://server.name:port/wasmql/v1/query', {
    method: 'POST',
    body: HistoryTools.combineRequests([
        request0,
        request1,
        request2
    ]),
});
if (queryReply.status !== 200)
    throw new Error(queryReply.status + ': ' + queryReply.statusText + ': ' + await queryReply.text());

// Split up responses
const responses = HistoryTools.splitResponses(new Uint8Array(await queryReply.arrayBuffer()));

Converting responses to JSON

The decodeQueryResponse method converts responses from binary to JSON. Call it from the WASM which generated the matching query.

// Look at responses
prettyPrint('tapos', chainClientWasm.decodeQueryResponse(responses[0]));
prettyPrint('accounts', chainClientWasm.decodeQueryResponse(responses[1]));
prettyPrint('tokens', tokenClientWasm.decodeQueryResponse(responses[2]));