mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
+2
-1
@@ -17,8 +17,9 @@
|
||||
"test": "npm run lint && npm run test-v3",
|
||||
"lint": "standard --env mocha --fix",
|
||||
"test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v3/",
|
||||
"test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/encryption.js",
|
||||
"test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/slp.js",
|
||||
"test:integration": "mocha test/v3/integration",
|
||||
"test:integration:slpdb": "mocha --timeout 25000 test/v3/integration/slp*.js",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v3/",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/routes/v3 -o docs"
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
const axios = require('axios')
|
||||
|
||||
const SLPSDK = require('@chris.troutner/bch-js')
|
||||
const SLP = new SLPSDK()
|
||||
|
||||
class Slpdb {
|
||||
// Gets transaction history for all tokens for an address. Can also specify
|
||||
// block height, but defaults to 0.
|
||||
async getHistoricalSlpTransactions (addressList, fromBlock = 0) {
|
||||
// Build SLPDB or query from addressList
|
||||
const orQueryArray = []
|
||||
for (const address of addressList) {
|
||||
const cashAddress = SLP.SLP.Address.toCashAddress(address)
|
||||
const slpAddress = SLP.SLP.Address.toSLPAddress(address)
|
||||
|
||||
const cashQuery = {
|
||||
'in.e.a': cashAddress.slice(12)
|
||||
}
|
||||
const slpQuery = {
|
||||
'slp.detail.outputs.address': slpAddress
|
||||
}
|
||||
|
||||
orQueryArray.push(cashQuery)
|
||||
orQueryArray.push(slpQuery)
|
||||
}
|
||||
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
find: {
|
||||
db: ['c', 'u'],
|
||||
$query: {
|
||||
$or: orQueryArray,
|
||||
'slp.valid': true,
|
||||
'blk.i': {
|
||||
$not: {
|
||||
$lte: fromBlock
|
||||
}
|
||||
}
|
||||
},
|
||||
$orderby: {
|
||||
'blk.i': -1
|
||||
}
|
||||
},
|
||||
project: {
|
||||
_id: 0,
|
||||
'tx.h': 1,
|
||||
'in.i': 1,
|
||||
'in.e': 1,
|
||||
'out.e': 1,
|
||||
'out.a': 1,
|
||||
'slp.detail': 1,
|
||||
blk: 1
|
||||
},
|
||||
limit: 500
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.runQuery(query)
|
||||
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
let transactions = []
|
||||
|
||||
// Add confirmed transactions
|
||||
if (result.data && result.data.c) {
|
||||
transactions = transactions.concat(result.data.c)
|
||||
}
|
||||
|
||||
// Add unconfirmed transactions
|
||||
if (result.data && result.data.u) {
|
||||
transactions = transactions.concat(result.data.u)
|
||||
}
|
||||
|
||||
return transactions
|
||||
}
|
||||
|
||||
async getTokenStats (tokenId) {
|
||||
const [
|
||||
totalMinted,
|
||||
totalBurned,
|
||||
tokenDetails,
|
||||
circulatingSupply
|
||||
] = await Promise.all([
|
||||
this.getTotalMinted(tokenId),
|
||||
this.getTotalBurned(tokenId),
|
||||
this.getTokenDetails(tokenId),
|
||||
this.getTotalCirculating(tokenId)
|
||||
])
|
||||
|
||||
tokenDetails.totalMinted = tokenDetails.initialTokenQty + totalMinted
|
||||
tokenDetails.totalBurned = totalBurned
|
||||
|
||||
// tokenDetails.circulatingSupply =
|
||||
// tokenDetails.totalMinted - tokenDetails.totalBurned
|
||||
tokenDetails.circulatingSupply = circulatingSupply
|
||||
|
||||
return tokenDetails
|
||||
}
|
||||
|
||||
generateCredentials () {
|
||||
// Generate the Basic Authentication header for a private instance of SLPDB.
|
||||
const SLPDB_PASS = process.env.SLPDB_PASS
|
||||
? process.env.SLPDB_PASS
|
||||
: 'BITBOX'
|
||||
const username = 'BITBOX'
|
||||
const password = SLPDB_PASS
|
||||
const combined = `${username}:${password}`
|
||||
var base64Credential = Buffer.from(combined).toString('base64')
|
||||
var readyCredential = `Basic ${base64Credential}`
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
authorization: readyCredential,
|
||||
timeout: 30000
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
async runQuery (query) {
|
||||
const queryString = JSON.stringify(query)
|
||||
const queryBase64 = Buffer.from(queryString).toString('base64')
|
||||
const url = `${process.env.SLPDB_URL}q/${queryBase64}`
|
||||
|
||||
const options = this.generateCredentials()
|
||||
|
||||
const response = await axios.get(url, options)
|
||||
return response
|
||||
}
|
||||
|
||||
async getTotalMinted (tokenId) {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ['g'],
|
||||
aggregate: [
|
||||
{
|
||||
$match: {
|
||||
'tokenDetails.tokenIdHex': tokenId,
|
||||
'graphTxn.outputs.status': {
|
||||
$in: [
|
||||
'BATON_SPENT_IN_MINT',
|
||||
'BATON_UNSPENT',
|
||||
'BATON_SPENT_NOT_IN_MINT'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$unwind: '$graphTxn.outputs'
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
count: {
|
||||
$sum: '$graphTxn.outputs.slpAmount'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.runQuery(query)
|
||||
|
||||
if (!result.data.g.length) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return parseFloat(result.data.g[0].count)
|
||||
}
|
||||
|
||||
async getTotalCirculating (tokenId) {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ['g'],
|
||||
aggregate: [
|
||||
{
|
||||
$match: {
|
||||
'tokenDetails.tokenIdHex': tokenId,
|
||||
'graphTxn.outputs': {
|
||||
$elemMatch: {
|
||||
status: 'UNSPENT',
|
||||
slpAmount: { $gte: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ $unwind: '$graphTxn.outputs' },
|
||||
{
|
||||
$match: {
|
||||
'graphTxn.outputs.status': 'UNSPENT',
|
||||
'graphTxn.outputs.slpAmount': { $gte: 0 }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
circulating_supply: {
|
||||
$sum: '$graphTxn.outputs.slpAmount'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
limit: 100000
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.runQuery(query)
|
||||
// console.log(`result.data: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
if (!result.data.g.length) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return parseFloat(result.data.g[0].circulating_supply)
|
||||
}
|
||||
|
||||
async getTotalBurned (tokenId) {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ['g'],
|
||||
aggregate: [
|
||||
{
|
||||
$match: {
|
||||
'tokenDetails.tokenIdHex': tokenId,
|
||||
'graphTxn.outputs.status': {
|
||||
$in: [
|
||||
'SPENT_NON_SLP',
|
||||
'BATON_SPENT_INVALID_SLP',
|
||||
'SPENT_INVALID_SLP',
|
||||
'BATON_SPENT_NON_SLP',
|
||||
'MISSING_BCH_VOUT',
|
||||
'BATON_MISSING_BCH_VOUT',
|
||||
'BATON_SPENT_NOT_IN_MINT',
|
||||
'EXCESS_INPUT_BURNED'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$unwind: '$graphTxn.outputs'
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
'graphTxn.outputs.status': {
|
||||
$in: [
|
||||
'SPENT_NON_SLP',
|
||||
'BATON_SPENT_INVALID_SLP',
|
||||
'SPENT_INVALID_SLP',
|
||||
'BATON_SPENT_NON_SLP',
|
||||
'MISSING_BCH_VOUT',
|
||||
'BATON_MISSING_BCH_VOUT',
|
||||
'BATON_SPENT_NOT_IN_MINT',
|
||||
'EXCESS_INPUT_BURNED'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
count: {
|
||||
$sum: '$graphTxn.outputs.slpAmount'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.runQuery(query)
|
||||
|
||||
if (!result.data.g.length) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return parseFloat(result.data.g[0].count)
|
||||
}
|
||||
|
||||
async getTokenDetails (tokenId) {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ['t'],
|
||||
find: {
|
||||
$query: {
|
||||
'tokenDetails.tokenIdHex': tokenId
|
||||
}
|
||||
},
|
||||
project: { tokenDetails: 1, tokenStats: 1, _id: 0 },
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.runQuery(query)
|
||||
|
||||
if (!result.data.t.length) {
|
||||
throw new Error('Token could not be found')
|
||||
}
|
||||
|
||||
const token = this.formatTokenOutput(result.data.t[0])
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
formatTokenOutput (token) {
|
||||
// console.log(`token: ${JSON.stringify(token, null, 2)}`)
|
||||
|
||||
token.tokenDetails.id = token.tokenDetails.tokenIdHex
|
||||
delete token.tokenDetails.tokenIdHex
|
||||
token.tokenDetails.documentHash = token.tokenDetails.documentSha256Hex
|
||||
delete token.tokenDetails.documentSha256Hex
|
||||
token.tokenDetails.initialTokenQty = parseFloat(
|
||||
token.tokenDetails.genesisOrMintQuantity
|
||||
)
|
||||
delete token.tokenDetails.genesisOrMintQuantity
|
||||
delete token.tokenDetails.transactionType
|
||||
delete token.tokenDetails.batonVout
|
||||
delete token.tokenDetails.sendOutputs
|
||||
|
||||
token.tokenDetails.blockCreated = token.tokenStats.block_created
|
||||
token.tokenDetails.blockLastActiveSend =
|
||||
token.tokenStats.block_last_active_send
|
||||
token.tokenDetails.blockLastActiveMint =
|
||||
token.tokenStats.block_last_active_mint
|
||||
token.tokenDetails.txnsSinceGenesis =
|
||||
token.tokenStats.qty_valid_txns_since_genesis
|
||||
token.tokenDetails.validAddresses =
|
||||
token.tokenStats.qty_valid_token_addresses
|
||||
token.tokenDetails.mintingBatonStatus =
|
||||
token.tokenStats.minting_baton_status
|
||||
|
||||
delete token.tokenStats.block_last_active_send
|
||||
delete token.tokenStats.block_last_active_mint
|
||||
delete token.tokenStats.qty_valid_txns_since_genesis
|
||||
delete token.tokenStats.qty_valid_token_addresses
|
||||
|
||||
token.tokenDetails.timestampUnix = token.tokenDetails.timestamp_unix
|
||||
delete token.tokenDetails.timestamp_unix
|
||||
|
||||
return token.tokenDetails
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Slpdb
|
||||
+267
-222
@@ -7,6 +7,9 @@ const BigNumber = require('bignumber.js')
|
||||
|
||||
const RouteUtils = require('../../util/route-utils')
|
||||
const routeUtils = new RouteUtils()
|
||||
|
||||
const Slpdb = require('./services/slpdb')
|
||||
|
||||
// const strftime = require('strftime')
|
||||
const wlogger = require('../../util/winston-logging')
|
||||
|
||||
@@ -50,6 +53,7 @@ class Slp {
|
||||
_this.BigNumber = BigNumber
|
||||
_this.bchjs = bchjs
|
||||
_this.rawTransactions = rawTransactions
|
||||
_this.slpdb = new Slpdb()
|
||||
|
||||
_this.router = router
|
||||
|
||||
@@ -60,14 +64,24 @@ class Slp {
|
||||
_this.router.get('/balancesForAddress/:address', _this.balancesForAddress)
|
||||
_this.router.post('/balancesForAddress', _this.balancesForAddressBulk)
|
||||
_this.router.get('/balancesForToken/:tokenId', _this.balancesForTokenSingle)
|
||||
_this.router.get('/balance/:address/:tokenId', _this.balancesForAddressByTokenID)
|
||||
_this.router.get(
|
||||
'/balance/:address/:tokenId',
|
||||
_this.balancesForAddressByTokenID
|
||||
)
|
||||
_this.router.get('/convert/:address', _this.convertAddressSingle)
|
||||
_this.router.post('/convert', _this.convertAddressBulk)
|
||||
_this.router.post('/validateTxid', _this.validateBulk)
|
||||
_this.router.get('/validateTxid/:txid', _this.validateSingle)
|
||||
_this.router.get('/txDetails/:txid', _this.txDetails)
|
||||
_this.router.get('/tokenStats/:tokenId', _this.tokenStats)
|
||||
_this.router.get('/transactions/:tokenId/:address', _this.txsTokenIdAddressSingle)
|
||||
_this.router.get(
|
||||
'/transactions/:tokenId/:address',
|
||||
_this.txsTokenIdAddressSingle
|
||||
)
|
||||
_this.router.get(
|
||||
'/transactionHistoryAllTokens/:address',
|
||||
_this.txsByAddressSingle
|
||||
)
|
||||
}
|
||||
|
||||
// DRY error handler.
|
||||
@@ -98,18 +112,24 @@ class Slp {
|
||||
|
||||
token.tokenDetails.blockCreated = token.tokenStats.block_created
|
||||
token.tokenDetails.blockLastActiveSend =
|
||||
token.tokenStats.block_last_active_send
|
||||
token.tokenStats.block_last_active_send
|
||||
token.tokenDetails.blockLastActiveMint =
|
||||
token.tokenStats.block_last_active_mint
|
||||
token.tokenStats.block_last_active_mint
|
||||
token.tokenDetails.txnsSinceGenesis =
|
||||
token.tokenStats.qty_valid_txns_since_genesis
|
||||
token.tokenDetails.validAddresses = token.tokenStats.qty_valid_token_addresses
|
||||
token.tokenDetails.totalMinted = parseFloat(token.tokenStats.qty_token_minted)
|
||||
token.tokenDetails.totalBurned = parseFloat(token.tokenStats.qty_token_burned)
|
||||
token.tokenStats.qty_valid_txns_since_genesis
|
||||
token.tokenDetails.validAddresses =
|
||||
token.tokenStats.qty_valid_token_addresses
|
||||
token.tokenDetails.totalMinted = parseFloat(
|
||||
token.tokenStats.qty_token_minted
|
||||
)
|
||||
token.tokenDetails.totalBurned = parseFloat(
|
||||
token.tokenStats.qty_token_burned
|
||||
)
|
||||
token.tokenDetails.circulatingSupply = parseFloat(
|
||||
token.tokenStats.qty_token_circulating_supply
|
||||
)
|
||||
token.tokenDetails.mintingBatonStatus = token.tokenStats.minting_baton_status
|
||||
token.tokenDetails.mintingBatonStatus =
|
||||
token.tokenStats.minting_baton_status
|
||||
|
||||
delete token.tokenStats.block_last_active_send
|
||||
delete token.tokenStats.block_last_active_mint
|
||||
@@ -123,17 +143,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/list/{tokenId} List single SLP token by id.
|
||||
* @apiName List single SLP token by id.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns the list single SLP token by id.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/list/{tokenId} List single SLP token by id.
|
||||
* @apiName List single SLP token by id.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns the list single SLP token by id.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/list/259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async listSingleToken (req, res, next) {
|
||||
try {
|
||||
const tokenId = req.params.tokenId
|
||||
@@ -156,17 +176,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/list/ List Bulk SLP token .
|
||||
* @apiName List Bulk SLP token.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns the list bulk SLP token by id.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {post} /slp/list/ List Bulk SLP token .
|
||||
* @apiName List Bulk SLP token.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns the list bulk SLP token by id.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/list" -H "accept:application/json" -H "Content-Type: application/json" -d '{"tokenIds":["7380843cd1089a1a01783f86af37734dc99667a1cdc577391b5f6ea42fc1bfb4","9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
async listBulkToken (req, res, next) {
|
||||
try {
|
||||
const tokenIds = req.body.tokenIds
|
||||
@@ -216,14 +236,14 @@ class Slp {
|
||||
const txids = []
|
||||
|
||||
if (tokenRes.data.t.length) {
|
||||
tokenRes.data.t.forEach(token => {
|
||||
tokenRes.data.t.forEach((token) => {
|
||||
txids.push(token.tokenDetails.tokenIdHex)
|
||||
token = _this.formatTokenOutput(token)
|
||||
formattedTokens.push(token.tokenDetails)
|
||||
})
|
||||
}
|
||||
|
||||
tokenIds.forEach(tokenId => {
|
||||
tokenIds.forEach((tokenId) => {
|
||||
if (!txids.includes(tokenId)) {
|
||||
formattedTokens.push({
|
||||
id: tokenId,
|
||||
@@ -277,14 +297,14 @@ class Slp {
|
||||
const formattedTokens = []
|
||||
|
||||
if (tokenRes.data.t.length) {
|
||||
tokenRes.data.t.forEach(token => {
|
||||
tokenRes.data.t.forEach((token) => {
|
||||
token = _this.formatTokenOutput(token)
|
||||
formattedTokens.push(token.tokenDetails)
|
||||
})
|
||||
}
|
||||
|
||||
let t
|
||||
formattedTokens.forEach(token => {
|
||||
formattedTokens.forEach((token) => {
|
||||
if (token.id === tokenId) t = token
|
||||
})
|
||||
|
||||
@@ -304,17 +324,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/balancesForAddress/{address} List SLP balance for address.
|
||||
* @apiName List SLP balance for address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List SLP balance for address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/balancesForAddress/{address} List SLP balance for address.
|
||||
* @apiName List SLP balance for address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List SLP balance for address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balancesForAddress/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
// Retrieve token balances for all tokens for a single address.
|
||||
async balancesForAddress (req, res, next) {
|
||||
try {
|
||||
@@ -342,7 +362,7 @@ class Slp {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error:
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -416,7 +436,7 @@ class Slp {
|
||||
|
||||
const tokenIds = []
|
||||
if (tokenRes.data.g.length > 0) {
|
||||
tokenRes.data.g = tokenRes.data.g.map(token => {
|
||||
tokenRes.data.g = tokenRes.data.g.map((token) => {
|
||||
token.tokenId = token._id
|
||||
tokenIds.push(token.tokenId)
|
||||
token.balance = parseFloat(token.balanceString)
|
||||
@@ -426,7 +446,7 @@ class Slp {
|
||||
return token
|
||||
})
|
||||
|
||||
const promises = tokenIds.map(async tokenId => {
|
||||
const promises = tokenIds.map(async (tokenId) => {
|
||||
const query2 = {
|
||||
v: 3,
|
||||
q: {
|
||||
@@ -454,7 +474,6 @@ class Slp {
|
||||
baseURL: url2,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
const tokenRes2 = await _this.axios.request(opt)
|
||||
// console.log(`tokenRes2.data: ${JSON.stringify(tokenRes2.data, null, 2)}`)
|
||||
@@ -464,8 +483,8 @@ class Slp {
|
||||
|
||||
const details = await _this.axios.all(promises)
|
||||
|
||||
tokenRes.data.g = tokenRes.data.g.map(token => {
|
||||
details.forEach(detail => {
|
||||
tokenRes.data.g = tokenRes.data.g.map((token) => {
|
||||
details.forEach((detail) => {
|
||||
if (detail.t[0].tokenDetails.tokenIdHex === token.tokenId) {
|
||||
token.decimalCount = detail.t[0].tokenDetails.decimals
|
||||
}
|
||||
@@ -489,17 +508,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/balancesForAddress List SLP balances for an array of addresses.
|
||||
* @apiName List SLP balances for an array of addresses.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns SLP balances for an array of addresses.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {post} /slp/balancesForAddress List SLP balances for an array of addresses.
|
||||
* @apiName List SLP balances for an array of addresses.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns SLP balances for an array of addresses.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/balancesForAddress" -d "{\"addresses\":[\"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57\"]}" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async balancesForAddressBulk (req, res, next) {
|
||||
try {
|
||||
const addresses = req.body.addresses
|
||||
@@ -550,7 +569,7 @@ class Slp {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error:
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -559,7 +578,7 @@ class Slp {
|
||||
|
||||
// Collect an array of promises, one for each request to slpserve.
|
||||
// This is a nested array of promises.
|
||||
const balancesPromises = addresses.map(async address => {
|
||||
const balancesPromises = addresses.map(async (address) => {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
@@ -622,7 +641,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
// console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`)
|
||||
@@ -630,7 +648,7 @@ class Slp {
|
||||
const tokenIds = []
|
||||
|
||||
if (tokenRes.data.g.length > 0) {
|
||||
tokenRes.data.g = tokenRes.data.g.map(token => {
|
||||
tokenRes.data.g = tokenRes.data.g.map((token) => {
|
||||
token.tokenId = token._id
|
||||
tokenIds.push(token.tokenId)
|
||||
token.balance = parseFloat(token.balanceString)
|
||||
@@ -640,7 +658,7 @@ class Slp {
|
||||
}
|
||||
|
||||
// Collect another array of promises.
|
||||
const promises = tokenIds.map(async tokenId => {
|
||||
const promises = tokenIds.map(async (tokenId) => {
|
||||
const query2 = {
|
||||
v: 3,
|
||||
q: {
|
||||
@@ -667,7 +685,6 @@ class Slp {
|
||||
baseURL: url2,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
const tokenRes2 = await _this.axios.request(opt)
|
||||
// console.log(`tokenRes2.data: ${JSON.stringify(tokenRes2.data, null, 2)}`)
|
||||
@@ -678,8 +695,8 @@ class Slp {
|
||||
// Wait for all the promises to resolve.
|
||||
const details = await Promise.all(promises)
|
||||
|
||||
tokenRes.data.g = tokenRes.data.g.map(token => {
|
||||
details.forEach(detail => {
|
||||
tokenRes.data.g = tokenRes.data.g.map((token) => {
|
||||
details.forEach((detail) => {
|
||||
if (detail.t[0].tokenDetails.tokenIdHex === token.tokenId) {
|
||||
token.decimalCount = detail.t[0].tokenDetails.decimals
|
||||
}
|
||||
@@ -706,17 +723,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/balancesForToken/{TokenId} List SLP addresses and balances for tokenId.
|
||||
* @apiName List SLP addresses and balances for tokenId.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List SLP addresses and balances for tokenId.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/balancesForToken/{TokenId} List SLP addresses and balances for tokenId.
|
||||
* @apiName List SLP addresses and balances for tokenId.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List SLP addresses and balances for tokenId.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balancesForToken/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
// Retrieve token balances for all addresses by single tokenId.
|
||||
async balancesForTokenSingle (req, res, next) {
|
||||
try {
|
||||
@@ -785,7 +802,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
// Get data from SLPDB.
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
@@ -814,17 +830,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/balance/{address}/{TokenId} List single slp token balance for address.
|
||||
* @apiName List single slp token balance for address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List single slp token balance for address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balance/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m/1cda254d0a995c713b7955298ed246822bee487458cd9747a91d9e81d9d28125" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/balance/{address}/{TokenId} List single slp token balance for address.
|
||||
* @apiName List single slp token balance for address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Returns List single slp token balance for address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/balance/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m/1cda254d0a995c713b7955298ed246822bee487458cd9747a91d9e81d9d28125" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
// Retrieve token balances for a single token class, for a single address.
|
||||
async balancesForAddressByTokenID (req, res, next) {
|
||||
try {
|
||||
@@ -858,7 +874,7 @@ class Slp {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error:
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -928,7 +944,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`)
|
||||
@@ -943,7 +958,7 @@ class Slp {
|
||||
}
|
||||
|
||||
if (tokenRes.data.g.length > 0) {
|
||||
tokenRes.data.g.forEach(async token => {
|
||||
tokenRes.data.g.forEach(async (token) => {
|
||||
if (token._id === tokenId) {
|
||||
resVal = {
|
||||
cashAddress: _this.bchjs.SLP.Address.toCashAddress(slpAddr),
|
||||
@@ -979,17 +994,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/convert/{address} Convert address to slpAddr, cashAddr and legacy.
|
||||
* @apiName Convert address to slpAddr, cashAddr and legacy.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Convert address to slpAddr, cashAddr and legacy.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/convert/{address} Convert address to slpAddr, cashAddr and legacy.
|
||||
* @apiName Convert address to slpAddr, cashAddr and legacy.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Convert address to slpAddr, cashAddr and legacy.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/convert/simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async convertAddressSingle (req, res, next) {
|
||||
try {
|
||||
const address = req.params.address
|
||||
@@ -1009,7 +1024,9 @@ class Slp {
|
||||
}
|
||||
obj.slpAddress = slpAddr
|
||||
obj.cashAddress = _this.bchjs.SLP.Address.toCashAddress(slpAddr)
|
||||
obj.legacyAddress = _this.bchjs.SLP.Address.toLegacyAddress(obj.cashAddress)
|
||||
obj.legacyAddress = _this.bchjs.SLP.Address.toLegacyAddress(
|
||||
obj.cashAddress
|
||||
)
|
||||
|
||||
res.status(200)
|
||||
return res.json(obj)
|
||||
@@ -1023,17 +1040,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/convert/ Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
* @apiName Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {post} /slp/convert/ Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
* @apiName Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Convert multiple addresses to cash, legacy and simpleledger format.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/convert" -H "accept:application/json" -H "Content-Type: application/json" -d '{"addresses":["simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
async convertAddressBulk (req, res, next) {
|
||||
const addresses = req.body.addresses
|
||||
|
||||
@@ -1073,7 +1090,9 @@ class Slp {
|
||||
}
|
||||
obj.slpAddress = slpAddr
|
||||
obj.cashAddress = _this.bchjs.SLP.Address.toCashAddress(slpAddr)
|
||||
obj.legacyAddress = _this.bchjs.SLP.Address.toLegacyAddress(obj.cashAddress)
|
||||
obj.legacyAddress = _this.bchjs.SLP.Address.toLegacyAddress(
|
||||
obj.cashAddress
|
||||
)
|
||||
|
||||
convertedAddresses.push(obj)
|
||||
}
|
||||
@@ -1083,17 +1102,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /slp/validateTxid/ Validate multiple SLP transactions by txid.
|
||||
* @apiName Validate multiple SLP transactions by txid.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Validate multiple SLP transactions by txid.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {post} /slp/validateTxid/ Validate multiple SLP transactions by txid.
|
||||
* @apiName Validate multiple SLP transactions by txid.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Validate multiple SLP transactions by txid.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X POST "https://api.fullstack.cash/v3/slp/validateTxid" -H "accept:application/json" -H "Content-Type: application/json" -d '{"txids":["f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a","fb0eeaa501a6e1acb721669c62a3f70741f48ae0fd7f4b8e1d72088785c51952"]}'
|
||||
*
|
||||
*
|
||||
*/
|
||||
async validateBulk (req, res, next) {
|
||||
try {
|
||||
const txids = req.body.txids
|
||||
@@ -1137,7 +1156,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
// console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`)
|
||||
@@ -1149,7 +1167,7 @@ class Slp {
|
||||
|
||||
const tokenIds = []
|
||||
if (concatArray.length > 0) {
|
||||
concatArray.forEach(token => {
|
||||
concatArray.forEach((token) => {
|
||||
tokenIds.push(token.tx.h) // txid
|
||||
|
||||
const validationResult = {
|
||||
@@ -1167,7 +1185,7 @@ class Slp {
|
||||
|
||||
// If a user-provided txid doesn't exist in the data, add it with
|
||||
// valid:false property.
|
||||
txids.forEach(txid => {
|
||||
txids.forEach((txid) => {
|
||||
if (!tokenIds.includes(txid)) {
|
||||
formattedTokens.push({
|
||||
txid: txid,
|
||||
@@ -1185,7 +1203,7 @@ class Slp {
|
||||
const thisTxid = txids[i]
|
||||
|
||||
// Find the element that matches the current txid.
|
||||
const elem = formattedTokens.filter(x => x.txid === thisTxid)
|
||||
const elem = formattedTokens.filter((x) => x.txid === thisTxid)
|
||||
|
||||
newOutput.push(elem[0])
|
||||
}
|
||||
@@ -1204,17 +1222,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid.
|
||||
* @apiName Validate single SLP transaction by txid.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Validate single SLP transaction by txid.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid.
|
||||
* @apiName Validate single SLP transaction by txid.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Validate single SLP transaction by txid.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/validateTxid/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async validateSingle (req, res, next) {
|
||||
try {
|
||||
const txid = req.params.txid
|
||||
@@ -1249,7 +1267,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
// Get data from SLPDB.
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
@@ -1267,7 +1284,9 @@ class Slp {
|
||||
txid: concatArray[0].tx.h,
|
||||
valid: concatArray[0].slp.valid
|
||||
}
|
||||
if (!result.valid) result.invalidReason = concatArray[0].slp.invalidReason
|
||||
if (!result.valid) {
|
||||
result.invalidReason = concatArray[0].slp.invalidReason
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200)
|
||||
@@ -1286,17 +1305,17 @@ class Slp {
|
||||
// }
|
||||
|
||||
/**
|
||||
* @api {get} /slp/txDetails/{txid} SLP transaction details.
|
||||
* @apiName SLP transaction details.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Transaction details on a token transfer.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/txDetails/{txid} SLP transaction details.
|
||||
* @apiName SLP transaction details.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Transaction details on a token transfer.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/txDetails/8ab4ac5dea3f9024e3954ee5b61452955d659a34561f79ef62ac44e133d0980e" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async txDetails (req, res, next) {
|
||||
try {
|
||||
// Validate input parameter
|
||||
@@ -1332,7 +1351,6 @@ class Slp {
|
||||
baseURL: url,
|
||||
headers: options.headers,
|
||||
timeout: options.timeout
|
||||
|
||||
}
|
||||
// Get token data from SLPDB
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
@@ -1349,7 +1367,10 @@ class Slp {
|
||||
|
||||
// Get information on the transaction from Insight API.
|
||||
// const retData = await transactions.transactionsFromInsight(txid)
|
||||
const retData = await _this.rawTransactions.getRawTransactionsFromNode(txid, true)
|
||||
const retData = await _this.rawTransactions.getRawTransactionsFromNode(
|
||||
txid,
|
||||
true
|
||||
)
|
||||
// console.log(`retData: ${JSON.stringify(retData, null, 2)}`)
|
||||
|
||||
// Return both the tx data from Insight and the formatted token information.
|
||||
@@ -1374,60 +1395,29 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/tokenStats/{tokenId} List stats for a single slp token.
|
||||
* @apiName List stats for a single slp token.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/tokenStats/{tokenId} List stats for a single slp token.
|
||||
* @apiName List stats for a single slp token.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/tokenStats/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
async tokenStats (req, res, next) {
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === '') {
|
||||
res.status(400)
|
||||
return res.json({ error: 'tokenId can not be empty' })
|
||||
}
|
||||
|
||||
try {
|
||||
const query = {
|
||||
v: 3,
|
||||
q: {
|
||||
db: ['t'],
|
||||
find: {
|
||||
$query: {
|
||||
'tokenDetails.tokenIdHex': tokenId
|
||||
}
|
||||
},
|
||||
project: { tokenDetails: 1, tokenStats: 1, _id: 0 },
|
||||
limit: 10
|
||||
}
|
||||
const tokenId = req.params.tokenId
|
||||
if (!tokenId || tokenId === '') {
|
||||
res.status(400)
|
||||
return res.json({ error: 'tokenId can not be empty' })
|
||||
}
|
||||
|
||||
const s = JSON.stringify(query)
|
||||
const b64 = Buffer.from(s).toString('base64')
|
||||
const url = `${process.env.SLPDB_URL}q/${b64}`
|
||||
const opt = {
|
||||
method: 'get',
|
||||
baseURL: url
|
||||
}
|
||||
// Get data from BitDB.
|
||||
const tokenRes = await _this.axios.request(opt)
|
||||
|
||||
const formattedTokens = []
|
||||
|
||||
if (tokenRes.data.t.length) {
|
||||
tokenRes.data.t.forEach(token => {
|
||||
token = _this.formatTokenOutput(token)
|
||||
formattedTokens.push(token.tokenDetails)
|
||||
})
|
||||
}
|
||||
const tokenStats = await _this.slpdb.getTokenStats(tokenId)
|
||||
|
||||
res.status(200)
|
||||
return res.json(formattedTokens[0])
|
||||
return res.json(tokenStats)
|
||||
} catch (err) {
|
||||
wlogger.error('Error in slp.ts/tokenStats().', err)
|
||||
return _this.errorHandler(err, res)
|
||||
@@ -1436,17 +1426,17 @@ class Slp {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /slp/transactions/{tokenId}/{address} SLP transactions by tokenId and address.
|
||||
* @apiName SLP transactions by tokenId and address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Transactions by tokenId and address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
* @api {get} /slp/transactions/{tokenId}/{address} SLP transactions by tokenId and address.
|
||||
* @apiName SLP transactions by tokenId and address.
|
||||
* @apiGroup SLP
|
||||
* @apiDescription Transactions by tokenId and address.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -X GET "https://api.fullstack.cash/v3/slp/transactions/9ba379fe8171176d4e7e6771d9a24cd0e044c7b788d5f86a3fdf80904832b2c0/simpleledger:qrxa0unrn67rtn85v7asfddhhth43ecnxua0antk2l" -H "accept:application/json"
|
||||
*
|
||||
*
|
||||
*/
|
||||
// Retrieve transactions by tokenId and address.
|
||||
async txsTokenIdAddressSingle (req, res, next) {
|
||||
try {
|
||||
@@ -1547,7 +1537,7 @@ class Slp {
|
||||
const tokenOutputs = transaction.slp.detail.outputs
|
||||
|
||||
const sendOutputs = ['0']
|
||||
tokenOutputs.map(x => {
|
||||
tokenOutputs.map((x) => {
|
||||
const string = parseFloat(x.amount) * 100000000
|
||||
sendOutputs.push(string.toString())
|
||||
})
|
||||
@@ -1564,6 +1554,61 @@ class Slp {
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// Retrieve transactions by address.
|
||||
async txsByAddressSingle (req, res, next) {
|
||||
try {
|
||||
// Validate the input data.
|
||||
const address = req.params.address
|
||||
if (!address || address === '') {
|
||||
res.status(400)
|
||||
return res.json({ error: 'address can not be empty' })
|
||||
}
|
||||
|
||||
// Ensure the input is a valid BCH address.
|
||||
try {
|
||||
_this.bchjs.SLP.Address.toCashAddress(address)
|
||||
} catch (err) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error: `Invalid BCH address. Double check your address is valid: ${address}`
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure it is using the correct network.
|
||||
const cashAddr = _this.bchjs.SLP.Address.toCashAddress(address)
|
||||
const networkIsValid = routeUtils.validateNetwork(cashAddr)
|
||||
if (!networkIsValid) {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
error:
|
||||
'Invalid network. Trying to use a testnet address on mainnet, or vice versa.'
|
||||
})
|
||||
}
|
||||
|
||||
const transactions = await _this.slpdb.getHistoricalSlpTransactions([
|
||||
address
|
||||
])
|
||||
// console.log(`transactions: ${JSON.stringify(transactions, null, 2)}`)
|
||||
|
||||
res.status(200)
|
||||
return res.json(transactions)
|
||||
} catch (err) {
|
||||
wlogger.error('Error in slp.ts/txsByAddressSingle().', err)
|
||||
|
||||
// Decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
}
|
||||
|
||||
res.status(500)
|
||||
return res.json({
|
||||
error: `Error in /transactionHistoryAllTokens/:address: ${err.message}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Slp
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
These integration tests need to be run against a live SLPDB. They query
|
||||
against a live SLPDB and test the results against known token stats.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const assert = require('chai').assert
|
||||
// const axios = require('axios')
|
||||
|
||||
// Used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
// Exit if SLPDB URL is not defined.
|
||||
if (!process.env.SLPDB_URL) {
|
||||
throw new Error('SLPDB_URL and SLPDB_PASS must be defined in order to run these tests.')
|
||||
}
|
||||
|
||||
const SLP = require('../../../src/routes/v3/slp')
|
||||
const slp = new SLP()
|
||||
|
||||
const { mockReq, mockRes } = require('../mocks/express-mocks')
|
||||
|
||||
describe('#slp', () => {
|
||||
let req, res
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock the req and res objects used by Express routes.
|
||||
req = mockReq
|
||||
res = mockRes
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should get token stats for token with no mint baton', async () => {
|
||||
req.params.tokenId =
|
||||
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
||||
// 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
|
||||
const result = await slp.tokenStats(req, res)
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
// Assert that expected properties exist.
|
||||
assert.property(result, 'decimals')
|
||||
assert.property(result, 'timestamp')
|
||||
assert.property(result, 'versionType')
|
||||
assert.property(result, 'documentUri')
|
||||
assert.property(result, 'symbol')
|
||||
assert.property(result, 'name')
|
||||
assert.property(result, 'containsBaton')
|
||||
assert.property(result, 'id')
|
||||
assert.property(result, 'documentHash')
|
||||
assert.property(result, 'initialTokenQty')
|
||||
assert.property(result, 'blockCreated')
|
||||
assert.property(result, 'blockLastActiveSend')
|
||||
assert.property(result, 'blockLastActiveMint')
|
||||
assert.property(result, 'txnsSinceGenesis')
|
||||
assert.property(result, 'validAddresses')
|
||||
assert.property(result, 'mintingBatonStatus')
|
||||
assert.property(result, 'timestampUnix')
|
||||
assert.property(result, 'totalMinted')
|
||||
assert.property(result, 'totalBurned')
|
||||
assert.property(result, 'circulatingSupply')
|
||||
|
||||
// baton was never created.
|
||||
assert.equal(result.containsBaton, false)
|
||||
})
|
||||
|
||||
it('should get token stats for token with a mint baton', async () => {
|
||||
req.params.tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
|
||||
const result = await slp.tokenStats(req, res)
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
// Assert that expected properties exist.
|
||||
assert.property(result, 'decimals')
|
||||
assert.property(result, 'timestamp')
|
||||
assert.property(result, 'versionType')
|
||||
assert.property(result, 'documentUri')
|
||||
assert.property(result, 'symbol')
|
||||
assert.property(result, 'name')
|
||||
assert.property(result, 'containsBaton')
|
||||
assert.property(result, 'id')
|
||||
assert.property(result, 'documentHash')
|
||||
assert.property(result, 'initialTokenQty')
|
||||
assert.property(result, 'blockCreated')
|
||||
assert.property(result, 'blockLastActiveSend')
|
||||
assert.property(result, 'blockLastActiveMint')
|
||||
assert.property(result, 'txnsSinceGenesis')
|
||||
assert.property(result, 'validAddresses')
|
||||
assert.property(result, 'mintingBatonStatus')
|
||||
assert.property(result, 'timestampUnix')
|
||||
assert.property(result, 'totalMinted')
|
||||
assert.property(result, 'totalBurned')
|
||||
assert.property(result, 'circulatingSupply')
|
||||
|
||||
// baton was created.
|
||||
assert.equal(result.containsBaton, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
These integration tests need to be run against a live SLPDB. They query
|
||||
against a live SLPDB and test the results against known token stats.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
// const chai = require('chai')
|
||||
// const assert = chai.assert
|
||||
// const axios = require('axios')
|
||||
|
||||
// Used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
// Exit if SLPDB URL is not defined.
|
||||
if (!process.env.SLPDB_URL) {
|
||||
throw new Error('SLPDB_URL and SLPDB_PASS must be defined in order to run these tests.')
|
||||
}
|
||||
|
||||
const SLPDB = require('../../../src/routes/v3/services/slpdb')
|
||||
const slpdb = new SLPDB()
|
||||
|
||||
describe('#slpdb', () => {
|
||||
describe('#getTotalCirculating', () => {
|
||||
it('should get circulating supply', async () => {
|
||||
const result = await slpdb.getTotalCirculating('b10677aef051b73e6b170c1c0824da33a3e0680ab5a01cd8d76aa77840fccfb4')
|
||||
console.log('result: ', result)
|
||||
})
|
||||
})
|
||||
})
|
||||
+179
-1
@@ -341,6 +341,183 @@ const mockTwoRedundentTxid = {
|
||||
u: []
|
||||
}
|
||||
|
||||
const mockTxHistory = [
|
||||
{
|
||||
tx: {
|
||||
h: '3a7646b3976a8745928c7192c8dde989bfa275fa6d7bce950180ab8002cf6cef'
|
||||
},
|
||||
in: [
|
||||
{
|
||||
i: 0,
|
||||
e: {
|
||||
h: '7bdd586ebd1e5f3dfd5295ab6e896c48b25855ab77a72c035ce1e7aacc965c2d',
|
||||
i: 1,
|
||||
s: 'RzBEAiAsu5o9EnxgZChjxKygxhGLhlfxJJbQfd4PP/vp2od3fAIgPpMqg+Sp4YpiInQZ6weJwELq8LEIrHXJpueBz7yF5gRBIQLsyL3809mRphc/+tPAcTQO5bi0kqZRFKpKIHXE3qN8Rw==',
|
||||
a: 'simpleledger:qrrrpqmdkggpnw0czwg0jgcjd7yhu25jy5zxh2gqdq'
|
||||
}
|
||||
},
|
||||
{
|
||||
i: 1,
|
||||
e: {
|
||||
h: 'df49feff24dc34a10a44a0cbd7c908d964801ec7218512c84574fbce698535f0',
|
||||
i: 3,
|
||||
s: 'SDBFAiEAhW3zbKTlPrOXD2E2oEcNof6vCMPGYIg8vOVSE9c8IakCIFLg/gxydG9eL8HMAzkScGElKWJRnfhVMpMHU3evNcrZQSEDRS7F+pSC8OxSldsT4FctJLZBU7f2+FDiE05ae1xqtN0=',
|
||||
a: 'simpleledger:qpkpeqfslejw5pptzcy25h2jxhsc9k0vts43n26up0'
|
||||
}
|
||||
}
|
||||
],
|
||||
out: [
|
||||
{
|
||||
e: {
|
||||
v: 0,
|
||||
i: 0,
|
||||
s: 'agRTTFAAAQEEU0VORCA46XxdfTWFosvz+VgMgsozmF+csIRdTcziIMtwn5U4sAgAAAAAAJiWgAgAAAAAJHfU+g=='
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 546,
|
||||
i: 1,
|
||||
s: 'dqkUqo4lVohqOK6rDd7nIIkFQ6Uf41aIrA==',
|
||||
a: 'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza'
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 546,
|
||||
i: 2,
|
||||
s: 'dqkUgYSfxf90wVkERCiOMyh16iB4d92IrA==',
|
||||
a: 'simpleledger:qzqcf879la6vzkgygs5guvegwh4zq7rhm5nu0pljjl'
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 21849,
|
||||
i: 3,
|
||||
s: 'dqkUxjCDbbIQGbn4E5D5IxJviX4qkiWIrA==',
|
||||
a: 'simpleledger:qrrrpqmdkggpnw0czwg0jgcjd7yhu25jy5zxh2gqdq'
|
||||
}
|
||||
}
|
||||
],
|
||||
slp: {
|
||||
detail: {
|
||||
decimals: 8,
|
||||
tokenIdHex: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
transactionType: 'SEND',
|
||||
versionType: 1,
|
||||
documentUri: 'psfoundation.cash',
|
||||
documentSha256Hex: null,
|
||||
symbol: 'PSF',
|
||||
name: 'Permissionless Software Foundation',
|
||||
txnBatonVout: null,
|
||||
txnContainsBaton: false,
|
||||
outputs: [
|
||||
{
|
||||
address: 'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza',
|
||||
amount: '0.1'
|
||||
},
|
||||
{
|
||||
address: 'simpleledger:qzqcf879la6vzkgygs5guvegwh4zq7rhm5nu0pljjl',
|
||||
amount: '6.11833082'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
blk: {
|
||||
h: '000000000000000001a47bf337ca211c1e7218234d0d70117c64d6f213f5dacb',
|
||||
i: 634833,
|
||||
t: 1589300214
|
||||
}
|
||||
},
|
||||
{
|
||||
tx: {
|
||||
h: '3b11b48cab1e7c8384facf482b3f6bfe659a58245ab4aa4147b42b5cb2a5fac5'
|
||||
},
|
||||
in: [
|
||||
{
|
||||
i: 0,
|
||||
e: {
|
||||
h: 'e4e1e1f6b502cbd42f69b919634cea3e37fc8595e6ca800e0a7d8f6dcdfb249e',
|
||||
i: 3,
|
||||
s: 'SDBFAiEAjCGRmU28x24LMTwg5XqC+fLf3zGhTYCsSWpmF8Eshq0CIAJhxAKMbwKWC4ASmjWzplpno2ch+hGNUD6HNWOdbd4SQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=',
|
||||
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
|
||||
}
|
||||
},
|
||||
{
|
||||
i: 1,
|
||||
e: {
|
||||
h: '438420345dd8b7fb4ea74aaf2e3090f44899bf3c71662243946a441de6dae720',
|
||||
i: 2,
|
||||
s: 'SDBFAiEAhmLYL4QY3mvZo3/i0XG9PJAYMruw3MaGOLJ3Z4si7hwCIAaaGwRnj/cZK3L/KKFBoMtItpGRZ10GowNN//vgn6PKQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=',
|
||||
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
|
||||
}
|
||||
}
|
||||
],
|
||||
out: [
|
||||
{
|
||||
e: {
|
||||
v: 0,
|
||||
i: 0,
|
||||
s: 'agRTTFAAAQEEU0VORCCk+1wtoaoGTiUBikP5FlBABx2emEuhkMIip/WQU6+EsggAAAAAAA9CQAgAAAAXQnHEwA=='
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 546,
|
||||
i: 1,
|
||||
s: 'dqkUqo4lVohqOK6rDd7nIIkFQ6Uf41aIrA==',
|
||||
a: 'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza'
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 546,
|
||||
i: 2,
|
||||
s: 'dqkUWQQVny9pv6Y+76cSYzoNltwufoiIrA==',
|
||||
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
|
||||
}
|
||||
},
|
||||
{
|
||||
e: {
|
||||
v: 43074,
|
||||
i: 3,
|
||||
s: 'dqkUWQQVny9pv6Y+76cSYzoNltwufoiIrA==',
|
||||
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
|
||||
}
|
||||
}
|
||||
],
|
||||
slp: {
|
||||
detail: {
|
||||
decimals: 2,
|
||||
tokenIdHex: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
transactionType: 'SEND',
|
||||
versionType: 1,
|
||||
documentUri: 'troutsblog.com',
|
||||
documentSha256Hex: null,
|
||||
symbol: 'TROUT',
|
||||
name: "Trout's test token",
|
||||
txnBatonVout: null,
|
||||
txnContainsBaton: false,
|
||||
outputs: [
|
||||
{
|
||||
address: 'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza',
|
||||
amount: '10000'
|
||||
},
|
||||
{
|
||||
address: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7',
|
||||
amount: '998990000'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
blk: {
|
||||
h: '000000000000000002a4ee2ed6fe8a764ffef0b2556f545ca0b3ef39ecd2b7c4',
|
||||
i: 634832,
|
||||
t: 1589297145
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
mockList,
|
||||
mockSingleToken,
|
||||
@@ -355,5 +532,6 @@ module.exports = {
|
||||
mockFoobar,
|
||||
mockSingleValidTxid,
|
||||
mockTwoValidTxid,
|
||||
mockTwoRedundentTxid
|
||||
mockTwoRedundentTxid,
|
||||
mockTxHistory
|
||||
}
|
||||
|
||||
+67
-53
@@ -68,7 +68,7 @@ describe('#SLP', () => {
|
||||
res = mockRes
|
||||
|
||||
// Explicitly reset the parmas and body.
|
||||
// req.params = {}
|
||||
req.params = {}
|
||||
req.body = {}
|
||||
req.query = {}
|
||||
req.locals = {}
|
||||
@@ -397,6 +397,7 @@ describe('#SLP', () => {
|
||||
'Error message expected'
|
||||
)
|
||||
})
|
||||
|
||||
it('should validate array with single element', async () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === 'unit') {
|
||||
@@ -461,25 +462,24 @@ describe('#SLP', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('tokenStatsSingle()', () => {
|
||||
const tokenStatsSingle = slpRoute.tokenStats
|
||||
|
||||
describe('tokenStats()', () => {
|
||||
it('should throw 400 if tokenID is empty', async () => {
|
||||
req.params.tokenId = ''
|
||||
const result = await tokenStatsSingle(req, res)
|
||||
const result = await slpRoute.tokenStats(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAllKeys(result, ['error'])
|
||||
assert.include(result.error, 'tokenId can not be empty')
|
||||
})
|
||||
|
||||
it('returns proper error when downstream service stalls', async () => {
|
||||
// Mock the timeout error.
|
||||
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
||||
sandbox.stub(slpRoute.slpdb, 'getTokenStats').throws({ code: 'ECONNABORTED' })
|
||||
|
||||
req.params.tokenId =
|
||||
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
||||
|
||||
const result = await tokenStatsSingle(req, res)
|
||||
const result = await slpRoute.tokenStats(req, res)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
||||
@@ -492,12 +492,12 @@ describe('#SLP', () => {
|
||||
|
||||
it('returns proper error when downstream service is down', async () => {
|
||||
// Mock the timeout error.
|
||||
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
||||
sandbox.stub(slpRoute.slpdb, 'getTokenStats').throws({ code: 'ECONNREFUSED' })
|
||||
|
||||
req.params.tokenId =
|
||||
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
||||
|
||||
const result = await tokenStatsSingle(req, res)
|
||||
const result = await slpRoute.tokenStats(req, res)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
||||
@@ -507,50 +507,6 @@ describe('#SLP', () => {
|
||||
'Error message expected'
|
||||
)
|
||||
})
|
||||
//
|
||||
it('should get token stats for tokenId', async () => {
|
||||
// Mock the RPC call for unit tests.
|
||||
if (process.env.TEST === 'unit') {
|
||||
sandbox.stub(slpRoute.axios, 'request').resolves({
|
||||
data: {
|
||||
t: [
|
||||
{
|
||||
tokenDetails: mockData.mockTokenDetails,
|
||||
tokenStats: mockData.mockTokenStats
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
req.params.tokenId =
|
||||
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
||||
|
||||
const result = await tokenStatsSingle(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
'blockCreated',
|
||||
'blockLastActiveMint',
|
||||
'blockLastActiveSend',
|
||||
'containsBaton',
|
||||
'initialTokenQty',
|
||||
'mintingBatonStatus',
|
||||
'circulatingSupply',
|
||||
'decimals',
|
||||
'documentHash',
|
||||
'versionType',
|
||||
'timestamp',
|
||||
'documentUri',
|
||||
'name',
|
||||
'symbol',
|
||||
'id',
|
||||
'totalBurned',
|
||||
'totalMinted',
|
||||
'txnsSinceGenesis',
|
||||
'validAddresses'
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('balancesForTokenSingle()', () => {
|
||||
@@ -773,6 +729,64 @@ describe('#SLP', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('txsByAddressSingle()', () => {
|
||||
const txsByAddressSingle = slpRoute.txsByAddressSingle
|
||||
|
||||
it('should throw 400 if address is missing', async () => {
|
||||
const result = await txsByAddressSingle(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAllKeys(result, ['error'])
|
||||
assert.include(result.error, 'address can not be empty')
|
||||
})
|
||||
|
||||
it('should throw 400 if address is empty', async () => {
|
||||
req.params.address = ''
|
||||
const result = await txsByAddressSingle(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAllKeys(result, ['error'])
|
||||
assert.include(result.error, 'address can not be empty')
|
||||
})
|
||||
|
||||
it('should throw 400 if address is invalid', async () => {
|
||||
req.params.address = 'badAddress'
|
||||
|
||||
const result = await txsByAddressSingle(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAllKeys(result, ['error'])
|
||||
assert.include(result.error, 'Invalid BCH address.')
|
||||
})
|
||||
|
||||
it('should throw 400 if address network mismatch', async () => {
|
||||
req.params.address = 'slptest:qr83cu3p7yg9yac7qthwm0nul2ev2kukvsqmes3vl0'
|
||||
|
||||
const result = await txsByAddressSingle(req, res)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAllKeys(result, ['error'])
|
||||
assert.include(result.error, 'Invalid')
|
||||
})
|
||||
|
||||
it('should get tx history', async () => {
|
||||
if (process.env.TEST === 'unit') {
|
||||
sandbox
|
||||
.stub(slpRoute.slpdb, 'getHistoricalSlpTransactions')
|
||||
.resolves(mockData.mockTxHistory)
|
||||
}
|
||||
|
||||
// req.params.address = 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk'
|
||||
req.params.address =
|
||||
'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza'
|
||||
|
||||
const result = await slpRoute.txsByAddressSingle(req, res)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user