arrow-left 강좌 허브
레슨4: NFT를 발행하고 소각하기
예상 독서 시간

25분

난이도

중급

주요 요점

XRP Ledger에서 NFT를 발행하고 소각하는 방법을 알아보세요. XRPL.js 라이브러리 코드를 실험하려면 강의의 코드 샌드박스를 사용하세요.

레슨4

NFT를 발행하고 소각하기

XRP Ledger에서 NFT를 발행하고 소각하세요.

이 강의에서는 다음을 배웁니다.

  1. 대체 불가능한 토큰 (NFT)를 발행합니다 .
  2. 계정에 있는 기존 NFT 목록을 가져옵니다.
  3. NFT를 삭제(burn) 합니다.

코드를 편집해 보세요: mint-and-burn-nfts.js

아래의 대화형 코드 예제는 모든 XRP Ledger 네트워크( 테스트넷 , 개발넷 또는 메인넷) 와 함께 사용할 수 있습니다. 빌드할 때, 다른 XRP Ledger 네트워크나 추가 XRP Ledger 네트워크를 선택할 수 있도록 코드를 수정할 수 있습니다.

 

계정 받기

  1. 코드 샌드박스를 엽니다. 새 창에서 “Edit on Codepen”을 클릭하는 것을 권장합니다.
  2. 테스트 계정을 받습니다.
    1. 기존 NFT-Devnet 계정 시드가 있는 경우:
      1. Seed 필드에 계정 시드를 붙여 넣습니다.
      2. “Get Accounts from Seeds”를 클릭합니다.
    2. 기존 NFT-Devnet 계정이 없는 경우:
      1. “Get New Standby Account”를 클릭합니다.
      2. “Get New Operational Account”를 클릭합니다.

NFToken 발행

대체 불가능 토큰을 발행하기 위해:

  1. “Flag” 필드를 설정합니다. 테스트 목적으로 값을 8로 설정하는 것을 추천합니다. 이렇게 하면 tsTransferable 플래그가 설정되어 NFToken 객체를 다른 계정으로 전송할 수 있습니다. 그렇지 않으면 NFToken 객체를 발급 계정으로만 다시 전송할 수 있습니다. NFToken을 발행하는 데 사용할 수 있는 모든 플래그에 대한 정보는 NFToken Mint를 참조하세요.
  2. “Token URL”을 입력합니다. 이는 NFToken 개체와 관련된 데이터 또는 메타데이터를 가리키는 URI입니다. 고유한 샘플 URI가 없는 경우 제공된 샘플 URI를 사용할 수 있습니다.
  3. “Transfer Fee” 입력합니다. 이는 NFToken의 미래 판매 수익의 백분율로, 원래 제작자에게 반환됩니다. 이는 0-50000을 포함하는 값으로, 0.001% 단위로 0.000%에서 50.000% 사이의 전송률을 허용합니다. NFToken을 전송할 수 있도록 “Flags” 필드를 설정하지 않은 경우 이 필드를 0으로 설정합니다.
  4. “Mint Token”을 클릭합니다.

토큰 얻기

“Get Tokens”를 클릭하면 해당 계정이 소유한 NFT 토큰 목록을 가져옵니다.

토큰 소각

NFToken의 현재 소유자는 언제든지 NFToken 객체를 파기(burn) 할 수 있습니다.

NFToken을 영구적으로 파기하기 위해:

  1. “Token ID” 를 입력합니다.
  2. “Burn Token” 을 클릭합니다.

JavaScript 코드 연습: ripplex3-mint-nfts.js

mintToken()

