Merge pull request #3122 from XRPLF/NFT_refactor

Refactor NFT code samples and update the tutorials and screenshots
This commit is contained in:
Dennis Dawson
2025-06-18 09:40:27 -07:00
committed by GitHub
44 changed files with 5526 additions and 3654 deletions

View File

@@ -32,8 +32,9 @@ async function getAccount() {
} }
finally { finally {
// Disconnect from the client // Disconnect from the client
await client.disconnect(); if (client && client.isConnected()) {
} await client.disconnect();
} }
} // End of getAccount() } // End of getAccount()
async function getNewAccount1() { async function getNewAccount1() {
@@ -78,8 +79,9 @@ async function getAccountFromSeed(my_seed) {
} }
finally { finally {
// Disconnect from the client // Disconnect from the client
await client.disconnect(); if (client && client.isConnected()) {
} await client.disconnect();
} }
} // End of getAccountFromSeed() } // End of getAccountFromSeed()
// ***************************************************** // *****************************************************
@@ -169,8 +171,9 @@ async function getXrpBalance() {
} }
finally { finally {
// Disconnect from the client // Disconnect from the client
await client.disconnect(); if (client && client.isConnected()) {
} await client.disconnect();
} }
} // End of getXrpBalance() } // End of getXrpBalance()
// ******************************************************* // *******************************************************
@@ -202,8 +205,9 @@ async function getTokenBalance() {
} }
finally { finally {
// Disconnect from the client // Disconnect from the client
await client.disconnect(); if (client && client.isConnected()) {
} await client.disconnect();
} }
} // End of getTokenBalance() } // End of getTokenBalance()
// ******************************************************* // *******************************************************

View File

