Skip to content
This repository has been archived by the owner on Nov 10, 2021. It is now read-only.

Commit

Permalink
Merge pull request #36 from hicetnunc2000/develop
Browse files Browse the repository at this point in the history
Version 1.2.2
crzypatchwork authored Apr 9, 2021
2 parents 86064ef + aecf7d3 commit 6931658
Showing 4 changed files with 425 additions and 209 deletions.
5 changes: 4 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,10 @@ const mainnet = {
nftSwapMap: 523,
curationsPtr: 519,
nftRoyaltiesMap: 522,
daoLedger: 515
daoLedger: 515,
kolibriLedger: 380,
hDaoSwap: "KT1V41fGzkdTJki4d11T1Rp9yPkCmDhB7jph",
kolibriSwap: "KT1CiSKXR68qYSxnbzjwvfeMCRburaSDonT2",
}

module.exports = {
306 changes: 284 additions & 22 deletions conseilUtil.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
const conseiljs = require('conseiljs')
const fetch = require('node-fetch')
const log = require('loglevel')
const BigNumber = require('bignumber.js')

const logger = log.getLogger('conseiljs')
logger.setLevel('error', false)
conseiljs.registerLogger(logger)
conseiljs.registerFetch(fetch)
const conseilServer = 'https://conseil-prod.cryptonomic-infra.tech'
const conseilApiKey = 'aa73fa8a-8626-4f43-a605-ff63130f37b1' // signup at nautilus.cloud
const tezosNode = ''

const mainnet = require('./config').networkConfig


@@ -36,7 +35,7 @@ const hDAOFeed = async () => {
*/
const getCollectionForAddress = async (address) => {
let collectionQuery = conseiljs.ConseilQueryBuilder.blankQuery();
collectionQuery = conseiljs.ConseilQueryBuilder.addFields(collectionQuery, 'key', 'value');
collectionQuery = conseiljs.ConseilQueryBuilder.addFields(collectionQuery, 'key', 'value', 'operation_group_id');
collectionQuery = conseiljs.ConseilQueryBuilder.addPredicate(collectionQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.nftLedger])
collectionQuery = conseiljs.ConseilQueryBuilder.addPredicate(collectionQuery, 'key', conseiljs.ConseilOperator.STARTSWITH, [
`Pair 0x${conseiljs.TezosMessageUtils.writeAddress(address)}`,
@@ -46,10 +45,14 @@ const getCollectionForAddress = async (address) => {

const collectionResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', collectionQuery);
let collection = collectionResult.map((i) => {
return { piece: i.key.toString().replace(/.* ([0-9]{1,}$)/, '$1'), amount: Number(i.value) }
return {
piece: i['key'].toString().replace(/.* ([0-9]{1,}$)/, '$1'),
amount: Number(i['value']),
opId: i['operation_group_id']
}
})

const queryChunks = chunkArray(collection.map(i => i.piece), 20) // NOTE: consider increasing this number somewhat
const queryChunks = chunkArray(collection.map(i => i.piece), 50) // NOTE: consider increasing this number somewhat
const makeObjectQuery = (keys) => {
let mintedObjectsQuery = conseiljs.ConseilQueryBuilder.blankQuery();
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.addFields(mintedObjectsQuery, 'key_hash', 'value');
@@ -71,17 +74,84 @@ const getCollectionForAddress = async (address) => {
objectIpfsMap[objectId] = ipfsHash
}))))

collection = collection.map(i => { return {
ipfsHash: objectIpfsMap[i.piece.toString()],
...i
const operationGroupIds = collectionResult.map((r) => r.operation_group_id)
const priceQueryChunks = chunkArray(operationGroupIds, 30)
const makeLastPriceQuery = (opIds) => {
let lastPriceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
lastPriceQuery = conseiljs.ConseilQueryBuilder.addFields(lastPriceQuery, 'timestamp', 'amount', 'operation_group_hash', 'parameters_entrypoints', 'parameters');
lastPriceQuery = conseiljs.ConseilQueryBuilder.addPredicate(lastPriceQuery, 'kind', conseiljs.ConseilOperator.EQ, ['transaction']);
lastPriceQuery = conseiljs.ConseilQueryBuilder.addPredicate(lastPriceQuery, 'status', conseiljs.ConseilOperator.EQ, ['applied']);
lastPriceQuery = conseiljs.ConseilQueryBuilder.addPredicate(lastPriceQuery, 'internal', conseiljs.ConseilOperator.EQ, ['false']);
lastPriceQuery = conseiljs.ConseilQueryBuilder.addPredicate(lastPriceQuery, 'operation_group_hash', opIds.length > 1 ? conseiljs.ConseilOperator.IN : conseiljs.ConseilOperator.EQ, opIds);
lastPriceQuery = conseiljs.ConseilQueryBuilder.setLimit(lastPriceQuery, opIds.length);

return lastPriceQuery;
}

const priceQueries = priceQueryChunks.map((c) => makeLastPriceQuery(c))
const priceMap = {};
await Promise.all(
priceQueries.map(
async (q) =>
await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'operations', q).then((result) =>
result.map((row) => {
let amount = 0;
const action = row.parameters_entrypoints;

if (action === 'collect') {
amount = Number(row.parameters.toString().replace(/^Pair ([0-9]+) [0-9]+/, '$1'));
} else if (action === 'transfer') {
amount = Number(
row.parameters
.toString()
.replace(
/[{] Pair \"[1-9A-HJ-NP-Za-km-z]{36}\" [{] Pair \"[1-9A-HJ-NP-Za-km-z]{36}\" [(]Pair [0-9]+ [0-9]+[)] [}] [}]/,
'$1'
)
);
}

priceMap[row.operation_group_hash] = {
price: new BigNumber(row.amount),
amount,
timestamp: row.timestamp,
action,
};
})
)
)
)

collection = collection.map(i => {
let price = 0
let receivedOn = new Date()
let action = ''

try {
const priceRecord = priceMap[i.opId]
price = priceRecord.price.dividedToIntegerBy(priceRecord.amount).toNumber()
receivedOn = new Date(priceRecord.timestamp)
action = priceRecord.action === 'collect' ? 'Purchased' : 'Received'
} catch {
//
}

delete i.opId

return {
price: isNaN(price) ? 0 : price,
receivedOn,
action,
ipfsHash: objectIpfsMap[i.piece.toString()],
...i
}})

return collection.sort((a, b) => parseInt(b.piece) - parseInt(a.piece)) // sort descending by id – most-recently minted art first
return collection.sort((a, b) => b.receivedOn.getTime() - a.receivedOn.getTime()) // sort descending by date – most-recently acquired art first
}

const gethDaoBalanceForAddress = async (address) => {
let hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(hDaoBalanceQuery, 'value');
let hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery()
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(hDaoBalanceQuery, 'value')
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.daoLedger])
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'key', conseiljs.ConseilOperator.EQ, [
`Pair 0x${conseiljs.TezosMessageUtils.writeAddress(address)} 0`
@@ -92,7 +162,7 @@ const gethDaoBalanceForAddress = async (address) => {
let balance = 0

try {
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', hDaoBalanceQuery);
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', hDaoBalanceQuery)
balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
} catch (error) {
console.log(`gethDaoBalanceForAddress failed for ${JSON.stringify(hDaoBalanceQuery)} with ${error}`)
@@ -101,15 +171,166 @@ const gethDaoBalanceForAddress = async (address) => {
return balance
}

const getTokenBalance = async (big_map_id, address, fa2=false, token_id=0) => {
let tokenBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(tokenBalanceQuery, 'value');
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [big_map_id])
if (fa2) {
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'key', conseiljs.ConseilOperator.EQ, [
`Pair 0x${conseiljs.TezosMessageUtils.writeAddress(address)} ${token_id}`
])
}
else {
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'key', conseiljs.ConseilOperator.EQ, [
`0x${conseiljs.TezosMessageUtils.writeAddress(address)}`
])
}
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'value', conseiljs.ConseilOperator.EQ, [0], true)
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.setLimit(tokenBalanceQuery, 1)

let balance = 0

try {
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', tokenBalanceQuery);
balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
} catch (error) {
console.log(`getTokenBalance failed for ${JSON.stringify(tokenBalanceQuery)} with ${error}`)
}

return balance
}


const getTezBalanceForAddress = async (address) => {
let accountQuery = conseiljs.ConseilQueryBuilder.blankQuery();
accountQuery = conseiljs.ConseilQueryBuilder.addFields(accountQuery, 'balance');
accountQuery = conseiljs.ConseilQueryBuilder.addPredicate(accountQuery, 'account_id', conseiljs.ConseilOperator.EQ, [address], false);
accountQuery = conseiljs.ConseilQueryBuilder.setLimit(accountQuery, 1);

try {
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'accounts', accountQuery);
balance = balanceResult[0]['balance'] // TODO: consider bigNumber here, for the moment there is no reason for it
} catch (error) {
console.log(`getTezBalanceForAddress failed for ${JSON.stringify(accountQuery)} with ${error}`)
}

return balance
}


const gethDAOPerTez = async() => {
const tezBalance = await(getTezBalanceForAddress(mainnet.hDaoSwap))
const hdaoBalance = await(gethDaoBalanceForAddress(mainnet.hDaoSwap))
return hdaoBalance / tezBalance
}

const getKolibriPerTez = async() => {
const tezBalance = await(getTezBalanceForAddress(mainnet.kolibriSwap))
var kolibriBalance = await(getTokenBalance(mainnet.kolibriLedger, mainnet.kolibriSwap))

// TODO: Find a better way to get the balance, this is FA1.2, mike?
kolibriBalance = parseInt(kolibriBalance.replace("Pair {} ", "")) / (10**((18 - 6)))
return kolibriBalance / tezBalance
}


const gethDaoBalances = async () => {
let hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(hDaoBalanceQuery, 'key', 'value');
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.daoLedger])
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'value', conseiljs.ConseilOperator.EQ, [0], true)
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.setLimit(hDaoBalanceQuery, 500_000)