XRPL에 연결하고 계정 지갑을 가져옵니다.
트랜잭션을 정의합니다.
hex URI를 convertStringToHex 유틸리티를 사용하여 문자열로 변환합니다.
NFToken을 제3자에게 양도 가능하게 하려면 Flags 필드를 8로 설정합니다.
Transfer Fee는 0에서 50000까지의 값으로, 0.000%에서 50.000%까지의 로열티를 0.001 단위로 설정하는 데 사용됩니다.
TokenTaxon은 필수 값입니다. 발행자가 정의한 임의의 값입니다. 필드에 사용할 용도가 없으면 0으로 설정할 수 있습니다.
트랜잭션을 전송하고 응답을 기다립니다.
계정이 소유한 NFT 목록을 요청합니다.
결과를 보고합니다.
XRPL과 연결을 끊습니다.
// *******************************************************
// ********************** Mint Token *********************
// *******************************************************
      
async function mintToken() {
  results = 'Connecting to ' + getNet() + '....'
  standbyResultField.value = results
  let net = getNet()
  const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
  const client = new xrpl.Client(net)
  await client.connect()
  results += 'nConnected. Minting NFToken.'
  standbyResultField.value = results
      
  // Note that you must convert the token URL to a hexadecimal 
  // value for this transaction.
  // ------------------------------------------------------------------------
  const transactionBlob = {
    "TransactionType": "NFTokenMint",
    "Account": standby_wallet.classicAddress,
    "URI": xrpl.convertStringToHex(standbyTokenUrlField.value),

    "Flags": parseInt(standbyFlagsField.value),

    "TransferFee": parseInt(standbyTransferFeeField.value),

    "NFTokenTaxon": 0 //Required, but if you have no use for it, set to zero.
  }

  // ----------------------------------------------------- Submit signed blob 
  const tx = await client.submitAndWait(transactionBlob, { wallet: standby_wallet} )
  const nfts = await client.request({
    method: "account_nfts",
    account: standby_wallet.classicAddress  
  })
        
  // ------------------------------------------------------- Report results
  results += 'nnTransaction result: '+ tx.result.meta.TransactionResult
  results += 'nnnfts: ' + JSON.stringify(nfts, null, 2)
  standbyBalanceField.value = 
    (await client.getXrpBalance(standby_wallet.address))
  standbyResultField.value = results    
  client.disconnect()
} //End of mintToken()

getTokens()

XRPL에 연결하고 계정 지갑을 가져옵니다.
계정이 소유한 NFT 목록을 요청합니다.
결과를 보고합니다.
XRPL과 연결을 끊습니다.
// *******************************************************
// ******************* Get Tokens ************************
// *******************************************************
      
async function getTokens() {
  const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
  let net = getNet()
  const client = new xrpl.Client(net)
  results = 'Connecting to ' + net + '...'
  standbyResultField.value = results
  await client.connect()
  results += 'nConnected. Getting NFTokens...'
  standbyResultField.value = results
  const nfts = await client.request({
    method: "account_nfts",
    account: standby_wallet.classicAddress  
  })
  results += 'nNFTs:n ' + JSON.stringify(nfts,null,2)
  standbyResultField.value = results
  client.disconnect()
} //End of getTokens()

burnToken()

XRPL에 연결하고 계정 지갑을 가져옵니다.
트랜잭션을 정의합니다.
트랜잭션을 제출하고 결과를 기다립니다.
클라이언트가 소유한 NFTokens 목록을 요청합니다.
결과를 보고합니다.
XRPL과 연결을 끊습니다.
// *******************************************************
// ********************* Burn Token **********************
// *******************************************************
      
async function burnToken() {
  const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
  let net = getNet()
  const client = new xrpl.Client(net)
  results = 'Connecting to ' + net + '...'
  standbyResultField.value = results
  await client.connect()
  results += 'nConnected. Burning NFToken...'
  standbyResultField.value = results

  // ------------------------------------------------------- Prepare transaction
  const transactionBlob = {
    "TransactionType": "NFTokenBurn",
    "Account": standby_wallet.classicAddress,
    "NFTokenID": standbyTokenIdField.value
  }

  //---------------------------------- Submit transaction and wait for the results
  const tx = await client.submitAndWait(transactionBlob,{wallet: standby_wallet})
  const nfts = await client.request({
    method: "account_nfts",
    account: standby_wallet.classicAddress  
  })
  results += 'nTransaction result: '+ tx.result.meta.TransactionResult
  results += 'nBalance changes: ' +
  JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
  standbyResultField.value = results
  standbyBalanceField.value = 
    (await client.getXrpBalance(standby_wallet.address))
  results += 'nNFTs: n' + JSON.stringify(nfts,null,2)
  standbyResultField.value = results
  client.disconnect()
}// End of burnToken()

