Hey, everyone!
In our tutorial series we have created an in-browser Bitcoin Cash wallet and added the support for SLP tokens to it. This wallet is based on the mainnet.cash javascript library. It provides many useful functions to interact with the wallet and BCH network. The issue is - it works only in javascript environments like browsers or node.js. But what to do if you are proficient in some other programming language like python or C++? The answer is - use the mainnet.cash REST server which abstracts all the details away, while keeping your application slim.
We at mainnet.cash host our REST server at https://rest-unstable.mainnet.cash. We use it for testing purposes and further development of the library. Also it hosts our API reference listing all supported methods, which you can try out immediately. This API supports the OpenAPI specification, meaning that you can get a client library in a big variety of programming languages and technologies (see). We will be using this server in our tutorial, but if you are going to run an application on the BCH main network you are strongly (!) advised to run your own instance. It is simple.
So let’s try to rewrite our wallet and make it not to use the mainnet-js
javascript library, but rather to move the weight lifting to a REST web server. For this we remove the the mainnet-js and import the axios
library for simpler HTTP requests:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
Then in the run()
function let’s configure axios to point to our web server by default:
axios.defaults.baseURL = "https://rest-unstable.mainnet.cash/";
When working with REST server we use named
wallets for persistence reasons. For testing we will be always creating a random one:
const randomName = Math.random().toString(36).substring(6);
Now we can create the wallet on the server.
let wallet = (await axios.post("wallet/create", {
type: "wif",
network: "testnet",
name: randomName
})).data;
Note how we are sending the POST request to an endpoint (
wallet/create
) and supplying the method parameters as a JSON object enclosed into curly braces{}
.We stop the program execution and
await
for the result and get thedata
from the response.
The returned wallet
object contains many bits of useful information. From the data we are most interested in the walletId
. It is composed of a prefix and the private key encoded with the Wallet Import Format (WIF). Let’s construct the walletId
object which we will be using often:
const walletId = { walletId: "wif:testnet:" + wallet.wif };
Getting balance then becomes:
const balance = (await axios.post("wallet/balance", walletId)).data;
The objects returned are the same as in the javascript library so we do not need to change the rendering code at all. Neat!
Getting the deposit address and deposit QR code turns into:
const addr = (await axios.post("wallet/deposit_address", walletId)).data.cashaddr;
const qr = (await axios.post("wallet/deposit_qr", walletId)).data;
Balance watching is a bit different, since we can not receive the server response immediately for these kind of events. We use the websockets for this purpose:
const balanceSocket = new WebSocket("wss://rest-unstable.mainnet.cash/wallet");
balanceSocket.onopen = (event) => {
const request = {method: "watchBalance", data: {cashaddr: addr}};
balanceSocket.send(JSON.stringify(request));
};
balanceSocket.onmessage = (event) => {
const balance = JSON.parse(event.data);
// UI rendering
};
It is a bit verbose compared to javascript callback but does the same thing.
Note, how you form the request object with the method name and the data it will receive, then you send this object in the JSON encoded string form.
To get the testnet BCH we use the faucet
endpoint. Exceptionally we do not need to supply the walletId
parameter here. Our faucet grants the testnet BCH for free for everyone, but rate limits apply.
document.querySelector('#getSats').addEventListener("click", async () => {
await axios.post("faucet/get_testnet_bch", { cashaddr: addr });
});
Sending the funds is uncomplicated and we substitute the sending functions with:
await axios.post("wallet/send", {walletId: walletId.walletId, to: [{cashaddr: addr, value: 1000, unit: "sat"}]});
and
await axios.post("wallet/send_max", {walletId: walletId.walletId, cashaddr: addr});
Pretty simple!
Very similar to BCH we use wallet/slp
endpoints for the SLP but supply the tokenId
everywhere and use the slpaddr
parameter instead of cashaddr
. The rendering code is remaining the same.
const slpBalance = (await axios.post("wallet/slp/balance", walletId)).data;
const slpAddr = (await axios.post("wallet/slp/deposit_address", walletId)).data.slpaddr;
const slpQr = (await axios.post("wallet/slp/deposit_qr", walletId)).data;
Waiting for the SLP balance is again based on websockets and is similar to the BCH:
const slpBalanceSocket = new WebSocket("wss://rest-unstable.mainnet.cash/wallet");
slpBalanceSocket.onopen = (event) => {
const request = {method: "slpWatchBalance", data: {slpaddr: slpAddr, tokenId: tokenId}};
slpBalanceSocket.send(JSON.stringify(request));
};
slpBalanceSocket.onmessage = (event) => {
const slpBalance = JSON.parse(event.data);
// UI rendering
};
Getting the testnet SLP is done through our faucet
endpoint again:
await axios.post("faucet/get_testnet_slp", {slpaddr: slpAddr, tokenId: tokenId});
Finally sending some tokens and all tokens of a certain tokenId
kind is done with:
await axios.post("wallet/slp/send", {walletId: walletId.walletId, to: [{slpaddr: addr, value: 1, tokenId: tokenId}]});
await axios.post("wallet/slp/send_max", {walletId: walletId.walletId, slpaddr: addr, tokenId: tokenId});
So we do not actually see any UI difference, but as you could have guessed our webpage now consumes less RAM (1.2 MB REST vs 9.8 MB JS) according to Chrome memory profiler.
This concludes the article and our short tutorial series. Next time I am going to explain in details how to bootstrap your own REST server. What other topics do you want to learn about? Maybe how to create your own tokens, minting and burning them or maybe how to monitor transactions and blocks? Tell me in the comments! Be sure to check out our web site and library documentation. You can ask any questions in our telegram group. The source code of this tutorial is available on github.
Cheers,
pat for mainnet.cash team.
Appreciative work bro you are doing amazing keep doing great job