Skip to content

Common Patterns

In this chapter, we will cover the common use cases for ViteJS with examples, but first, in order to interact with the Vite network, you will need to create a connection.

import { ViteAPI } from '@vite/vitejs';
import WS_RPC from '@vite/vitejs-ws';
const providerWsURLs = {
    localnet: 'ws://localhost:23457',
    testnet: 'wss://buidl.vite.net/gvite/ws',
    mainnet: 'wss://node.vite.net/gvite/ws',
};
const provider = new WS_RPC(providerWsURLs.mainnet);
const viteApi = new ViteAPI(provider, () => {
    console.log('connected');
});

How to build a transaction

The first step in building a transaction is to create a send block with the account you intend to send from. In this example, we are making a block to issue a new token and signing it with an existing account (with at least 1000 VITE already in it as token issuance fee).

import { wallet, accountBlock } from '@vite/vitejs';
const myWallet = wallet.getWallet(mnenomics);
// or if you need to create a new wallet
// const myWallet = wallet.createWallet();

const account = myWallet.deriveAddress(0);

const block = accountBlock.createAccountBlock('issueToken', {
    address: account.address,
    tokenName: "Test Token",
    isReIssuable: true,
    maxSupply: 100000000,
    totalSupply: 100000000,
    isOwnerBurnOnly: false,
    decimals: 2,
    tokenSymbol: "TEST",
});
block.setProvider(viteApi); // this allow ViteJS to interact with the network
block.setPrivateKey(account.privateKey);
await block.autoSend();

How to stake for quota

Quota is used for "paying" transaction fees instead of spending additional funds; this is how Vite transactions are fee-less. Quota is consumed when sending account blocks. An account's quota recharges over time and you can get a higher quota limit by staking more Vite tokens. It's possible to send transactions without having any quota by using proof of work, but this is not recommended.

const block = accountBlock.createAccountBlock('stakeForQuota', {
    address: account.address,
    beneficiaryAddress: account.address, // or any other address that you want to give quota to
    // 134 Vite is the minimum staking amount for quota
    amount: '134000000000000000000',
});
block.setProvider(viteApi); // this allows ViteJS to interact with the network
block.setPrivateKey(account.privateKey);
await block.autoSend();

// Check quota of the account
viteApi.request('contract_getQuotaByAccount', address)
    .then(result => {
        console.log(result);
    })
    .catch(err => {
        console.warn(err);
    });

How to send funds to an address

Creating other kinds of transactions is fairly similar to the previous example. The main difference is the method name as the first argument and the parameters as the second argument. For the full list of methods and corresponding parameters, read the section on Transaction Types.

const block = accountBlock.createAccountBlock('send', {
    address: account.address,
    toAddress: 'vite_553462bca137bac29f440e9af4ab2e2c1bb82493e41d2bc8b2',
    tokenId: 'tti_5649544520544f4b454e6e40',
    amount: '1000000000000000000', // equal to 1 VITE since the Vite coin has 18 decimal points
});
block.sign(account.privateKey);
await block.autoSend();

How to receive a transaction

There are two main ways of receiving transactions. The first way is to sign and send each receive block individually.

const block = accountBlock.createAccountBlock('receive', {
    address: account.address,
    sendBlockHash: '44e69ea04fe830c9075c01e46f102d0c543b4119ce13ec575aee2e96501049fc',
});
block.setProvider(viteApi);
block.setPrivateKey(account.privateKey);
await block.autoSetPreviousAccountBlock();
block.sign(account.privateKey);
await block.autoSend();

The second and much simpler way is to create and start a ReceiveAccountBlockTask.

const receiveTask = new accountBlock.ReceiveAccountBlockTask({
    address: account.address,
    privateKey: account.privateKey,
    provider: viteApi,
});
receiveTask.start();

How to sign a transaction

As you may have noticed in previous examples, there are two main ways to sign a transaction (i.e. block). The first way is to set the private key of the block and call certain methods that sign the block for you. The second way is to call the sign method which will sign the transaction, but won't send the block to the network.

block.setPrivateKey(privateKey);
// This sets the block's previous block hash and other parameters needed to make a valid block
await block.autoSetPreviousAccountBlock();

block.sign(privateKey);
await block.autoSend();

How to deploy a contract

Deploying a contract is just like making any other transaction. For the parameters, the easiest way to get them is through the Solidity++ 0.8 Preview VS Code extension.

