diff --git a/src/slp/nft1.js b/src/slp/nft1.js index 0e52919..13b4ffe 100644 --- a/src/slp/nft1.js +++ b/src/slp/nft1.js @@ -1,5 +1,11 @@ /* This library wraps the slp-mdm library to generate the OP_RETURN for NFT1 tokens. + + NFT Group tokens (Parents) are generated and minted. They are like amorphous + NFTs; like stem cells that haven't specialized yet. + + NFT 'Children' are the 'real' NFTs. They are created by burning an NFT Group + (Parent) token. */ const Address = require("./address") @@ -21,8 +27,8 @@ class TokenType1 { TransactionBuilder.setAddress(addy) } - // Parent NFT - generateNFTParentOpReturn(configObj) { + // New NFT Group 'Parent' + newNFTGroupOpReturn(configObj) { try { // TODO: Add input validation. @@ -49,6 +55,46 @@ class TokenType1 { } } + // Mint additional NFT Group 'Parent' tokens. + mintNFTGroupOpReturn(tokenUtxos, mintQty, destroyBaton = false) { + try { + // Throw error if input is not an array. + if (!Array.isArray(tokenUtxos)) + throw new Error(`tokenUtxos must be an array.`) + + // Loop through the tokenUtxos array and find the minting baton. + let mintBatonUtxo + for (let i = 0; i < tokenUtxos.length; i++) { + if (tokenUtxos[i].utxoType === "minting-baton") + mintBatonUtxo = tokenUtxos[i] + } + + // Throw an error if the minting baton could not be found. + if (!mintBatonUtxo) + throw new Error(`Minting baton could not be found in tokenUtxos array.`) + + const tokenId = mintBatonUtxo.tokenId + + if (!tokenId) + throw new Error(`tokenId property not found in mint-baton UTXO.`) + + // Signal that the baton should be passed or detroyed. + let batonVout = 2 + if (destroyBaton) batonVout = null + + const script = slpMdm.NFT1.Group.mint( + tokenId, + batonVout, + new slpMdm.BN(mintQty) + ) + + return script + } catch (err) { + // console.log(`Error in generateMintOpReturn()`) + throw err + } + } + generateNFTChildOpReturn(configObj) { try { // TODO: Add input validation. diff --git a/test/unit/slp-nft1.js b/test/unit/slp-nft1.js index 1e2045d..46ac0f6 100644 --- a/test/unit/slp-nft1.js +++ b/test/unit/slp-nft1.js @@ -38,8 +38,8 @@ describe("#SLP NFT1", () => { sandbox.restore() }) - describe("#generateNFTOpReturn", () => { - it("should generate NFT Genesis OP_RETURN code", async () => { + describe("#newNFTGroupOpReturn", () => { + it("should generate new NFT Group OP_RETURN code", () => { const configObj = { name: "SLP Test Token", ticker: "SLPTEST", @@ -47,7 +47,62 @@ describe("#SLP NFT1", () => { documentHash: "" } - const result = await bchjs.SLP.NFT1.generateNFTParentOpReturn(configObj) + const result = bchjs.SLP.NFT1.newNFTGroupOpReturn(configObj) + // console.log(`result: `, result) + + assert.equal(Buffer.isBuffer(result), true) + }) + }) + + describe("#mintNFTGroupOpReturn", () => { + it("should generate NFT Group Mint OP_RETURN code", () => { + const tokenUtxoData = [ + { + txid: + "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", + vout: 2, + value: "546", + height: 638207, + confirmations: 63, + satoshis: 546, + utxoType: "minting-baton", + transactionType: "mint", + tokenId: + "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", + tokenType: 129, + tokenTicker: "NFTP", + tokenName: "NFT Parent", + tokenDocumentUrl: "FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + }, + { + txid: + "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76", + vout: 1, + value: "546", + height: 638207, + confirmations: 63, + satoshis: 546, + utxoType: "token", + tokenQty: 10, + transactionType: "mint", + tokenId: + "680967b3f6fe080dbca8dbd370665bd29742e3490db24e2f28d08b424511807e", + tokenType: 129, + tokenTicker: "NFTP", + tokenName: "NFT Parent", + tokenDocumentUrl: "FullStack.cash", + tokenDocumentHash: "", + decimals: 0, + mintBatonVout: 2, + isValid: true + } + ] + + const result = bchjs.SLP.NFT1.mintNFTGroupOpReturn(tokenUtxoData, 10) // console.log(`result: `, result) assert.equal(Buffer.isBuffer(result), true) @@ -55,7 +110,7 @@ describe("#SLP NFT1", () => { }) describe("#generateNFTChildOpReturn", () => { - it("should generate NFT Genesis OP_RETURN code", async () => { + it("should generate NFT Genesis OP_RETURN code", () => { const configObj = { name: "SLP Test Token", ticker: "SLPTEST", @@ -63,7 +118,7 @@ describe("#SLP NFT1", () => { documentHash: "" } - const result = await bchjs.SLP.NFT1.generateNFTChildOpReturn(configObj) + const result = bchjs.SLP.NFT1.generateNFTChildOpReturn(configObj) // console.log(`result: `, result) assert.equal(Buffer.isBuffer(result), true) diff --git a/test/unit/slp-tokentype1.js b/test/unit/slp-tokentype1.js index 3a92707..b0edd6d 100644 --- a/test/unit/slp-tokentype1.js +++ b/test/unit/slp-tokentype1.js @@ -39,7 +39,7 @@ describe("#SLP TokenType1", () => { }) describe("#generateSendOpReturn", () => { - it("should generate send OP_RETURN code", async () => { + it("should generate send OP_RETURN code", () => { // Mock UTXO. const tokenUtxos = [ { @@ -62,10 +62,7 @@ describe("#SLP TokenType1", () => { } ] - const result = await bchjs.SLP.TokenType1.generateSendOpReturn( - tokenUtxos, - 1 - ) + const result = bchjs.SLP.TokenType1.generateSendOpReturn(tokenUtxos, 1) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.hasAllKeys(result, ["script", "outputs"]) @@ -74,7 +71,7 @@ describe("#SLP TokenType1", () => { }) describe("#generateBurnOpReturn", () => { - it("should generate burn OP_RETURN code", async () => { + it("should generate burn OP_RETURN code", () => { // Mock UTXO. const tokenUtxos = [ { @@ -97,10 +94,7 @@ describe("#SLP TokenType1", () => { } ] - const result = await bchjs.SLP.TokenType1.generateBurnOpReturn( - tokenUtxos, - 1 - ) + const result = bchjs.SLP.TokenType1.generateBurnOpReturn(tokenUtxos, 1) // console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: `, result) @@ -109,7 +103,7 @@ describe("#SLP TokenType1", () => { }) describe("#generateGenesisOpReturn", () => { - it("should generate genesis OP_RETURN code", async () => { + it("should generate genesis OP_RETURN code", () => { const configObj = { name: "SLP Test Token", ticker: "SLPTEST", @@ -119,15 +113,13 @@ describe("#SLP TokenType1", () => { initialQty: 10 } - const result = await bchjs.SLP.TokenType1.generateGenesisOpReturn( - configObj - ) + const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) // console.log(`result: `, result) assert.equal(Buffer.isBuffer(result), true) }) - it("should work if user does not specify doc hash", async () => { + it("should work if user does not specify doc hash", () => { const configObj = { name: "SLP Test Token", ticker: "SLPTEST", @@ -136,9 +128,7 @@ describe("#SLP TokenType1", () => { initialQty: 10 } - const result = await bchjs.SLP.TokenType1.generateGenesisOpReturn( - configObj - ) + const result = bchjs.SLP.TokenType1.generateGenesisOpReturn(configObj) // console.log(`result: `, result) assert.equal(Buffer.isBuffer(result), true) @@ -146,9 +136,9 @@ describe("#SLP TokenType1", () => { }) describe("#generateMintOpReturn", () => { - it("should throw error if tokenUtxos is not an array.", async () => { + it("should throw error if tokenUtxos is not an array.", () => { try { - await bchjs.SLP.TokenType1.generateMintOpReturn({}, 100) + bchjs.SLP.TokenType1.generateMintOpReturn({}, 100) assert.equal(true, false, "Unexpected result.") } catch (err) { @@ -160,7 +150,7 @@ describe("#SLP TokenType1", () => { } }) - it("should throw error if minting baton is not in UTXOs.", async () => { + it("should throw error if minting baton is not in UTXOs.", () => { try { const utxos = [ { @@ -184,7 +174,7 @@ describe("#SLP TokenType1", () => { } ] - await bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) assert.equal(true, false, "Unexpected result.") } catch (err) { @@ -196,7 +186,7 @@ describe("#SLP TokenType1", () => { } }) - it("should throw error if tokenId is not included in minting-baton UTXO.", async () => { + it("should throw error if tokenId is not included in minting-baton UTXO.", () => { try { const utxos = [ { @@ -217,7 +207,7 @@ describe("#SLP TokenType1", () => { } ] - await bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) assert.equal(true, false, "Unexpected result.") } catch (err) { @@ -229,7 +219,7 @@ describe("#SLP TokenType1", () => { } }) - it("should throw error if decimals is not included in minting-baton UTXO.", async () => { + it("should throw error if decimals is not included in minting-baton UTXO.", () => { try { const utxos = [ { @@ -251,7 +241,7 @@ describe("#SLP TokenType1", () => { } ] - await bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) + bchjs.SLP.TokenType1.generateMintOpReturn(utxos, 100) assert.equal(true, false, "Unexpected result.") } catch (err) { @@ -263,7 +253,7 @@ describe("#SLP TokenType1", () => { } }) - it("should generate genesis OP_RETURN code", async () => { + it("should generate genesis OP_RETURN code", () => { tokenUtxo = [ { txid: @@ -285,10 +275,7 @@ describe("#SLP TokenType1", () => { } ] - const result = await bchjs.SLP.TokenType1.generateMintOpReturn( - tokenUtxo, - 100 - ) + const result = bchjs.SLP.TokenType1.generateMintOpReturn(tokenUtxo, 100) // console.log(`result: `, result) assert.equal(Buffer.isBuffer(result), true)