About a week ago I wrote about Ripple in a tutorial titled, Build a Ripple XRP Wallet for Android and iOS with Ionic Framework, which focused on mobile development as a followup to a cross-platform desktop application that I had written about before that.
There are many who would advise against storing sensitive wallet information on a computer or anything connected to the internet. As an alternative, people recommend printing your transaction signing information such as private keys and storing them on what’s called a paper wallet, destined for a locked safe in your home or the bank.
We’re going to see how to create a paper wallet for Ripple XRP coins using Angular and some packages provided by the Ripple development team.
The project we build is not going to be attractive because it is meant to be printed. For that reason it doesn’t need to be attractive. Take a look at the following image:
After clicking the generate button on the page, four values with four QR codes will be generated. The private values should be protected while the public values are meant to be shared.
The easiest way to get started with Angular is with the Angular CLI. With the CLI installed, execute the following to create a new project:
ng new ripple-paper-wallet
If you read my previous tutorial which used the ripple-lib package, you’ll remember that there wasn’t actually a way to generate key information. Instead, we’ll be using a different package created by Ripple.
We’re going to be using the ripple-keypairs package and it can be installed into our project by executing the following:
npm install ripple-keypairs --save
Most paper wallet generators that I’ve seen online for any cryptocurrency include QR codes. I couldn’t find a good NPM package for the job, but I found a great browser library called QRCode.js.
Download the repository and copy the qrcode.min.js file into your Angular project’s src/assets/ directory.
At this point we can focus on development because all the dependencies are in place.
We’re going to start development with the easy stuff. Technically, it is all easy and won’t take a lot of effort, but we’re going to start with bootstrapping the project.
Open the project’s src/index.html file and make it look like the following:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ripple XRP Paper Wallet</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script src="/assets/qrcode.min.js"></script>
</body>
</html>
We’ve included QRCode.js, which we had downloaded previously. While we don’t necessarily need to, let’s make some minor CSS changes to better format our printed results.
Open the project’s src/styles.css file and include the following:
table {
width: 100%;
border: 1px solid black;
padding: 20px;
}
h2 {
margin: 0;
margin-bottom: 20px;
}
Nothing crazy happening in the above. Basically just having the table take up the full width of the page and giving it a border.
Let’s get to the interesting stuff.
We’re going to have a TypeScript file for all our logic and an HTML file for all of our rendering. We’re going to see each of the full files and break them down after.
Open the project’s src/app/app.component.ts file and include the following TypeScript code:
import { Component, ViewChild, ElementRef } from '@angular/core';
import * as KeyPairs from "ripple-keypairs";
declare var QRCode: any;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
private qrSeed: any;
private qrPublicAddress: any;
private qrPrivateKey: any;
private qrPublicKey: any;
@ViewChild("qrseed")
public qrSeedImg: ElementRef;
@ViewChild("qrpublicaddress")
public qrPublicAddressImg: ElementRef;
@ViewChild("qrprivatekey")
public qrPrivateKeyImg: ElementRef;
@ViewChild("qrpublickey")
public qrPublicKeyImg: ElementRef;
public seed: string;
public address: string;
public privateKey: string;
public publicKey: string;
public constructor() {
this.seed = "";
this.address = "";
}
public generate() {
this.seed = KeyPairs.generateSeed();
let keypair = KeyPairs.deriveKeypair(this.seed);
this.privateKey = keypair.privateKey;
this.publicKey = keypair.publicKey;
this.address = KeyPairs.deriveAddress(this.publicKey);
if(!this.qrSeed && !this.qrPublicAddress) {
this.qrSeed = new QRCode(this.qrSeedImg.nativeElement, {
text: this.seed,
width: 200,
height: 200
});
this.qrPublicAddress = new QRCode(this.qrPublicAddressImg.nativeElement, {
text: this.address,
width: 200,
height: 200
});
this.qrPrivateKey = new QRCode(this.qrPrivateKeyImg.nativeElement, {
text: this.privateKey,
width: 200,
height: 200
});
this.qrPublicKey = new QRCode(this.qrPublicKeyImg.nativeElement, {
text: this.publicKey,
width: 200,
height: 200
});
} else {
this.qrSeed.clear();
this.qrPublicAddress.clear();
this.qrPrivateKey.clear();
this.qrPublicKey.clear();
this.qrSeed.makeCode(this.seed);
this.qrPublicAddress.makeCode(this.address);
this.qrPrivateKey.makeCode(this.privateKey);
this.qrPublicKey.makeCode(this.publicKey);
}
}
}
There is a lot happening in the above, so we need to take a moment to break everything down.
Remember that ripple-keypairs package that we downloaded? We’re importing it into our project to be used:
import * as KeyPairs from "ripple-keypairs";
Because we’re using a compiled browser based JavaScript file for QR codes, we need to declare it in our TypeScript file to prevent compiler errors.
declare var QRCode: any;
Had we used NPM, things would have been different in how we include it in our project.
Now we can focus on the variables that will be used within our project:
private qrSeed: any;
private qrPublicAddress: any;
private qrPrivateKey: any;
private qrPublicKey: any;
@ViewChild("qrseed")
public qrSeedImg: ElementRef;
@ViewChild("qrpublicaddress")
public qrPublicAddressImg: ElementRef;
@ViewChild("qrprivatekey")
public qrPrivateKeyImg: ElementRef;
@ViewChild("qrpublickey")
public qrPublicKeyImg: ElementRef;
public seed: string;
public address: string;
public privateKey: string;
public publicKey: string;
The private variables will be used to reference generated QR codes. These variables will not be the QR codes themselves, but more the objects that work with what we are rendering. The public variables referenced by @ViewChild
will be actual QR codes rendered on the screen. Because you should never try to interact directly with the DOM in Angular, you should reference them with @ViewChild
annotations. The other public variables will hold string values represent each of the codes that reside in our images. We want to have prints of both the QR codes and their values.
The generate
method is where things get interesting. The most important part is the generation of our wallet values:
this.seed = KeyPairs.generateSeed();
let keypair = KeyPairs.deriveKeypair(this.seed);
this.privateKey = keypair.privateKey;
this.publicKey = keypair.publicKey;
this.address = KeyPairs.deriveAddress(this.publicKey);
These values will be rendered to the screen, but we also need to turn them into QR codes. We need to first check if QR codes have previously been generated:
if(!this.qrSeed && !this.qrPublicAddress) {
this.qrSeed = new QRCode(this.qrSeedImg.nativeElement, {
text: this.seed,
width: 200,
height: 200
});
this.qrPublicAddress = new QRCode(this.qrPublicAddressImg.nativeElement, {
text: this.address,
width: 200,
height: 200
});
this.qrPrivateKey = new QRCode(this.qrPrivateKeyImg.nativeElement, {
text: this.privateKey,
width: 200,
height: 200
});
this.qrPublicKey = new QRCode(this.qrPublicKeyImg.nativeElement, {
text: this.publicKey,
width: 200,
height: 200
});
}
If the QR codes have not yet been generated, we’ll take the front facing elements and turn them into QR codes using the generated values.
If the QR codes had been previously generated, we do things a little differently:
this.qrSeed.clear();
this.qrPublicAddress.clear();
this.qrPrivateKey.clear();
this.qrPublicKey.clear();
this.qrSeed.makeCode(this.seed);
this.qrPublicAddress.makeCode(this.address);
this.qrPrivateKey.makeCode(this.privateKey);
this.qrPublicKey.makeCode(this.publicKey);
Trying to create QR codes multiple times will result in multiple QR codes. Instead, we need to clear the previous QR code and re-generate them. They will use our freshly generated wallet values.
So what does the HTML look like? Open the project’s src/app/app.component.html file and include the following:
<p><button type="button" (click)="generate()">Generate</button></p>
<p>Only items that are public should be shared</p>
<table>
<tr>
<td>
<h2>Private Seed</h2>
<div #qrseed></div>
<p>{{ seed }}</p>
</td>
<td>
<h2>Public Address</h2>
<div #qrpublicaddress></div>
<p>{{ address }}</p>
</td>
</tr>
<tr>
<td>
<h2>Private Key</h2>
<div #qrprivatekey></div>
<p>{{ privateKey }}</p>
</td>
<td>
<h2>Public Key</h2>
<div #qrpublickey></div>
<p>{{ publicKey }}</p>
</td>
</tr>
</table>
Each of the QR codes uses a local variable. Remember those @ViewChild
annotations that were in the TypeScript code? We obtained them from #qrseed
and similar. In the HTML we are also printing the public variables that represent the generated data.
You just saw how to generate a cold storage paper wallet for Ripple XRP coins using Angular and a library called ripple-keypairs. Generating paper wallets is great if you plan to store them in a safe, a bank, or anywhere else away from the internet.
In case you’d rather store your wallet information on your computer, check out a previous tutorial I wrote titled, Create a Cross-Platform Desktop Ripple XRP Wallet with Vue.js and Electron.
If you enjoyed this tutorial and feel like donating some XRP, my public address is rn7HJDaYaEcDJvcELQFbnGXAEo2YA1RLNc.