mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 09:02:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad4e05c289 | ||
|
|
0c1eda8e5a | ||
|
|
f15bc7e454 | ||
|
|
e9113d864e | ||
|
|
277c9ce0fd | ||
|
|
e0c07d1542 | ||
|
|
56bc942e87 | ||
|
|
34b9cefb65 | ||
|
|
6a35091eec | ||
|
|
02e3ff4dcd | ||
|
|
64f8adea25 | ||
|
|
bcf817f5c7 | ||
|
|
541377e548 | ||
|
|
84b44b299a | ||
|
|
b6456df1f4 | ||
|
|
b1826c04cb | ||
|
|
71755964c8 | ||
|
|
bd1c3b622d | ||
|
|
bdced13230 |
+1
-2
@@ -1,8 +1,7 @@
|
||||
Copyright 2021 Permissionless Software Foundation contributors
|
||||
Copyright 2022 Permissionless Software Foundation contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Generated
+4738
-2476
File diff suppressed because it is too large
Load Diff
+6
-8
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@psf/bch-js",
|
||||
"version": "5.0.0",
|
||||
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
|
||||
"version": "6.0.0",
|
||||
"description": "A JavaScript library for working with Bitcoin Cash, eCash, and SLP Tokens",
|
||||
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
||||
"contributors": [
|
||||
"Gabriel Cardona <gabriel@bitcoin.com>",
|
||||
@@ -37,13 +37,11 @@
|
||||
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@chris.troutner/bip32-utils": "1.0.5",
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@psf/bitcoincash-ops": "2.0.0",
|
||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||
"@psf/coininfo": "4.0.0",
|
||||
"@uppy/core": "1.10.4",
|
||||
"@uppy/tus": "1.5.12",
|
||||
"axios": "^0.21.4",
|
||||
"bc-bip68": "1.0.5",
|
||||
"bchaddrjs-slp": "0.2.5",
|
||||
@@ -65,7 +63,7 @@
|
||||
"wif": "2.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "0.17.7",
|
||||
"apidoc": "^0.50.5",
|
||||
"assert": "2.0.0",
|
||||
"chai": "4.1.2",
|
||||
"coveralls": "3.0.2",
|
||||
@@ -77,10 +75,10 @@
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "^4.3.8",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"mocha": "9.1.3",
|
||||
"mocha": "^9.2.1",
|
||||
"node-mocks-http": "1.7.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "18.0.0",
|
||||
"semantic-release": "^19.0.2",
|
||||
"sinon": "9.2.2",
|
||||
"standard": "^16.0.4"
|
||||
},
|
||||
|
||||
@@ -28,7 +28,6 @@ const Script = require('./script')
|
||||
const Price = require('./price')
|
||||
const Schnorr = require('./schnorr')
|
||||
const SLP = require('./slp/slp')
|
||||
const IPFS = require('./ipfs')
|
||||
const Encryption = require('./encryption')
|
||||
const Utxo = require('./utxo')
|
||||
const Transaction = require('./transaction')
|
||||
@@ -111,7 +110,6 @@ class BCHJS {
|
||||
this.SLP = new SLP(libConfig)
|
||||
this.SLP.HDNode = this.HDNode
|
||||
|
||||
this.IPFS = new IPFS()
|
||||
this.Utxo = new Utxo(libConfig)
|
||||
this.Transaction = new Transaction(libConfig)
|
||||
|
||||
|
||||
+15
-2
@@ -638,13 +638,24 @@ class ElectrumX {
|
||||
// Sort confirmed Transactions by the block height
|
||||
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
|
||||
|
||||
// Filter out unconfirmed transactions, with a height of 0 or less.
|
||||
txs = txs.filter(elem => elem.height > 0)
|
||||
|
||||
if (sortingOrder === 'DESCENDING') {
|
||||
return txs.sort((a, b) => b.height - a.height)
|
||||
// console.log('Sorting in descending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return b.height - a.height
|
||||
})
|
||||
}
|
||||
return txs.sort((a, b) => a.height - b.height)
|
||||
|
||||
// console.log('Sorting in ascending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return a.height - b.height
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Error in util.js/sortConfTxs()')
|
||||
throw err
|
||||
@@ -685,6 +696,8 @@ class ElectrumX {
|
||||
// Substitute zero-conf txs with the current block-height + 1
|
||||
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortingOrder: ${sortingOrder}`)
|
||||
|
||||
// Calculate the height of the next block
|
||||
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
||||
|
||||
|
||||
-454
@@ -1,454 +0,0 @@
|
||||
/*
|
||||
This library interacts with the IPFS REST API at FullStack.cash for uploading
|
||||
and downloading data from the IPFS network.
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
const Uppy = require('@uppy/core')
|
||||
const Tus = require('@uppy/tus')
|
||||
const fs = require('fs')
|
||||
|
||||
let _this
|
||||
|
||||
class IPFS {
|
||||
constructor (config) {
|
||||
this.IPFS_API = process.env.IPFS_API
|
||||
? process.env.IPFS_API
|
||||
: 'https://ipfs-file-upload.fullstackcash.nl'
|
||||
// : `http://localhost:5001`
|
||||
|
||||
// Default options when calling axios.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
// authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000 // Timeout if the server does not respond in time.
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
|
||||
_this.axios = axios
|
||||
_this.fs = fs
|
||||
|
||||
// Initialize Uppy
|
||||
_this.uppy = _this.initUppy()
|
||||
}
|
||||
|
||||
// Initializes Uppy, which is used for file uploads.
|
||||
initUppy () {
|
||||
const uppy = Uppy({
|
||||
allowMultipleUploads: false,
|
||||
meta: { test: 'avatar' },
|
||||
debug: false,
|
||||
restrictions: {
|
||||
maxFileSize: null,
|
||||
maxNumberOfFiles: 1,
|
||||
minNumberOfFiles: 1,
|
||||
allowedFileTypes: null // type of files allowed to load
|
||||
}
|
||||
})
|
||||
uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` })
|
||||
|
||||
return uppy
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.createFileModelServer() createFileModelServer()
|
||||
* @apiName createFileModelServer()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.createFileModelServer(path);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelServer (path) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path)) {
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
}
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
// console.log(`fileBuf: `, fileBuf)
|
||||
|
||||
// console.log(`Buffer length: ${fileBuf.length}`)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split('/')
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = fileName.split('.')
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: fileBuf.length,
|
||||
fileName: fileName,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error('Error in createFileModel()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileServer() uploadFileServer()
|
||||
* @apiName uploadFile()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileServer (path, modelId) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path)) {
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
'Must include a file model ID in order to upload a file.'
|
||||
)
|
||||
}
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split('/')
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: fileName,
|
||||
data: fileBuf,
|
||||
source: 'Local',
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length) {
|
||||
throw new Error('The file could not be uploaded')
|
||||
}
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error('Error in bch-js/src/ipfs.js/upload(): ')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.getStatus() getStatus()
|
||||
* @apiName getStatus()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Gets the status of the uploaded file. Will return an object
|
||||
* that indicates the payment status. If the file is paid, it should contain
|
||||
* an IPFS hash.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const modelId = "5ec7392c2acfe57aa62e945a"
|
||||
* let fileData = await bchjs.IPFS.getStatus(modelId)
|
||||
* console.log(fileData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "hasBeenPaid": true,
|
||||
* "satCost": 4403,
|
||||
* "bchAddr": "bchtest:qz5z82u0suqh80x5tfx4ht8kdrkkw664vcy44uz0wk",
|
||||
* "ipfsHash": "QmRDHPhY5hCNVRMVQvS2H9uty8P1skdwgLaHpUAkEvsjcE",
|
||||
* "fileId": "5ec7392c2acfe57aa62e945a",
|
||||
* "fileName": "ipfs-e2e.js"
|
||||
* }
|
||||
*/
|
||||
async getStatus (modelId) {
|
||||
try {
|
||||
if (!modelId) throw new Error('Must include a file model ID.')
|
||||
|
||||
const fileData = await _this.axios.get(
|
||||
`${this.IPFS_API}/files/${modelId}`
|
||||
)
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
hasBeenPaid: fileData.data.file.hasBeenPaid,
|
||||
satCost: fileData.data.file.hostingCost,
|
||||
bchAddr: fileData.data.file.bchAddr,
|
||||
ipfsHash: fileData.data.file.payloadLink,
|
||||
fileId: fileData.data.file._id,
|
||||
fileName: fileData.data.file.fileName
|
||||
}
|
||||
|
||||
return fileObj
|
||||
} catch (err) {
|
||||
console.error('Error in getStatus()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.createFileModelWeb() createFileModelWeb()
|
||||
* @apiName createFileModelWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.createFileModelWeb(file);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelWeb (file) {
|
||||
try {
|
||||
if (!file) throw new Error('File is required')
|
||||
|
||||
const { name, size } = file
|
||||
|
||||
if (!name || typeof name !== 'string') {
|
||||
throw new Error("File should have the property 'name' of string type")
|
||||
}
|
||||
|
||||
if (!size || typeof size !== 'number') {
|
||||
throw new Error("File should have the property 'size' of number type")
|
||||
}
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = name.split('.')
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: size,
|
||||
fileName: name,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error('Error in createFileModelWeb()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileWeb() uploadFileWeb()
|
||||
* @apiName uploadFileWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* * const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.uploadFile(file, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileWeb (file, modelId) {
|
||||
try {
|
||||
if (!file) throw new Error('File is required')
|
||||
|
||||
const { name, type, size } = file
|
||||
|
||||
if (!name || typeof name !== 'string') {
|
||||
throw new Error("File should have the property 'name' of string type")
|
||||
}
|
||||
|
||||
if (!type || typeof type !== 'string') {
|
||||
throw new Error("File should have the property 'type' of string type")
|
||||
}
|
||||
|
||||
if (!size || typeof size !== 'number') {
|
||||
throw new Error("File should have the property 'size' of number type")
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
'Must include a file model ID in order to upload a file.'
|
||||
)
|
||||
}
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: name,
|
||||
data: file,
|
||||
source: 'Local',
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length) {
|
||||
throw new Error('The file could not be uploaded')
|
||||
}
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error('Error in bch-js/src/ipfs.js/uploadFileWeb(): ')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IPFS
|
||||
+13
-11
@@ -41,7 +41,7 @@ class PsfSlpIndexer {
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
// _this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,8 +53,8 @@ class PsfSlpIndexer {
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let status = await bchjs.PsfSlpIndexer.status();
|
||||
* console.log(status);
|
||||
* let status = await bchjs.PsfSlpIndexer.status()
|
||||
* console.log(status)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -91,8 +91,8 @@ class PsfSlpIndexer {
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
|
||||
* console.log(balance);
|
||||
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n')
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -155,12 +155,14 @@ class PsfSlpIndexer {
|
||||
* @apiName Token Stats
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
* The second input is a Boolean, which determins the the transaction history
|
||||
* of the token is included in the returned data. The default is false.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
|
||||
* console.log(tokenStats);
|
||||
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', true)
|
||||
* console.log(tokenStats)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -194,13 +196,13 @@ class PsfSlpIndexer {
|
||||
*
|
||||
*/
|
||||
|
||||
async tokenStats (tokenId) {
|
||||
async tokenStats (tokenId, withTxHistory = false) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/token`,
|
||||
{ tokenId },
|
||||
{ tokenId, withTxHistory },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
@@ -221,8 +223,8 @@ class PsfSlpIndexer {
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
|
||||
* console.log(txData);
|
||||
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
|
||||
* console.log(txData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
|
||||
+50
@@ -150,6 +150,56 @@ class Util {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.chunk100() chunk100()
|
||||
* @apiName chunk100
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
|
||||
* Input: arrayToSlice - a one-dimensional array of elements.
|
||||
* Returns a two-dimensional array. An array of 100-element arrays.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
|
||||
*
|
||||
* const chunked = bchjs.Util.chunk20(bigArray)
|
||||
* console.log(chunked)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
|
||||
* [100,101,102,...,148,149,150]
|
||||
* ]
|
||||
*/
|
||||
chunk100 (arrayToSlice) {
|
||||
try {
|
||||
// Validate inputs
|
||||
if (!Array.isArray(arrayToSlice)) {
|
||||
throw new Error('input must be an array')
|
||||
}
|
||||
|
||||
let offset = 0
|
||||
const result = []
|
||||
|
||||
// Loop over the array and slice off chunks of 100 elements.
|
||||
while (offset < arrayToSlice.length) {
|
||||
const chunk = arrayToSlice.slice(offset, offset + 100)
|
||||
result.push(chunk)
|
||||
offset = offset + 100
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in chunk100()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.sleep() sleep()
|
||||
* @apiName sleep
|
||||
|
||||
+12
-2
@@ -404,8 +404,18 @@ class UTXO {
|
||||
if (!thisUtxo.isSlp) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.isSlp = false
|
||||
thisUtxo.address = addr
|
||||
|
||||
// Check the transaction to see if its a 'null' token, ignored by
|
||||
// the indexer.
|
||||
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
|
||||
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
|
||||
if (txData.txData.isValidSlp === null) {
|
||||
thisUtxo.isSlp = null
|
||||
} else {
|
||||
thisUtxo.isSlp = false
|
||||
}
|
||||
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +448,7 @@ class UTXO {
|
||||
|
||||
return outObj
|
||||
} catch (err) {
|
||||
// console.error('Error in bchjs.utxo.get2(): ', err)
|
||||
console.error('Error in bchjs.Utxo.get(): ', err)
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs covering SLP tokens.
|
||||
These tests are specific to the ABC chain.
|
||||
*/
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 1
|
||||
}
|
||||
|
||||
describe('#SLP', () => {
|
||||
// before(() => {
|
||||
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
|
||||
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
|
||||
// })
|
||||
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
|
||||
bchjs = new BCHJS({ restURL: process.env.RESTURL })
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
describe('#tokenUtxoDetails', () => {
|
||||
it('should handle a range of UTXO types', async () => {
|
||||
const utxos = [
|
||||
// Malformed SLP tx
|
||||
{
|
||||
note: 'Malformed SLP tx',
|
||||
tx_hash:
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Normal TX (non-SLP)
|
||||
{
|
||||
note: 'Normal TX (non-SLP)',
|
||||
tx_hash:
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
tx_pos: 0,
|
||||
value: 400000
|
||||
},
|
||||
// Valid PSF SLP tx
|
||||
{
|
||||
note: 'Valid PSF SLP tx',
|
||||
tx_hash:
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Valid SLP token not in whitelist
|
||||
{
|
||||
note: 'Valid SLP token not in whitelist',
|
||||
tx_hash:
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on BCHN network.
|
||||
{
|
||||
note: 'Token send on BCHN network',
|
||||
tx_hash:
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on ABC network.
|
||||
{
|
||||
note: 'Token send on ABC network',
|
||||
tx_hash:
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Known invalid SLP token send of PSF tokens.
|
||||
{
|
||||
note: 'Known invalid SLP token send of PSF tokens',
|
||||
tx_hash:
|
||||
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
// Malformed SLP tx
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
|
||||
// Valid PSF SLP tx
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
|
||||
// Valid SLP token not in whitelist
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
// assert.equal(data[4].isValid, true)
|
||||
assert.equal(data[4].isValid, null)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, true)
|
||||
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid3', () => {
|
||||
it('should invalidate a known invalid TXID', async () => {
|
||||
const txid =
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, null)
|
||||
})
|
||||
|
||||
it('should validate a known valid TXID', async () => {
|
||||
const txid =
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, true)
|
||||
})
|
||||
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Unprocessed SLP TX
|
||||
// "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed" // Mint baton
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result[0].valid, null)
|
||||
assert.equal(result[1].valid, null)
|
||||
assert.equal(result[2].valid, true)
|
||||
// assert.equal(result[2].valid, null)
|
||||
assert.equal(result[3].valid, true)
|
||||
assert.equal(result[4].valid, null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid', () => {
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Token send on BCHN network.
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
// Token send on ABC network.
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result[0].valid, null)
|
||||
assert.equal(result[1].valid, null)
|
||||
assert.equal(result[2].valid, true)
|
||||
// assert.equal(result[2].valid, null)
|
||||
assert.equal(result[3].valid, true)
|
||||
assert.equal(result[4].valid, null)
|
||||
assert.equal(result[5].valid, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
*/
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
/*
|
||||
Testing sentRawTransaction isn't really possible with an integration test,
|
||||
as the endpoint really needs an e2e test to be properly tested. The tests
|
||||
below expect error messages returned from the server, but at least test
|
||||
that the server is responding on those endpoints, and responds consistently.
|
||||
*/
|
||||
describe('sendRawTransaction', () => {
|
||||
it('should send a single transaction hex', async () => {
|
||||
try {
|
||||
const hex =
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send an array of tx hexes', async () => {
|
||||
try {
|
||||
const hexes = [
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs covering SLP tokens.
|
||||
These tests are specific to the ABC chain.
|
||||
*/
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 1
|
||||
}
|
||||
|
||||
describe('#SLP', () => {
|
||||
// before(() => {
|
||||
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
|
||||
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
|
||||
// })
|
||||
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
describe('#decodeOpReturn', () => {
|
||||
it('should decode a NFT Child transaction', async () => {
|
||||
const txid =
|
||||
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
|
||||
|
||||
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.property(data, 'tokenType')
|
||||
assert.property(data, 'txType')
|
||||
assert.property(data, 'ticker')
|
||||
assert.property(data, 'name')
|
||||
assert.property(data, 'tokenId')
|
||||
assert.property(data, 'documentUri')
|
||||
assert.property(data, 'documentHash')
|
||||
assert.property(data, 'decimals')
|
||||
assert.property(data, 'mintBatonVout')
|
||||
assert.property(data, 'qty')
|
||||
|
||||
assert.equal(data.tokenType, 65)
|
||||
assert.equal(data.mintBatonVout, 0)
|
||||
assert.equal(data.qty, '1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenUtxoDetails', () => {
|
||||
it('should handle a range of UTXO types', async () => {
|
||||
const utxos = [
|
||||
// Malformed SLP tx
|
||||
{
|
||||
note: 'Malformed SLP tx',
|
||||
tx_hash:
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Normal TX (non-SLP)
|
||||
{
|
||||
note: 'Normal TX (non-SLP)',
|
||||
tx_hash:
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
tx_pos: 0,
|
||||
value: 400000
|
||||
},
|
||||
// Valid PSF SLP tx
|
||||
{
|
||||
note: 'Valid PSF SLP tx',
|
||||
tx_hash:
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Valid SLP token not in whitelist
|
||||
{
|
||||
note: 'Valid SLP token not in whitelist',
|
||||
tx_hash:
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on BCHN network.
|
||||
{
|
||||
note: 'Token send on BCHN network',
|
||||
tx_hash:
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on ABC network.
|
||||
{
|
||||
note: 'Token send on ABC network',
|
||||
tx_hash:
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Known invalid SLP token send of PSF tokens.
|
||||
{
|
||||
note: 'Known invalid SLP token send of PSF tokens',
|
||||
tx_hash:
|
||||
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
// Malformed SLP tx
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
|
||||
// Valid PSF SLP tx
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
|
||||
// Valid SLP token not in whitelist
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid3', () => {
|
||||
it('should invalidate a known invalid TXID', async () => {
|
||||
const txid =
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, null)
|
||||
})
|
||||
|
||||
it('should validate a known valid TXID', async () => {
|
||||
const txid =
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, true)
|
||||
})
|
||||
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Unprocessed SLP TX
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.equal(result[0].txid, txids[0])
|
||||
assert.equal(result[0].valid, null)
|
||||
|
||||
assert.equal(result[1].txid, txids[1])
|
||||
assert.equal(result[1].valid, null)
|
||||
|
||||
assert.equal(result[2].txid, txids[2])
|
||||
assert.equal(result[2].valid, true)
|
||||
|
||||
// True in validateTxid() (no whitelist SLPDB) but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, true)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
// True when no whitelist SLPDB is used.
|
||||
assert.equal(result[4].valid, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid', () => {
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Token send on BCHN network.
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
// Token send on ABC network.
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.equal(result[0].txid, txids[0])
|
||||
assert.equal(result[0].valid, null)
|
||||
|
||||
assert.equal(result[1].txid, txids[1])
|
||||
assert.equal(result[1].valid, null)
|
||||
|
||||
assert.equal(result[2].txid, txids[2])
|
||||
assert.equal(result[2].valid, true)
|
||||
|
||||
// True in validateTxid() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, true)
|
||||
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, true)
|
||||
|
||||
assert.equal(result[5].txid, txids[5])
|
||||
assert.equal(result[5].valid, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Integration tests for the utxo.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
// sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
||||
assert.isArray(result[0].slpUtxos.type1.tokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.tokens)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should sort UTXOs from Electrumx', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
|
||||
it('should sort UTXOs from Utxos.get()', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const utxos = await bchjs.Utxo.get(addr)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -40,13 +40,24 @@ describe('#psf-slp-indexer', () => {
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should get stats on a token', async () => {
|
||||
it('should get stats on a token, without tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
|
||||
it('should get stats on a token, with tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId, true)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
|
||||
@@ -6,6 +6,7 @@ const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
@@ -14,154 +15,39 @@ describe('#UTXO', () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
if (process.env.TESTSLP) {
|
||||
describe('#getOld', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
||||
assert.isArray(result[0].slpUtxos.type1.tokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.tokens)
|
||||
})
|
||||
|
||||
it('should use the whitelist when flag is set', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
// Most token UTXOs should end up in the nullUtxos array.
|
||||
assert.isAbove(result[0].bchUtxos.length, 0)
|
||||
assert.isAbove(result[0].nullUtxos.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should sort UTXOs from Electrumx', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
|
||||
it('should sort UTXOs from Utxos.get()', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const utxos = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId:
|
||||
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId:
|
||||
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId:
|
||||
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId:
|
||||
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
@@ -199,7 +85,7 @@ describe('#UTXO', () => {
|
||||
|
||||
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
|
||||
// The psf-slp-indexer needs to be updated to fix this issue.
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
it('should handle minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
@@ -218,6 +104,15 @@ describe('#UTXO', () => {
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
})
|
||||
|
||||
it('should handle Group NFTs', async () => {
|
||||
const addr = 'bitcoincash:qrnghwrfgccf3s5e9wnglzxegcnhje9rkcwv2eka33'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
/*
|
||||
Unit tests for the IPFS Class.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
const mockData = require('./fixtures/ipfs-mock')
|
||||
|
||||
describe('#IPFS', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#initUppy', () => {
|
||||
it('should initialize uppy', () => {
|
||||
bchjs.IPFS.initUppy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createFileModelServer', () => {
|
||||
it('should throw an error if file does not exist', async () => {
|
||||
try {
|
||||
const path = '/non-existant-file'
|
||||
|
||||
await bchjs.IPFS.createFileModelServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Could not find this file')
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new file model', async () => {
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'post')
|
||||
.resolves({ data: mockData.mockNewFileModel })
|
||||
|
||||
const result = await bchjs.IPFS.createFileModelServer(path)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, 'hostingCostBCH')
|
||||
assert.property(result, 'hostingCostUSD')
|
||||
assert.property(result, 'file')
|
||||
|
||||
assert.property(result.file, 'payloadLink')
|
||||
assert.property(result.file, 'hasBeenPaid')
|
||||
assert.property(result.file, '_id')
|
||||
assert.property(result.file, 'schemaVersion')
|
||||
assert.property(result.file, 'size')
|
||||
assert.property(result.file, 'fileName')
|
||||
assert.property(result.file, 'fileExtension')
|
||||
assert.property(result.file, 'createdTimestamp')
|
||||
assert.property(result.file, 'hostingCost')
|
||||
assert.property(result.file, 'walletIndex')
|
||||
assert.property(result.file, 'bchAddr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#uploadFileServer', () => {
|
||||
it('should throw an error if file does not exist', async () => {
|
||||
try {
|
||||
const path = '/non-existant-file'
|
||||
|
||||
await bchjs.IPFS.uploadFileServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Could not find this file')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
|
||||
await bchjs.IPFS.uploadFileServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the file was not uploaded', async () => {
|
||||
try {
|
||||
const mock = {
|
||||
successful: [],
|
||||
failed: [
|
||||
{
|
||||
id: 'file id'
|
||||
}
|
||||
]
|
||||
}
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
|
||||
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
await bchjs.IPFS.uploadFileServer(path, '5ec562319bfacc745e8d8a52')
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'The file could not be uploaded')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return file object if the file is uploaded', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
|
||||
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
const result = await bchjs.IPFS.uploadFileServer(
|
||||
path,
|
||||
'5ec562319bfacc745e8d8a52'
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'schemaVersion')
|
||||
assert.property(result, 'size')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
assert.property(result, 'fileExtension')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getStatus', () => {
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
await bchjs.IPFS.getStatus()
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('should get data on an unpaid file', async () => {
|
||||
const modelId = '5ec7392c2acfe57aa62e945a'
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'get')
|
||||
.resolves({ data: mockData.unpaidFileData })
|
||||
|
||||
const result = await bchjs.IPFS.getStatus(modelId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'hasBeenPaid')
|
||||
assert.property(result, 'satCost')
|
||||
assert.property(result, 'bchAddr')
|
||||
assert.property(result, 'ipfsHash')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
})
|
||||
|
||||
it('should get data on an unpaid file', async () => {
|
||||
const modelId = '5ec7392c2acfe57aa62e945a'
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'get')
|
||||
.resolves({ data: mockData.paidFileData })
|
||||
|
||||
const result = await bchjs.IPFS.getStatus(modelId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'hasBeenPaid')
|
||||
assert.property(result, 'satCost')
|
||||
assert.property(result, 'bchAddr')
|
||||
assert.property(result, 'ipfsHash')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createFileModelWeb', () => {
|
||||
it('should throw an error if file is undefined', async () => {
|
||||
try {
|
||||
let file
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'File is required')
|
||||
}
|
||||
})
|
||||
it('should throw an error if file is empty', async () => {
|
||||
try {
|
||||
const file = {}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'name' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
size: 5000
|
||||
}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw an error if 'size' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'size\' of number type'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new file model', async () => {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'post')
|
||||
.resolves({ data: mockData.mockNewFileModel })
|
||||
|
||||
const result = await bchjs.IPFS.createFileModelWeb(file)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, 'hostingCostBCH')
|
||||
assert.property(result, 'hostingCostUSD')
|
||||
assert.property(result, 'file')
|
||||
|
||||
assert.property(result.file, 'payloadLink')
|
||||
assert.property(result.file, 'hasBeenPaid')
|
||||
assert.property(result.file, '_id')
|
||||
assert.property(result.file, 'schemaVersion')
|
||||
assert.property(result.file, 'size')
|
||||
assert.property(result.file, 'fileName')
|
||||
assert.property(result.file, 'fileExtension')
|
||||
assert.property(result.file, 'createdTimestamp')
|
||||
assert.property(result.file, 'hostingCost')
|
||||
assert.property(result.file, 'walletIndex')
|
||||
assert.property(result.file, 'bchAddr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#uploadFileWeb', () => {
|
||||
it('should throw an error if file is undefined', async () => {
|
||||
try {
|
||||
let file
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'File is required')
|
||||
}
|
||||
})
|
||||
it('should throw an error if file is empty', async () => {
|
||||
try {
|
||||
const file = {}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'name' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'size' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
type: 'text/plain'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'size\' of number type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'type' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'type\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the file was not uploaded', async () => {
|
||||
try {
|
||||
const mock = {
|
||||
successful: [],
|
||||
failed: [
|
||||
{
|
||||
id: 'file id'
|
||||
}
|
||||
]
|
||||
}
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
|
||||
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
await bchjs.IPFS.uploadFileWeb(file, '5ec562319bfacc745e8d8a52')
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'The file could not be uploaded')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return file object if the file is uploaded', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
|
||||
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
const result = await bchjs.IPFS.uploadFileWeb(
|
||||
file,
|
||||
'5ec562319bfacc745e8d8a52'
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'schemaVersion')
|
||||
assert.property(result, 'size')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
assert.property(result, 'fileExtension')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -249,6 +249,10 @@ describe('#utxo', () => {
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.resolves(mockData.psfSlpIndexerUtxos01)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
||||
.resolves({ txData: { isValidSlp: false } })
|
||||
|
||||
// Mock function to return the same input. Good enough for this test.
|
||||
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
|
||||
|
||||
@@ -278,6 +282,9 @@ describe('#utxo', () => {
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.resolves(mockData.fulcrumUtxos02)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
||||
.resolves({ txData: { isValidSlp: false } })
|
||||
|
||||
// Force psf-slp-indexer to return no UTXOs
|
||||
sandbox
|
||||
|
||||
Reference in New Issue
Block a user