@@ -0,0 +1,209 @@
// ******************************************************
// ************* Get the Preferred Network **************
// ******************************************************
function getNet() {
let net
if (document.getElementById("tn").checked) net = "wss://s.altnet.rippletest.net:51233/"
if (document.getElementById("dn").checked) net = "wss://s.devnet.rippletest.net:51233/"
return net
} // End of getNet()
// *******************************************************
// ************* Get Account *****************************
// *******************************************************
async function getAccount() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
resultField.value = `===Getting Account===\n\nConnected to ${net}.`
try {
let faucetHost = null
const my_wallet = (await client.fundWallet(null, { faucetHost})).wallet
const newAccount = [my_wallet.address, my_wallet.seed]
return newAccount
}
catch (error) {
console.error('===Error getting account:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
if (client && client.isConnected()) {
await client.disconnect();
}
}
} // End of getAccount()
async function getNewAccount1() {
account1address.value = "=== Getting new account. ===\n\n"
account1seed.value = ""
const accountInfo= await getAccount()
account1address.value = accountInfo[0]
account1seed.value = accountInfo[1]
}
async function getNewAccount2() {
account2address.value = "=== Getting new account. ===\n\n"
account2seed.value = ""
const accountInfo= await getAccount()
account2address.value = accountInfo[0]
account2seed.value = accountInfo[1]
}
// *****************************************************
// ********** Get Account from Seed ********************
// *****************************************************
async function getAccountFromSeed(my_seed) {
const net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = '===Finding wallet.===\n\n'
resultField.value = results
try {
const wallet = xrpl.Wallet.fromSeed(my_seed)
const address = wallet.address
results += "===Wallet found.===\n\n"
results += "Account address: " + address + "\n\n"
resultField.value = results
return (address)
}
catch (error) {
console.error('===Error getting account from seed:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
} // End of getAccountFromSeed()
// *****************************************************
// ********** Get Account from Seed1 *******************
// *****************************************************
async function getAccountFromSeed1() {
account1address.value = await getAccountFromSeed(account1seed.value)
}
// *****************************************************
// ********** Get Account from Seed2 *******************
// *****************************************************
async function getAccountFromSeed2() {
account2address.value = await getAccountFromSeed(account2seed.value)
}
// *****************************************************
// ************ Gather Account Info ********************
// *****************************************************
function gatherAccountInfo() {
let accountData = account1name.value + "\n" + account1address.value + "\n" + account1seed.value + "\n"
accountData += account2name.value + "\n" + account2address.value + "\n" + account2seed.value
resultField.value = accountData
}
// *****************************************************
// ********** Distribute Account Info ******************
// *****************************************************
function distributeAccountInfo() {
let accountInfo = resultField.value.split("\n")
account1name.value = accountInfo[0]
account1address.value = accountInfo[1]
account1seed.value = accountInfo[2]
account2name.value = accountInfo[3]
account2address.value = accountInfo[4]
account2seed.value = accountInfo[5]
}
// *****************************************************
// ************ Populate Active Form 1 *****************
// *****************************************************
function populate1() {
accountNameField.value = account1name.value
accountAddressField.value = account1address.value
accountSeedField.value = account1seed.value
getXrpBalance()
}
// *****************************************************
// ************ Populate Active Form 2 *****************
// *****************************************************
function populate2() {
accountNameField.value = account2name.value
accountAddressField.value = account2address.value
accountSeedField.value = account2seed.value
getXrpBalance()
}
// *******************************************************
// **************** Get XRP Balance *********************
// *******************************************************
async function getXrpBalance() {
const net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Getting XRP balance...===\n\n`
resultField.value = results
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const balance = await client.getXrpBalance(wallet.address)
results += accountNameField.value + " current XRP balance: " + balance + "\n\n"
xrpBalanceField.value = await client.getXrpBalance(accountAddressField.value)
resultField.value = results
}
catch (error) {
console.error('Error getting XRP balance:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
} // End of getXrpBalance()
// *******************************************************
// ************** Get Token Balance *********************
// *******************************************************
async function getTokenBalance() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}.===\n===Getting account token balance...===\n\n`
resultField.value += results
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const balance = await client.request({
command: "gateway_balances",
account: wallet.address,
ledger_index: "validated",
})
results = accountNameField.value + "\'s token balance(s): " + JSON.stringify(balance.result, null, 2) + "\n"
resultField.value += results
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
}
catch (error) {
console.error('Error getting token balance:', error);
results = `\nError: ${error.message}\n`
resultField.value += results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
} // End of getTokenBalance()

View File

@@ -0,0 +1,340 @@
<html>
<head>
<title>Authorize Minter of NFTs</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='transaction-support.js'></script>
<script src='mint-nfts.js'></script>
<script src='authorized-minter.js'></script>
</head>
<body>
<h1>Authorize Minter of NFTs</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label
for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
<td rowspan="4" align="center">
<p>
<img id="nftImage"
src="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"
width="150" height="150">
</td>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT configuration flags.">
<label for="flagsField">Flags</label>
</span>
</td>
<td>
<input type="text" id="flagsField" size="40"></input>
</td>
<td align="right">
<span class="tooltip" tooltip-data="URL to the stored NFT.">
<label for="nftURLfield">NFT URL</label>
</span>&nbsp;&nbsp;
<input type="text" id="nftURLfield" size="30"
value="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Percentage of sale price collected by the issuer when the NFT is sold. Enter a value from 0 to 50000, where 1000=1%.">
<label for="transferFeeField">Transfer Fee</label>
</span>
<p id="error-message"></p>
</td>
<td>
<input type="text" id="transferFeeField" size="40"></input>
</td>
<td>
<button type="button" onClick="mintNFT()">Mint NFT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT Taxon. Integer value used to identify NFTs minted in a series or collection. This value is required. Set it to 0 if you have no use for it.">
<label for="nftTaxonField">NFT Taxon</label>
</span>
</td>
<td>
<input type="text" id="nftTaxonField" size="40" value="0"></input>
</td>
<td>
<button type="button" onClick="getNFTs()">Get NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency for the offer.">
<label for="currencyField">Currency</label>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="burnNFT()">Burn NFT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuer of the currency used.">
<label for="issuerField">Issuer</label>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
</td>
<td>
<button type="button" onClick="authorizeMinter()">Authorize Minter</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<label for="destinationField">Destination</label>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="mintOther()">Mint Other</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of days the offer is valid.">
<label for="expirationField">Expiration (days)</label>
</span>
</td>
<td>
<input type="text" id="expirationField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT ID, used to transfer or burn the NFT after it is created.">
<label for="nftIdField">NFT ID</label>
</span>
</td>
<td>
<input type="text" id="nftIdField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Account address that is authorized to mint NFTs for this account.">
<label for="authorizedMinterField">Authorized Minter</label>
</span>
</td>
<td>
<input type="text" id="authorizedMinterField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Account that is the original issuer of the NFT.">
<label for="nftIssuerField">NFT Issuer</label>
</span>
</td>
<td>
<input type="text" id="nftIssuerField" size="40"></input>
</td>
</tr>
<tr>
<td colspan="2">
<p align="left">
<textarea id="resultField" cols="75" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br />
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const imageURLInput = document.getElementById('nftURLfield'); // Correct ID to nftURLfield
const displayImage = document.getElementById('nftImage');
const errorMessage = document.getElementById('error-message');
if (imageURLInput) {
imageURLInput.addEventListener('change', () => {
const newURL = imageURLInput.value;
displayImage.src = ''; // Clear previous image
errorMessage.style.display = 'none';
try {
new URL(newURL);
} catch (_) {
errorMessage.textContent = 'Invalid URL. Please enter a valid URL, including "https://" or "http://".';
errorMessage.style.display = 'block';
return;
}
displayImage.onload = () => {
// Image loaded. You might add a console log here, or update UI.
console.log(`Image loaded from: ${newURL}`);
};
displayImage.onerror = () => {
errorMessage.textContent = 'Error loading image from the provided URL.';
errorMessage.style.display = 'block';
displayImage.src = ''; // Clear the image on error
};
displayImage.src = newURL; // Load the image
});
}
});
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function () {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>

View File

@@ -0,0 +1,97 @@
// *******************************************************
// ************ Authorize Minter ***********************
// *******************************************************
async function authorizeMinter() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
const net = getNet();
const client = new xrpl.Client(net);
let results = `\n=== Connected. Authorizing Minter. ===`;
resultField.value = results;
try {
await client.connect();
tx_json = {
"TransactionType": "AccountSet",
"Account": wallet.address,
"NFTokenMinter": authorizedMinterField.value,
"SetFlag": xrpl.AccountSetAsfFlags.asfAuthorizedNFTokenMinter
}
const prepared = await client.autofill(tx_json)
const signed = wallet.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
results += '\nAccount setting succeeded.\n'
results += JSON.stringify(result, null, 2)
resultField.value = results
} catch (error) {
console.error("Error setting minter:", error);
results = `\n\n=== Error setting minter: ${error.message}`;
resultField.value += results;
} finally {
if (client && client.isConnected()) {
await client.disconnect();
}
}
} // End of authorizeMinter()
// *******************************************************
// **************** Mint Other *************************
// *******************************************************
async function mintOther() {
let results = 'Connecting to ' + getNet() + '....'
resultField.value = results
const net = getNet()
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const client = new xrpl.Client(net)
try {
await client.connect()
results += '\nConnected. Minting NFT.'
resultField.value = results
// ------------------------------------------------------------------------
const tx_json = {
"TransactionType": "NFTokenMint",
"Account": wallet.classicAddress,
"URI": xrpl.convertStringToHex(nftURLfield.value),
"Flags": parseInt(flagsField.value),
"TransferFee": parseInt(transferFeeField.value),
"Issuer": nftIssuerField.value,
"NFTokenTaxon": nftTaxonField.value //Required, but if you have no use for it, set to zero.
}
if (amountField.value) {
tx_json.Amount = configureAmount(amountField.value);
}
if (expirationField.value) {
tx_json.Expiration = configureExpiration(expirationField.value);
}
if (destinationField.value) {
tx_json.Destination = destinationField.value;
}
// ----------------------------------------------------- Submit transaction
const tx = await client.submitAndWait(tx_json, { wallet: wallet })
const nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress
})
// ------------------------------------------------------- Report results
results += '\n\n=== Transaction result: ' + tx.result.meta.TransactionResult
results += '\n\n=== NFTs: ' + JSON.stringify(nfts, null, 2)
resultField.value = results + (await client.getXrpBalance(wallet.address))
} catch (error) {
results += '\n\nAn error occurred: ' + error.message
console.error(error) // Log the error for debugging
resultField.value = results
} finally {
if (client.isConnected()) { // Check if the client is connected before attempting to disconnect
client.disconnect()
results += '\nDisconnected from XRPL.'
resultField.value = results
}
}
} //End of mintOther()

