I’ve been spending a lot of time researching and watching the blockchain market because it is a very interesting subject matter. Not too long ago I had written an article titled, Create a Cross-Platform Desktop Ripple XRP Wallet with Vue.js and Electron, which focused on the Ripple, the banking network and cryptocurrency.
Ripple isn’t the only cryptocurrency that I’m following. I’m also following DigiByte, which targets digital payments and has the DGB coin. If you’re not familiar with DigiByte, the official wallet forces you to download around 12GB of data to synchronize with the network. It isn’t really appealing to anyone who just wants to sit on some DGB. The good news, is this can be avoided by building your own wallet and client.
We’re going to see how to build a DigiByte DGB client for storing, sending, and managing coins without having to synchronize with the network, using JavaScript and Node.js.
If at any point in this tutorial you feel like donating DGB, my public address is D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm. DGB is cheap right now, but it could pick up steam like some of the other cryptocurrencies.
We’re going to be creating a project with Node.js, but don’t get ahead of yourself. The extent of our application will be CLI based, not extravagant like the Ripple XRP wallet tutorial I had written. That doesn’t mean that we can’t accomplish a lot within a Terminal.
Let’s go ahead and create a new project. Within a new directory on your computer, execute the following commands:
npm init -y
npm install request digibyte --save
The above commands will initialize a Node.js project and install two project dependencies. The request
package will allow us to make HTTP requests from within Node.js. The digibyte
package will allow us to work with parts of the DigiByte network such as create private keys and addresses. It will not allow us to broadcast transactions. Instead we’ll be using HTTP for that.
If you’d like more information on how to use HTTP with Node.js, check out a previous tutorial I wrote titled, Consume Remote API Data from within a Node.js Application.
With the project initialized, go ahead and create two files:
touch app.js
touch dgb.js
If you don’t have the touch
command, go ahead and create the files manually. The dgb.js file will have our JavaScript class for working with DigiByte and the app.js file will have all of our driver logic.
Before we get invested in code, we need to take a moment to understand the RESTful APIs that are available and what they’re going to do for us in our project.
We’re going to be doing as much as we can using the digibyte-lib library for Node.js. This includes generating private keys, addresses, and formatting transactions to be broadcasted across the network. However, the digibyte-lib library doesn’t tell us the market value of DGB, the current balance of our address, or allow us to send DGB to other addresses.
When it comes to tracking the market value of a DGB coin, we can make use of the CoinMarketCap API. A sample request to this API can be seen below:
curl https://api.coinmarketcap.com/v1/ticker/digibyte/
If it worked correctly, you should see the price in USD per DGB and trends in the market that might be useful to you. For this project, we’re only concerned about the price per DGB. It will be useful when determining the total monetary value of our wallet.
This brings us to working with less generic data.
DigiByte is making use of Insight to work with the blockchain network. Insight is used for other cryptocurrencies, each with their own host and custom configuration. The DigiByte configuration is found at DigiExplorer.
You probably don’t have a DigiByte address yet, so let’s experiment with mine. If we wish to know my balance, we’ll consume from the following endpoint:
curl https://digiexplorer.info/api/addr/D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm
As a result, you should see how many transactions have happened against the address or the current balance. The data would look something like this:
{
"addrStr": "D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm",
"balance": 21.42859477,
"balanceSat": 2142859477,
"totalReceived": 21.42859477,
"totalReceivedSat": 2142859477,
"totalSent": 0,
"totalSentSat": 0,
"unconfirmedBalance": 0,
"unconfirmedBalanceSat": 0,
"unconfirmedTxApperances": 0,
"txApperances": 3,
"transactions": [
"a72537b428cf35c11a309eb09c7dabae2af4fa0a3849a34ef64f986baf80eb8a",
"063b29f0934b0a3bc871a8f07215352d5dce40fbb0744c072e3d55a242d497d4",
"c0d14c3635dadb74019056f72a3d8c9abfb6dc20dd896a1e9664180f82fb4395"
]
}
For this project, the balance
property and the balanceSat
property is important to us. The balanceSat
represents the balance in Satoshi.
The next endpoint we’ll explore has to do with unspent transaction output (UTXO), something heavily advertised when it comes to DigiByte. Basically it is all of our transactions that still contain a balance that can be spent. To query for it, you’d do the following:
curl https://digiexplorer.info/api/addr/D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm/utxo
If you have any UTXO for this particular address, it would look something like the following:
[
{
"address": "D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm",
"txid": "a72537b428cf35c11a309eb09c7dabae2af4fa0a3849a34ef64f986baf80eb8a",
"vout": 0,
"ts": 1513633346,
"scriptPubKey": "76a91490f80b2ee147906cc4b6c8efacbd99c0d6ffa75e88ac",
"amount": 20.22104477,
"confirmations": 6,
"confirmationsFromCache": true
},
{
"address": "D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm",
"txid": "063b29f0934b0a3bc871a8f07215352d5dce40fbb0744c072e3d55a242d497d4",
"vout": 0,
"ts": 1513630852,
"scriptPubKey": "76a91490f80b2ee147906cc4b6c8efacbd99c0d6ffa75e88ac",
"amount": 0.099,
"confirmations": 6,
"confirmationsFromCache": true
},
{
"address": "D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm",
"txid": "c0d14c3635dadb74019056f72a3d8c9abfb6dc20dd896a1e9664180f82fb4395",
"vout": 1,
"ts": 1513629908,
"scriptPubKey": "76a91490f80b2ee147906cc4b6c8efacbd99c0d6ffa75e88ac",
"amount": 1.10855,
"confirmations": 6,
"confirmationsFromCache": true
}
]
When it comes to spending or sending your balance, you can’t just subtract from the total. You need to work with the UXTO information. It took me a while to figure this out because if you try to send from one transaction id and you try to send a value less than your total balance, but more than your UXTO, you’ll get an error.
The final DigiExplorer endpoint that we’ll be working with is responsible for sending transactions across the network. You’ll use this if you want to do anything beyond hold DGB.
Don’t try to execute this yet, but eventually we’ll do something along the lines of:
curl -X POST -H "Content-Type: application/json" -d '{ "rawtx": "SERIALIZED_TRANSACTION_HERE" }' https://digiexplorer.info/api/tx/send
We’re going to be generating some logic for creating a serialized transaction and then posting it to the network. Of course it should be used with caution so you don’t end up losing your DGB coins.
You can learn more about the Insight API and CoinMarketCap API in their official documentation.
With the API stuff out of the way, we can focus on the development of our own DigiByte management tool. Within the dgb.js file, start by including the following code:
const DigiByte = require("digibyte");
const Request = require("request");
class DGB {
constructor() {
this.explorerUrl = "https://digiexplorer.info";
this.marketUrl = "https://api.coinmarketcap.com/v1/ticker";
}
}
exports.DGB = DGB;
We’re just setting up our class and exporting it so it can be accessed from the app.js file in the future. Now we can just create a bunch of functions for different tasks.
Let’s start with the oddball function in the group. Let’s try to get the current market information for DGB:
getMarketValue() {
return new Promise((resolve, reject) => {
Request.get(this.marketUrl + "/digibyte/", (error, response, body) => {
if(error) {
reject(error);
}
resolve(JSON.parse(body)[0]);
});
});
}
Using the request
package we can target the CoinMarketCap API and return the response as a promise to whatever calls it. We, and by that I mean you, probably don’t have an address yet. We can get address information by generating or importing a private key:
generatePrivateKey() {
return new DigiByte.PrivateKey();
}
importWIFPrivateKey(privateKey) {
return DigiByte.PrivateKey.fromWIF(privateKey);
}
Generating a private key does not directly create a WIF formatted private key, but it can be easily converted. Regardless of what you wish to create your private key as, it is incredibly important that you keep it private and safe. Losing your private key will result in losing all your DigiByte currency with no option of recovery.
We won’t do it from the dgb.js file, but an address can be generated from a private key by doing:
var privateKey = generatePrivateKey();
var address = privateKey.toAddress();
Now that you have an address, you can get the value of your wallet. This can be done by creating the following function:
getWalletValue(address) {
return new Promise((resolve, reject) => {
Request.get(this.explorerUrl + "/api/addr/" + address, (error, response, body) => {
if(error) {
reject(error);
}
resolve(JSON.parse(body));
});
});
}
So far we are just wrapping APIs that we’ve already seen in Node.js code. Of course the exception being how we generated private key information. The private key information was generated with the NPM package that we had installed.
Finally we have our function for getting the unspent transaction output (UTXO) information:
getUnspentTransactionOutput(address) {
return new Promise((resolve, reject) => {
Request.get(this.explorerUrl + "/api/addr/" + address + "/utxo", (error, response, body) => {
if(error) {
reject(error);
}
resolve(JSON.parse(body));
});
});
}
This is where things can get a little strange. We need to create a function that will prepare a transaction to be sent, but not necessarily send it yet. Take a look at the following JavaScript code:
createTransaction(sourcePrivateKey, sourceAddress, destinationAddress, satoshis) {
return new Promise((resolve, reject) => {
getUnspentTransactionOutput(sourceAddress).then(utxos => {
if(utxos.length == 0) {
reject({ "message": "The source address has no unspent transactions" });
}
var changePrivateKey = new DigiByte.PrivateKey();
var changeAddress = changePrivateKey.toAddress();
var transaction = new DigiByte.Transaction();
for(var i = 0; i < utxos.length; i++) {
transaction.from(utxos[i]);
}
transaction.to(destinationAddress, satoshis);
transaction.change(changeAddress);
transaction.sign(sourcePrivateKey);
resolve(transaction);
}, error => {
reject(error);
});
});
}
In the above createTransaction
function, we expect a source private key, a source address, a destination address, and a number of Satoshi to send. Assume you’re sending me some DGB. My D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm address will be the destination address. Your private key and corresponding public address will be the source information.
The first step towards creating a transaction is to get the source UTXO information. If there is no UTXO information, that means we don’t have anything we can send, so we should cancel.
If we have DGB to send, we need to create a new private key and address. When you send DGB, you’re sending everything, however, you can specify how much the destination receives and where to send the remainder. So if you have 10 DGB and you’re sending 1 DGB to me, you have to specify where the other 9 DGB go.
transaction.from(utxos[i]);
Remember we talked about sending from the balance versus sending from the UTXO? You have to send from the UTXO information, and this can be multiple. After defining the destination and the change address for the remainder, we have to sign the transaction using the original private key.
When we want to broadcast the transaction, we can serialize it like so:
var serialized = transaction.serialize();
So what would the POST request for broadcasting a transaction look like? Take the following for example:
sendTransaction(transaction) {
return new Promise((resolve, reject) => {
Request.post({
"headers": { "content-type": "application/json" },
"url": this.explorerUrl + "/api/tx/send",
"body": JSON.stringify({
"rawtx": transaction.serialize()
})
}, (error, response, body) => {
if(error) {
reject(error);
}
resolve(JSON.parse(body));
});
});
}
In the above we’re just making our previous cURL Node.js friendly. While everything above would work without issue, it isn’t the safest. For example, what happens after we send the remainder to our change address? We didn’t exactly save the new private key.
Here is a modified version of what we want to accomplish:
createAndSendTransaction(sourcePrivateKey, sourceAddress, destinationAddress, satoshis) {
return new Promise((resolve, reject) => {
getUnspentTransactionOutput(sourceAddress).then(utxos => {
if(utxos.length == 0) {
reject({ "message": "The source address has no unspent transactions" });
}
var changePrivateKey = new DigiByte.PrivateKey();
var changeAddress = changePrivateKey.toAddress();
var transaction = new DigiByte.Transaction();
for(var i = 0; i < utxos.length; i++) {
transaction.from(utxos[i]);
}
transaction.to(destinationAddress, satoshis);
transaction.change(changeAddress);
transaction.sign(sourcePrivateKey);
this.sendTransaction(transaction).then(result => {
resolve(Object.assign(result, {
"source_private_key": sourcePrivateKey.toWIF(),
"source_address": sourceAddress,
"change_private_key": changePrivateKey.toWIF(),
"change_address": changeAddress,
"destination_address": destinationAddress,
"sent_amount": satoshis
}));
}, error => {
reject(error);
});
}, error => {
reject(error);
});
});
}
In the above createAndSendTransaction
function we are creating the transaction, sending it, and returning information about what happened. This includes our change of address information.
It may not be the best solution out there, but it works and should be enough to get you started.
We have a nice JavaScript class created for DigiByte interaction, so let’s take it for a test drive. Within the app.js file, include the following:
const { DGB } = require("./dgb");
var digibyte = new DGB();
var privateKey = digibyte.importWIFPrivateKey("PRIVATE_KEY_HERE");
digibyte.getWalletValue(privateKey.toAddress()).then(wallet => {
digibyte.getMarketValue().then(market => {
console.log(wallet.balance + " DGB / " + (wallet.balance * market.price_usd).toFixed(2) + " USD");
console.log(market.name + ": " + market.price_usd + " USD");
});
});
The above code will import your private key and tell you your wallet value both in quantity and USD value. If you wan’t to send some DGB, for example to me, you could do something like this:
const { DGB } = require("./dgb");
var digibyte = new DGB();
var privateKey = digibyte.importWIFPrivateKey("PRIVATE_KEY_HERE");
digibyte.createAndSendTransaction(privateKey, privateKey.toAddress(), "D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm", 5000000000).then(result => {
console.log("SEND RESPONSE: ", result);
}, error => {
console.log("SEND ERROR: ", error);
});
Don’t forget to keep track of your new private key in the response after sending. The above code would send me 50 DGB, which as of now is less than $2.00.
If you’re like me, you probably had no idea how to even obtain your first DigiByte DGB coin. Finding an exchange that sells them and isn’t a scam is hard to find. Mining your own DGB is not worth it from what I understand. So how do you ride this wave and get this fancy new coin?
I got my DGB coins by using the service ShapeShift. This service allows you to convert a ton of different coins at a very small and very reasonable commission.
On ShapeShift, choose your source coin and choose DigiByte as your destination coin.
Remember that public address you created for DigiByte? Use it as the destination address. Use a public address for your source coin so that ShapeShift can refund you if the conversion isn’t successful.
Continue to follow the steps, and you should have some DigiByte in your possession. The network is very fast, but it could take some time to receive if your source coin is slow to send.
You just saw how to create your own DigiByte DGB coin management tool with Node.js. We saw how to leverage various APIs and Node.js packages to manage our wallet and send coins.
If you’d like to donate some DGB, my public address is D9Ms9hnm32q9nceN2b9jNshuZhWcobrmQm.
There are some things I wish I knew from the beginning when I started to develop for DigiByte:
As long as you’re careful and you double-check all your code, you should be fine with development. However, if you do make a mistake, there is no coming back from it. When you test, use small values.
If you want to see how to develop a nice UI with Ripple that can be translated over to DigiByte, check out my previous tutorial that makes use of Vue.js and Electron.