Building a Ripple Dapp (XRP)
Open-source hardware wallet. Bitcoin, Ethereum, Solana, TON, TRON, Cosmos, and all EVM chains. Your keys never leave the device.
Open-source hardware wallet. Bitcoin, Ethereum, Solana, TON, TRON, Cosmos, and all EVM chains. Your keys never leave the device.
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 Vault Desktop Application.
Straight to the code: https://github.com/BitHighlander/ripple-dapp
git clone https://github.com/BitHighlander/dapp-template ripple-dappTo use this tutorial we start from the example template repo.
git clone https://github.com/BitHighlander/dapp-template dash-dappgit clone https://github.com/BitHighlander/dapp-template dash-dappgit remote rm originIf 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 masterNow build:
yarn && yarn devRun 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 getPubkeys to generate an Xpub for XRP.
We 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 Vault 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.