let balance = 0
let hdaoMap = {}

try {
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', hDaoBalanceQuery);


balanceResult.forEach(row => {
hdaoMap[conseiljs.TezosMessageUtils.readAddress(row['key'].toString().replace(/^Pair 0x([0-9a-z]{1,}) [0-9]+/, '$1'))] = row['value']
})
//#balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
} catch (error) {
console.log(`gethDaoBalanceForAddress failed for ${JSON.stringify(hDaoBalanceQuery)} with ${error}`)
}


return hdaoMap

}

const getObjektOwners = async (objekt_id) => {
let objektBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
objektBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(objektBalanceQuery, 'key', 'value');
objektBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(objektBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.nftLedger])
objektBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(objektBalanceQuery, 'key', conseiljs.ConseilOperator.ENDSWITH, [` ${objekt_id}`], false)
objektBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(objektBalanceQuery, 'value', conseiljs.ConseilOperator.EQ, [0], true)
objektBalanceQuery = conseiljs.ConseilQueryBuilder.setLimit(objektBalanceQuery, 500_000)

let objektMap = {}

try {
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', objektBalanceQuery);


balanceResult.forEach(row => {
objektMap[conseiljs.TezosMessageUtils.readAddress(row['key'].toString().replace(/^Pair 0x([0-9a-z]{1,}) [0-9]+/, '$1'))] = row['value']
})
//#balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
} catch (error) {
console.log(`getObjektOwners failed for ${JSON.stringify(objektBalanceQuery)} with ${error}`)
}