View File

@@ -0,0 +1,320 @@
<html>
<head>
<title>Batch Mint NFTs</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='transaction-support.js'></script>
<script src='mint-nfts.js'></script>
<script src="batch-minting.js"></script>
</head>
<body>
<h1>Batch Mint NFTs</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label
for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
<td rowspan="4" align="center">
<p>
<img id="nftImage"
src="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"
width="150" height="150">
</td>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT configuration flags.">
<label for="flagsField">Flags</label>
</span>
</td>
<td>
<input type="text" id="flagsField" size="40"></input>
</td>
<td align="right">
<span class="tooltip" tooltip-data="URL to the stored NFT.">
<label for="nftURLfield">NFT URL</label>
</span>&nbsp;&nbsp;
<input type="text" id="nftURLfield" size="30"
value="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Percentage of sale price collected by the issuer when the NFT is sold. Enter a value from 0 to 50000, where 1000=1%.">
<label for="transferFeeField">Transfer Fee</label>
</span>
<p id="error-message"></p>
</td>
<td>
<input type="text" id="transferFeeField" size="40"></input>
</td>
<td>
<button type="button" onClick="batchMintNFTs()">Batch Mint NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT Taxon. Integer value used to identify NFTs minted in a series or collection. This value is required. Set it to 0 if you have no use for it.">
<label for="nftTaxonField">NFT Taxon</label>
</span>
</td>
<td>
<input type="text" id="nftTaxonField" size="40" value="0"></input>
</td>
<td>
<button type="button" onClick="getBatchNFTs()">Get Batch NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency for the offer.">
<label for="currencyField">Currency</label>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuer of the currency used.">
<label for="issuerField">Issuer</label>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<label for="destinationField">Destination</label>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of days the offer is valid.">
<label for="expirationField">Expiration (days)</label>
</span>
</td>
<td>
<input type="text" id="expirationField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of NFTs to produce in a batch.">
<label for="nftCountField">NFT Count</label>
</span>
</td>
<td>
<input type="text" id="nftCountField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT ID, used to transfer or burn the NFT after it is created.">
<label for="nftIdField">NFT ID</label>
</span>
</td>
<td>
<input type="text" id="nftIdField" size="40"></input>
</td>
</tr> <tr>
<td colspan="2">
<p align="left">
<textarea id="resultField" cols="75" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br />
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const imageURLInput = document.getElementById('nftURLfield'); // Correct ID to nftURLfield
const displayImage = document.getElementById('nftImage');
const errorMessage = document.getElementById('error-message');
if (imageURLInput) {
imageURLInput.addEventListener('change', () => {
const newURL = imageURLInput.value;
displayImage.src = ''; // Clear previous image
errorMessage.style.display = 'none';
try {
new URL(newURL);
} catch (_) {
errorMessage.textContent = 'Invalid URL. Please enter a valid URL, including "https://" or "http://".';
errorMessage.style.display = 'block';
return;
}
displayImage.onload = () => {
// Image loaded. You might add a console log here, or update UI.
console.log(`Image loaded from: ${newURL}`);
};
displayImage.onerror = () => {
errorMessage.textContent = 'Error loading image from the provided URL.';
errorMessage.style.display = 'block';
displayImage.src = ''; // Clear the image on error
};
displayImage.src = newURL; // Load the image
});
}
});
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function () {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>

View File

@@ -0,0 +1,272 @@
// *******************************************************
// ****************** Batch Mint ***********************
// *******************************************************
async function batchMintNFTs() {
let client; // Declare client here so it's accessible in finally block
try {
//--------------------- Connect to the XRP Ledger and get the account wallet.
let net = getNet();
client = new xrpl.Client(net); // Assign client
results = 'Connecting to ' + getNet() + '....';
resultField.value = results;
await client.connect();
results += '\nConnected, finding wallet.';
resultField.value = results;
let wallet;
try {
wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
} catch (error) {
results += '\nError: Invalid account seed. Please check your seed.';
resultField.value = results;
return; // Stop execution if wallet cannot be derived
}
resultField.value = results;
//----------------- Get account information, particularly the Sequence number.
let account_info;
try {
account_info = await client.request({
"command": "account_info",
"account": wallet.address
});
} catch (error) {
results += `\nError retrieving account info for ${wallet.address}: ${error.message}`;
resultField.value = results;
return;
}
let my_sequence = account_info.result.account_data.Sequence;
results += "\n\nSequence Number: " + my_sequence + "\n\n";
resultField.value = results;
/* ###################################
Create ticket numbers for the batch
Without tickets, if one transaction fails, all others in the batch fail.
With tickets, there can be failures, but the rest will continue, and you
can investigate any problems afterward.
*/
//---------------------- Parse the requested number from nftCountField.
const nftCount = parseInt(nftCountField.value);
if (isNaN(nftCount) || nftCount <= 0) {
results += '\nError: Please enter a valid number of NFTs to mint.';
resultField.value = results;
return;
}
//-------------------------------------------- Create the transaction hash.
let ticketTransaction;
try {
ticketTransaction = await client.autofill({
"TransactionType": "TicketCreate",
"Account": wallet.address,
"TicketCount": nftCount,
"Sequence": my_sequence
});
} catch (error) {
results += `\nError autofilling ticket creation transaction: ${error.message}`;
resultField.value = results;
return;
}
//---------------------------------------------------- Sign the transaction.
const signedTransaction = wallet.sign(ticketTransaction);
//-------------------------- Submit the transaction and wait for the result.
let tx;
try {
tx = await client.submitAndWait(signedTransaction.tx_blob);
} catch (error) {
results += `\nError submitting ticket creation transaction: ${error.message}`;
resultField.value = results;
return;
}
if (tx.result.meta.TransactionResult !== 'tesSUCCESS') {
results += `\nError creating tickets. Transaction failed with result: ${tx.result.meta.TransactionResult}`;
resultField.value = results;
return;
}
results += `\nTickets created successfully. Transaction result: ${tx.result.meta.TransactionResult}\n\n`;
resultField.value = results;
let response;
try {
response = await client.request({
"command": "account_objects",
"account": wallet.address,
"type": "ticket"
});
} catch (error) {
results += `\nError retrieving account tickets: ${error.message}`;
resultField.value = results;
return;
}
//------------------------------------ Populate the tickets array variable.
let tickets = [];
if (response.result.account_objects && response.result.account_objects.length > 0) {
for (let i = 0; i < nftCount; i++) {
if (response.result.account_objects[i]) {
tickets[i] = response.result.account_objects[i].TicketSequence;
} else {
results += `\nWarning: Fewer tickets found than requested. Expected ${nftCount}, found ${response.result.account_objects.length}.`;
resultField.value = results;
break; // Exit loop if tickets run out
}
}
} else {
results += '\nError: No tickets found for the account.';
resultField.value = results;
return;
}
//-------------------------------------------------------- Report progress.
results += "Tickets generated, minting NFTs.\n\n";
resultField.value = results;
// ###################################
// Mint NFTs
let mintedNFTsCount = 0;
for (let i = 0; i < tickets.length; i++) {
const transactionParams = {
"TransactionType": "NFTokenMint",
"Account": wallet.classicAddress,
"URI": xrpl.convertStringToHex(nftURLfield.value),
"Flags": parseInt(flagsField.value),
"TransferFee": parseInt(transferFeeField.value),
"Sequence": 0, // Sequence is 0 when using TicketSequence
"TicketSequence": tickets[i],
"LastLedgerSequence": null, // Optional, can be used for time limits
"NFTokenTaxon": nftTaxonField.value,
};
// Add optional fields
if (amountField.value) {
transactionParams.Amount = configureAmount(amountField.value);
}
if (expirationField.value) {
transactionParams.Expiration = configureExpiration(expirationField.value);
}
if (destinationField.value) {
transactionParams.Destination = destinationField.value;
}
try {
const mintTx = await client.submit(transactionParams, {
wallet: wallet
});
results += `\nNFT ${i+1} minted successfully.`;
mintedNFTsCount++;
resultField.value = results;
} catch (error) {
console.log(error);
}
// Add a small delay to avoid hitting rate limits if many NFTs are being minted
await new Promise(resolve => setTimeout(resolve, 500));
}
results += `\n\nAttempted to mint ${nftCount} NFTs. Successfully minted ${mintedNFTsCount} NFTs.`;
results += "\n\nFetching minted NFTs...\n";
let nfts;
try {
nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
limit: 400
});
results += JSON.stringify(nfts, null, 2);
while (nfts.result.marker) {
nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
limit: 400,
marker: nfts.result.marker
});
results += '\n' + JSON.stringify(nfts, null, 2);
}
} catch (error) {
results += `\nError fetching account NFTs: ${error.message}`;
}
try {
xrpBalanceField.value = (await client.getXrpBalance(wallet.address));
} catch (error) {
results += `\nError fetching XRP balance: ${error.message}`;
}
resultField.value = results;
} catch (error) {
results += `\nAn unexpected error occurred during batch minting: ${error.message}`;
resultField.value = results;
} finally {
if (client && client.isConnected()) {
client.disconnect();
results += '\nDisconnected from XRP Ledger.';
resultField.value = results;
}
}
} // End of batchMint()
// *******************************************************
// **************** Get Batch Tokens *********************
// *******************************************************
async function getBatchNFTs() {
let client; // Declare client here for finally block access
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
let net = getNet();
client = new xrpl.Client(net); // Assign client
results = 'Connecting to ' + net + '...';
resultField.value = results;
await client.connect();
results += '\nConnected. Getting NFTs...';
resultField.value = results;
results += "\n\nNFTs:\n";
let nfts;
try {
nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
limit: 400
});
results += JSON.stringify(nfts, null, 2);
while (nfts.result.marker) {
nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
limit: 400,
marker: nfts.result.marker
});
results += '\n' + JSON.stringify(nfts, null, 2);
}
} catch (error) {
results += `\nError fetching account NFTs: ${error.message}`;
}
resultField.value = results;
} catch (error) {
results += `\nAn unexpected error occurred while getting batch NFTs: ${error.message}`;
resultField.value = results;
} finally {
if (client && client.isConnected()) {
client.disconnect();
results += '\nDisconnected from XRP Ledger.';
resultField.value = results;
}
}
} //End of getBatchNFTs()