const block = accountBlock.createAccountBlock('createContract',{
    abi:[{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"SayHello","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"transfer","type":"event"}],
    code: '608060405234801561001057600080fd5b50610141806100206000396000f3fe608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806391a6cb4b14610046575b600080fd5b6100896004803603602081101561005c57600080fd5b81019080803574ffffffffffffffffffffffffffffffffffffffffff16906020019092919050505061008b565b005b8074ffffffffffffffffffffffffffffffffffffffffff164669ffffffffffffffffffff163460405160405180820390838587f1505050508074ffffffffffffffffffffffffffffffffffffffffff167faa65281f5df4b4bd3c71f2ba25905b907205fce0809a816ef8e04b4d496a85bb346040518082815260200191505060405180910390a25056fea165627a7a7230582023e9669dd6fec3b6b2a84a1fd7c9939f49197203d0e1db312278e633c219c2480029',
    responseLatency: 2,
    params: ['vite_13f1f8e230f2ffa1e030e664e525033ff995d6c2bb15af4cf9'] // passed to the contract's constructor
});
block.sign(account.privateKey);
await block.autoSend();

How to call a contract

Calling a contract's methods is just like sending funds, but with the addition of the contract's abi, method name, and arguments to pass.

let block = await accountBlock.createAccountBlock('callContract', {
    address: account.address,
    abi: contract.abi,
    toAddress: contract.address,
    params: ['method', 'params'],
    methodName: 'methodName',
    amount: '1000000000000000000',
    tokenId: 'tti_5649544520544f4b454e6e40',
});
block.sign(account.privateKey);
await block.autoSend();

How to read contract event logs

Getting a contract's event log first involves fetching them from a Vite node's RPC API. Then decoding them into a human-readable format.

Note

The following example has been adapted from the Vuilder Kit.

import { abi } from '@vite/vitejs';
// ...
const getPastEvents = async (
    eventName: string = 'allEvents',
    {
        fromHeight = 0,
        toHeight = 0,
    }: {
        filter?: Object;
        fromHeight?: Number;
        toHeight?: Number;
    }
) => {
    let result: any[] = [];
    let logs = await viteApi.request('ledger_getVmLogsByFilter', {
        addressHeightRange: {
            [contract.address!]: {
                fromHeight: fromHeight.toString(),
                toHeight: toHeight.toString(),
            },
        },
    });
    const filteredAbi =
        eventName === 'allEvents'
            ? contract.abi
            : contract.abi.filter((a: any) => {
                    return a.name === eventName;
              });
    if (logs) {
        for (let log of logs) {
            let vmLog = log.vmlog;
            let topics = vmLog.topics;
            for (let abiItem of filteredAbi) {
                let signature = abi.encodeLogSignature(abiItem);
                if (abiItem.type === 'event' && signature === topics[0]) {
                    let dataHex;
                    if (vmLog.data) {
                        dataHex = Buffer.from(vmLog.data, 'base64').toString('hex');
                    }
                    let returnValues = abi.decodeLog(abiItem, dataHex, topics);
                    let item = {
                        returnValues: returnValues,
                        event: abiItem.name,
                        raw: {
                            data: dataHex,
                            topics: topics,
                        },
                        signature: signature,
                        accountBlockHeight: log.accountBlockHeight,
                        accountBlockHash: log.accountBlockHash,
                        address: log.address,
                    };
                    result.push(item);
                    break;
                }
            }
        }
    }
    return result;
};

How to read contract state

Prior to Solidity++ 0.8, contracts had "off-chain" functions. These were used for querying the contracts written in Solidity++ 0.4. Starting in Solidity++ 0.8, off-chain methods were deprecated. Because of this, the RPC method for querying contracts written in 0.4 vs 0.8 are different - contract_callOffChainMethod and contract_query respectively.

Note

The following example has been adapted from the Vuilder Kit.

import { abi, utils } from '@vite/vitejs';
// ...
const queryContract = async (methodName: string, params: any[]) => {
    const methodAbi = contract.abi.find((x: { name: string }) => {
        return x.name === methodName;
    });
    if (!methodAbi) {
        throw new Error('method not found:' + methodName);
    }
    let data = abi.encodeFunctionCall(methodAbi, params);
    let dataBase64 = utils._Buffer.from(data, 'hex').toString('base64');
    let codeBase64;
    if (contract.offchainCode && contract.offchainCode.length > 0)
        codeBase64 = utils._Buffer.from(contract.offchainCode, 'hex').toString('base64');
    while (true) {
        let result = codeBase64
            ? await viteApi.request('contract_callOffChainMethod', {
                    address: contract.address,
                    code: codeBase64,
                    data: dataBase64,
              })
            : await viteApi.request('contract_query', {
                    address: contract.address,
                    data: dataBase64,
              });
        // parse result
        if (result) {
            let resultBytes = utils._Buffer.from(result, 'base64').toString('hex');
            let outputs = [];
            for (let i = 0; i < methodAbi.outputs.length; i++) {
                outputs.push(methodAbi.outputs[i].type);
            }
            return abi.decodeParameters(outputs, resultBytes);
        }
        console.log('Query failed, try again.');
        await utils.sleep(500);
    }
};

Tips

You can also use callOffChainContract and queryContractState in ViteAPI class.