return objektMap

}

const getObjektMintingsLastWeek = async () => {
var d = new Date();
d.setDate(d.getDate()-5);
let mintOperationQuery = conseiljs.ConseilQueryBuilder.blankQuery();
mintOperationQuery = conseiljs.ConseilQueryBuilder.addFields(mintOperationQuery, 'source');
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'kind', conseiljs.ConseilOperator.EQ, ['transaction'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'timestamp', conseiljs.ConseilOperator.AFTER, [d.getTime()]) // 2021 Feb 1
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'status', conseiljs.ConseilOperator.EQ, ['applied'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'destination', conseiljs.ConseilOperator.EQ, [mainnet.protocol])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'parameters_entrypoints', conseiljs.ConseilOperator.EQ, ['mint_OBJKT'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addOrdering(mintOperationQuery, 'block_level', conseiljs.ConseilSortDirection.DESC)
mintOperationQuery = conseiljs.ConseilQueryBuilder.setLimit(mintOperationQuery, 900_000) // TODO: this is hardwired and will not work for highly productive artists

const mintOperationResult = await conseiljs.TezosConseilClient.getTezosEntityData(
{ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' },
'mainnet',
'operations',
mintOperationQuery);

const mints = mintOperationResult.map(r => r['source'])

var initialValue = {}
var reducer = function(minters, mintOp) {
if (!minters[mintOp]) {
minters[mintOp] = 1;
} else {
minters[mintOp] = minters[mintOp] + 1;
}
return minters;
}
return mints.reduce(reducer, initialValue)
}


/**
* Queries Conseil in two steps to get all the objects minted by a specific address. Step 1 is to query for all 'mint_OBJKT' operations performed by the account to get the list of operation group hashes. Then that list is partitioned into chunks and another query (or set of queries) is run to get big_map values. These values are then parsed into an array of 3-tuples containing the hashed big_map key that can be used to query a Tezos node directly, the nft token id and the ipfs item hash.
*
* @param {string} address
* @returns
*/
const getArtisticOutputForAddress = async (address) => {
let mintOperationQuery = conseiljs.ConseilQueryBuilder.blankQuery();
mintOperationQuery = conseiljs.ConseilQueryBuilder.addFields(mintOperationQuery, 'operation_group_hash');
let mintOperationQuery = conseiljs.ConseilQueryBuilder.blankQuery()
mintOperationQuery = conseiljs.ConseilQueryBuilder.addFields(mintOperationQuery, 'operation_group_hash')
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'kind', conseiljs.ConseilOperator.EQ, ['transaction'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'timestamp', conseiljs.ConseilOperator.AFTER, [1612240919000]) // 2021 Feb 1
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'status', conseiljs.ConseilOperator.EQ, ['applied'])
@@ -123,14 +344,14 @@ const getArtisticOutputForAddress = async (address) => {
{ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' },
'mainnet',
'operations',
mintOperationQuery);
mintOperationQuery)

const operationGroupIds = mintOperationResult.map(r => r['operation_group_hash'])
const queryChunks = chunkArray(operationGroupIds, 30)

const makeObjectQuery = (opIds) => {
let mintedObjectsQuery = conseiljs.ConseilQueryBuilder.blankQuery();
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.addFields(mintedObjectsQuery, 'key_hash', 'value');
let mintedObjectsQuery = conseiljs.ConseilQueryBuilder.blankQuery()
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.addFields(mintedObjectsQuery, 'key_hash', 'value')
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintedObjectsQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.nftMetadataMap])
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintedObjectsQuery, 'operation_group_id', (opIds.length > 1 ? conseiljs.ConseilOperator.IN : conseiljs.ConseilOperator.EQ), opIds)
mintedObjectsQuery = conseiljs.ConseilQueryBuilder.setLimit(mintedObjectsQuery, opIds.length)
@@ -153,14 +374,17 @@ const getArtisticOutputForAddress = async (address) => {
}