View File

@@ -0,0 +1,291 @@
<html>
<head>
<title>Broker NFTs</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src="transaction-support.js"></script>
<script src="mint-nfts.js"></script>
<script src="transfer-nfts.js"></script>
<script src="broker-nfts.js"></script>
</head>
<body>
<h1>Broker NFTs</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label
for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
<td rowspan="4" align="center">
<p>
<img id="nftImage"
src="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"
width="150" height="150">
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td>
</td>
<td align="right">
<span class="tooltip" tooltip-data="URL to the stored NFT.">
<label for="nftURLfield">NFT URL</label>
</span>
</td>
<td>
<input type="text" id="nftURLfield" size="40"
placeholder="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"></input>
<br />
<p id="error-message"></p>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT ID code, used to identify the token after it's minted.">
<label for="nftIdField">NFT ID</label>
</span>
</td>
<td>
<input type="text" id="nftIdField" size="40"></input>
</td>
<td align="center" valign="top">
<button type="button" onClick="getNFTs()">Get NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Account address of the Owner of an NFT offered to sell or buy.">
<label for="nftOwnerField">NFT Owner Address</label>
</span>
</td>
<td>
<input type="text" id="nftOwnerField" size="40"></input>
</td>
<td colspan="2" align="center" valign="top">
<button type="button" onClick="getOffers()" width="40">Get Offers</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Index of the sell offer to broker.">
<label for="nftSellOfferIndexField">Sell Offer Index</label>
</span>
</td>
<td>
<input type="text" id="nftSellOfferIndexField" size="40"></input>
<br>
</td>
<td align="middle" valign="top" colspan="2">
<button type="button" onClick="brokerSale()">Broker Sale</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Index of the buy offer to broker.">
<label for="nftBuyOfferIndexField">Buy Offer Index</label>
</span>
</td>
<td>
<input type="text" id="nftBuyOfferIndexField" size="40"></input>
<br>
</td>
<td align="middle" valign="top" colspan="2">
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Fee collected by the broker account when the brokered deal is complete.">
<label for="brokerFeeField">Broker Fee</label>
</span>
</td>
<td>
<input type="text" id="brokerFeeField" size="40"></input>
</td>
</tr>
<tr valign="top">
<td colspan="2">
<p align="left">
<textarea id="resultField" cols="75" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br />
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const imageURLInput = document.getElementById('nftURLfield'); // Correct ID to nftURLfield
const displayImage = document.getElementById('nftImage');
const loadButton = document.getElementById('showNFTbutton');
const errorMessage = document.getElementById('error-message');
if (imageURLInput) {
imageURLInput.addEventListener('change', () => {
const newURL = imageURLInput.value;
displayImage.src = ''; // Clear previous image
errorMessage.style.display = 'none';
try {
new URL(newURL);
} catch (_) {
errorMessage.textContent = 'Invalid URL. Please enter a valid URL, including "https://" or "http://".';
errorMessage.style.display = 'block';
return;
}
displayImage.onload = () => {
// Image loaded. You might add a console log here, or update UI.
console.log(`Image loaded from: ${newURL}`);
};
displayImage.onerror = () => {
errorMessage.textContent = 'Error loading image from the provided URL.';
errorMessage.style.display = 'block';
displayImage.src = ''; // Clear the image on error
};
displayImage.src = newURL; // Load the image
});
}
});
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function () {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>

View File

@@ -0,0 +1,44 @@
// *******************************************************
// ******************* Broker Sale ***********************
// *******************************************************
async function brokerSale() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
const net = getNet();
const client = new xrpl.Client(net);
let results = `\n=== Connected. Brokering the sale. ===`;
resultField.value = results;
try {
await client.connect();
// Prepare transaction -------------------------------------------------------
const brokerTx = {
"TransactionType": "NFTokenAcceptOffer",
"Account": wallet.classicAddress,
"NFTokenSellOffer": nftSellOfferIndexField.value,
"NFTokenBuyOffer": nftBuyOfferIndexField.value,
"NFTokenBrokerFee": brokerFeeField.value
}
console.log(JSON.stringify(brokerTx, null, 2));
// Submit transaction --------------------------------------------------------
const tx = await client.submitAndWait(brokerTx, { wallet: wallet })
// Check transaction results -------------------------------------------------
results += "\n\nTransaction result:\n" +
JSON.stringify(tx.result.meta.TransactionResult, null, 2)
results += "\nBalance changes:\n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
resultField.value += results
} catch (error) {
console.error("Error in broker sale:", error);
results = `\n\n=== Error in broker sale: ${error.message} ===`; // User friendly
resultField.value += results;
}
finally {
if (client && client.isConnected()) {
await client.disconnect();
}
}
}// End of brokerSale()