상호 트랜잭션 oPmintToken(), oPgetTokens() and oPburnToken()

// **********************************************************************
// ****** Reciprocal Transactions ***************************************
// **********************************************************************
   
// *******************************************************
// ************** Operational Mint Token *****************
// *******************************************************
      
async function oPmintToken() {
  results = 'Connecting to ' + getNet() + '....'
  operationalResultField.value = results
  let net = getNet()
  const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
  const client = new xrpl.Client(net)
  await client.connect()
  results += 'nConnected. Minting NFToken.'
  operationalResultField.value = results

  // Note that you must convert the token URL to a hexadecimal 
  // value for this transaction.
  // ------------------------------------------------------------------------
  const transactionBlob = {
    "TransactionType": 'NFTokenMint',
    "Account": operational_wallet.classicAddress,
    "URI": xrpl.convertStringToHex(operationalTokenUrlField.value),
    "Flags": parseInt(operationalFlagsField.value),
    "TransferFee": parseInt(operationalTransferFeeField.value),
    "NFTokenTaxon": 0 //Required, but if you have no use for it, set to zero.
  }
      
  // ----------------------------------------------------- Submit signed blob 
  const tx = await client.submitAndWait(transactionBlob, { wallet: operational_wallet} )
  const nfts = await client.request({
    method: "account_nfts",
    account: operational_wallet.classicAddress  
  })
        
  // ------------------------------------------------------- Report results
  results += 'nnTransaction result: '+ tx.result.meta.TransactionResult
  results += 'nnnfts: ' + JSON.stringify(nfts, null, 2)
  operationalBalanceField.value = 
    (await client.getXrpBalance(operational_wallet.address))
  operationalResultField.value = results    

  client.disconnect()
} //End of oPmintToken
      
// *******************************************************
// ************** Operational Get Tokens *****************
// *******************************************************

async function oPgetTokens() {
  const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
  let net = getNet()
  const client = new xrpl.Client(net)
  results = 'Connecting to ' + getNet() + '...'
 operationalResultField.value = results
 await client.connect()
  results += 'nConnected. Getting NFTokens...'
  operationalResultField.value = results
  const nfts = await client.request({
    method: "account_nfts",
    account: operational_wallet.classicAddress  
  })
  results += 'nNFTs:n ' + JSON.stringify(nfts,null,2)
  operationalResultField.value = results
  client.disconnect()
} //End of oPgetTokens
      
// *******************************************************
// ************* Operational Burn Token ******************
// *******************************************************
      
async function oPburnToken() {
  const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
  let net = getNet()
  const client = new xrpl.Client(net)
  results = 'Connecting to ' + getNet() + '...'
  operationalResultField.value = results
  await client.connect()
  results += 'nConnected. Burning NFToken...'
  operationalResultField.value = results
      
  // ------------------------------------------------------- Prepare transaction
  const transactionBlob = {
    "TransactionType": "NFTokenBurn",
    "Account": operational_wallet.classicAddress,
    "NFTokenID": operationalTokenIdField.value
  }
      
  //-------------------------------------------------------- Submit signed blob
  const tx = await client.submitAndWait(transactionBlob,{wallet: operational_wallet})
  const nfts = await client.request({
    method: "account_nfts",
    account: operational_wallet.classicAddress  
  })
  results += 'nTransaction result: '+ tx.result.meta.TransactionResult
  results += 'nBalance changes: ' +
    JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
  operationalResultField.value = results
  operationalBalanceField.value = 
    (await client.getXrpBalance(operational_wallet.address))
  operationalBalanceField.value = 
    (await client.getXrpBalance(operational_wallet.address))
  results += 'nNFTs: n' + JSON.stringify(nfts,null,2)
  operationalResultField.value = results
  client.disconnect()
}
// End of oPburnToken()

