WIP - refactoring the NFT tutorials, code, and screenshots

This commit is contained in:
Dennis Dawson
2025-05-23 09:54:46 -07:00
parent a74231ab0d
commit 08f2fde9e4
27 changed files with 3385 additions and 616 deletions

View File

@@ -0,0 +1,207 @@
// ******************************************************
// ************* 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
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,330 @@
<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 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,91 @@
// *******************************************************
// ************ 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 configureAccount()
// *******************************************************
// **************** 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
// This version adds the "Issuer" field.
// ------------------------------------------------------------------------
const tx_json = {
"TransactionType": "NFTokenMint",
"Account": wallet.classicAddress,
"URI": xrpl.convertStringToHex(nftURLfield.value),
"Flags": parseInt(flagsField.value),
"TransferFee": parseInt(transferFeeField.value),
"Issuer": issuerField.value,
"NFTokenTaxon": 0 //Required, but if you have no use for it, set to zero.
}
// ----------------------------------------------------- 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))
// The line below seems redundant if the previous line already updates resultField.value
// resultField.value = results
} 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,292 @@
// *******************************************************
// ****************** 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,
};
if (amountField.value) {
const amount = currencyField.value === "XRP" ?
xrpl.xrpToDrops(amountField.value) : // Use xrpToDrops for XRP
{
currency: currencyField.value,
issuer: issuerField.value,
value: amountField.value,
};
transactionParams.Amount = amount;
}
if (expirationField.value) {
const days = parseInt(expirationField.value, 10);
if (isNaN(days) || days <= 0) {
results += `\nWarning: Invalid expiration days for NFT ${i+1}. Skipping expiration for this NFT.`;
resultField.value = results;
} else {
const expirationDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000);
transactionParams.Expiration = xrpl.isoTimeToRippleTime(expirationDate);
}
}
if (destinationField.value) {
// Basic XRP address validation
if (!xrpl.isValidAddress(destinationField.value)) {
results += `\nWarning: Invalid destination address for NFT ${i+1}. Skipping destination for this NFT.`;
resultField.value = results;
} else {
transactionParams.Destination = destinationField.value;
}
}
try {
const mintTx = await client.submit(transactionParams, {
wallet: wallet
});
results += `\nNFT ${i+1} minted successfully.`;
mintedNFTsCount++;
console.log(mintTx.result.nfts)
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,324 @@
// *********************************************************
// *************** 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;
}
console.log("Creating Sell Offer Transaction:", JSON.stringify(transactionJson, null, 2));
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();
console.log("Amount:", amount);
// 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;
}
console.log("Buy Offer Transaction JSON:\n" + JSON.stringify(transactionJson, null, 2));
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. ==="
}
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) {
nftBuyOffers = '=== No buy offers. ==='
}
// Check transaction results -------------------------------------------------
results += "\n=== Transaction result:\n" +
JSON.stringify(tx.result.meta.TransactionResult, null, 2)
results += "\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)
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
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)
await client.connect()
let results = '\n=== Connected. Accepting buy offer. ==='
resultField.value = results
// 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
client.disconnect()
}// End of acceptBuyOffer()

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,7 @@
---
html: transfer-nfts-using-javascript.html
parent: nfts-using-javascript.html
seo:
description: Use a JavaScript test harness to send XRP, trade currencies, and mint and trade NFTs.
labels:
- Quickstart
- Tokens
- Non-fungible Tokens, NFTs
---
@@ -19,43 +16,52 @@ This example shows how to:
5. Get a list of offers for a particular NFT.
6. Cancel an offer.
[![Quickstart form with NFT transfer fields](/docs/img/quickstart13.png)](/docs/img/quickstart13.png)
[![Transfer NFTs Test Harness](../../../img/mt-transfer-nft-1-empty-form.png)](../../../img/mt-transfer-nft-1-empty-form.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) archive to try each of the samples in your own browser.
You can download the [NFT Modular Tutorials](../../../../_code-samples/nft-modular-tutorials/nft-modular-tutorials.zip) archive to try each of the samples in your own browser.
# Usage
## Get Accounts
1. Open `4.transfer-nfts.html` in a browser.
2. Choose your ledger instance (**Testnet** or **Devnet**).
1. Open `transfer-nfts.html` in a browser.
2. Choose your preferred test network (**Devnet** or **Testnet**).
3. Get test accounts.
1. If you have existing test account seeds
1. Paste account seeds in the **Seeds** field.
2. Click **Get Accounts from Seeds**.
2. If you do not have test account seeds:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Form with account information](/docs/img/quickstart14.png)](/docs/img/quickstart14.png)
[![Form with account information](../../../img/mt-transfer-nft-2-accounts-loaded.png)](../../../img/mt-transfer-nft-2-accounts-loaded.png)
## Create a Sell Offer
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/mZlznG9ZJNY?si=TFTtl_MPb7WPtHHV" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To create an NFT sell offer:
To create a NFT sell offer:
1. Enter the **Amount** of the sell offer in drops (millionths of an XRP).
2. Set the **Flags** field to _1_.
3. Enter the **NFT ID** of the NFT you want to sell.
4. Optionally, enter a number of days until **Expiration**.
1. Enter the **Amount** of the sell offer.
1. If using _XRP_, enter the **Amount** in drops (millionths of an XRP; for example, 20000000).
2. If using issued currency, enter the **Currency** code, **Issuer** account address, and the **Amount**.
2. Optionally, include a **Destination** account address. If present, only that account will have permission to accept the sell offer.
3. Optionally, enter a number of days until **Expiration** of the offer.
4. Enter the **NFT ID** of the NFT you want to sell.
5. Click **Create Sell Offer**.
The important piece of information in the response is the NFT Offer Index, labeled as `nft_offer_index`, which you use to accept the sell offer.
[![NFT Sell Offer](../../../img/mt-transfer-nfts-3-create-sell-offer.png)](../../../img/mt-transfer-nfts-3-create-sell-offer.png)
[![NFT Sell Offer](/docs/img/quickstart15.png)](/docs/img/quickstart15.png)
## Get Offers
To list the buy and sell offers associated with an NFT:
1. Enter the **NFT ID**.
2. Click **Get Offers**.
The key piece of information is the NFT Offer ID (labeled as `nft_offer_index`), which you use to accept a sell or buy offer.
[![Get offers](../../../img/mt-transfer-nfts-4-get-offers.png)](../../../img/mt-transfer-nfts-4-get-offers.png)
## Accept Sell Offer
@@ -66,39 +72,33 @@ To accept an available sell offer:
1. Enter the **NFT Offer Index** (labeled as `nft_offer_index` in the token offer results. This is different from the `NFTokenID`.)
2. Click **Accept Sell Offer**.
[![Accept Sell Offer](/docs/img/quickstart16.png)](/docs/img/quickstart16.png)
[![Accept Sell Offer](../../../img/mt-transfer-nfts-5-accept-sell-offer.png)](../../../img/mt-transfer-nfts-5-accept-sell-offer.png)
## Create a Buy Offer
You can offer to buy a NFT from another account.
You can offer to buy an NFT from another account.
To create an offer to buy a NFT:
To create an offer to buy an NFT:
1. Enter the **Amount** of your offer.
2. Enter the **NFT ID**.
3. Enter the owners account string in the **Owner** field.
4. Optionally enter the number of days until **Expiration**.
1. If paying XRP, enter the **Amount** of XRP in drops (1000000 equals 1 XRP).
2. If paying an issued currency, enter the **Currency**, **Issuer**, and **Amount**.
2. Optionally enter the number of days until **Expiration**.
3. Enter the **NFT ID**.
4. Enter the owners account address in the **NFT Owner Address** field.
5. Click **Create Buy Offer**.
[![NFT Buy Offer](/docs/img/quickstart17.png)](/docs/img/quickstart17.png)
## Accept a Buy Offer
To accept an offer to buy a NFT:
To accept an offer to buy an NFT:
1. Enter the **NFT Offer Index** (the `nft_offer_index` of the NFT buy offer).
3. Click **Accept Buy Offer**.
[![Accept Buy Offer](/docs/img/quickstart18.png)](/docs/img/quickstart18.png)
## Get Offers
To list the buy and sell offers associated with a NFT:
1. Enter the **NFT ID**.
2. Click **Get Offers**.
[![Get offers](/docs/img/quickstart19.png)](/docs/img/quickstart19.png)
## Cancel Offer
To cancel a buy or sell offer that you have created:

View File

@@ -28,7 +28,8 @@ Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tu
To get test accounts:
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. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.