const getArtisticUniverse = async (max_time) => {
var d = new Date();
d.setDate(d.getDate()-14);
let mintOperationQuery = conseiljs.ConseilQueryBuilder.blankQuery();
mintOperationQuery = conseiljs.ConseilQueryBuilder.addFields(mintOperationQuery, 'operation_group_hash');
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'kind', conseiljs.ConseilOperator.EQ, ['transaction'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'timestamp', conseiljs.ConseilOperator.AFTER, [1612240919000]) // 2021 Feb 1
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'timestamp', conseiljs.ConseilOperator.AFTER, [d.getTime()]) //Two weeks ago
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'status', conseiljs.ConseilOperator.EQ, ['applied'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'destination', conseiljs.ConseilOperator.EQ, [mainnet.protocol])
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'parameters_entrypoints', conseiljs.ConseilOperator.EQ, ['mint_OBJKT'])
mintOperationQuery = conseiljs.ConseilQueryBuilder.setLimit(mintOperationQuery, 30_000)
mintOperationQuery = conseiljs.ConseilQueryBuilder.addOrdering(mintOperationQuery, 'block_level', conseiljs.ConseilSortDirection.DESC)
mintOperationQuery = conseiljs.ConseilQueryBuilder.setLimit(mintOperationQuery, 2500)

const mintOperationResult = await conseiljs.TezosConseilClient.getTezosEntityData(
{ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' },
@@ -181,7 +405,7 @@ const getArtisticUniverse = async (max_time) => {
})

let swapsQuery = conseiljs.ConseilQueryBuilder.blankQuery();
swapsQuery = conseiljs.ConseilQueryBuilder.addFields(swapsQuery, 'key', 'value');
swapsQuery = conseiljs.ConseilQueryBuilder.addFields(swapsQuery, 'key', 'value')
swapsQuery = conseiljs.ConseilQueryBuilder.addPredicate(swapsQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.nftSwapMap])
swapsQuery = conseiljs.ConseilQueryBuilder.setLimit(swapsQuery, 30_000) // NOTE, limited to 30_000

