Zero to One DApp Development Guide on Vite
Introduction
This article introduces how to develop DApp on Vite. It mainly focuses on the interactions between Vite smart contract and frontend web application. In the meantime, we also cover some key points that developers should pay attention to.
Typically, readers should have the following skills: HTML/CSS/JS, VUE framework, Linux, basic understanding about smart contracts and Vite public chain. We have assumed that readers already have all above knowledge.
What you can learn
In this article, you will learn every step of DApp development on Vite public chain. If you are not familiar with Vite, see here for official technical documents.
Content
- Pre-preparation
- Development steps
- Contract debugging environment
- Writing Vite smart contract
- Creation and deployment of Vite smart contract
- Calling Vite smart contract from frontend web application
- DApp Debugging
- Applying for listing DApp in Vite mobile wallet
- Useful tips
- Reference
Pre-preparation
- Purchase a server. One VPS with 2 CPUs and 4 GB memory is recommended
- Install Vite full node
- Configure node_config.json to enable WS API on the node:
"WSEnabled": true,
"WSHost": "0.0.0.0",
"WSPort": 41420,
- Configure node_config.json to enable vmLog for smart contract:
// save vmlog for all contracts
"VmLogAll":true
Or
// save vmlog for the specified contract by address
"VmLogWhiteList":["vite_replaceWithYourContractAddress”]
- If you use a VPS (such as Alibaba Cloud or AWS), don’t forget to configure security rule to expose relevant ports. For example, 41420 for WS and 48132 for RPC.
- Install Nginx on the server in order to deploy web application.
- Register a domain name and bind to your DApp web application (If your DApp doesn’t need domain name, skip this step).
- Buy some VITE coins. In Vite Pre-mainnet 10 VITE will be cost for deploying a smart contract. In addition, executing contract consumes quota, which must be obtained through staking VITE coins. The exact amount of staking depends on the complexity of the contract and the number of requests the contract is supposed to process per second.
Development Steps
- DApp functional design and smart contract design
- Vue-based web application development and UI design
- Smart contract writing and testing
- Deployment of smart contract in test environment
- DApp & smart contract integration and debugging
- Testing contract in local browser using imported account
- Testing in official test wallet
- Debugging in official test wallet
- Deployment of smart contract in the Pre-mainnet
- Listing your DApp in Vite official wallet app
Feel free to adjust the steps according to your actual demand
Contract Debugging Environment
The official document has mentioned two categories of local environments for debugging: development environment (installation package name begins with "contractdev") and test environment (installation package name begins with "contracttest"). Here I recommend test environment package. For contract development, you can use Vscode with Soliditypp plugin, which currently supports windows, linux and mac system. The Soliditypp plugin has integrated development environment, and a local node will launch automatically when you debug contract in the IDE.
- Vscode Soliditypp plugin installation
- Test environment
After development is complete in Vscode, you may want to set up a Vite test environment node and deploy your contract. Test environment is closer to Vite pre-mainnet. In test environment, all interactions between front-end web application and smart contract need to be verified.
To make it easy for developers to work in test environment, Vite dev team provides several scripts in installation package.
See: Test environment
- Pre-mainnet
This is the production environment of Vite network. You need a full node to deploy your DApp in Vite Pre-mainnet. Refer to how to set up Vite full node.
Note: ONLY deploy DApp to the Pre-mainnet after it has been fully tested in test environment.
Writing Vite Smart Contract
IDE
- Soliditypp plugin of Vscode
Programming language
- The language of Vite smart contract is called Solidity++, which is an extension of Ethereum Solidity by adding asynchronous calls
- Data types, functions, keywords
If this is your first time to write smart contract, it is highly recommended you start with Ethereum because Ethereum has plenty of documentation while Vite official documents focus more on the difference between Solidity and Solidity++.
Solidity is very suitable for beginners. After you learned the syntax of solidity, you should be good to start Vite smart contract programming.
Common Solidity++ syntax
- Define a method:
onMessage
onMessage BetAndRoll(uint16 rollTargets) payable {
// msg.sender - caller's address
// msg.amount - transfer amount
// msg.tokenid - TokenId
// Get random numbers
// uint64 randomNumber = random64();
// uint64 nextRandom = nextrandom();
// TODO add your logic here….
// emit trigger event
// emit win(betAddr, rollTargets, betAmount, rollNum, winAmount);
}
- Define a method to query contract's state off chain:
getter
Getter getTokenList() returns(tokenId[] memory) {
Return tokens;
}
- Define an event:
event
event win(address indexed addr, uint256 rollTarget, uint256 betAmount, uint256 rollNum, uint256 winAmount);
Reference link: https://vite.wiki/tutorial/contract/soliditypp.html
Tips
For DApps aim to be listed in official wallet, it is required to open the source code of smart contract. You can refer to the source code of existing smart contracts in Vite block explorer for your reference in development.
You can refer to existing Ethereum smart contracts for common algorithms or code snippets on GitHub. They may be reused in Vite smart contract directly or after modified slightly.
It is highly recommended to deploy the contract to test environment after all functions are implemented and all code pass compilation without error in Vscode.
At this stage, it is necessary to pay attention to whether the quota consumed by each contract request is reasonable. If the cost is too high or exceeds expectation, you probably should optimize it, or even re-consider if the original business process is suitable for implementation in smart contract. In other words, the most effective way to lower quota consumption is to reduce complexity of the contract.
Creation and deployment of Vite smart contract
Create smart contract in development environment & test environment
Use the official scripts to create contract
See official document if you need more details
In addition, "invalid command 'jq'" error may occur when executing the scripts on some servers. In this case, you need to install jq. Taking Ubuntu as an example, execute apt-get install jq
.
Create smart contract in the Pre-mainnet
- Compile smart contract
Compile contract by executing the following command (solppc is available in installation package of both development and test environment):
./solppc --abi --bin SimpleBet.solpp
This will generate output in 3 parts:
- Binary:...// This is compiled code of the contract.
- OffChain Binary:... // This is compiled code of getter functions
- Contract JSON ABI:...// This is ABI of the contract
- Create smart contract
Vite official documents introduce two choices to create a contract:
- Via RPC API, not recommended.
- Via
createContract
method in vitejs, recommended.
To create a smart contract in the Pre-mainnet, you need:
- Hold 10 VITE or more in the account for contract deployment (creating a smart contract in the Pre-mainnet costs 10 VITE)
- Stake at least 1,000 VITE for the account for obtaining sufficient quota (if you choose to use pow, check whether pow module is configured in
PublicModules
of node_config.json) - Ignore
times
parameter or fill in a number between 10-100.
Example:
let Vite_TokenId = "tti_5649544520544f4b454e6e40";
// the content of "Contract JSON ABI" generated from compilation. Note: this is a byte array (no quotes around)
let newabi = abicode;
// the content of "Binary" generated from compilation
let newhexcode = binary;
// create contract using account imported
myAccount.createContract({
abi:newabi,
hexCode: newhexcode,
confirmTimes: 2,????? // Confirmation number between 0 ~ 75
// times: 0,??????????? // Quota multiply factor. Default is 0
params: [],
tokenId: Vite_TokenId,? // Default is the TokenId of VITE
amount: '0',??????????????// Default is '0'
fee: '10000000000000000000',?// Default is '10000000000000000000’, equivalent to 10 VITE
}).then((accountBlock) => {
// contract address
let contractAddress = accountBlock.toAddress;
console.log("contractAddress is " + contractAddress) ;
}).catch(err => {
console.warn(err);
});
Calling Vite Smart Contract from Frontend Web Application
- Web application calls a method of smart contract
- Various events are triggered during execution of the method
- Web application captures the events separately and send feedback to users
How to call smart contract
- By importing an account in the code
Recommended for debugging
import WS_RPC from '@vite/vitejs-ws';
import { client, account, hdAccount, constant } from '@vite/vitejs';
let { Vite_TokenId } = constant;
let provider = new WS_RPC("ws://example.com");
let myClient = new client(provider);
// import an account via private key
let myAccount = new account({
client: myClient,
privateKey: 'your privateKey'
});
// or import an account via mnemonic phrase
// let myHdAccount = new hdAccount({
// client: myClient,
// mnemonic: 'your mnemonic'
// });
// let myAccount = myHdAccount.getAccount()
let abi = []; // contract ABI
let contractAddress =""; // contract address
let callContractBlock = await myAccount.callContract({
accountAddress: myAccountParam.address,
toAddress: contractAddress, // contract address
tokenId: "tti_5649544520544f4b454e6e40", // TokenId
amount: "0", // the amount of tokens to be sent
abi: abi[1], // ABI of the method to be called
methodName:"BetAndRoll", // method name
params: [num], // parameter list
});
- By calling Vite bridge URI
Recommended for calling DApp from Vite wallet. This will send transaction to smart contract through an account in wallet.
See here for Vite bridge installation.
import { abi as abiutils,utils } from '@vite/vitejs';
import Bridge from "@vite/bridge";
// get current account address by method `wallet.currentAddress`
let address = "";
// contract address
let contractAddress = "";
let abi = []; // contract ABI
let num = 0; // parameter
this.money = 0; // the amount to transfer. If TokenId is not specified, the transfer is done in VITE
let bridge = new Bridge();
// encode ABI, parameter list and method name into a function call
const hexData=abiutils.encodeFunctionCall(abi[1], [num], 'BetAndRoll');
// convert data format
const base64Data=utils._Buffer.from(hexData,'hex').toString('base64');
// Compose URI
let uri3 = `vite:${contractAddress}/BetAndRoll?amount=${this.money}&data=`+base64Data;
// Trigger Vite wallet to send the transaction (function call)
bridge["wallet.sendTxByURI"]({uri:uri3, address:address}).then(accountBlock => {
// Transaction is sent out successfully
console.log(accountBlock);
}).catch((err) => {
// Transaction is cancelled
console.log(err);
});
Query contract
- Query states of contract
Call getter
methods
// contract address
let contractAddress = "vite_8a50b500773c7abe0ceeb7e6e25a97871868585159d8333cc0";
let offchaincode=""; // the content of "OffChain Binary" generated in compilation
let abi = []; // contract ABI
let params = []; // parameter list or empty array if no parameter
// encode ABI and parameter list into a function call
let data = abiutils.encodeFunctionCall(abi[2],params);
let dataBase64 = utils._Buffer.from(data, 'hex').toString('base64');
let result = await myClient.request('contract_callOffChainMethod',{
'selfAddr':contractAddress,
'offChainCode':offchaincode,
'data':dataBase64
});
if (result) {
let resultBytes = Buffer.from(result, 'base64').toString('hex');
let outputs = [];
for (let i = 0; i < abi.outputs.length; i++) {
outputs.push(abi.outputs[i].type);
}
let offchainDecodeResult = abiutils.decodeParameters(outputs, resultBytes);
let resultList=[];
for (let i = 0; i < abi.outputs.length; i++) {
if (abi.outputs[i].name) {
resultList.push({'name':abi.outputs[i].name, 'value':offchainDecodeResult[i]});
} else {
resultList.push({'name':'', 'value':offchainDecodeResult[i]});
}
}
// resultList is a list of states
console.log(resultList);
}
Frequently used query code
- Query quota
import WS_RPC from '@vite/vitejs-ws';
import { client, account, hdAccount, constant } from '@vite/vitejs';
let { Vite_TokenId } = constant;
let provider = new WS_RPC("ws://example.com");
let myClient = new client(provider);
myClient.pledge.getPledgeQuota("vite_8a50b500773c7abe0ceeb7e6e25a97871868585159d8333cc0").then((result) => {
// result is quota value
});
- Query balance
myClient.ledger.getAccountByAccAddr(currentAddr).then((result) => {
// result is an account including balance
});
- Query block height
myClient.ledger.getSnapshotChainHeight().then((result) => {
// result is the latest snapshot block height
});-
- Query vmlog event
Make sure you have configured settings in node_config.json to keep vmlog.
// get the latest N blocks for the given contract address
let blockList = await myClient.ledger.getBlocksByAccAddr(contactAddr, offset, limit);
// get vmlog of each block in a loop
for (var i = 0; i < blockList.length; i++) {
let contractTx = blockList[i];
let contractBlockHash = contractTx.hash;
let abi = []; // contract ABI
let vmLogs = [];
let vmLogList = await myClient.request('ledger_getVmLogList', contractBlockHash);
// Note: no every block has vmlog data. For example, transfer transaction doesn't have vmlog.
if (vmLogList) {
vmLogList.forEach(vmLog => {
let topics = vmLog.topics;
for (let j = 0; j < abi.length; j++) {
let abiItem = abi[j];
if (abiutils.encodeLogSignature(abiItem) === topics[0]) {
let dataBytes = '';
if (vmLog.data) {
dataBytes = utils._Buffer.from(vmLog.data, 'base64');
}
let log ={topic: topics[0],args: abiutils.decodeLog(abiItem.inputs, dataBytes.toString('hex'), topics.slice(1)),event: abiItem.name};
vmLogs.push(log);
break;
}
}
});
}
// events are saved in vmLogs
console.log(vmLogs);
}
- Event subscription (Recommended)
Event subscription is more efficient compared to polling. Detailed description and examples are provided in Vite official documents.
Reference links:
DApp Debugging
Local debugging
- It is recommended to use a desktop browser like Google Chrome. You should turn on mobile view to debug compatibility issues.
- Calling contract can only be done through
callContract
method of vitejs using a local account imported in the code. - Use test environment
Debugging in Vite test wallet
- Download Vite test wallet
See here for additional information about Vite test wallet
- Call
bridge["wallet.sendTxByURI"]
to talk to your contract - Deploy your smart contract to the Pre-mainnet after sufficient tests in test environment
Applying for Listing DApp in Vite Mobile Wallet
Listing process
- The current process is not finalized and may change in the future. Contact Vite team for the latest process.
See the current listing process
Useful Tips
How much VITE should I stake for my contract
- This mainly depends on in how many TPS that your contract is supposed to execute. Vite quota model defines staking 10,000 VITE can give you 1 UT quota per second, which translates to 1 UTPS throughput. However, quota consumption in smart contract is often higher. Each contract response may consume 2-3 UT or more according to how complex your contract's business logic is. The complexity can be measured in lines of code executed. Therefore, you must evaluate the quota consumption of your contract and perform necessary optimization if the quota is cost too fast.
- Assume your contract is called once a second and each response costs a quota of 3 UT, you should stake at least 30,000 VITE.
- Always guarantee your contract is sufficiently tested in test environment before deployed to the Pre-mainnet, in the purpose of saving deployment cost.
Take use of Vite Github repository
- Vite official wiki is a good place to look for technical documents. However, if you do not find some answers, in this case, you can look into Vite Github repository. You may find some in-developing documents by searching keywords.
You can also search for code examples and unit test cases, for reference of how to use the APIs. Never forget to star the repository:)
Reference
- Vite dApp Development Guide: https://vite.wiki/tutorial/contract/dapp.html
- Quota: https://vite.wiki/tutorial/rule/quota.html
- Smart Contract: https://vite.wiki/tutorial/contract/contract.html
- VEP 6: Vite URI Formatting: https://vite.wiki/vep/vep-6.html
- VEP-12: The Implementation of Random Numbers in Vite: https://vite.wiki/vep/vep-12.html
- Vite RPC API: https://vite.wiki/api/rpc/
- ViteJS: https://vite.wiki/api/vitejs/
All code in this article were used and tested in a real DApp
Author: 闫冬(Dong YAN), founder of N4Q.org