feat(Utils.tokenUtxoDetails): Refactored and added isValid() property

This commit is contained in:
Chris Troutner
2020-01-11 14:17:23 -08:00
parent d735c2f180
commit ed7fe90c27
4 changed files with 418 additions and 194 deletions
+2
View File
@@ -51,6 +51,8 @@ class BCHJS {
apiToken: this.apiToken
}
// console.log(`apiToken: ${this.apiToken}`)
// Populate Blockbook endpoints.
this.Blockbook = new Blockbook(libConfig)
+92 -89
View File
@@ -985,14 +985,14 @@ class Utils {
// Loop through each element in the array and spot check the required
// properties.
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
const utxo = utxos[i]
// Throw error if utxo does not have a satoshis property.
if (!thisUtxo.satoshis)
if (!utxo.satoshis)
throw new Error(`utxo ${i} does not have a satoshis property.`)
// Throw error if utxo does not have a txid property.
if (!thisUtxo.txid)
if (!utxo.txid)
throw new Error(`utxo ${i} does not have a txid property.`)
}
@@ -1010,11 +1010,11 @@ class Utils {
// further processing.
for (let i = 0; i < utxos.length; i++) {
const thisValidation = validations[i]
const thisUtxo = utxos[i]
const utxo = utxos[i]
// Only need to worry about validations that are still true.
if (thisValidation) {
const slpData = await this.decodeOpReturn(thisUtxo.txid)
const slpData = await this.decodeOpReturn(utxo.txid)
//console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
// Handle Genesis and Mint SLP transactions.
@@ -1023,15 +1023,15 @@ class Utils {
slpData.transactionType === "mint"
) {
if (
thisUtxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
thisUtxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
utxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
)
// Can safely be marked as false.
validations[i] = false
} else if (slpData.transactionType === "send") {
// Filter out any vouts that match.
const voutMatch = slpData.spendData.filter(
x => thisUtxo.vout === x.vout
x => utxo.vout === x.vout
)
//console.log(`voutMatch: ${JSON.stringify(voutMatch, null, 2)}`)
@@ -1045,7 +1045,7 @@ class Utils {
// UTXOs only contain dust values. <--- NOT TRUE
// Note: This is not a very accurate way to make a determination.
// See https://gist.github.com/christroutner/434ae0c710001b57e33a4fa8abb7d478
//if (thisUtxo.satoshis > 546) validations[i] = false
//if (utxo.satoshis > 546) validations[i] = false
}
return validations
@@ -1065,13 +1065,15 @@ class Utils {
* If the
* UTXO does not belong to a SLP transaction, it will return false.
* If the UTXO is part of an SLP transaction, it will return the UTXO object
* with additional SLP information attached.
* 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.
*
* @apiExample Example usage:
*
* (async () => {
* try {
* const u = await bchjs.Insight.utxos(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
* const u = await bchjs.Blockbook.utxos(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
* const utxos = u.utxos
*
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
@@ -1097,10 +1099,13 @@ class Utils {
* "tokenDocumentUrl": "",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenQty": 2
* "tokenQty": 2,
* "isValid": true
* }
*/
// CT 1/11/20: Refactored to comply with this GitHub Issue:
// https://github.com/Bitcoin-com/slp-sdk/issues/84
async tokenUtxoDetails(utxos) {
try {
// Throw error if input is not an array.
@@ -1109,61 +1114,57 @@ class Utils {
// Loop through each element in the array and validate the input before
// further processing.
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
const utxo = utxos[i]
// Convert Blockbook UTXOs to Insight format.
if (thisUtxo.value) thisUtxo.satoshis = Number(thisUtxo.value)
if (utxo.value) utxo.satoshis = Number(utxo.value)
// Throw error if utxo does not have a satoshis property.
if (!thisUtxo.satoshis)
if (!utxo.satoshis)
throw new Error(`utxo ${i} does not have a satoshis property.`)
// Throw error if utxo does not have a txid property.
if (!thisUtxo.txid)
if (!utxo.txid)
throw new Error(`utxo ${i} does not have a txid property.`)
}
// Create an array of txid strings to feed to validateTxid
const txids = utxos.map(x => x.txid)
// console.log(`txids: ${JSON.stringify(txids, null, 2)}`)
// Output Array
const outAry = []
// Validate the array of txids.
const validations = await this.validateTxid(txids)
// console.log(`validations: ${JSON.stringify(validations, null, 2)}`)
const outAry = [] // Output Array
// Loop through each element and compute final validation on any that
// returned true.
// Loop on utxos, not validations since txids can be the same and
// UTXOs are differentiated by vout value.
// Loop through each utxo
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
// console.log(`thisUtxo: ${JSON.stringify(thisUtxo, null, 2)}`)
const utxo = utxos[i]
const thisValidation = validations.filter(x => {
if (x !== null) return x.txid === thisUtxo.txid
// Get raw transaction data from the full node and attempt to decode
// the OP_RETURN data.
// If there is no OP_RETURN, mark the UTXO as false.
let slpData = false
try {
slpData = await this.decodeOpReturn(utxo.txid)
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
} catch (err) {
// console.log(`decodeOpReturn err: `, err)
return false
})
// console.log(
// `thisValidation: ${JSON.stringify(thisValidation, null, 2)}`
// )
if (
err.message === "Not an OP_RETURN" ||
err.message === "Not a SLP OP_RETURN"
) {
// An error will be thrown if the txid is not SLP.
// Mark as false and continue the loop.
outAry.push(false)
// If the utxo is not SLP, then skip the loop.
if (!thisValidation[0] || !thisValidation[0].valid) {
outAry[i] = false
continue
continue
} else {
throw err
}
}
const slpData = await this.decodeOpReturn(thisUtxo.txid)
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
// If there is an OP_RETURN, attempt to decode it.
// Handle Genesis SLP transactions.
if (slpData.transactionType === "genesis") {
if (
thisUtxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
thisUtxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
utxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
) {
// Can safely be marked as false.
outAry[i] = false
@@ -1171,24 +1172,24 @@ class Utils {
// If this is a valid SLP UTXO, then return the decoded OP_RETURN data.
else {
thisUtxo.tokenType = "minting-baton"
thisUtxo.tokenId = thisUtxo.txid
thisUtxo.tokenTicker = slpData.ticker
thisUtxo.tokenName = slpData.name
thisUtxo.tokenDocumentUrl = slpData.documentUrl
thisUtxo.tokenDocumentHash = slpData.documentHash
thisUtxo.decimals = slpData.decimals
utxo.tokenType = "minting-baton"
utxo.tokenId = utxo.txid
utxo.tokenTicker = slpData.ticker
utxo.tokenName = slpData.name
utxo.tokenDocumentUrl = slpData.documentUrl
utxo.tokenDocumentHash = slpData.documentHash
utxo.decimals = slpData.decimals
// something
outAry[i] = thisUtxo
outAry[i] = utxo
}
}
// Handle Mint SLP transactions.
if (slpData.transactionType === "mint") {
if (
thisUtxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
thisUtxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
utxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
) {
// Can safely be marked as false.
outAry[i] = false
@@ -1199,33 +1200,30 @@ class Utils {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
// Hydrate the UTXO object with information about the SLP token.
thisUtxo.utxoType = "token"
thisUtxo.transactionType = "mint"
thisUtxo.tokenId = slpData.tokenId
utxo.utxoType = "token"
utxo.transactionType = "mint"
utxo.tokenId = slpData.tokenId
thisUtxo.tokenTicker = genesisData.ticker
thisUtxo.tokenName = genesisData.name
thisUtxo.tokenDocumentUrl = genesisData.documentUrl
thisUtxo.tokenDocumentHash = genesisData.documentHash
thisUtxo.decimals = genesisData.decimals
utxo.tokenTicker = genesisData.ticker
utxo.tokenName = genesisData.name
utxo.tokenDocumentUrl = genesisData.documentUrl
utxo.tokenDocumentHash = genesisData.documentHash
utxo.decimals = genesisData.decimals
thisUtxo.mintBatonVout = slpData.mintBatonVout
thisUtxo.batonStillExists = slpData.batonStillExists
utxo.mintBatonVout = slpData.mintBatonVout
utxo.batonStillExists = slpData.batonStillExists
// Calculate the real token quantity.
thisUtxo.tokenQty =
slpData.quantity / Math.pow(10, thisUtxo.decimals)
utxo.tokenQty = slpData.quantity / Math.pow(10, utxo.decimals)
outAry[i] = thisUtxo
outAry[i] = utxo
}
}
// Handle Send SLP transactions.
if (slpData.transactionType === "send") {
// Filter out any vouts that match.
const voutMatch = slpData.spendData.filter(
x => thisUtxo.vout === x.vout
)
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.
@@ -1236,31 +1234,36 @@ class Utils {
// If UTXO passes validation, then return formatted token data.
else {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
// console.log(
// `genesisData: ${JSON.stringify(genesisData, null, 2)}`
// )
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// console.log(`thisUtxo: ${JSON.stringify(thisUtxo, null, 2)}`)
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
// Hydrate the UTXO object with information about the SLP token.
thisUtxo.utxoType = "token"
thisUtxo.transactionType = "send"
thisUtxo.tokenId = slpData.tokenId
thisUtxo.tokenTicker = genesisData.ticker
thisUtxo.tokenName = genesisData.name
thisUtxo.tokenDocumentUrl = genesisData.documentUrl
thisUtxo.tokenDocumentHash = genesisData.documentHash
thisUtxo.decimals = genesisData.decimals
utxo.utxoType = "token"
utxo.transactionType = "send"
utxo.tokenId = slpData.tokenId
utxo.tokenTicker = genesisData.ticker
utxo.tokenName = genesisData.name
utxo.tokenDocumentUrl = genesisData.documentUrl
utxo.tokenDocumentHash = genesisData.documentHash
utxo.decimals = genesisData.decimals
// Calculate the real token quantity.
thisUtxo.tokenQty =
voutMatch[0].quantity / Math.pow(10, thisUtxo.decimals)
utxo.tokenQty = voutMatch[0].quantity / Math.pow(10, utxo.decimals)
// console.log(`thisUtxo: ${JSON.stringify(thisUtxo, null, 2)}`)
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
outAry[i] = thisUtxo
outAry[i] = utxo
}
}
// Finally, validate the SLP txid with SLPDB.
if (outAry[i]) {
const isValid = await this.validateTxid(utxo.txid)
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid[0].valid
}
}
return outAry
+213 -2
View File
@@ -99,7 +99,8 @@ describe(`#SLP`, () => {
"tokenDocumentUrl",
"tokenDocumentHash",
"decimals",
"tokenQty"
"tokenQty",
"isValid"
])
})
@@ -134,7 +135,8 @@ describe(`#SLP`, () => {
"tokenDocumentUrl",
"tokenDocumentHash",
"decimals",
"tokenQty"
"tokenQty",
"isValid"
])
})
@@ -167,6 +169,215 @@ describe(`#SLP`, () => {
assert.equal(result.length, 2)
assert.equal(result[0], false)
})
// This captures an important corner-case. When an SLP token is created, the
// change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
it("should return details on minting baton from genesis transaction", async () => {
const utxos = [
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
vout: 3,
amount: 0.00002015,
satoshis: 2015,
height: 594892,
confirmations: 5
},
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
vout: 2,
amount: 0.00000546,
satoshis: 546,
height: 594892,
confirmations: 5
}
]
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.property(data[1], "txid")
assert.property(data[1], "vout")
assert.property(data[1], "amount")
assert.property(data[1], "satoshis")
assert.property(data[1], "height")
assert.property(data[1], "confirmations")
assert.property(data[1], "tokenType")
assert.property(data[1], "tokenId")
assert.property(data[1], "tokenTicker")
assert.property(data[1], "tokenName")
assert.property(data[1], "tokenDocumentUrl")
assert.property(data[1], "tokenDocumentHash")
assert.property(data[1], "decimals")
assert.property(data[1], "isValid")
assert.equal(data[1].isValid, true)
})
it("should return details for a MINT token utxo", async () => {
const utxos = [
{
txid:
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 600297,
confirmations: 76
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], "txid")
assert.property(data[0], "vout")
assert.property(data[0], "amount")
assert.property(data[0], "satoshis")
assert.property(data[0], "height")
assert.property(data[0], "confirmations")
assert.property(data[0], "utxoType")
assert.property(data[0], "transactionType")
assert.property(data[0], "tokenId")
assert.property(data[0], "tokenTicker")
assert.property(data[0], "tokenName")
assert.property(data[0], "tokenDocumentUrl")
assert.property(data[0], "tokenDocumentHash")
assert.property(data[0], "decimals")
assert.property(data[0], "mintBatonVout")
assert.property(data[0], "batonStillExists")
assert.property(data[0], "tokenQty")
assert.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should return details for a simple SEND SLP token utxo", async () => {
const utxos = [
{
txid:
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], "txid")
assert.property(data[0], "vout")
assert.property(data[0], "amount")
assert.property(data[0], "satoshis")
assert.property(data[0], "height")
assert.property(data[0], "confirmations")
assert.property(data[0], "utxoType")
assert.property(data[0], "tokenId")
assert.property(data[0], "tokenTicker")
assert.property(data[0], "tokenName")
assert.property(data[0], "tokenDocumentUrl")
assert.property(data[0], "tokenDocumentHash")
assert.property(data[0], "decimals")
assert.property(data[0], "tokenQty")
assert.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should handle BCH and SLP utxos in the same TX", async () => {
const utxos = [
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 3,
value: "6816",
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 2,
value: "546",
height: 606848,
confirmations: 13,
satoshis: 546
}
]
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should handle problematic utxos", async () => {
const utxos = [
{
txid:
"0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959",
vout: 0,
amount: 0.00018362,
satoshis: 18362,
height: 613483,
confirmations: 124
},
{
txid:
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 612075,
confirmations: 1532
}
]
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should return false for BCH-only UTXOs", async () => {
const utxos = [
{
txid:
"a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b",
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424704
},
{
txid:
"53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44",
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424634
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
assert.equal(false, data[0])
assert.equal(false, data[1])
})
})
})
})
+111 -103
View File
@@ -4,7 +4,10 @@ const assert2 = require("chai").assert
const SLP = require("../../src/slp/slp")
//const slp = new SLP("http://decatur.hopto.org:12400/v3/")
//const slp = new SLP("https://rest.bitcoin.com/v2/")
const slp = new SLP("https://mainnet.bchjs.cash/v3/")
const slp = new SLP({
restURL: "https://mainnet.bchjs.cash/v3/",
apiToken: process.env.BCHJSTOKEN
})
const nock = require("nock") // http call mocking
const sinon = require("sinon")
@@ -814,11 +817,6 @@ describe("#SLP Utils", () => {
if (process.env.TEST === "unit") {
// Stub the call to validateTxid
sandbox.stub(slp.Utils, "validateTxid").resolves([
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
valid: true
},
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
@@ -868,21 +866,22 @@ describe("#SLP Utils", () => {
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert2.equal(data[0], false, "Change UTXO marked as false.")
assert2.hasAnyKeys(data[1], [
"txid",
"vout",
"amount",
"satoshis",
"height",
"confirmations",
"tokenType",
"tokenId",
"tokenTicker",
"tokenName",
"tokenDocumentUrl",
"tokenDocumentHash",
"decimals"
])
assert2.property(data[1], "txid")
assert2.property(data[1], "vout")
assert2.property(data[1], "amount")
assert2.property(data[1], "satoshis")
assert2.property(data[1], "height")
assert2.property(data[1], "confirmations")
assert2.property(data[1], "tokenType")
assert2.property(data[1], "tokenId")
assert2.property(data[1], "tokenTicker")
assert2.property(data[1], "tokenName")
assert2.property(data[1], "tokenDocumentUrl")
assert2.property(data[1], "tokenDocumentHash")
assert2.property(data[1], "decimals")
assert2.property(data[1], "isValid")
assert2.equal(data[1].isValid, true)
})
it("should return details for a MINT token utxo", async () => {
@@ -945,29 +944,29 @@ describe("#SLP Utils", () => {
const data = await slp.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert2.hasAnyKeys(data[0], [
"txid",
"vout",
"amount",
"satoshis",
"height",
"confirmations",
"utxoType",
"transactionType",
"tokenId",
"tokenTicker",
"tokenName",
"tokenDocumentUrl",
"tokenDocumentHash",
"decimals",
"mintBatonVout",
"batonStillExists",
"tokenQty"
])
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], "utxoType")
assert2.property(data[0], "transactionType")
assert2.property(data[0], "tokenId")
assert2.property(data[0], "tokenTicker")
assert2.property(data[0], "tokenName")
assert2.property(data[0], "tokenDocumentUrl")
assert2.property(data[0], "tokenDocumentHash")
assert2.property(data[0], "decimals")
assert2.property(data[0], "mintBatonVout")
assert2.property(data[0], "batonStillExists")
assert2.property(data[0], "tokenQty")
assert2.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should return details for a simple SEND SLP token utxo", async () => {
// Mock the call to REST API
// // Mock the call to REST API
if (process.env.TEST === "unit") {
// Stub the call to validateTxid
sandbox.stub(slp.Utils, "validateTxid").resolves([
@@ -1032,24 +1031,24 @@ describe("#SLP Utils", () => {
]
const data = await slp.Utils.tokenUtxoDetails(utxos)
//console.log(`data: ${JSON.stringify(data, null, 2)}`)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert2.hasAnyKeys(data[0], [
"txid",
"vout",
"amount",
"satoshis",
"height",
"confirmations",
"utxoType",
"tokenId",
"tokenTicker",
"tokenName",
"tokenDocumentUrl",
"tokenDocumentHash",
"decimals",
"tokenQty"
])
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], "utxoType")
assert2.property(data[0], "tokenId")
assert2.property(data[0], "tokenTicker")
assert2.property(data[0], "tokenName")
assert2.property(data[0], "tokenDocumentUrl")
assert2.property(data[0], "tokenDocumentHash")
assert2.property(data[0], "decimals")
assert2.property(data[0], "tokenQty")
assert2.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should handle BCH and SLP utxos in the same TX", async () => {
@@ -1088,74 +1087,82 @@ describe("#SLP Utils", () => {
assert2.isArray(result)
assert2.equal(result.length, 2)
assert2.equal(result[0], false)
assert.equal(result[1].isValid, true)
})
it("should handle problematic utxos", async () => {
// Mock external dependencies.
// Mock the call to REST API
if (process.env.TEST === "unit") {
// Stub the call to validateTxid
sandbox.stub(slp.Utils, "validateTxid").resolves([
{
txid:
"b35746c9e7f086fe87a3680e866bc859395483cde46b4667937ffc9b638baa4a",
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
valid: true
},
{
txid:
"eb313e0319f17fe42e38e0321c8bcadb219cf665b3123be6bb3f05749839b9ef",
valid: false
}
])
// Stub the calls to decodeOpReturn.
sandbox.stub(slp.Utils, "decodeOpReturn").resolves({
tokenType: 1,
transactionType: "send",
tokenId:
"155784a206873c98acc09e8dabcccf6abf13c4c14d8662190534138a16bb93ce",
spendData: [
{
quantity: "1200000000000",
sentTo: "bchtest:qpt74e74f75w6s7cd8r9p5fumvdhqf995gp3mkk6xw",
vout: 1
},
{
quantity: "8800000000000",
sentTo: "bchtest:qq9zyh5sqwqlfc3q3rf2t8x82zp87euf7576n4465c",
vout: 2
}
]
})
sandbox
.stub(slp.Utils, "decodeOpReturn")
.onCall(0)
.throws({ message: "Not an OP_RETURN" })
.onCall(1)
.resolves({
tokenType: 1,
transactionType: "send",
tokenId:
"f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f",
spendData: [
{
quantity: "5000000",
sentTo:
"bitcoincash:qr06e2fetka9fyh207ff3cq8xh9zfe78gyx24yadjz",
vout: 1
},
{
quantity: "395010942",
sentTo:
"bitcoincash:qqzjzzlmx8h3hum3drsuk894jnf8r909kueykgrkk2",
vout: 2
}
]
})
.onCall(2)
.resolves({
tokenType: 1,
transactionType: "genesis",
ticker: "AUDC",
name: "AUD Coin",
documentUrl: "audcoino@gmail.com",
documentHash: "",
decimals: 6,
mintBatonVout: 0,
initialQty: 2000000000000,
tokensSentTo:
"bitcoincash:qp5pup5vpdcq3qyq8f858nuqmgfq8krupvsxxuxctx",
batonHolder: "NEVER_CREATED"
})
}
// sandbox
// .stub(slp.Utils, "validateTxid")
// .resolves(mockData.mockDualValidation)
// sandbox
// .stub(slp.Utils, "decodeOpReturn")
// .resolves(mockData.mockDualOpData)
// const slp2 = new SLP({ restURL: `https://tapi.bchjs.cash/v3/` })
const utxos = [
{
txid:
"eb313e0319f17fe42e38e0321c8bcadb219cf665b3123be6bb3f05749839b9ef",
vout: 1,
value: "10000000",
height: 1346640,
confirmations: 225,
satoshcreateTokenTxis: 10000000
"0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959",
vout: 0,
amount: 0.00018362,
satoshis: 18362,
height: 613483,
confirmations: 124
},
{
txid:
"b35746c9e7f086fe87a3680e866bc859395483cde46b4667937ffc9b638baa4a",
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
vout: 1,
value: "546",
height: 1346569,
confirmations: 296,
satoshis: 546
amount: 0.00000546,
satoshis: 546,
height: 612075,
confirmations: 1532
}
]
@@ -1165,6 +1172,7 @@ describe("#SLP Utils", () => {
assert2.isArray(result)
assert2.equal(result.length, 2)
assert2.equal(result[0], false)
assert2.equal(result[1].isValid, true)
})
it("should return false for BCH-only UTXOs", async () => {