View File

@@ -0,0 +1,313 @@
<html>
<head>
<title>Mint NFTs</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='transaction-support.js'></script>
<script src='mint-nfts.js'></script>
</head>
<body>
<h1>Mint NFTs</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label
for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
<td rowspan="4" align="center">
<p>
<img id="nftImage"
src="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"
width="150" height="150">
</td>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT configuration flags.">
<label for="flagsField">Flags</label>
</span>
</td>
<td>
<input type="text" id="flagsField" size="40"></input>
</td>
<td align="right">
<span class="tooltip" tooltip-data="URL to the stored NFT.">
<label for="nftURLfield">NFT URL</label>
</span>&nbsp;&nbsp;
<input type="text" id="nftURLfield" size="30"
value="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Percentage of sale price collected by the issuer when the NFT is sold. Enter a value from 0 to 50000, where 1000=1%.">
<label for="transferFeeField">Transfer Fee</label>
</span>
<p id="error-message"></p>
</td>
<td>
<input type="text" id="transferFeeField" size="40"></input>
</td>
<td>
<button type="button" onClick="mintNFT()">Mint NFT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT Taxon. Integer value used to identify NFTs minted in a series or collection. This value is required. Set it to 0 if you have no use for it.">
<label for="nftTaxonField">NFT Taxon</label>
</span>
</td>
<td>
<input type="text" id="nftTaxonField" size="40" value="0"></input>
</td>
<td>
<button type="button" onClick="getNFTs()">Get NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency for the offer.">
<label for="currencyField">Currency</label>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="burnNFT()">Burn NFT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuer of the currency used.">
<label for="issuerField">Issuer</label>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of currency to send. If XRP, you can enter 1 per XRP: the amount is converted to drops for you.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<label for="destinationField">Destination</label>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of days the offer is valid.">
<label for="expirationField">Expiration (days)</label>
</span>
</td>
<td>
<input type="text" id="expirationField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT ID, used to transfer or burn the NFT after it is created.">
<label for="nftIdField">NFT ID</label>
</span>
</td>
<td>
<input type="text" id="nftIdField" size="40"></input>
</td>
</tr>
<tr>
<td colspan="2">
<p align="left">
<textarea id="resultField" cols="75" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br />
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const imageURLInput = document.getElementById('nftURLfield'); // Correct ID to nftURLfield
const displayImage = document.getElementById('nftImage');
const errorMessage = document.getElementById('error-message');
if (imageURLInput) {
imageURLInput.addEventListener('change', () => {
const newURL = imageURLInput.value;
displayImage.src = ''; // Clear previous image
errorMessage.style.display = 'none';
try {
new URL(newURL);
} catch (_) {
errorMessage.textContent = 'Invalid URL. Please enter a valid URL, including "https://" or "http://".';
errorMessage.style.display = 'block';
return;
}
displayImage.onload = () => {
// Image loaded. You might add a console log here, or update UI.
console.log(`Image loaded from: ${newURL}`);
};
displayImage.onerror = () => {
errorMessage.textContent = 'Error loading image from the provided URL.';
errorMessage.style.display = 'block';
displayImage.src = ''; // Clear the image on error
};
displayImage.src = newURL; // Load the image
});
}
});
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function () {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>

View File

@@ -0,0 +1,141 @@
// *******************************************************
// ********************** Mint NFT ***********************
// *******************************************************
async function mintNFT() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
const net = getNet();
const client = new xrpl.Client(net);
let results = `\n=== Connected. Minting NFT ===`;
resultField.value = results;
try {
await client.connect();
// Prepare transaction parameters
const transactionParams = {
TransactionType: "NFTokenMint",
Account: wallet.classicAddress,
URI: xrpl.convertStringToHex(nftURLfield.value),
Flags: parseInt(flagsField.value, 10), // Parse to integer
TransferFee: parseInt(transferFeeField.value, 10), // Parse to integer
NFTokenTaxon: parseInt(nftTaxonField.value, 10), // Parse to integer
};
// Add optional fields
if (amountField.value) {
transactionParams.Amount = configureAmount(amountField.value);
}
if (expirationField.value) {
transactionParams.Expiration = configureExpiration(expirationField.value);
}
if (destinationField.value) {
transactionParams.Destination = destinationField.value;
}
console.log("Mint NFT Transaction Parameters:", transactionParams); // Log before submitting
// Submit transaction
const tx = await client.submitAndWait(transactionParams, { wallet });
// Get minted NFTs
const nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
});
// Report results
results += `\n\n=== Transaction result: ${tx.result.meta.TransactionResult} ===`;
results += `\n\n=== NFTs: ${JSON.stringify(nfts, null, 2)} ===`;
results += `\n\n=== XRP Balance: ${await client.getXrpBalance(wallet.address)} ===`; // Await here
resultField.value = results;
} catch (error) {
console.error("Error minting NFT:", error);
results += `\n\n=== Error minting NFT: ${error.message} ===`; // Use error.message
resultField.value = results;
} finally {
if (client && client.isConnected()) { // Check if connected before disconnecting
await client.disconnect();
}
}
} // End of mintToken()
// *******************************************************
// ******************** Get NFTs *************************
// *******************************************************
async function getNFTs() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
const net = getNet();
const client = new xrpl.Client(net);
let results = '\n=== Connected. Getting NFTs. ===';
resultField.value = results;
try {
await client.connect();
const nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress,
});
results = '\n=== NFTs:\n ' + JSON.stringify(nfts, null, 2) + ' ==='; // Consistent formatting
resultField.value = results;
} catch (error) {
console.error("Error getting NFTs:", error);
results += `\n\n=== Error getting NFTs: ${error.message} ===`; // User-friendly
resultField.value = results;
} finally {
if (client && client.isConnected()) {
await client.disconnect();
}
}
} // End of getNFTs()
// *******************************************************
// ********************** Burn NFT ***********************
// *******************************************************
async function burnNFT() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
const net = getNet();
const client = new xrpl.Client(net);
let results = '\n=== Connected. Burning NFT. ===';
resultField.value = results;
try {
await client.connect();
// Prepare transaction
const transactionBlob = {
TransactionType: "NFTokenBurn",
Account: wallet.classicAddress,
NFTokenID: nftIdField.value,
};
console.log("Burn NFT Transaction Parameters:", transactionBlob); // Log before submit
// Submit transaction and wait for the results
const tx = await client.submitAndWait(transactionBlob, { wallet });
const nfts = await client.request({ // Get nfts after burning.
method: "account_nfts",
account: wallet.classicAddress,
});
results = `\n=== Transaction result: ${tx.result.meta.TransactionResult} ===`;
results += '\n\n=== Balance changes: ' +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) + ' ===';
results += '\n\n=== NFTs: \n' + JSON.stringify(nfts, null, 2) + ' ===';
resultField.value = results;
xrpBalanceField.value = (await client.getXrpBalance(wallet.address)); // Await
} catch (error) {
console.error("Error burning NFT:", error);
results = `\n\n=== Error burning NFT: ${error.message} ===`; // User friendly
resultField.value = results;
} finally {
if (client && client.isConnected()) {
await client.disconnect();
}
}
} // End of burnNFT()