HTML 폼 미리보기: 3.mint-nfts.html

볼드체 글씨는 새로운 기능을 지원하기 위해 양식에 변경 사항이 있음을 나타냅니다.

<html>
  <head>
    <title>Token Test Harness</title>
    <link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
    <style>
       body{font-family: "Work Sans", sans-serif;padding: 20px;background: #fafafa;}
       h1{font-weight: bold;}
       input, button {padding: 6px;margin-bottom: 8px;}
       button{font-weight: bold;font-family: "Work Sans", sans-serif;}
       td{vertical-align: top;padding-right:10px;}
    </style>
    <script src='https://unpkg.com/xrpl@2.2.3'></script>
    <script>
      if (typeof module !== "undefined") {
        const xrpl = require('xrpl')
      }
    </script>
  </head>
  
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->

  <body>
    <h1>Token Test Harness</h1>
    <form id="theForm">
      Choose your ledger instance:  
      <input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233" >
      <label for="testnet">Testnet</label>
      
      <input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
      <label for="devnet">Devnet</label>
      <br/><br/>
      <button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button>
      <br/>
      <textarea id="seeds" cols="40" rows= "2"></textarea>
      <br/><br/>
      <table>
        <tr valign="top">
          <td>
            <button type="button" onClick="getAccount('standby')">Get New Standby Account</button>
            <table>
              <tr valign="top">
                <td align="left">
                  Standby Account<br/> <input type="text" id="standbyAccountField" size="30" />
                </td>
                <td></td>
              </tr>
              <tr>
                <td align="left">
                  Public Key<br/>
                  <input type="text" id="standbyPubKeyField" size="30"></input>                                                 </td>
                <td align="left">
                  Private Key<br/>
                  <input type="text" id="standbyPrivKeyField" size="30"></input>
                </td>
              </tr>
              <tr>
                <td align="left">
                  Seed <br/>
                  <input type="text" id="standbySeedField" size="30"></input>
                  <br>
                </td>
                <td align="left">
                  XRP Balance <br/>
                  <input type="text" id="standbyBalanceField" size="30"></input>
                </td>
            </tr>
            <tr>
                <td align="left">
                  Amount<br/>
                  <input type="text" id="standbyAmountField" size="30"></input>
                </td>
                <td align="left">
                  Destination Account <br/>
                <input type="text" id="standbyDestinationField" size="30"></input>
                </td>
              </tr>
              <tr valign="top">
                <td><button type="button" onClick="configureAccount('standby',document.querySelector('#standbyDefault').checked)">Configure Account</button><br/>
                  <input type="checkbox" id="standbyDefault" checked="true"/>
                  <label for="standbyDefault">Allow Rippling</label>
                </td>

                <td>
                  Currency<br/>
                  <input type="text" id="standbyCurrencyField" size="30" value="USD"></input>
                </td>
              </tr>
              <tr>
                <td colspan=2>NFToken URL<br/>
                  <input type="text" id="standbyTokenUrlField"
                  value = "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi" size="70"/>
                </td>
              </tr>
              <tr>
                <td colspan=2>Flags<br/>
                  <input type="text" id="standbyFlagsField" value="1" size="10"/></td>
              </tr>
              <tr>
                <td colspan=2>NFToken ID<br/>
                  <input type="text" id="standbyTokenIdField" value="" size="70"/></td>
              </tr>
              <tr>
                <td colspan=2>Transfer Fee<br/>
                  <input type="text" id="standbyTransferFeeField" value="" size="70"/></td>
              </tr>
              <tr>
                <td colspan=2>
                  <p align="right">
                    <button type="button" onClick="sendXRP()">Send XRP ↓</button>
                    <button type="button" onClick="createTrustline()">Create TrustLine</button>
                    <button type="button" onClick="sendCurrency()">Send Currency</button>
                    <button type="button" onClick="getBalances()">Get Balances</button>
                    <br/>
                    <button type="button" onClick="mintToken()">Mint NFToken</button>
                    <button type="button" onClick="getTokens()">Get NFTokens</button>
                    <button type="button" onClick="burnToken()">Burn NFToken</button>
                  </p>
                </td>
              </tr>
            </table>
          </td>
          <td valign="top">
            <textarea id="standbyResultField" cols="60" rows="20" ></textarea>
          </td>
        </tr>
      </table>
      <br/><br/>
      <table>
        <tr valign="top">
          <td>
            <button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
            <table>
              <tr valign="top">
                <td align="left">
                  Operational Account<br/> <input type="text" id="operationalAccountField" size="30" />
                </td>
                <td></td>
              </tr>
              <tr>
                <td align="left">Public Key<br/>
                  <input type="text" id="operationalPubKeyField" size="30" />                                                 </td>
                <td align="left">
                  Private Key<br/>
                  <input type="text" id="operationalPrivKeyField" size="30"></input>
                </td>
              </tr>
              <tr>
                <td align="left">
                  Seed <br/>
                  <input type="text" id="operationalSeedField" size="30"></input>
                  <br>
                </td>
                <td align="left">
                  XRP Balance <br/>
                  <input type="text" id="operationalBalanceField" size="30" />
                </td>
            </tr>
            <tr>
                <td align="left">
                  Amount<br/>
                  <input type="text" id="operationalAmountField" size="30" />
                </td>
                <td align="left">
                  Destination Account <br/>
                <input type="text" id="operationalDestinationField" size="30" />
                </td>
              </tr>
              <tr valign="top">
                <td><button type="button" onClick="configureAccount('operational',document.querySelector('#operationalDefault').checked)">Configure Account</button><br/>
                  <input type="checkbox" id="operatoinalDefault" checked="true"/>
                  <label for="operationalDefault">Allow Rippling</label>
                </td>

                <td>
                  Currency<br/>
                  <input type="text" id="operationalCurrencyField" size="30" value="USD"></input>
                </td>
              </tr>
                            <tr>
                <td colspan=2>NFToken URL<br/>
                  <input type="text" id="operationalTokenUrlField"
                  value = "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi" size="70"/>
                </td>
              </tr>
              <tr>
                <td colspan=2>Flags<br/>
                  <input type="text" id="operationalFlagsField" value="1" size="10"/></td>
              </tr>
              <tr>
                <td colspan=2>NFToken ID<br/>
                  <input type="text" id="operationalTokenIdField" value="" size="70"/></td>
              </tr>
              <tr>
                <td colspan=2>Transfer Fee<br/>
                  <input type="text" id="operationalTransferFeeField" value="" size="70"/></td>
              </tr>
              <tr>
                <td colspan=2>
                  <p align="right">
                    <button type="button" onClick="oPsendXRP()">Send XRP ↑</button>
                    <button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
                    <button type="button" onClick="oPsendCurrency()">Send Currency</button>
                    <button type="button" onClick="getBalances()">Get Balances</button>
                    <br/>
                    <button type="button" onClick="oPmintToken()">Mint NFToken</button>
                    <button type="button" onClick="oPgetTokens()">Get NFTokens</button>
                    <button type="button" onClick="oPburnToken()">Burn NFToken</button>
                  </p>
                </td>
              </tr>
            </table>
          </td>
          <td>
            <textarea id="operationalResultField" cols="60" rows="20" ></textarea>
          </td>
        </tr>
      </table>
    </form>
  </body>
  <script src='ripplex1-send-xrp.js' async></script>
  <script src='ripplex2-send-currency.js' async></script>
  <script src='ripplex3-mint-nfts.js' async></script>
</html>

Additional resources

수업 종료

지금까지 배운 내용을 테스트할 시간입니다!

1. NFTokenTaxon ID는 무엇에 사용되나요?
2. 5000의 TransferFee는 어떤 수수료를 뜻하나요?
3. NFToken의 URI는 어떤 정보를 저장하기 위한 것인가요?