@@ -239,13 +463,48 @@ const getArtisticUniverse = async (max_time) => {
return universe
}


const getFeaturedArtisticUniverse = async(max_time) => {

hdaoMap = await gethDaoBalances()

mintsPerCreator = await getObjektMintingsLastWeek()

artisticUniverse = await getArtisticUniverse(max_time)

hdaoPerTez = await gethDAOPerTez()

// Cost to be on feed per objekt last 7 days shouldn't be higher than:
// 0.1tez
// 1 hDAO
// But not lower than:
// 0.01 hDAO
//
// We should probably add more thresholds like $, € and yen
// It should be cheap but not too cheap and it shouldn't be
// affected by tez or hDAO volatility

thresholdHdao = Math.min(1_000_000, Math.max(100_000 * hdaoPerTez, 10_000))

return artisticUniverse.filter(function (o) {
return (((hdaoMap[o.minter] || 0) / Math.max(mintsPerCreator[o.minter] || 1, 1)) > thresholdHdao)
})
}

const getRecommendedCurateDefault = async() => {
hdaoPerTez = await gethDAOPerTez()
kolPerTez = await getKolibriPerTez()
hdaoPerKol = hdaoPerTez / kolPerTez
//Minimum of $0.1, 0.1 hDAO, and 0.1tez, in hDAO
return Math.floor(Math.min(hdaoPerKol * 0.1, 0.1, 0.1 * hdaoPerTez) * 1_000_000)
}

/**
* Returns object ipfs hash and swaps if any
*
* @param {number} objectId
* @returns
*/

const getObjectById = async (objectId) => {
let objectQuery = conseiljs.ConseilQueryBuilder.blankQuery();
objectQuery = conseiljs.ConseilQueryBuilder.addFields(objectQuery, 'value');
@@ -304,5 +563,8 @@ module.exports = {
getArtisticOutputForAddress,
getObjectById,
getArtisticUniverse,
hDAOFeed
getFeaturedArtisticUniverse,
hDAOFeed,
getRecommendedCurateDefault,
getObjektOwners
}
253 changes: 100 additions & 153 deletions index.js
Original file line number Diff line number Diff line change
@@ -9,128 +9,26 @@ const { random } = require('lodash')
const BURN_ADDRESS = 'tz1burnburnburnburnburnburnburjAYjjX'

require('dotenv').config()
const { Semaphore } = require('prex')

const reducer = (accumulator, currentValue) => parseInt(accumulator) + parseInt(currentValue)

const getIpfsHash = async (ipfsHash) => {

return await axios.get('https://cloudflare-ipfs.com/ipfs/' + ipfsHash).then(res => res.data)
/* const nftDetailJson = await nftDetails.json();
const nftName = nftDetailJson.name;
const nftDescription = nftDetailJson.description;
const nftCreators = nftDetailJson.creators.join(', ');
const nftArtifact = `https://cloudflare-ipfs.com/ipfs/${nftDetailJson.formats[0].uri.toString().slice(7)}`;
const nftArtifactType = nftDetailJson.formats[0].mimeType.toString();
return { name: nftName, description: nftDescription, creators: nftCreators, artifactUrl: nftArtifact, artifactType: nftArtifactType }; */
}
const getObjkts = async () => {
return await axios.get(`https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens`).then(res => res.data)
}

const getTokenHolders = async (tk_id) => {
return await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + tk_id).then(res => res.data)
}

const getTokenHoldersArr = async (arr) => {

return await arr.map(async e => await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + e).then(res => res.data))
/* await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + arr[0]).then(res => console.log(res.data))
*//* var result = arr.map(async e => {
return await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + e).then(res => res.data)
})
console.log(result) */
}

const owners = async (obj) => {
var owners = await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + obj.token_id).then(res => res.data)
var owners = await conseilUtil.getObjektOwners(obj.token_id)
var values_arr = (_.values(owners))
obj.total_amount = (values_arr.map(e => parseInt(e))).length > 0 ? values_arr.filter(e => parseInt(e) > 0).reduce(reducer) : 0
obj.owners = owners
console.log(obj)
//obj.total_amount = (values_arr.map(e => parseInt(e))).reduce(reducer)
return obj
}

