<html>
<head>
<title>XRPL Token Test Harness</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<script src='https://unpkg.com/xrpl@2.6.0'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<div id="selectLedger">
<div class="appField">
<strong>Choose your ledger instance:</strong><br />
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233" checked>
<label for="testnet">Testnet</label>
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233">
<label for="devnet">Devnet</label>
</div>
<div class="appField">
<button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button><br />
<textarea id="seeds" cols="40" rows="2"></textarea>
</div>
</div>
<div id="flexContainer">
<div class="flexColumn">
<div id="standbyApp">
<button type="button" onClick="getAccount('standby')">Create Standby Account</button><br />
<h2>Standby Account Info</h2>
<div class="appField">Standby Account<br />
<div id="standbyAccountField" class="appValue"></div>
</div>
<div class="appField">Public Key<br />
<div id="standbyPubKeyField" class="appValue"></div>
</div>
<div class="appField">Private Key<br />
<div id="standbyPrivKeyField" class="appValue"></div>
</div>
<div class="appField"> Seed<br />
<div id="standbySeedField" class="appValue"></div>
</div>
<div class="appField"> XRP Balance<br />
<div id="standbyBalanceField" class="appValue"></div>
</div>
<div id="standbyTransact">
<p><strong>Send XRP</strong></p>
<div class="appField">
Amount<br />
<input id="standbyAmountField" class="appValue"></input>
</div>
<div class="appField">
Destination Account <br />
<input id="standbyDestinationField" class="appValue"></input>
</div>
<p align="right">
<button type="button" onClick="sendXRP()">Send XRP →</button>
</p>
<p><strong>Transaction Log</strong></p>
<div id="standbyResultField"></div>
</div>
</div>
</div>
<div class="flexColumn">
<div id="operationalApp">
<button type="button" onClick="getAccount('operational')">Create Operational Account</button><br />
<h2>Operational Account Info</h2>
<div class="appField">Operational Account<br />
<div id="operationalAccountField" class="appValue"></div>
</div>
<div class="appField">Public Key<br />
<div id="operationalPubKeyField" class="appValue"></div>
</div>
<div class="appField">Private Key<br />
<div id="operationalPrivKeyField" class="appValue"></div>
</div>
<div class="appField"> Seed<br />
<div id="operationalSeedField" class="appValue"></div>
</div>
<div class="appField"> XRP Balance<br />
<div id="operationalBalanceField" class="appValue"></div>
</div>
<div id="operationalTransact">
<p><strong>Send XRP</strong></p>
<div class="appField">
Amount<br />
<input id="operationalAmountField" class="appValue"></input>
</div>
<div class="appField">
Destination Account <br />
<input id="operationalDestinationField" class="appValue"></input>
</div>
<p align="right">
<button type="button" onClick="oPsendXRP()"> ← Send XRP</button>
</p>
<p><strong>Transaction Log</strong></p>
<div id="operationalResultField"></div>
</div>
</div>
</div>
</div>
</body>
</html>
body {
font-family: "Work Sans", sans-serif;
padding: 20px;
background: #fafafa;
font-size: 0.8em;
}
h1 {
font-weight: bold;
}
button {
padding: 12px;
margin-bottom: 8px;
font-size: 1em;
background: #fff;
border-radius: 0.5em;
border: solid 2px #444;
}
button {
font-weight: bold;
font-family: "Work Sans", sans-serif;
}
button:hover {
background: #eee;
cursor: pointer;
}
td {
vertical-align: top;
padding-right: 10px;
}
#selectLedger {
background: #ff78bb;
padding: 1em 1em 0 1em;
border-radius: 1em;
width: 80%;
margin: 0 0 1em 0;
font-size: 1.25em;
line-height: 2em;
}
.appField {
vertical-align: top;
display: inline-block;
margin: 0 1em 0 0;
}
.appValue {
display: inline-block;
overflow-wrap: break-word;
padding: 0.25em;
height: 2.5em;
font-size: 10px;
background: #efefef;
min-width: 100px;
max-width:300px;
border-radius: 0.5em;
margin: 0.25em 0 0.5em 0;
}
#flexContainer{
display:flex;
align-content:flex-start;
margin:0 -.5em;
}
.flexColumn{
width:100%;
flex:1 0 50%;
max-width:50%;
margin:0 .5em;
}
#standbyApp {
background: #86e3b0;
padding: 1.5em;
border-radius: 1em;
}
#standbyTransact {
display: inline-block;
background: #42df89;
border-radius: 0.5em;
display: inline-block;
margin: 0.5em 0;
padding: 0 1.5em;
min-width: 90%;
}
#standbyResultField {
display: inline-block;
padding: 0.25em;
border: solid 1px white;
height: 2em;
font-size: 1em;
background: #efefef;
width: 100%;
border-radius: 3px;
margin: 0.25em 0 0.5em 0;
height: 100px;
overflow-y: scroll;
}
#operationalApp {
background: #58bbfd;
padding: 1.5em;
border-radius: 1em;
}
#operationalTransact {
display: inline-block;
background: #19a3ff;
border-radius: 0.5em;
display: inline-block;
margin: 0.5em 0;
padding: 0 1.5em;
min-width: 90%;
}
#operationalResultField {
display: inline-block;
padding: 0.25em;
border: solid 1px white;
height: 2em;
font-size: 1em;
background: #efefef;
width: 100%;
border-radius: 3px;
margin: 0.25em 0 0.5em 0;
height: 100px;
overflow-y: scroll;
}
input {
border: none;
}
// ************* Define HTML Form Fields as constants **************
const tn = document.getElementById("tn");
const dn = document.getElementById("dn");
const standbyResultField = document.getElementById("standbyResultField");
const operationalResultsField = document.getElementById("operationalResultField");
const standbyAccountField = document.getElementById("standbyAccountField");
const standbyPubKeyField = document.getElementById("standbyPubKeyField");
const standbyPrivKeyField = document.getElementById("standbyPrivKeyField");
const standbyBalanceField = document.getElementById("standbyBalanceField");
const standbySeedField = document.getElementById("standbySeedField");
const operationalAccountField = document.getElementById("operationalAccountField");
const operationalPubKeyField = document.getElementById("operationalPubKeyField");
const operationalPrivKeyField = document.getElementById("operationalPrivKeyField");
const operationalSeedField = document.getElementById("operationalSeedField");
const operationalBalanceField = document.getElementById("operationalBalanceField");
const seeds = document.getElementById("seeds");
// ************* Get the Preferred Network **************
function getNet() {
let net;
if (tn.checked) net = "wss://s.altnet.rippletest.net:51233";
if (dn.checked) net = "wss://s.devnet.rippletest.net:51233";
return net;
} // End of getNet()
// ************* Get Account *****************************
async function getAccount(type) {
let net = getNet();
const client = new xrpl.Client(net);
results = "Connecting to " + net + "....<br/>";
// This uses the default faucet for Testnet/Devnet
let faucetHost = null;
let amount = '930';
if (type == "standby") {
standbyResultField.innerHTML = results;
} else {
operationalResultField.innerHTML = results;
}
await client.connect();
results += "\nConnected, funding wallet.<br/>";
if (type == "standby") {
standbyResultField.innerHTML = results;
} else {
operationalResultField.innerHTML = results;
}
// -----------------------------------Create and fund a test account wallet
const my_wallet = (await client.fundWallet(null, {amount, faucetHost })).wallet;
results += "\nGot a wallet.<br/>";
if (type == "standby") {
standbyResultField.innerHTML = results;
} else {
operationalResultField.innerHTML = results;
}
// -----------------------------------Get the current balance.
const my_balance = await client.getXrpBalance(my_wallet.address);
if (type == "standby") {
standbyAccountField.innerHTML = my_wallet.address;
standbyPubKeyField.innerHTML = my_wallet.publicKey;
standbyPrivKeyField.innerHTML = my_wallet.privateKey;
standbyBalanceField.innerHTML = await client.getXrpBalance(
my_wallet.address
);
standbySeedField.innerHTML = my_wallet.seed;
results += "\nStandby account created.<br/>";
standbyResultField.innerHTML = results;
} else {
operationalAccountField.innerHTML = my_wallet.address;
operationalPubKeyField.innerHTML = my_wallet.publicKey;
operationalPrivKeyField.innerHTML = my_wallet.privateKey;
operationalSeedField.innerHTML = my_wallet.seed;
operationalBalanceField.innerHTML = await client.getXrpBalance(
my_wallet.address
);
results += "\nOperational account created.<br/>";
operationalResultField.innerHTML = results;
}
// --------------- Capture the seeds for both accounts for ease of reload.
seeds.value =
standbySeedField.innerHTML + "\n" + operationalSeedField.innerHTML;
client.disconnect();
} // End of getAccount()
// *******************************************************
// ********** Get Accounts from Seeds ********************
// *******************************************************
async function getAccountsFromSeeds() {
let net = getNet();
const client = new xrpl.Client(net);
results = "Connecting to " + getNet() + "....<br/>";
standbyResultField.innerHTML = results;
await client.connect();
results += "\nConnected, finding wallets.<br/>";
standbyResultField.innerHTML = results;
// -----------------------------------Find the test account wallets
var lines = seeds.value.split("\n");
const standby_wallet = xrpl.Wallet.fromSeed(lines[0]);
const operational_wallet = xrpl.Wallet.fromSeed(lines[1]);
// -----------------------------------Get the current balance.
const standby_balance = await client.getXrpBalance(standby_wallet.address);
const operational_balance = await client.getXrpBalance(
operational_wallet.address
);
// ------------------Populate the fields for Standby and Operational accounts
standbyAccountField.innerHTML = standby_wallet.address;
standbyPubKeyField.innerHTML = standby_wallet.publicKey;
standbyPrivKeyField.innerHTML = standby_wallet.privateKey;
standbySeedField.innerHTML = standby_wallet.seed;
standbyBalanceField.innerHTML = await client.getXrpBalance(
standby_wallet.address
);
operationalAccountField.innerHTML = operational_wallet.address;
operationalPubKeyField.innerHTML = operational_wallet.publicKey;
operationalPrivKeyField.innerHTML = operational_wallet.privateKey;
operationalSeedField.innerHTML = operational_wallet.seed;
operationalBalanceField.innerHTML = await client.getXrpBalance(
operational_wallet.address
);
client.disconnect();
} // End of getAccountsFromSeeds()
// *******************************************************
// ******************** Send XRP *************************
// *******************************************************
async function sendXRP() {
results = "Connecting to the selected ledger.<br/>";
standbyResultField.innerHTML = results;
let net = getNet();
results = "Connecting to " + getNet() + "....<br/>";
const client = new xrpl.Client(net);
await client.connect();
results += "\nConnected. Sending XRP.<br/>";
standbyResultField.innerHTML = results;
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.innerHTML);
const operational_wallet = xrpl.Wallet.fromSeed(
operationalSeedField.innerHTML
);
const sendAmount = standbyAmountField.value;
results += "\nstandby_wallet.address: = " + standby_wallet.address + "<br/>";
standbyResultField.innerHTML = results;
// ------------------------------------------------------- Prepare transaction
// Note that the destination is hard coded.
const prepared = await client.autofill({
TransactionType: "Payment",
Account: standby_wallet.address,
Amount: xrpl.xrpToDrops(sendAmount),
Destination: standbyDestinationField.value
});
// ------------------------------------------------ Sign prepared instructions
const signed = standby_wallet.sign(prepared);
// -------------------------------------------------------- Submit signed blob
const tx = await client.submitAndWait(signed.tx_blob);
results +=
"<br/>Balance changes: " +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2);
standbyResultField.innerHTML = results;
standbyBalanceField.innerHTML = await client.getXrpBalance(
standby_wallet.address
);
operationalBalanceField.innerHTML = await client.getXrpBalance(
operational_wallet.address
);
client.disconnect();
} // End of sendXRP()
// **********************************************************************
// ****** Reciprocal Transactions ***************************************
// **********************************************************************
// *******************************************************
// ********* Send XRP from Operational account ***********
// *******************************************************
async function oPsendXRP() {
results = "Connecting to the selected ledger.<br/>";
operationalResultField.innerHTML = results;
let net = getNet();
results = "Connecting to " + getNet() + "....<br/>";
const client = new xrpl.Client(net);
await client.connect();
results += "<br/>Connected. Sending XRP.<br/>";
operationalResultField.innerHTML = results;
const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value);
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.innerHTML);
const sendAmount = operationalAmountField.value;
results +=
"\noperational_wallet.address: = " + operational_wallet.address + "<br/>";
operationalResultField.innerHTML = results;
// ------------------------------------------------------- Prepare transaction
// Note that the destination is hard coded.
const prepared = await client.autofill({
TransactionType: "Payment",
Account: operational_wallet.address,
Amount: xrpl.xrpToDrops(operationalAmountField.value),
Destination: operationalDestinationField.value
});
// ------------------------------------------------ Sign prepared instructions
const signed = operational_wallet.sign(prepared);
// -------------------------------------------------------- Submit signed blob
const tx = await client.submitAndWait(signed.tx_blob);
results +=
"<br/>Balance changes: " +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2);
operationalResultField.innerHTML = results;
standbyBalanceField.innerHTML = await client.getXrpBalance(
standby_wallet.address
);
operationalBalanceField.innerHTML = await client.getXrpBalance(
operational_wallet.address
);
client.disconnect();
} // End of oPsendXRP()
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.