mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
feat(endpoint): Added getTokenData() method to bch-api
This commit is contained in:
@@ -35,6 +35,7 @@ class PsfSlpIndexer {
|
||||
this.router.post('/address', this.getAddress)
|
||||
this.router.post('/txid', this.getTxid)
|
||||
this.router.post('/token', this.getTokenStats)
|
||||
this.router.post('/token/data', this.getTokenData)
|
||||
|
||||
_this = this
|
||||
}
|
||||
@@ -224,6 +225,176 @@ class PsfSlpIndexer {
|
||||
}
|
||||
}
|
||||
|
||||
async getTokenData (req, res, next) {
|
||||
try {
|
||||
// Verify env var is set for interacting with the indexer.
|
||||
_this.checkEnvVar()
|
||||
const tokenData = {}
|
||||
|
||||
const tokenId = req.body.tokenId
|
||||
if (!tokenId || tokenId === '') {
|
||||
res.status(400)
|
||||
return res.json({
|
||||
success: false,
|
||||
error: 'tokenId can not be empty'
|
||||
})
|
||||
}
|
||||
|
||||
// get token stats
|
||||
const withTxHistory = false
|
||||
const response = await _this.axios.post(
|
||||
`${_this.psfSlpIndexerApi}slp/token/`,
|
||||
{ tokenId, withTxHistory }
|
||||
)
|
||||
// console.log('response', response.data)
|
||||
|
||||
const tokenStats = response.data.tokenData
|
||||
|
||||
tokenData.genesisData = tokenStats
|
||||
// try to get immutable data
|
||||
try {
|
||||
const immutableData = await _this.getCIDData(tokenStats.documentUri)
|
||||
tokenData.immutableData = immutableData
|
||||
} catch (error) {
|
||||
tokenData.immutableData = ''
|
||||
}
|
||||
|
||||
// try to get mutable data
|
||||
try {
|
||||
const mutableData = await _this.getMutableData(tokenStats.documentHash)
|
||||
tokenData.mutableData = mutableData
|
||||
} catch (error) {
|
||||
tokenData.mutableData = ''
|
||||
}
|
||||
res.status(200)
|
||||
return res.json(tokenData)
|
||||
} catch (err) {
|
||||
return _this.errorHandler(err, res)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves the mutable data associated with a token document hash.
|
||||
async getMutableData (documentHash) {
|
||||
try {
|
||||
if (!documentHash || typeof documentHash !== 'string') {
|
||||
throw new Error(
|
||||
'documentHash string required when calling mutableData().'
|
||||
)
|
||||
}
|
||||
|
||||
// Gets the OP_RETURN data and decodes it
|
||||
const mutableData = await _this.decodeOpReturn(documentHash)
|
||||
const jsonData = JSON.parse(mutableData)
|
||||
|
||||
const mspAddress = jsonData.mspAddress
|
||||
|
||||
// Gets the mspAddress transactions
|
||||
const transactions = await _this.bchjs.Electrumx.transactions(mspAddress)
|
||||
|
||||
const mspTxs = transactions.transactions
|
||||
// console.log(`mspTxs: ${JSON.stringify(mspTxs, null, 2)}`)
|
||||
|
||||
let data = false
|
||||
// Maps each transaction of the mspAddress
|
||||
// if finds an OP_RETURN decode is and closes the loop
|
||||
// Start with the newest TXID entry and scan the history to find the first
|
||||
// entry with an IPFS CID.
|
||||
for (let i = mspTxs.length - 1; i > -1; i--) {
|
||||
const tx = mspTxs[i]
|
||||
const txid = tx.tx_hash
|
||||
console.log(`Retrieving and decoding txid ${txid}`)
|
||||
|
||||
data = await _this.decodeOpReturn(txid)
|
||||
// console.log('data: ', data)
|
||||
|
||||
// Try parse the OP_RETURN data to a JSON object.
|
||||
if (data) {
|
||||
try {
|
||||
// console.log('Mutable Data : ', data)
|
||||
|
||||
// Convert the OP_RETURN data to a JSON object.
|
||||
const obj = JSON.parse(data)
|
||||
console.log(`obj: ${JSON.stringify(obj, null, 2)}`)
|
||||
|
||||
// Keep searching if this TX does not have a cid value.
|
||||
if (!obj.cid) continue
|
||||
|
||||
// TODO: Ensure data was generated by the MSP address and not an
|
||||
// update from a different address.
|
||||
|
||||
break
|
||||
} catch (error) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data) {
|
||||
console.log('Mutable Data Not found ')
|
||||
}
|
||||
|
||||
// Get the CID:
|
||||
const obj = JSON.parse(data)
|
||||
const cid = obj.cid
|
||||
if (!cid) {
|
||||
throw new Error('CID could not be found in OP_RETURN data')
|
||||
}
|
||||
|
||||
const result = await _this.getCIDData(cid)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.log('Error in getMutableData().')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes the OP_RETURN of a transaction if this exists
|
||||
async decodeOpReturn (txid) {
|
||||
try {
|
||||
if (!txid || typeof txid !== 'string') {
|
||||
throw new Error('txid must be a string.')
|
||||
}
|
||||
// get transaction data
|
||||
const txData = await _this.bchjs.Electrumx.txData(txid)
|
||||
let data = false
|
||||
// Maps the vout of the transaction in search of an OP_RETURN
|
||||
for (let i = 0; i < txData.details.vout.length; i++) {
|
||||
const vout = txData.details.vout[i]
|
||||
|
||||
const script = _this.bchjs.Script.toASM(
|
||||
Buffer.from(vout.scriptPubKey.hex, 'hex')
|
||||
).split(' ')
|
||||
|
||||
if (script[0] === 'OP_RETURN') {
|
||||
data = Buffer.from(script[1], 'hex').toString('ascii')
|
||||
break
|
||||
}
|
||||
}
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log('Error in decodeOpReturn().')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getCIDData (cid) {
|
||||
try {
|
||||
if (!cid || typeof cid !== 'string') {
|
||||
throw new Error('cid must be a string.')
|
||||
}
|
||||
const dataUrl = `https://${cid}.ipfs.dweb.link/data.json`
|
||||
|
||||
const response = await _this.axios.get(dataUrl)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.log('Error in getCIDData().')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Check the the environment variable is set correctly.
|
||||
checkEnvVar () {
|
||||
_this.psfSlpIndexerApi = process.env.SLP_INDEXER_API
|
||||
|
||||
Reference in New Issue
Block a user