diff --git a/README.md b/README.md index 7adf3ea..2240516 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Here are two YouTube walk-through videos to help you get started: ### Quick Links - [npm Library](https://www.npmjs.com/package/@chris.troutner/bch-js) -- [Documentation](https://bchjs.cash/bch-js/index.html) +- [Documentation](https://bchjs.fullstack.cash/) - [Examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples) - [api.fullstack.cash](https://api.fullstack.cash) - The REST API this library talks to by default. - [FullStack.cash Account](https://fullstack.cash/login) - Get your API key to unlock increased rate limits. @@ -81,8 +81,7 @@ This library sets itself apart from BITBOX with the following features: - [ECMAScript 2017 standard JavaScript](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017) used instead of TypeScript. Works natively with node.js v10 or higher. -- [slp-sdk](https://github.com/Bitcoin-com/slp-sdk) features are integrated -into this library too. Currently only simple token sending is supported. If you need SLP token functionality, you should use [slp-sdk](https://github.com/Bitcoin-com/slp-sdk), [slp-cli-wallet](https://www.npmjs.com/package/slp-cli-wallet). +- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully support all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs). - [Semantic Release](https://github.com/semantic-release/semantic-release) for continuous delivery using semantic versioning. @@ -98,7 +97,7 @@ dependencies in case they are ever inaccessible from GitHub or npm. ## Documentation: Full documentation for this library can be found here: -- [Documentation](https://bchjs.cash/bch-js/index.html) +- [Documentation](https://bchjs.fullstack.cash/) Original documentation on BITBOX is available at: diff --git a/src/slp/utils.js b/src/slp/utils.js index 5254515..d7d2454 100644 --- a/src/slp/utils.js +++ b/src/slp/utils.js @@ -838,10 +838,12 @@ class Utils { * @apiDescription Hydrate a UTXO with SLP token metadata. * * Expects an array of UTXO objects as input. Returns an array of equal size. + * Returns UTXO data hydrated with token information. * If the - * UTXO does not belong to a SLP transaction, it will return false. + * UTXO does not belong to a SLP transaction, it will return an `isValid` property + * set to false. * If the UTXO is part of an SLP transaction, it will return the UTXO object - * with additional SLP information attached. An isValid property will be included. + * with additional SLP information attached. An `isValid` property will be included. * If its value is true, the UTXO is a valid SLP UTXO. If the value is null, * then SLPDB has not yet processed that txid and validity has not been confirmed. * @@ -948,7 +950,9 @@ class Utils { // console.log(`error: `, err) // An error will be thrown if the txid is not SLP. // Mark as false and continue the loop. - outAry.push(false) + // outAry.push(false) + utxo.isValid = false + outAry.push(utxo) continue } @@ -964,7 +968,9 @@ class Utils { utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens. ) { // Can safely be marked as false. - outAry[i] = false + // outAry[i] = false + utxo.isValid = false + outAry[i] = utxo } // If this is a valid SLP UTXO, then return the decoded OP_RETURN data. @@ -999,7 +1005,9 @@ class Utils { utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens. ) { // Can safely be marked as false. - outAry[i] = false + // outAry[i] = false + utxo.isValid = false + outAry[i] = utxo } // If UTXO passes validation, then return formatted token data. @@ -1040,17 +1048,14 @@ class Utils { // const voutMatch = slpData.spendData.filter(x => utxo.vout === x.vout) // console.log(`voutMatch: ${JSON.stringify(voutMatch, null, 2)}`) - // If there are no vout matches, the UTXO can safely be marked as false. - // if (voutMatch.length === 0) { - // outAry[i] = false - // } - // Figure out what token quantity is represented by this utxo. const tokenQty = slpData.amounts[utxo.vout - 1] // console.log(`tokenQty: `, tokenQty) if (!tokenQty) { - outAry[i] = false + // outAry[i] = false + utxo.isValid = false + outAry[i] = utxo } // If UTXO passes validation, then return formatted token data. @@ -1081,7 +1086,7 @@ class Utils { } // Finally, validate the SLP txid with SLPDB. - if (outAry[i]) { + if (outAry[i].tokenType) { const isValid = await this.validateTxid(utxo.txid) // console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`) diff --git a/test/integration/slp.js b/test/integration/slp.js index 52965ae..b55275d 100644 --- a/test/integration/slp.js +++ b/test/integration/slp.js @@ -255,7 +255,7 @@ describe(`#SLP`, () => { const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert.equal(data[0], false, "Change UTXO marked as false.") + assert.equal(data[0].isValid, false, "Change UTXO marked as false.") assert.property(data[1], "txid") assert.property(data[1], "vout") @@ -373,7 +373,7 @@ describe(`#SLP`, () => { assert.isArray(result) assert.equal(result.length, 2) - assert.equal(result[0], false) + assert.equal(result[0].isValid, false) assert.equal(result[1].isValid, true) }) @@ -404,7 +404,7 @@ describe(`#SLP`, () => { assert.isArray(result) assert.equal(result.length, 2) - assert.equal(result[0], false) + assert.equal(result[0].isValid, false) assert.equal(result[1].isValid, true) }) @@ -434,8 +434,8 @@ describe(`#SLP`, () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert.isArray(data) - assert.equal(false, data[0]) - assert.equal(false, data[1]) + assert.equal(data[0].isValid, false) + assert.equal(data[1].isValid, false) }) }) diff --git a/test/unit/slp-utils.js b/test/unit/slp-utils.js index 82f7aba..aed1812 100644 --- a/test/unit/slp-utils.js +++ b/test/unit/slp-utils.js @@ -725,7 +725,15 @@ describe("#SLP Utils", () => { const data = await slp.Utils.tokenUtxoDetails(utxos) // console.log(`data: ${JSON.stringify(data, null, 2)}`) - assert2.equal(data[0], false, "Change UTXO marked as false.") + // assert2.equal(data[0], false, "Change UTXO marked as false.") + assert2.property(data[0], "txid") + assert2.property(data[0], "vout") + assert2.property(data[0], "amount") + assert2.property(data[0], "satoshis") + assert2.property(data[0], "height") + assert2.property(data[0], "confirmations") + assert2.property(data[0], "isValid") + assert2.equal(data[0].isValid, false) assert2.property(data[1], "txid") assert2.property(data[1], "vout") @@ -953,8 +961,19 @@ describe("#SLP Utils", () => { assert2.isArray(result) assert2.equal(result.length, 2) - assert2.equal(result[0], false) + + assert2.property(result[0], "txid") + assert2.property(result[0], "vout") + assert2.property(result[0], "value") + assert2.property(result[0], "satoshis") + assert2.property(result[0], "height") + assert2.property(result[0], "confirmations") + assert2.property(result[0], "isValid") + assert2.equal(result[0].isValid, false) + assert.equal(result[1].isValid, true) + assert.equal(result[1].utxoType, "token") + assert.equal(result[1].transactionType, "send") }) it("should handle problematic utxos", async () => { @@ -1022,11 +1041,22 @@ describe("#SLP Utils", () => { assert2.isArray(result) assert2.equal(result.length, 2) - assert2.equal(result[0], false) + + assert2.property(result[0], "txid") + assert2.property(result[0], "vout") + assert2.property(result[0], "amount") + assert2.property(result[0], "satoshis") + assert2.property(result[0], "height") + assert2.property(result[0], "confirmations") + assert2.property(result[0], "isValid") + assert2.equal(result[0].isValid, false) + assert2.equal(result[1].isValid, true) + assert.equal(result[1].utxoType, "token") + assert.equal(result[1].transactionType, "send") }) - it("should return false for BCH-only UTXOs", async () => { + it("should return isValid=false for BCH-only UTXOs", async () => { // Mock live network calls sandbox @@ -1054,12 +1084,26 @@ describe("#SLP Utils", () => { } ] - const data = await slp.Utils.tokenUtxoDetails(utxos) - // console.log(`data: ${JSON.stringify(data, null, 2)}`) + const result = await slp.Utils.tokenUtxoDetails(utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) - assert2.isArray(data) - assert2.equal(false, data[0]) - assert2.equal(false, data[1]) + assert2.isArray(result) + + assert2.property(result[0], "txid") + assert2.property(result[0], "vout") + assert2.property(result[0], "amount") + assert2.property(result[0], "satoshis") + assert2.property(result[0], "confirmations") + assert2.property(result[0], "isValid") + assert2.equal(result[0].isValid, false) + + assert2.property(result[1], "txid") + assert2.property(result[1], "vout") + assert2.property(result[1], "amount") + assert2.property(result[1], "satoshis") + assert2.property(result[1], "confirmations") + assert2.property(result[1], "isValid") + assert2.equal(result[1].isValid, false) }) it("should decode a Genesis transaction", async () => { @@ -1121,10 +1165,17 @@ describe("#SLP Utils", () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) + assert2.equal(data[0].utxoType, "token") assert2.equal(data[0].tokenQty, 100) + assert2.equal(data[0].isValid, true) + assert2.equal(data[0].tokenType, 1) + assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(false, data[2]) + assert2.equal(data[1].isValid, true) + assert2.equal(data[1].tokenType, 1) + + assert2.equal(data[2].isValid, false) }) it("should decode a Mint transaction", async () => { @@ -1212,10 +1263,17 @@ describe("#SLP Utils", () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) + assert2.equal(data[0].utxoType, "token") assert2.equal(data[0].tokenQty, 100) + assert2.equal(data[0].isValid, true) + assert2.equal(data[0].tokenType, 1) + assert2.equal(data[1].utxoType, "minting-baton") - assert2.equal(false, data[2]) + assert2.equal(data[1].isValid, true) + assert2.equal(data[1].tokenType, 1) + + assert2.equal(data[2].isValid, false) }) it("should decode a NFT Group Genesis transaction", async () => { @@ -1299,13 +1357,16 @@ describe("#SLP Utils", () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) - assert2.equal(data[0], false) + + assert2.equal(data[0].isValid, false) assert2.equal(data[1].utxoType, "minting-baton") + assert2.equal(data[1].isValid, true) assert2.equal(data[1].tokenType, 129) assert2.equal(data[2].utxoType, "token") assert2.equal(data[2].tokenType, 129) + assert2.equal(data[1].isValid, true) }) it("should decode a NFT Group Mint transaction", async () => { @@ -1398,13 +1459,16 @@ describe("#SLP Utils", () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) - assert2.equal(data[0], false) + + assert2.equal(data[0].isValid, false) assert2.equal(data[1].utxoType, "minting-baton") assert2.equal(data[1].tokenType, 129) + assert2.equal(data[1].isValid, true) assert2.equal(data[2].utxoType, "token") assert2.equal(data[2].tokenType, 129) + assert2.equal(data[2].isValid, true) }) it("should decode a NFT Child Genesis transaction", async () => { @@ -1465,10 +1529,12 @@ describe("#SLP Utils", () => { // console.log(`data: ${JSON.stringify(data, null, 2)}`) assert2.isArray(data) - assert2.equal(data[1], false) assert2.equal(data[0].utxoType, "token") assert2.equal(data[0].tokenType, 65) + assert2.equal(data[0].isValid, true) + + assert2.equal(data[1].isValid, false) }) it("should decode an NFT Child Send transaction", async () => { @@ -1543,8 +1609,9 @@ describe("#SLP Utils", () => { assert2.equal(data[0].utxoType, "token") assert2.equal(data[0].transactionType, "send") assert2.equal(data[0].tokenType, 65) + assert2.equal(data[0].isValid, true) - assert2.equal(data[1], false) + assert2.equal(data[1].isValid, false) }) it("should decode an NFT Group Send transaction", async () => { @@ -1635,12 +1702,14 @@ describe("#SLP Utils", () => { assert2.equal(data[0].utxoType, "token") assert2.equal(data[0].transactionType, "send") assert2.equal(data[0].tokenType, 129) + assert2.equal(data[0].isValid, true) assert2.equal(data[1].utxoType, "token") assert2.equal(data[1].transactionType, "send") assert2.equal(data[1].tokenType, 129) + assert2.equal(data[1].isValid, true) - assert2.equal(data[2], false) + assert2.equal(data[2].isValid, false) }) })