Skip to content
This repository was archived by the owner on Nov 10, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ const mainnet = {
nftSwapMap: 523,
curationsPtr: 519,
nftRoyaltiesMap: 522,
daoLedger: 515
daoLedger: 515,
kolibriLedger: 380,
hDaoSwap: "KT1V41fGzkdTJki4d11T1Rp9yPkCmDhB7jph",
kolibriSwap: "KT1CiSKXR68qYSxnbzjwvfeMCRburaSDonT2",
}

module.exports = {
Expand Down
171 changes: 169 additions & 2 deletions conseilUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ 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


Expand Down Expand Up @@ -101,6 +100,132 @@ 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))
//console.log("Tez balance", tezBalance)
var kolibriBalance = await(getTokenBalance(mainnet.kolibriLedger, mainnet.kolibriSwap))
kolibriBalance = parseInt(kolibriBalance.replace("Pair {} ", "")) / (10**((18 - 6)))
//console.log(kolibriBalance)
//console.log(typeof(kolibriBalance))
//console.log("Kol balance", kolibriBalance)
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 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.
*
Expand Down Expand Up @@ -239,6 +364,46 @@ 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()
//console.log("hdaoPerTez", hdaoPerTez)
//console.log("kolPerTez", kolPerTez)
hdaoPerKol = hdaoPerTez / kolPerTez
//console.log("hdaoPerKol", hdaoPerKol)

//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
*
Expand Down Expand Up @@ -304,5 +469,7 @@ module.exports = {
getArtisticOutputForAddress,
getObjectById,
getArtisticUniverse,
hDAOFeed
getFeaturedArtisticUniverse,
hDAOFeed,
getRecommendedCurateDefault
}
30 changes: 26 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,19 @@ const randomFeed = async (counter, res) => {
})
}

const getFeed = async (counter, res) => {
const getFeed = async (counter, res, 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)
Expand Down Expand Up @@ -315,6 +320,18 @@ const app = express()
app.use(express.json())
app.use(cors({ origin: '*' }))

app.post('/featured', 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, true)
})

app.post('/feed', async (req, res) => {
/*
var counter = req.query.counter
Expand All @@ -324,7 +341,7 @@ app.post('/feed', async (req, res) => {
max_time = null
}
*/
await getFeed(req.body.counter, res)
await getFeed(req.body.counter, res, false)
})

app.post('/random', async (req, res) => {
Expand Down Expand Up @@ -356,6 +373,11 @@ 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.json({ amount: amt })
})

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