--- html: transfer-nftokens.html parent: xrpl-quickstart.html blurb: Use a JavaScript test harness to send XRP, trade currencies, and mint and trade NFTokens. labels: - Quickstart - Tokens - Non-fungible Tokens, NFTs --- # 4. Transfer NFTokens {% include '_snippets/nfts-disclaimer.md' %} This example shows how to: 1. Create NFToken Sell Offers. 2. Create NFToken Buy Offers. 3. Accept NFToken Sell Offers. 4. Accept NFToken Buy Offers. 5. Get a list of offers for a particular NFToken. 6. Cancel an offer.  You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/content/_code-samples/quickstart/js/quickstart.zip) archive to try each of the samples in your own browser. # Usage ## Get Accounts 1. Open `4.transfer-nftokens.html` in a browser. 2. Get test accounts. 1. If you have existing NFT-Devnet account seeds 1. Paste account seeds in the **Seeds** field. 2. Click **Get Accounts from Seeds**. 2. If you do not have NFT-Devnet account seeds: 3. Visit the [XRP Testnet Faucet](https://xrpl.org/xrp-testnet-faucet.html) page. 4. Click **Generate NFT-Devnet credentials**. 5. Copy the account **Secret**. 6. Paste the secret in a persistent location, such as a notepad, and press return. 7. Click **Generate NFT-Devnet credentials** to create a second account. 8. Copy the account **Secret**. 9. Paste the secret in the persistent location. 10. Copy both secrets, separated by a return. 11. Paste them in the **Account** **Seeds** field. 12. Click **Get Accounts from Seeds**.  ## Create a Sell Offer To create a NFToken sell offer: 1. Enter the **Amount** of the sell offer in drops (millionths of an XRP). 2. Set the **Flags** field to _1_. 3. Enter the **Token ID** of the NFToken you want to sell. 4. Click **Create Sell Offer**. The important piece of information in the response is the Token Offer Index, labeled as _Index,_ which is used to accept the sell offer.  ## Accept Sell Offer Once a sell offer is available, another account can opt to accept the offer and purchase the NFToken. To accept an available sell offer: 1. Enter the **Token Offer Index** (labeled as _Index_ in the token offer results. This is not the same as the Token ID). 2. Click **Accept Sell Offer**.  ## Create a Buy Offer You can offer to buy a NFToken from another account. To create an offer to buy a NFToken: 1. Enter the **Amount** of your offer. 2. Enter the **Token ID**. 3. Enter the owner’s account string in the **Owner** field. 4. Click **Create Buy Offer**.  ## Accept a Buy Offer To accept an offer to buy a NFToken: 1. Enter the **Token Offer ID** (the **Index** of the token offer). 2. Enter the buyer’s account string in the **Owner** field (the buyer is the owner of the token offer). 3. Click **Accept Buy Offer**.  ## Get Offers Click **Get Offers** to get the buy and sell offers associated with a particular account.  ## Cancel Offer To cancel a buy or sell offer that you have created: 1. Enter the **Token Offer ID**. 2. Click **Cancel Offer**.  # Code Walkthrough You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/content/_code-samples/quickstart/js/quickstart.zip) archive to try each of the samples in your own browser. ## Create Sell Offer ``` // ******************************************************* // ****************** Create Sell Offer ****************** // ******************************************************* ``` Connect to the ledger and get the wallet accounts. ``` async function createSellOffer() { const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results += '\nConnected. Creating sell offer...' document.getElementById('standbyResultField').value = results ``` Define the transaction. The _Flags_ value of 1 indicates that this transaction is a sell offer. ``` const transactionBlob = { "TransactionType": "NFTokenCreateOffer", "Account": standby_wallet.classicAddress, "TokenID": standbyTokenIdField.value, "Amount": standbyAmountField.value, "Flags": parseInt(standbyFlagsField.value) } ``` Submit the transaction and wait for the results. ``` const tx = await client.submitAndWait(transactionBlob,{wallet: standby_wallet}) ``` ``` results += '\n\n***Sell Offers***\n' ``` Request the list of sell offers for the token. ``` let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftSellOffers = "No sell offers." } results += JSON.stringify(nftSellOffers,null,2) results += '\n\n***Buy Offers***\n' ``` Request the list of buy offers for the token. ``` let nftBuyOffers try { nftBuyOffers = await client.request({ method: "nft_buy_offers", tokenid: standbyTokenIdField.value }) } catch (err) { results += 'No buy offers.' } results += JSON.stringify(nftBuyOffers,null,2) ``` Report the results of the transaction. ``` results += '\n\nTransaction result:\n' + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += '\n\nBalance changes:\n' + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) ``` Get the current XRP balances for the operational and standby accounts. ``` document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) document.getElementById('standbyBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of createSellOffer() } ``` ## Create Buy Offer ``` // ******************************************************* // ***************** Create Buy Offer ******************** // ******************************************************* async function createBuyOffer() { ``` Get the account wallets and connect to the ledger. ``` const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) let results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results = '\nConnected. Creating buy offer...' document.getElementById('standbyResultField').value = results ``` Define the transaction. Setting the _Flags_ value to _null_ indicates that this is a buy offer. ``` const transactionBlob = { "TransactionType": "NFTokenCreateOffer", "Account": standby_wallet.classicAddress, "Owner": standbyOwnerField.value, "TokenID": standbyTokenIdField.value, "Amount": standbyAmountField.value, "Flags": null } ``` Submit the transaction and wait for the results. ``` const tx = await client.submitAndWait(transactionBlob,{wallet: standby_wallet}) ``` Request the list of sell offers for the token. ``` results += "\n\n***Sell Offers***\n" let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftSellOffers = "No sell offers." } results += JSON.stringify(nftSellOffers,null,2) ``` Request the list of buy offers for the token. ``` results += "\n\n***Buy Offers***\n" let nftBuyOffers try { nftBuyOffers = await client.request({ method: "nft_buy_offers", tokenid: standbyTokenIdField.value }) } catch (err) { results += "No buy offers." } results += JSON.stringify(nftBuyOffers,null,2) ``` Report the results of the transaction. ``` results += "\n\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += "\n\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of operationalCreateBuyOffer() } ``` ## Cancel Offer ``` // ******************************************************* // ******************** Cancel Offer ********************* // ******************************************************* async function cancelOffer() { ``` Get the standby wallet and connect to the ledger. ``` const wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const client = new xrpl.Client("wss://xls20-sandbox.rippletest.net:51233") results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results += "\nConnected. Cancelling offer..." document.getElementById('standbyResultField').value = results ``` Insert the token offer index as a new array. This example destroys one offer at a time. In practice you could implement the function to accept multiple token offer index values and destroy all of the token offers in one operation. ``` const tokenOfferIDs = [standbyTokenOfferIndexField.value] ``` Define the transaction. ``` const transactionBlob = { "TransactionType": "NFTokenCancelOffer", "Account": wallet.classicAddress, "TokenOffers": tokenOfferIDs } ``` Submit the transaction and wait for the results. ``` const tx = await client.submitAndWait(transactionBlob,{wallet}) ``` Request the list of sell offers for the token. ``` results += "\n\n***Sell Offers***\n" let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftSellOffers = "No sell offers." } results += JSON.stringify(nftSellOffers,null,2) ``` Request the list of buy offers for the token. ``` results += "\n\n***Buy Offers***\n" let nftBuyOffers try { nftBuyOffers = await client.request({ method: "nft_buy_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftBuyOffers = "No buy offers." } results += JSON.stringify(nftBuyOffers,null,2) ``` Report the transaction results. ``` results += "\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += "\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of cancelOffer() } ``` ## Get Offers ``` // ******************************************************* // ******************** Get Offers *********************** // ******************************************************* async function getOffers() { ``` Get the standby account wallet and connect to the ledger. ``` const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results += '\nConnected. Getting offers...' document.getElementById('standbyResultField').value = results ``` Request the list of sell offers for the token. ``` results += '\n\n***Sell Offers***\n' let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftSellOffers = 'No sell offers.' } results += JSON.stringify(nftSellOffers,null,2) document.getElementById('standbyResultField').value = results ``` Request the list of buy offers for the token. ``` results += '\n\n***Buy Offers***\n' let nftBuyOffers try { nftBuyOffers = await client.request({ method: "nft_buy_offers", tokenid: standbyTokenIdField.value }) } catch (err) { nftBuyOffers = 'No buy offers.' } results += JSON.stringify(nftBuyOffers,null,2) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of getOffers() } ``` ## Accept Sell Offer ``` // ******************************************************* // ****************** Accept Sell Offer ****************** // ******************************************************* async function acceptSellOffer() { ``` Get the account wallets and connect to the ledger. ``` const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results += '\nConnected. Accepting sell offer...\n\n' document.getElementById('standbyResultField').value = results ``` Define the transaction. ``` const transactionBlob = { "TransactionType": "NFTokenAcceptOffer", "Account": standby_wallet.classicAddress, "SellOffer": standbyTokenOfferIndexField.value, } ``` Submit the transaction and wait for the results. ``` const tx = await client.submitAndWait(transactionBlob,{wallet: standby_wallet}) ``` Request the list of NFTs for the standby account. ``` const nfts = await client.request({ method: "account_nfts", account: standby_wallet.classicAddress }) ``` Get the balances for both accounts. ``` document.getElementById('standbyBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) ``` Report the transaction results. ``` results += 'Transaction result:\n' results += JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += '\nBalance changes:' results += JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) results += JSON.stringify(nfts,null,2) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of acceptSellOffer() } ``` ## Accept Buy Offer ``` // ******************************************************* // ******************* Accept Buy Offer ****************** // ******************************************************* async function acceptBuyOffer() { ``` Get the account wallets and connect to the ledger. ``` const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('standbyResultField').value = results await client.connect() results += '\nConnected. Accepting buy offer...' document.getElementById('standbyResultField').value = results ``` Prepare the transaction. ``` const transactionBlob = { "TransactionType": "NFTokenAcceptOffer", "Account": standby_wallet.classicAddress, "BuyOffer": standbyTokenOfferIndexField.value } ``` Submit the transaction and wait for the results. ``` const tx = await client.submitAndWait(transactionBlob,{wallet: standby_wallet}) ``` Request the current list of NFTs for the standby account. ``` const nfts = await client.request({ method: "account_nfts", account: standby_wallet.classicAddress }) results += JSON.stringify(nfts,null,2) document.getElementById('standbyResultField').value = results ``` Report the transaction result. ``` result += "\n\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) result += "\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) ``` Request the XRP balance for both accounts. ``` document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) document.getElementById('standbyBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('standbyResultField').value = results ``` Disconnect from the ledger. ``` client.disconnect() // End of acceptBuyOffer() } ``` ## Reciprocal Transactions These functions duplicate the functions of the standby account for the operational account. See the corresponding standby account function for walkthrough information. ``` // ******************************************************* // *********** Operational Create Sell Offer ************* // ******************************************************* async function oPcreateSellOffer() { const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('operationalResultField').value = results await client.connect() results += '\nConnected. Creating sell offer...' document.getElementById('operationalResultField').value = results // Prepare transaction ------------------------------------------------------- const transactionBlob = { "TransactionType": "NFTokenCreateOffer", "Account": operational_wallet.classicAddress, "TokenID": operationalTokenIdField.value, "Amount": operationalAmountField.value, "Flags": parseInt(operationalFlagsField.value) } // Submit transaction -------------------------------------------------------- const tx = await client.submitAndWait(transactionBlob,{wallet: operational_wallet}) results += '\n\n***Sell Offers***\n' let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: operationalTokenIdField.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", tokenid: operationalTokenIdField.value }) } catch (err) { results += 'No buy offers.' } results += JSON.stringify(nftBuyOffers,null,2) // Check transaction results ------------------------------------------------- results += '\n\nTransaction result:\n' + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += '\n\nBalance changes:\n' + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) document.getElementById('standbyBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('operationalResultField').value = results client.disconnect() } // End of oPcreateSellOffer() // ******************************************************* // ************** Operational Create Buy Offer *********** // ******************************************************* async function oPcreateBuyOffer() { const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) let results = 'Connecting to ' + net + '...' document.getElementById('operationalResultField').value = results await client.connect() results += '\nConnected. Creating buy offer...' document.getElementById('operationalResultField').value = results // Prepare transaction ------------------------------------------------------- const transactionBlob = { "TransactionType": "NFTokenCreateOffer", "Account": operational_wallet.classicAddress, "Owner": operationalOwnerField.value, "TokenID": operationalTokenIdField.value, "Amount": operationalAmountField.value, "Flags": null } // Submit transaction -------------------------------------------------------- const tx = await client.submitAndWait(transactionBlob,{wallet: operational_wallet}) results +="\n\n***Sell Offers***\n" let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: operationalTokenIdField.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", tokenid: operationalTokenIdField.value }) } catch (err) { nftBuyOffers = "No buy offers." } results += JSON.stringify(nftBuyOffers,null,2) // Check transaction results ------------------------------------------------- results +="\n\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += "\n\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) client.disconnect() // End of oPcreateBuyOffer() } // ******************************************************* // ************* Operational Cancel Offer **************** // ******************************************************* async function oPcancelOffer() { const wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) const client = new xrpl.Client("wss://xls20-sandbox.rippletest.net:51233") results = 'Connecting to ' + getNet() + '...' document.getElementById('operationalResultField').value = results await client.connect() results += "\nConnected. Cancelling offer..." document.getElementById('operationalResultField').value = results const tokenOfferIDs = [operationalTokenOfferIndexField.value] // Prepare transaction ------------------------------------------------------- const transactionBlob = { "TransactionType": "NFTokenCancelOffer", "Account": wallet.classicAddress, "TokenOffers": tokenOfferIDs } // Submit transaction -------------------------------------------------------- const tx = await client.submitAndWait(transactionBlob,{wallet}) results += "\n\n***Sell Offers***\n" let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: operationalTokenIdField.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", tokenid: operationalTokenIdField.value }) } catch (err) { nftBuyOffers = "No buy offers." } results += JSON.stringify(nftBuyOffers,null,2) // Check transaction results ------------------------------------------------- results += "\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += "\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) document.getElementById('operationalResultField').value = results client.disconnect() // End of oPcancelOffer() } // ******************************************************* // **************** Operational Get Offers *************** // ******************************************************* async function oPgetOffers() { const standby_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('operationalResultField').value = results await client.connect() results += '\nConnected. Getting offers...' results += '\n\n***Sell Offers***\n' let nftSellOffers try { nftSellOffers = await client.request({ method: "nft_sell_offers", tokenid: operationalTokenIdField.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", tokenid: operationalTokenIdField.value }) } catch (err) { nftBuyOffers = 'No buy offers.' } results += JSON.stringify(nftBuyOffers,null,2) document.getElementById('operationalResultField').value = results client.disconnect() // End of oPgetOffers() } // ******************************************************* // *************** Operational Accept Sell Offer ********* // ******************************************************* async function oPacceptSellOffer() { const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('operationalResultField').value = results await client.connect() results = '\nConnected. Accepting sell offer...\n\n' document.getElementById('operationalResultField').value = results // Prepare transaction ------------------------------------------------------- const transactionBlob = { "TransactionType": "NFTokenAcceptOffer", "Account": operational_wallet.classicAddress, "SellOffer": operationalTokenOfferIndexField.value, } // Submit transaction -------------------------------------------------------- const tx = await client.submitAndWait(transactionBlob,{wallet: operational_wallet}) const nfts = await client.request({ method: "account_nfts", account: operational_wallet.classicAddress }) // Check transaction results ------------------------------------------------- document.getElementById('standbyBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) results += 'Transaction result:\n' results += JSON.stringify(tx.result.meta.TransactionResult, null, 2) results += '\nBalance changes:' results += JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) results += JSON.stringify(nfts,null,2) document.getElementById('operationalResultField').value = results client.disconnect() // End of acceptSellOffer() } // ******************************************************* // ********* Operational Accept Buy Offer **************** // ******************************************************* async function oPacceptBuyOffer() { const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value) const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value) let net = getNet() const client = new xrpl.Client(net) results = 'Connecting to ' + getNet() + '...' document.getElementById('operationalResultField').value = results await client.connect() results += '\nConnected. Accepting buy offer...' document.getElementById('operationalResultField').value = results // Prepare transaction ------------------------------------------------------- const transactionBlob = { "TransactionType": "NFTokenAcceptOffer", "Account": operational_wallet.classicAddress, "BuyOffer": operationalTokenOfferIndexField.value } // Submit transaction -------------------------------------------------------- const tx = await client.submitAndWait(transactionBlob,{wallet: operational_wallet}) const nfts = await client.request({ method: "account_nfts", account: operational_wallet.classicAddress }) results += JSON.stringify(nfts,null,2) document.getElementById('operationalResultField').value = results // Check transaction results ------------------------------------------------- result += "\n\nTransaction result:\n" + JSON.stringify(tx.result.meta.TransactionResult, null, 2) result += "\nBalance changes:\n" + JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2) document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(operational_wallet.address)) document.getElementById('operationalBalanceField').value = (await client.getXrpBalance(standby_wallet.address)) document.getElementById('operationalResultField').value = results client.disconnect() // End of acceptBuyOffer() } ``` ## 4.transfer-nfts.html Update the form with fields and buttons to support the new functions. ```