Highlander •
Building a Ripple Dapp (XRP)

⚠️ SECURITY ALERT: KeepKey does NOT provide phone support. If you are on the phone with someone claiming to be from KeepKey, they are a SCAMMER!
Highlander •

Let's build a basic send and receive ripple Dapp.
(tutorial is dependent on a 2.0+ release of keepkey-desktop.)
This Dapp is live, (guide)
In this tutorial, we will create an application that sends and receives the cryptocurrency XRP. This exercise will demonstrate how quickly and easily multi-chain applications can be developed on KeepKey using the KeepKey Desktop Application.
Straight to the code: https://github.com/BitHighlander/ripple-dapp
git clone https://github.com/BitHighlander/dapp-template ripple-dapp
To use this tutorial we start from the example template repo.
git clone https://github.com/BitHighlander/dapp-template dash-dapp
git clone https://github.com/BitHighlander/dapp-template dash-dapp
git remote rm origin
If you are using an IDE like WebStorm or VSCode, simply open the new dash-dapp directory in your IDE here.
Now push as a new project to GitHub.
WebStorm:

Push new Branch as master.
Git commands:
git remote add origin <our_repo_url> git push origin master
Now build:
yarn && yarn dev
Run this app, and connect your KeepKey. Set up a wallet if you haven't already.
Now we need the application to load the users’ XRP balances on startup. To do this, we will import the KeepKey SDK. We will use the API call
getPubkeysWe use the SDK to get the publicKey for Dash:
//init let sdk try { sdk = await KeepKeySdk.create(configKeepKey) localStorage.setItem("apiKey", configKeepKey.apiKey); console.log("config: ", configKeepKey.apiKey) } catch (e) { setKeepKeyError('Bridge is offline!') }
First, we check if the KeepKey Desktop is running.
Now we import all the state vars we will need for this wallet:
//State vars const [address, setAddress] = useState('') const [balance, setBalance] = useState('0.000') const [amount, setAmount] = useState('0.00000000') const [toAddress, setToAddress] = useState('') const [txid, setTxid] = useState(null) const [signedTx, setSignedTx] = useState(null) const [keepkeyConnected, setKeepKeyConnected] = useState(false) const [keepkeyError, setKeepKeyError] = useState('') const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const { isOpen, onOpen, onClose } = useDisclosure()
These are all generic params for any wallet. We add XRP specific next:
const [sequence, setSequence] = useState('0') const [ledgerIndexCurrent, setLedgerIndexCurrent] = useState('')
First, we get an XRP address:
//Unsigned TX let addressInfo = { addressNList: [2147483692, 2147483792, 2147483648, 0, 0], coin: 'Bitcoin', scriptType: 'p2wpkh', showDisplay: false } //rippleGetAddress let address = await sdk.address.xrpGetAddress({ address_n: addressInfo.addressNList }) console.log("address: ", address) setAddress(address)
Now we use xrpl to get Ripple network info:
let client = new xrpl.Client("wss://xrplcluster.com/") await client.connect() console.log("checkpoint2") const ledgerIndexCurrent = await client.getLedgerIndex() console.log("ledgerIndexCurrent: ", ledgerIndexCurrent) setLedgerIndexCurrent(ledgerIndexCurrent) console.log("checkpoint3") const response = await client.request({ "command": "account_info", "account": address, "ledger_index": "validated" }) console.log("checkpoint4") console.log(response.result.account_data) let balance = response.result.account_data.Balance let sequence = response.result.account_data.Sequence console.log("sequence: ", sequence) //set balance setBalance(balance / 1000000) setSequence(sequence)
let tx = { "type": "auth/StdTx", "value": { "fee": { "amount": [ { "amount": "1000", "denom": "drop" } ], "gas": "28000" }, "memo": "KeepKey", "msg": [ { "type": "ripple-sdk/MsgSend", "value": { "amount": [ { "amount": parseFloat(amount) * 1000000, "denom": "drop" } ], "from_address": fromAddress, "to_address": toAddress } } ], "signatures": null } } //Unsigned TX let unsignedTx = { "HDwalletPayload": { addressNList: [2147483692, 2147483792, 2147483648, 0, 0], tx: tx, flags: undefined, sequence, lastLedgerSequence: parseInt(ledgerIndexCurrent + 1000000000).toString(), payment: { amount: parseInt(amount * 1000000).toString(), destination: toAddress, destinationTag: "1234567890", }, }, "verbal": "Ripple transaction" } //push tx to api console.log("unsignedTx: ", JSON.stringify(unsignedTx.HDwalletPayload)) let responseSign = await sdk.xrp.xrpSignTransaction(unsignedTx.HDwalletPayload)
const buffer = Buffer.from(signedTx, 'base64'); const bufString = buffer.toString('hex'); console.log("bufString", bufString) setLoading(true) const submitResponse = await client.submitAndWait(bufString) console.log("submitResponse: ", submitResponse) setLoading(false) setTxid(submitResponse.result.hash)
And finally, we add a URL link to lookup your signed and broadcasted transaction!
<small><a href='https://xrpl.org/require-destination-tags.html' target="_blank">more info</a></small>
View it live here.