feat(endpoint): Added getTokenData() method to bch-api

This commit is contained in:
Daniel Gonzalez
2022-04-15 18:25:12 -04:00
parent 88231cb303
commit dbc4e3e731
4 changed files with 551 additions and 3 deletions
+171
View File
@@ -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