View File

@@ -0,0 +1,153 @@
body {
font-family: "Inter", sans-serif;
padding: 20px;
background: #abe2ff;
}
h1 {
font-weight: bold;
}
td {
padding-left: 25px;
vertical-align: top;
}
input,
button {
padding: 6px;
margin-bottom: 8px;
border: none
}
input:read-only {
background-color:rgb(11, 96, 132);
color:white;
border: 0;
}
button {
font-weight: bold;
font-family: "Work Sans", sans-serif;
background-color: #006aff;
-webkit-text-fill-color: white;
width: 144px;
}
button:hover {
background-color: #0555c5;
cursor: pointer;
}
label {
font-weight: bold;
}
td {
vertical-align: middle;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 30px;
height: 16px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
left: 4px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked+.slider {
background-color: #2196F3;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(13px);
-ms-transform: translateX(13px);
transform: translateX(13px);
}
/* Rounded sliders */
.slider.round {
border-radius: 17px;
}
.slider.round:before {
border-radius: 50%;
}
.tooltip {
position: relative;
border-bottom: 1px dotted black;
}
.tooltip:before {
content: attr(tooltip-data);
position: absolute;
width: 250px;
background-color: #006aff;
color: #fff;
text-align: center;
padding: 15px;
line-height: 1.1;
border-radius: 5px;
z-index: 1;
opacity: 0;
transition: opacity .5s;
bottom: 125%;
left: 50%;
margin-left: -60px;
font-size: 0.70em;
visibility: hidden;
}
.tooltip:after {
content: "";
position: absolute;
bottom: 75%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
opacity: 0;
transition: opacity .5s;
border-color: #000 transparent transparent transparent;
visibility: hidden;
}
.tooltip:hover:before,
.tooltip:hover:after {
opacity: 1;
visibility: visible;
}

View File

@@ -0,0 +1,36 @@
// ****************************************
// ********* Configure Amount *************
// ****************************************
function configureAmount() {
let amount = '';
if (currencyField.value === "XRP" || currencyField.value === "") {
if (amountField.value !== '') {
amount = amountField.value; // XRP amount should be a string of drops
} else {
amount = undefined;
}
} else if (currencyField.value !== "") {
amount = {
currency: currencyField.value,
issuer: issuerField.value,
value: amountField.value,
};
} else {
amount = undefined; // Or handle the case where no currency is provided
}
return amount;
}
// ****************************************
// ********* Configure Expiration *********
// ****************************************
function configureExpiration() {
let expiration = ""
var days = expirationField.value
let d = new Date()
d.setDate(d.getDate() + parseInt(days))
expiration = xrpl.isoTimeToRippleTime(d)
return expiration
} // End of configureExpiration()

View File

@@ -0,0 +1,328 @@
<html>
<head>
<title>Transfer NFTs</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src="transaction-support.js"></script>
<!-- <script src='send-xrp.js'></script> -->
<script src="transfer-nfts.js"></script>
<script src="mint-nfts.js"></script>
</head>
<body>
<h1>Transfer NFTs</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label
for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
<td rowspan="4" align="center">
<p>
<img id="nftImage"
src="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"
width="150" height="150">
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
<td>
<span class="tooltip" tooltip-data="URL to the stored NFT.">
<label for="nftURLfield">NFT URL</label>
</span>
<input type="text" id="nftURLfield" size="30"
placeholder="https://ipfs.io/ipfs/bafybeigjro2d2tc43bgv7e4sxqg7f5jga7kjizbk7nnmmyhmq35dtz6deq"></input>
<br />
<p id="error-message"></p>
</td>
</tr>
<tr>
<td align="right" >
<span class="tooltip" tooltip-data="Currency for the offer.">
<label for="currencyField">Currency</label>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
<td align="center" valign="top">
<button type="button" onClick="getNFTs()">Get NFTs</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuer of the currency used.">
<label for="issuerField">Issuer</label>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="createSellOffer()">Create Sell Offer</button>&nbsp;&nbsp;
<button type="button" onClick="createBuyOffer()">Create Buy Offer</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="acceptSellOffer()">Accept Sell Offer</button>&nbsp;&nbsp;
<button type="button" onClick="acceptBuyOffer()">Accept Buy Offer</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<label for="destinationField">Destination</label>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getOffers()" width="40">Get Offers</button>&nbsp;&nbsp;
<button type="button" onClick="cancelOffer()">Cancel Offer</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of days the offer is valid.">
<label for="expirationField">Expiration (days)</label>
</span>
</td>
<td>
<input type="text" id="expirationField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT ID code, used to identify the token after it's minted.">
<label for="nftIdField">NFT ID</label>
</span>
</td>
<td>
<input type="text" id="nftIdField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="NFT Offer ID code, used to identify an offer to sell or buy an NFT.">
<label for="nftOfferIdField">NFT Offer ID</label>
</span>
</td>
<td>
<input type="text" id="nftOfferIdField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Account address of the Owner of an NFT offered to sell or buy.">
<label for="nftOwnerField">NFT Owner Address</label>
</span>
</td>
<td>
<input type="text" id="nftOwnerField" size="40"></input>
</td>
</tr>
<tr valign="top">
<td colspan="2">
<p align="left">
<textarea id="resultField" cols="75" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br />
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const imageURLInput = document.getElementById('nftURLfield'); // Correct ID to nftURLfield
const displayImage = document.getElementById('nftImage');
const loadButton = document.getElementById('showNFTbutton');
const errorMessage = document.getElementById('error-message');
if (imageURLInput) {
imageURLInput.addEventListener('change', () => {
const newURL = imageURLInput.value;
displayImage.src = ''; // Clear previous image
errorMessage.style.display = 'none';
try {
new URL(newURL);
} catch (_) {
errorMessage.textContent = 'Invalid URL. Please enter a valid URL, including "https://" or "http://".';
errorMessage.style.display = 'block';
return;
}
displayImage.onload = () => {
// Image loaded. You might add a console log here, or update UI.
console.log(`Image loaded from: ${newURL}`);
};
displayImage.onerror = () => {
errorMessage.textContent = 'Error loading image from the provided URL.';
errorMessage.style.display = 'block';
displayImage.src = ''; // Clear the image on error
};
displayImage.src = newURL; // Load the image
});
}
});
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function () {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>

View File

@@ -0,0 +1,339 @@
// *********************************************************
// *************** Create Sell Offer ***********************
// *********************************************************
async function createSellOffer() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
let results = '\nCreating sell offer...';
resultField.value = results;
try {
const client = new xrpl.Client(getNet());
await client.connect();
try {
const destination = destinationField.value || undefined;
const expiration = expirationField.value ? configureExpiration() : undefined;
const transactionJson = {
TransactionType: "NFTokenCreateOffer",
Account: wallet.classicAddress,
NFTokenID: nftIdField.value,
Flags: 1,
};
const amount = configureAmount();
if (amount) { // Only add Amount if it's defined
transactionJson.Amount = amount;
} else {
console.warn("Amount is undefined. Sell offer might be invalid.");
results += "\nWarning: Amount is undefined. Sell offer might be invalid, unless you plan to give away the NFT.";
resultField.value = results;
}
if (expiration) {
transactionJson.Expiration = expiration;
}
if (destination) {
transactionJson.Destination = destination;
}
const tx = await client.submitAndWait(transactionJson, { wallet });
results += `\nSell offer created successfully!\nTransaction Hash: ${tx.result.hash}\nEngine Result: ${tx.result.engine_result}`;
resultField.value = results;
} finally {
client.disconnect();
}
} catch (error) {
console.error("Error creating sell offer:", error);
results = `\nError: ${error.message || error}`;
resultField.value = results;
}
}// End of createSellOffer()
// *******************************************************
// ***************** Create Buy Offer ********************
// *******************************************************
async function createBuyOffer() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
let net = getNet();
const client = new xrpl.Client(net);
await client.connect();
let results = '\n=== Connected. Creating buy offer. ===';
resultField.value = results;
try {
// Use the external configureAmount() function
let amount = configureAmount();
// Use the external configureExpiration() function
let expiration = configureExpiration(); // This will return a number or an empty string from the original logic
let transactionJson = {
"TransactionType": "NFTokenCreateOffer",
"Account": wallet.classicAddress,
"Owner": nftOwnerField.value,
"NFTokenID": nftIdField.value,
"Flags": 0, // Ensure no tfSellNFToken flag for a buy offer
};
// Only add Amount if it's defined (not undefined or an empty string)
if (amount !== undefined && amount !== '') {
transactionJson.Amount = amount;
} else {
results += "\nError: Amount field is required for a buy offer.";
resultField.value = results;
client.disconnect();
return;
}
if (destinationField.value !== '') {
transactionJson.Destination = destinationField.value;
}
// Only add Expiration if it's not an empty string
if (expiration > 0) {
transactionJson.Expiration = expiration;
}
const tx = await client.submitAndWait(transactionJson, { wallet: wallet });
results += "\n\n=== Sell Offers ===\n";
let nftSellOffers;
try {
nftSellOffers = await client.request({
method: "nft_sell_offers",
nft_id: nftIdField.value
});
} catch (err) {
nftSellOffers = "=== No sell offers. ===";
}
results += JSON.stringify(nftSellOffers, null, 2);
results += "\n\n=== Buy Offers ===\n";
let nftBuyOffers;
try {
nftBuyOffers = await client.request({
method: "nft_buy_offers",
nft_id: nftIdField.value
});
results += JSON.stringify(nftBuyOffers, null, 2);
} catch (err) {
results += "=== No buy offers. ===";
}
// Check transaction results -------------------------------------------------
results += "\n\n=== Transaction result:\n" +
JSON.stringify(tx.result.meta.TransactionResult, null, 2);
results += "\n\n=== Balance changes:\n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2);
resultField.value = results;
} catch (error) {
console.error('Error creating buy offer:', error);
results += "\n\n=== Error: " + error;
resultField.value = results;
} finally {
client.disconnect();
}
}// End of createBuyOffer()
// *******************************************************
// ******************** Cancel Offer *********************
// *******************************************************
async function cancelOffer() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = "\n=== Connected. Cancelling offer. ==="
resultField.value = results
const tokenOfferIDs = [nftOfferIdField.value]
// Prepare transaction -------------------------------------------------------
const transactionJson = {
"TransactionType": "NFTokenCancelOffer",
"Account": wallet.classicAddress,
"NFTokenOffers": tokenOfferIDs
}
// Submit transaction --------------------------------------------------------
const tx = await client.submitAndWait(transactionJson, { wallet })
results = "\n\n=== Sell Offers===\n"
let nftSellOffers
try {
nftSellOffers = await client.request({
method: "nft_sell_offers",
nft_id: nftIdField.value
})
} catch (err) {
nftSellOffers = '=== No sell offers. ===\n'
}
results += JSON.stringify(nftSellOffers, null, 2)
results += "\n\n=== Buy Offers ===\n"
let nftBuyOffers
try {
nftBuyOffers = await client.request({
method: "nft_buy_offers",
nft_id: nftIdField.value
})
} catch (err) {
nftBuyOffers = '=== No buy offers. ==='
}
results += JSON.stringify(nftBuyOffers, null, 2)
resultField.value += results
// Check transaction results -------------------------------------------------
results = "\n=== Transaction result:\n" +
JSON.stringify(tx.result.meta.TransactionResult, null, 2)
results += "\n\n=== Balance changes:\n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value += results
client.disconnect() // End of cancelOffer()
}
// *******************************************************
// ******************** Get Offers ***********************
// *******************************************************
async function getOffers() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = '\nConnected. Getting offers...'
resultField.value = results
// --- Sell Offers ---
results += '\n\n=== Sell Offers ===\n'
let nftSellOffers
try {
nftSellOffers = await client.request({
method: "nft_sell_offers",
nft_id: nftIdField.value
})
} catch (err) {
nftSellOffers = 'No sell offers found for this NFT ID.'
}
results += JSON.stringify(nftSellOffers, null, 2)
resultField.value = results
// --- Buy Offers ---
results = '\n\n=== Buy Offers ===\n'
let nftBuyOffers
try {
nftBuyOffers = await client.request({
method: "nft_buy_offers",
nft_id: nftIdField.value
})
} catch (err) {
// Log the actual error for debugging
nftBuyOffers = 'No buy offers found for this NFT ID.' // More descriptive
}
results += JSON.stringify(nftBuyOffers, null, 2) // Append the JSON string
resultField.value += results // Update the display with buy offers
client.disconnect()
}// End of getOffers()
// *******************************************************
// ****************** Accept Sell Offer ******************
// *******************************************************
async function acceptSellOffer() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let net = getNet()
const client = new xrpl.Client(net)
try {
await client.connect()
let results = '\n=== Connected. Accepting sell offer. ===\n\n'
resultField.value = results
// Prepare transaction -------------------------------------------------------
const transactionJson = {
"TransactionType": "NFTokenAcceptOffer",
"Account": wallet.classicAddress,
"NFTokenSellOffer": nftOfferIdField.value,
}
// Submit transaction --------------------------------------------------------
const tx = await client.submitAndWait(transactionJson, { wallet: wallet })
const nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress
})
// Check transaction results -------------------------------------------------
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
results += '=== Transaction result:\n'
results += JSON.stringify(tx.result.meta.TransactionResult, null, 2)
results += '\n=== Balance changes:'
results += JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
results += JSON.stringify(nfts, null, 2)
resultField.value += results
} catch (error) {
console.error('Error accepting sell offer:', error)
resultField.value = `Error: ${error.message || error}`
} finally {
client.disconnect()
}
}// End of acceptSellOffer()
// *******************************************************
// ******************* Accept Buy Offer ******************
// *******************************************************
async function acceptBuyOffer() {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value);
let net = getNet();
const client = new xrpl.Client(net);
let results = '\n=== Connected. Accepting buy offer. ==='; // Declare results locally
try {
await client.connect();
resultField.value = results; // Update UI after connection
// Prepare transaction -------------------------------------------------------
const transactionJson = {
"TransactionType": "NFTokenAcceptOffer",
"Account": wallet.classicAddress,
"NFTokenBuyOffer": nftOfferIdField.value
};
// Submit transaction --------------------------------------------------------
const tx = await client.submitAndWait(transactionJson, { wallet: wallet });
const nfts = await client.request({
method: "account_nfts",
account: wallet.classicAddress
});
results += JSON.stringify(nfts, null, 2);
resultField.value = results;
// Check transaction results -------------------------------------------------
results += "\n\nTransaction result:\n" +
JSON.stringify(tx.result.meta.TransactionResult, null, 2);
results += "\nBalance changes:\n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2);
xrpBalanceField.value = (await client.getXrpBalance(wallet.address));
resultField.value = results;
} catch (error) {
console.error('Error in acceptBuyOffer:', error); // Log the full error
results = `\n=== Error accepting buy offer: ${error.message || 'Unknown error'} ===`;
resultField.value = results;
} finally {
if (client && client.isConnected()) {
client.disconnect();
}
}
} // End of acceptBuyOffer()

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,8 @@ Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tu
To get test accounts: To get test accounts:
1. Open `create-offers.html` in a browser. 1. Open `create-offers.html` in a browser.
2. Get test accounts. 2. Choose your preferred test network (**Devnet** or **Testnet**).
3. Get test accounts.
1. If you copied the gathered information from another tutorial: 1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field. 1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**. 2. Click **Distribute Account Info**.