mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-26 22:55:49 +00:00
Changed folder structure
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
const { app, BrowserWindow, ipcMain } = require('electron')
|
||||
const fs = require("fs");
|
||||
const path = require('path')
|
||||
const xrpl = require("xrpl")
|
||||
const { initialize, subscribe, saveSaltedSeed, loadSaltedSeed } = require('../library/5_helpers')
|
||||
const { sendXrp } = require('../library/7_helpers')
|
||||
const { verify } = require('../library/8_helpers')
|
||||
|
||||
const TESTNET_URL = "wss://s.altnet.rippletest.net:51233"
|
||||
|
||||
const WALLET_DIR = 'Wallet'
|
||||
|
||||
const createWindow = () => {
|
||||
|
||||
const appWindow = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'view', '8_preload.js'),
|
||||
},
|
||||
})
|
||||
|
||||
appWindow.loadFile(path.join(__dirname, 'view', '8_domain-verification.html'))
|
||||
|
||||
return appWindow
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
const appWindow = createWindow()
|
||||
|
||||
if (!fs.existsSync(WALLET_DIR)) {
|
||||
// Create Wallet directory in case it does not exist yet
|
||||
fs.mkdirSync(path.join(__dirname, WALLET_DIR));
|
||||
}
|
||||
|
||||
let seed = null;
|
||||
|
||||
ipcMain.on('seed-entered', async (event, providedSeed) => {
|
||||
seed = providedSeed
|
||||
appWindow.webContents.send('open-password-dialog')
|
||||
})
|
||||
|
||||
ipcMain.on('password-entered', async (event, password) => {
|
||||
if (!fs.existsSync(path.join(__dirname, WALLET_DIR , 'seed.txt'))) {
|
||||
saveSaltedSeed('../' + WALLET_DIR, seed, password)
|
||||
} else {
|
||||
seed = loadSaltedSeed('../' + WALLET_DIR, password)
|
||||
}
|
||||
|
||||
const wallet = xrpl.Wallet.fromSeed(seed)
|
||||
|
||||
const client = new xrpl.Client(TESTNET_URL)
|
||||
|
||||
await client.connect()
|
||||
|
||||
await subscribe(client, wallet, appWindow)
|
||||
|
||||
await initialize(client, wallet, appWindow)
|
||||
|
||||
ipcMain.on('send-xrp-action', (event, paymentData) => {
|
||||
sendXrp(paymentData, client, wallet).then((result) => {
|
||||
appWindow.webContents.send('send-xrp-transaction-finish', result)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('destination-account-change', (event, destinationAccount) => {
|
||||
verify(destinationAccount, client).then((result) => {
|
||||
appWindow.webContents.send('update-domain-verification-data', result)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
ipcMain.on('request-seed-change', (event) => {
|
||||
fs.rmSync(path.join(__dirname, WALLET_DIR , 'seed.txt'))
|
||||
fs.rmSync(path.join(__dirname, WALLET_DIR , 'salt.txt'))
|
||||
appWindow.webContents.send('open-seed-dialog')
|
||||
})
|
||||
|
||||
// We have to wait for the application frontend to be ready, otherwise
|
||||
// we might run into a race condition and the ope-dialog events
|
||||
// get triggered before the callbacks are attached
|
||||
appWindow.once('ready-to-show', () => {
|
||||
// If there is no seed present yet, ask for it, otherwise query for the password
|
||||
// for the seed that has been saved
|
||||
if (!fs.existsSync(path.join(__dirname, WALLET_DIR, 'seed.txt'))) {
|
||||
appWindow.webContents.send('open-seed-dialog')
|
||||
} else {
|
||||
appWindow.webContents.send('open-password-dialog')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(main)
|
||||
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>XRPL Wallet Tutorial (JavaScript / Electron)</title>
|
||||
|
||||
<link rel="stylesheet" href="../../bootstrap/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="../../bootstrap/custom.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<main class="bg-light">
|
||||
|
||||
<div class="sidebar d-flex flex-column flex-shrink-0 p-3 text-white bg-dark">
|
||||
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
|
||||
<img class="logo" height="40"/>
|
||||
</a>
|
||||
<hr>
|
||||
<ul class="nav nav-pills flex-column mb-auto" role="tablist">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link active" id="dashboard-tab" data-bs-toggle="tab" data-bs-target="#dashboard"
|
||||
type="button" role="tab" aria-controls="dashboard" aria-selected="true">
|
||||
Dashboard
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="nav-link" data-bs-toggle="tab" id="transactions-tab" data-bs-target="#transactions"
|
||||
type="button" role="tab" aria-controls="transactions" aria-selected="false">
|
||||
Transactions
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="main-content tab-content d-flex flex-column flex-shrink-0 p-3">
|
||||
|
||||
<div class="header border-bottom">
|
||||
<h3>
|
||||
Build a XRPL Wallet
|
||||
<small class="text-muted">- Part 8/8</small>
|
||||
</h3>
|
||||
<button type="button" class="btn btn-primary" id="send-xrp-modal-button">
|
||||
Send XRP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade show active" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
|
||||
<h3>Account:</h3>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Classic Address: <strong id="account-address-classic"></strong></li>
|
||||
<li class="list-group-item">X-Address: <strong id="account-address-x"></strong></li>
|
||||
<li class="list-group-item">XRP Balance: <strong id="account-balance"></strong></li>
|
||||
<li class="list-group-item">XRP Reserved: <strong id="account-reserve"></strong></li>
|
||||
</ul>
|
||||
<div class="spacer"></div>
|
||||
<h3>
|
||||
Ledger
|
||||
<small class="text-muted">(Latest validated ledger)</small>
|
||||
</h3>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Ledger Index: <strong id="ledger-index"></strong></li>
|
||||
<li class="list-group-item">Ledger Hash: <strong id="ledger-hash"></strong></li>
|
||||
<li class="list-group-item">Close Time: <strong id="ledger-close-time"></strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="transactions" role="tabpanel" aria-labelledby="transactions-tab">
|
||||
<h3>Transactions:</h3>
|
||||
<table id="tx-table" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Confirmed</th>
|
||||
<th>Type</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Value Delivered</th>
|
||||
<th>Hash</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="send-xrp-modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="send-xrp-modal-label">Send XRP</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group mb-3">
|
||||
<div class="accountVerificationIndicator">
|
||||
<span>Verification status:</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" value="rP4zcp52pa7ZjhjtU9LrnFcitBUadNW8Xz"
|
||||
id="input-destination-address">
|
||||
<span class="input-group-text">To (Address)</span>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" value="12345"
|
||||
id="input-destination-tag">
|
||||
<span class="input-group-text">Destination Tag</span>
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" class="form-control" value="100"
|
||||
id="input-xrp-amount">
|
||||
<span class="input-group-text">Amount of XRP</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="send-xrp-submit-button">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<dialog id="seed-dialog">
|
||||
<form method="dialog">
|
||||
<div>
|
||||
<label for="seed-input">Enter seed:</label>
|
||||
<input type="text" id="seed-input" name="seed-input" />
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Confirm</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<dialog id="password-dialog">
|
||||
<form method="dialog">
|
||||
<div>
|
||||
<label for="password-input">Enter password (min-length 5):</label><br />
|
||||
<input type="text" id="password-input" name="password-input" />
|
||||
</div>
|
||||
<div>
|
||||
<button type="button">Change Seed</button>
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
</body>
|
||||
|
||||
<script src="../../bootstrap/bootstrap.bundle.min.js"></script>
|
||||
<script src="8_renderer.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onOpenSeedDialog: (callback) => {
|
||||
ipcRenderer.on('open-seed-dialog', callback)
|
||||
},
|
||||
onEnterSeed: (seed) => {
|
||||
ipcRenderer.send('seed-entered', seed)
|
||||
},
|
||||
onOpenPasswordDialog: (callback) => {
|
||||
ipcRenderer.on('open-password-dialog', callback)
|
||||
},
|
||||
onEnterPassword: (password) => {
|
||||
ipcRenderer.send('password-entered', password)
|
||||
},
|
||||
requestSeedChange: () => {
|
||||
ipcRenderer.send('request-seed-change')
|
||||
},
|
||||
onUpdateLedgerData: (callback) => {
|
||||
ipcRenderer.on('update-ledger-data', callback)
|
||||
},
|
||||
onUpdateAccountData: (callback) => {
|
||||
ipcRenderer.on('update-account-data', callback)
|
||||
},
|
||||
onUpdateTransactionData: (callback) => {
|
||||
ipcRenderer.on('update-transaction-data', callback)
|
||||
},
|
||||
onClickSendXrp: (paymentData) => {
|
||||
ipcRenderer.send('send-xrp-action', paymentData)
|
||||
},
|
||||
onSendXrpTransactionFinish: (callback) => {
|
||||
ipcRenderer.on('send-xrp-transaction-finish', callback)
|
||||
},
|
||||
|
||||
// Step 8 code additions - start
|
||||
onDestinationAccountChange: (callback) => {
|
||||
ipcRenderer.send('destination-account-change', callback)
|
||||
},
|
||||
onUpdateDomainVerificationData: (callback) => {
|
||||
ipcRenderer.on('update-domain-verification-data', callback)
|
||||
},
|
||||
// Step 8 code additions - start
|
||||
|
||||
})
|
||||
@@ -0,0 +1,113 @@
|
||||
window.electronAPI.onOpenSeedDialog((_event) => {
|
||||
const seedDialog = document.getElementById('seed-dialog');
|
||||
const seedInput = seedDialog.querySelector('input');
|
||||
const submitButton = seedDialog.querySelector('button[type="submit"]');
|
||||
|
||||
submitButton.addEventListener('click', () => {
|
||||
const seed = seedInput.value;
|
||||
window.electronAPI.onEnterSeed(seed)
|
||||
seedDialog.close()
|
||||
});
|
||||
|
||||
seedDialog.showModal()
|
||||
})
|
||||
|
||||
window.electronAPI.onOpenPasswordDialog((_event) => {
|
||||
const passwordDialog = document.getElementById('password-dialog')
|
||||
const passwordInput = passwordDialog.querySelector('input')
|
||||
const submitButton = passwordDialog.querySelector('button[type="submit"]')
|
||||
const changeSeedButton = passwordDialog.querySelector('button[type="button"]')
|
||||
|
||||
submitButton.addEventListener('click', () => {
|
||||
const password = passwordInput.value
|
||||
window.electronAPI.onEnterPassword(password)
|
||||
passwordDialog.close()
|
||||
});
|
||||
|
||||
changeSeedButton.addEventListener('click', () => {
|
||||
passwordDialog.close()
|
||||
window.electronAPI.requestSeedChange()
|
||||
});
|
||||
|
||||
passwordDialog.showModal()
|
||||
});
|
||||
|
||||
const ledgerIndexEl = document.getElementById('ledger-index')
|
||||
const ledgerHashEl = document.getElementById('ledger-hash')
|
||||
const ledgerCloseTimeEl = document.getElementById('ledger-close-time')
|
||||
|
||||
window.electronAPI.onUpdateLedgerData((_event, ledger) => {
|
||||
ledgerIndexEl.innerText = ledger.ledgerIndex
|
||||
ledgerHashEl.innerText = ledger.ledgerHash
|
||||
ledgerCloseTimeEl.innerText = ledger.ledgerCloseTime
|
||||
})
|
||||
|
||||
const accountAddressClassicEl = document.getElementById('account-address-classic')
|
||||
const accountAddressXEl = document.getElementById('account-address-x')
|
||||
const accountBalanceEl = document.getElementById('account-balance')
|
||||
const accountReserveEl = document.getElementById('account-reserve')
|
||||
|
||||
window.electronAPI.onUpdateAccountData((_event, value) => {
|
||||
accountAddressClassicEl.innerText = value.classicAddress
|
||||
accountAddressXEl.innerText = value.xAddress
|
||||
accountBalanceEl.innerText = value.xrpBalance
|
||||
accountReserveEl.innerText = value.xrpReserve
|
||||
})
|
||||
|
||||
const txTableBodyEl = document.getElementById('tx-table').tBodies[0]
|
||||
|
||||
window.electronAPI.onUpdateTransactionData((_event, transactions) => {
|
||||
for (let transaction of transactions) {
|
||||
txTableBodyEl.insertAdjacentHTML( 'beforeend',
|
||||
"<tr>" +
|
||||
"<td>" + transaction.confirmed + "</td>" +
|
||||
"<td>" + transaction.type + "</td>" +
|
||||
"<td>" + transaction.from + "</td>" +
|
||||
"<td>" + transaction.to + "</td>" +
|
||||
"<td>" + transaction.value + "</td>" +
|
||||
"<td>" + transaction.hash + "</td>" +
|
||||
"</tr>"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const modalButton = document.getElementById('send-xrp-modal-button')
|
||||
const modalDialog = new bootstrap.Modal(document.getElementById('send-xrp-modal'))
|
||||
modalButton.addEventListener('click', () => {
|
||||
modalDialog.show()
|
||||
})
|
||||
|
||||
// Step 8 code additions - start
|
||||
const accountVerificationEl = document.querySelector('.accountVerificationIndicator span')
|
||||
// Step 8 code additions - end
|
||||
|
||||
const destinationAddressEl = document.getElementById('input-destination-address')
|
||||
const destinationTagEl = document.getElementById('input-destination-tag')
|
||||
const amountEl = document.getElementById('input-xrp-amount')
|
||||
const sendXrpButtonEl = document.getElementById('send-xrp-submit-button')
|
||||
|
||||
// Step 8 code additions - start
|
||||
destinationAddressEl.addEventListener('input', (event) => {
|
||||
window.electronAPI.onDestinationAccountChange(destinationAddressEl.value)
|
||||
})
|
||||
|
||||
window.electronAPI.onUpdateDomainVerificationData((_event, result) => {
|
||||
accountVerificationEl.textContent = `Domain: ${result.domain || 'n/a'} Verified: ${result.verified}`
|
||||
})
|
||||
// Step 8 code additions - end
|
||||
|
||||
sendXrpButtonEl.addEventListener('click', () => {
|
||||
modalDialog.hide()
|
||||
const destinationAddress = destinationAddressEl.value
|
||||
const destinationTag = destinationTagEl.value
|
||||
const amount = amountEl.value
|
||||
|
||||
window.electronAPI.onClickSendXrp({destinationAddress, destinationTag, amount})
|
||||
})
|
||||
|
||||
window.electronAPI.onSendXrpTransactionFinish((_event, result) => {
|
||||
alert('Result: ' + result.result.meta.TransactionResult)
|
||||
destinationAddressEl.value = ''
|
||||
destinationTagEl.value = ''
|
||||
amountEl.value = ''
|
||||
})
|
||||
Reference in New Issue
Block a user