const totalAmountIntegral = async (obj) => {
var owners = await axios.get('https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=' + obj.token_id).then(res => res.data)
console.log(owners)
var values_arr = (_.values(owners))
obj.total_amount = (values_arr.map(e => parseInt(e))).length > 0 ? (values_arr.filter(e => parseInt(e))) : 0

obj.owners = owners
return obj
}

const objktAmount = async (arr) => {
return await arr.map(e => totalAmountIntegral(e))
//console.log(await getTokenHoldersArr(arr.map(e => _.values(e.token_id)[0])))
}

const objktOwners = async (arr) => {
return await arr.map(e => totalAmountIntegral(e))
}


const getObjktLedger = async () => await axios.get('https://better-call.dev/v1/bigmap/mainnet/511/keys?size=6500').then(res => res.data.map(e => ({ amount: parseInt(e.data.value.value), tz: e.data.key.children[0].value, tk_id: parseInt(e.data.key.children[1].value) })))
const gethDAOLedger = async (counter) => await axios.get('https://api.better-call.dev/v1/bigmap/mainnet/519/keys?size=10&offset=' + counter * 10).then(res => res.data.map(e => {
return { token_id: parseInt(e.data.key.value), hDAO_balance: parseInt(e.data.value.children[0].value) }
}))

//gethDAOLedger()


const getSwaps = async () => {
return await axios.get(`https://api.better-call.dev/v1/bigmap/mainnet/523/keys?size=6000`).then(res => {
return (res.data).map(e => {
var obj = {}

obj['swap_id'] = e.data.key.value
e.data.value != null ? e.data.value.children.map(e => obj[e.name] = e.value) : null
return obj
})
})
}

const merge = (a, b) => {
a.forEach((e1) => {
b.forEach((e2) => {
if (e1.token_id === e2.tk_id) {
_.assign(e1, e2)
}
})
})
return a
}

const mergeSwaps = (arr, swaps) => {
arr.forEach((e1) => {

e1.swaps = []

swaps.forEach((e2) => {
if (parseInt(e1.token_id) === parseInt(e2.objkt_id)) {
e1.swaps.push(e2)
}
})
})
return arr
}

const desc = arr => _.sortBy(arr, e => parseInt(e.objectId)).reverse()
const offset = (arr, set) => arr.slice(set * 30, set * 30 + 30)

const filter = (data, tz) => _.filter(data, (e) => {
if (e.token_info != undefined) {
return e.token_info.creators[0] === tz
}
})

const filterTz = (data, tz) => _.filter(data, { tz: tz })

const test = async () => console.log(desc(await getObjkts()))

