Merge pull request #3122 from XRPLF/NFT_refactor
Refactor NFT code samples and update the tutorials and screenshots
@@ -32,8 +32,9 @@ async function getAccount() {
|
||||
}
|
||||
finally {
|
||||
// Disconnect from the client
|
||||
if (client && client.isConnected()) {
|
||||
await client.disconnect();
|
||||
}
|
||||
} }
|
||||
} // End of getAccount()
|
||||
|
||||
async function getNewAccount1() {
|
||||
@@ -78,8 +79,9 @@ async function getAccountFromSeed(my_seed) {
|
||||
}
|
||||
finally {
|
||||
// Disconnect from the client
|
||||
if (client && client.isConnected()) {
|
||||
await client.disconnect();
|
||||
}
|
||||
} }
|
||||
} // End of getAccountFromSeed()
|
||||
|
||||
// *****************************************************
|
||||
@@ -169,8 +171,9 @@ async function getXrpBalance() {
|
||||
}
|
||||
finally {
|
||||
// Disconnect from the client
|
||||
if (client && client.isConnected()) {
|
||||
await client.disconnect();
|
||||
}
|
||||
} }
|
||||
} // End of getXrpBalance()
|
||||
|
||||
// *******************************************************
|
||||
@@ -202,8 +205,9 @@ async function getTokenBalance() {
|
||||
}
|
||||
finally {
|
||||
// Disconnect from the client
|
||||
if (client && client.isConnected()) {
|
||||
await client.disconnect();
|
||||
}
|
||||
} }
|
||||
} // End of getTokenBalance()
|
||||
|
||||
// *******************************************************
|
||||
|
||||
209
_code-samples/nft-modular-tutorials/account-support.js
Normal 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()
|
||||
340
_code-samples/nft-modular-tutorials/authorized-minter.html
Normal 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>
|
||||
|
||||
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
|
||||
<label for="dn">Devnet</label>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
97
_code-samples/nft-modular-tutorials/authorized-minter.js
Normal 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()
|
||||
|
||||
320
_code-samples/nft-modular-tutorials/batch-minting.html
Normal 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>
|
||||
|
||||
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
|
||||
<label for="dn">Devnet</label>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
272
_code-samples/nft-modular-tutorials/batch-minting.js
Normal 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()
|
||||
291
_code-samples/nft-modular-tutorials/broker-nfts.html
Normal 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>
|
||||
|
||||
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
|
||||
<label for="dn">Devnet</label>
|
||||
|
||||
<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>
|
||||
44
_code-samples/nft-modular-tutorials/broker-nfts.js
Normal 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()
|
||||
313
_code-samples/nft-modular-tutorials/mint-nfts.html
Normal 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>
|
||||
|
||||
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
|
||||
<label for="dn">Devnet</label>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
141
_code-samples/nft-modular-tutorials/mint-nfts.js
Normal 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()
|
||||
153
_code-samples/nft-modular-tutorials/modular-tutorials.css
Normal 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;
|
||||
}
|
||||
BIN
_code-samples/nft-modular-tutorials/nft-modular-tutorials.zip
Normal file
36
_code-samples/nft-modular-tutorials/transaction-support.js
Normal 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()
|
||||
328
_code-samples/nft-modular-tutorials/transfer-nfts.html
Normal 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>
|
||||
|
||||
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
|
||||
<label for="dn">Devnet</label>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
339
_code-samples/nft-modular-tutorials/transfer-nfts.js
Normal 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()
|
||||
BIN
docs/img/mt-auth-minter-1-empty-form.png
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
docs/img/mt-auth-minter-2-authorize-minter.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
docs/img/mt-auth-minter-3-mint-other.png
Normal file
|
After Width: | Height: | Size: 372 KiB |
BIN
docs/img/mt-batch-mint-1-empty-form.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
docs/img/mt-batch-mint-2-batch-mint.png
Normal file
|
After Width: | Height: | Size: 352 KiB |
BIN
docs/img/mt-batch-mint-3-get-batch-mint.png
Normal file
|
After Width: | Height: | Size: 388 KiB |
BIN
docs/img/mt-broker-nfts-1-empty-form.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
docs/img/mt-broker-nfts-2-broker-form-with-accounts.png
Normal file
|
After Width: | Height: | Size: 317 KiB |
BIN
docs/img/mt-broker-nfts-3-get-offers.png
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
docs/img/mt-broker-nfts-4-broker-sale.png
Normal file
|
After Width: | Height: | Size: 391 KiB |
BIN
docs/img/mt-mint-token-1-empty-form.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
docs/img/mt-mint-token-2-accounts.png
Normal file
|
After Width: | Height: | Size: 297 KiB |
BIN
docs/img/mt-mint-token-3-mint-token.png
Normal file
|
After Width: | Height: | Size: 385 KiB |
BIN
docs/img/mt-mint-token-4-get-tokens.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
BIN
docs/img/mt-mint-token-5-burn-token.png
Normal file
|
After Width: | Height: | Size: 336 KiB |
BIN
docs/img/mt-transfer-nft-1-empty-form.png
Normal file
|
After Width: | Height: | Size: 254 KiB |
BIN
docs/img/mt-transfer-nft-2-accounts-loaded.png
Normal file
|
After Width: | Height: | Size: 318 KiB |
BIN
docs/img/mt-transfer-nfts-3-create-sell-offer.png
Normal file
|
After Width: | Height: | Size: 366 KiB |
BIN
docs/img/mt-transfer-nfts-4-get-offers.png
Normal file
|
After Width: | Height: | Size: 419 KiB |
BIN
docs/img/mt-transfer-nfts-5-accept-sell-offer.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
BIN
docs/img/mt-transfer-nfts-6-create-buy-offer.png
Normal file
|
After Width: | Height: | Size: 371 KiB |
BIN
docs/img/mt-transfer-nfts-7-accept-buy-offer.png
Normal file
|
After Width: | Height: | Size: 365 KiB |
BIN
docs/img/mt-transfer-nfts-8-cancel-offer.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
@@ -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**.
|
||||
|
||||