mainnet.cash - using REST server
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. https://read.cash/@pat/mainnetcash-getting-started-a75b2fc6 https://read.cash/@pat/mainnetcash-slp-wallet-4bc39068 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. https://rest-unstable.mainnet.cash/api-docs/ https://github.com/OpenAPITools/openapi-generator#overview https://github.com/mainnet-cash/mainnet-js/blob/master/generated/serve/docker/README.md 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 the `data` 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. https://rest-unstable.mainnet.cash/faucet.html 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**. https://mainnet.cash https://mainnet.cash/tutorial/ https://github.com/mainnet-cash/mainnet-tutorial/blob/master/4.tutorial.rest.bch.html Cheers, pat for mainnet.cash team.
2 comments
really appreciate these tutorials. they're a great resource.
Appreciative work bro you are doing amazing keep doing great job