const customFloor = function (value, roundTo) {
return Math.floor(value / roundTo) * roundTo;
@@ -144,29 +42,30 @@ const randomFeed = async (counter, res) => {
feed = await feed.map(async e => {
e.token_info = await getIpfsHash(e.ipfsHash)
e.token_id = parseInt(e.objectId)
console.log(e)
return e
})
var promise = Promise.all(feed.map(e => e))
promise.then(async (results) => {
var aux_arr = results.map(e => e)
//res.set('Cache-Control', `public, max-age=${cache_time}`)
console.log(aux_arr)
res.json({ result: aux_arr })
})
}

const getFeed = async (counter, res) => {

const getFeed = async (counter, featured) => {
/* const now_time = Date.now()
const immutable = (typeof max_time !== 'undefined') && (max_time < now_time)
max_time = (typeof max_time !== 'undefined') ? max_time : customFloor(now_time, ONE_MINUTE_MILLIS)
*/
console.log('feed')
var arr = await conseilUtil.getArtisticUniverse(0)
console.log(`feed, featured: ${featured}`)
var arr
if (featured) {
arr = await conseilUtil.getFeaturedArtisticUniverse(0)
} else {
arr = await conseilUtil.getArtisticUniverse(0)
}

var feed = offset(desc(arr), counter)
console.log(feed)
// console.log(feed)
feed = await feed.map(async e => {
e.token_info = await getIpfsHash(e.ipfsHash)
e.token_id = parseInt(e.objectId)
@@ -182,18 +81,16 @@ const getFeed = async (counter, res) => {
cache_time = (int)(((max_time + ONE_MINUTE_MILLIS) - now_time) / 1000)
} */
var promise = Promise.all(feed.map(e => e))
promise.then(async (results) => {
return promise.then(async (results) => {
var aux_arr = results.map(e => e)

//res.set('Cache-Control', `public, max-age=${cache_time}`)

console.log(aux_arr)
res.json({ result: aux_arr })
// console.log(aux_arr)
return aux_arr
})
}

const filterObjkts = (arr, id_arr) => _.filter(arr, { token_id: tk.id })

const getTzLedger = async (tz, res) => {
/* var ledger = desc(await getObjktLedger())
var objkts = await getObjkts()
@@ -224,6 +121,8 @@ const getTzLedger = async (tz, res) => {
return arr
}))

validCreations = validCreations.sort((a, b) => parseInt(b.objectId) - parseInt(a.objectId))

var arr = []
console.log([...collection, ...validCreations])
var arr = [...collection, ...validCreations]
@@ -252,8 +151,6 @@ const getTzLedger = async (tz, res) => {
hdao: hdao
})
})

//return tzLedger
}

const getObjktById = async (id, res) => {
@@ -264,20 +161,6 @@ const getObjktById = async (id, res) => {
console.log(objkt)

return objkt
//res.json({ result : objkt })
//var objkts = await getObjkts()
//var swaps = await getSwaps()
//res.json({ result : mergeSwaps([objkt], swaps)[0] })
//console.log(_.filter(mergeSwaps(objkts, swaps), {token_id : id}))
// var arr = await objktOwners(_.filter(mergeSwaps(objkts, swaps), {token_id : id}))
// var promise = Promise.all(arr.map(e => e))

/* promise.then((results) => {
var aux_arr = results.map(e => e)
console.log(aux_arr)
res.json({ result : aux_arr })
}) */
//https://api.better-call.dev/v1/contract/mainnet/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens/holders?token_id=842
}

const mergehDAO = async (obj) => {
@@ -302,29 +185,86 @@ const hDAOFeed = async (counter, res) => {
})
}

//getObjkts()
//testSwaps()
//getFeed(1)
//getTzLedger('tz1UBZUkXpKGhYsP5KtzDNqLLchwF4uHrGjw')
//getObjktById(15306)
//const test2 = async () => console.log(await getObjktLedger())
//test2()
// list of restricted addresses
const restrictedAdddressesCacheTimeLimit = ONE_MINUTE_MILLIS // the blockchain updates about once a minute
let restrictedAddressesCache = null
const restrictedAddressesLock = new Semaphore(1)
const getRestrictedAddresses = async () => {
await restrictedAddressesLock.wait()
if (restrictedAddressesCache && Date.now() - restrictedAddressesCache.expires < restrictedAdddressesCacheTimeLimit) {
restrictedAddressesLock.release()
// console.log('ADDRESS restrictions from CACHE')
return restrictedAddressesCache.data
}

const list = await axios.get('https://raw.githubusercontent.com/hicetnunc2000/hicetnunc/main/filters/w.json').then(res => res.data)
restrictedAddressesCache = {
expires: Date.now(),
data: list
}
restrictedAddressesLock.release()
// console.log('ADDRESS restrictions from NEW')
return list
}

// list of restricted objkts
const restrictedObjectsCacheTimeLimit = ONE_MINUTE_MILLIS // the blockchain updates about once a minute
let restrictedObjectsCache = null
const restrictedObjectsLock = new Semaphore(1)
const getRestrictedObjkts = async () => {
await restrictedObjectsLock.wait()
if (restrictedObjectsCache && Date.now() - restrictedObjectsCache.expires < restrictedObjectsCacheTimeLimit) {
restrictedObjectsLock.release()
// console.log('OBJKT restrictions from CACHE')
return restrictedObjectsCache.data
}

const list = await axios.get('https://raw.githubusercontent.com/hicetnunc2000/hicetnunc/main/filters/o.json').then(res => res.data)
restrictedObjectsCache = {
expires: Date.now(),
data: list
}
restrictedObjectsLock.release()
return list
}

const app = express()

app.use(express.json())
app.use(cors({ origin: '*' }))

app.post('/feed', async (req, res) => {
/*
var counter = req.query.counter
var max_time = req.query.hasOwnProperty('time') ? customFloor(req.query.time, ONE_MINUTE_MILLIS) : null
const now_time_qt = customFloor(Date.now(), ONE_MINUTE_MILLIS)
if (max_time != null & max_time > now_time_qt) {
max_time = null
}
*/
await getFeed(req.body.counter, res)
// used for very simple caching of the feed
const feedCacheTimeLimit = ONE_MINUTE_MILLIS // the blockchain updates about once a minute
const feedCache = {}
const feedLocks = {}

const getFeedLock = (key) => {
if (!feedLocks[key]) {
feedLocks[key] = new Semaphore(1)
}
return feedLocks[key]
}

app.post('/feed|/featured', async (req, res) => {
const feedOffset = req.body.counter || 0
const isFeatured = req.path === '/featured'
const lockKey = `${feedOffset}-${isFeatured ? 'featured' : ''}`

await getFeedLock(lockKey).wait()
if (feedCache[lockKey] && Date.now() - feedCache[lockKey].expires < feedCacheTimeLimit) {
getFeedLock(lockKey).release()
// console.log('Feed from CACHE')
return res.json({ result: feedCache[lockKey].data })
}

const aux_arr = await getFeed(feedOffset, isFeatured)
feedCache[lockKey] = {
expires: Date.now(),
data: aux_arr
}
getFeedLock(lockKey).release()
// console.log('Feed from NEW')
return res.json({ result: aux_arr })
})

app.post('/random', async (req, res) => {
@@ -334,7 +274,7 @@ app.post('/random', async (req, res) => {
app.post('/tz', async (req, res) => {

// list of restricted addresses
var list = await axios.get('https://raw.githubusercontent.com/hicetnunc2000/hicetnunc/main/filters/w.json').then(res => res.data)
var list = await getRestrictedAddresses()

list.includes(req.body.tz)
?
@@ -347,7 +287,7 @@ app.post('/tz', async (req, res) => {
app.post('/objkt', async (req, res) => {

// list of restricted objkts
var list = await axios.get('https://raw.githubusercontent.com/hicetnunc2000/hicetnunc/main/filters/o.json').then(res => res.data)
var list = await getRestrictedObjkts()

list.includes(parseInt(req.body.objkt_id))
?
@@ -356,13 +296,20 @@ app.post('/objkt', async (req, res) => {
res.json({ result: await getObjktById(req.body.objkt_id) })
})

app.get('/recommend_curate', async (req, res) => {
const amt = await conseilUtil.getRecommendedCurateDefault()
res.set('Cache-Control', `public, max-age=300`)
res.json({ amount: amt })
})

app.post('/hdao', async (req, res) => {
await hDAOFeed(parseInt(req.body.counter), res)
})

const testhdao = async () => await hDAOFeed(parseInt(0))
// const testhdao = async () => await hDAOFeed(parseInt(0))
//testhdao()

//app.listen(3001)
console.log('SERVER RUNNING ON localhost:3001')
module.exports.handler = serverless(app)

70 changes: 37 additions & 33 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
{
"name": "hicetnunc-apiv2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/hicetnunc2000/hicetnunc.git"
},
"author": "@hicetnunc2000",
"license": "MIT",
"bugs": {
"url": "https://github.com/hicetnunc2000/hicetnunc/issues"
},
"homepage": "https://github.com/hicetnunc2000/hicetnunc#readme",
"dependencies": {
"axios": "^0.21.1",
"conseiljs": "5.0.7-2",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"lodash": "^4.17.21",
"loglevel": "1.7.1",
"node-fetch": "2.6.1",
"serverless-dotenv-plugin": "^3.8.1",
"serverless-http": "^2.7.0"
},
"engines": {
"node": "12.20.1",
"npm": "6.14.10"
}
"name": "hicetnunc-apiv2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/hicetnunc2000/hicetnunc-api.git"
},
"author": "@hicetnunc2000",
"license": "MIT",
"bugs": {
"url": "https://github.com/hicetnunc2000/hicetnunc-api/issues"
},
"homepage": "https://github.com/hicetnunc2000/hicetnunc-api#readme",
"dependencies": {
"axios": "^0.21.1",
"bignumber.js": "9.0.1",
"cloud-local-storage": "0.0.11",
"conseiljs": "5.0.8-1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"fetch": "^1.1.0",
"lodash": "^4.17.21",
"loglevel": "1.7.1",
"node-fetch": "2.6.1",
"prex": "^0.4.7",
"serverless-dotenv-plugin": "^3.8.1",
"serverless-http": "^2.7.0"
},
"engines": {
"node": "12.20.1",
"npm": "6.14.10"
}
}

0 comments on commit 6931658

Please sign in to comment.