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

Commit 880fda8

Browse files
authored
Merge pull request #22 from veqtor/hdao_feed_filter
Add new feed type - hDAO thresholded
2 parents 92ea387 + 36c1e3e commit 880fda8

File tree

3 files changed

+199
-7
lines changed

3 files changed

+199
-7
lines changed

config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ const mainnet = {
99
nftSwapMap: 523,
1010
curationsPtr: 519,
1111
nftRoyaltiesMap: 522,
12-
daoLedger: 515
12+
daoLedger: 515,
13+
kolibriLedger: 380,
14+
hDaoSwap: "KT1V41fGzkdTJki4d11T1Rp9yPkCmDhB7jph",
15+
kolibriSwap: "KT1CiSKXR68qYSxnbzjwvfeMCRburaSDonT2",
1316
}
1417

1518
module.exports = {

conseilUtil.js

Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ conseiljs.registerFetch(fetch)
99
const conseilServer = 'https://conseil-prod.cryptonomic-infra.tech'
1010
const conseilApiKey = 'aa73fa8a-8626-4f43-a605-ff63130f37b1' // signup at nautilus.cloud
1111
const tezosNode = ''
12-
1312
const mainnet = require('./config').networkConfig
1413

1514

@@ -101,6 +100,132 @@ const gethDaoBalanceForAddress = async (address) => {
101100
return balance
102101
}
103102

103+
const getTokenBalance = async (big_map_id, address, fa2=false, token_id=0) => {
104+
let tokenBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
105+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(tokenBalanceQuery, 'value');
106+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [big_map_id])
107+
if (fa2) {
108+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'key', conseiljs.ConseilOperator.EQ, [
109+
`Pair 0x${conseiljs.TezosMessageUtils.writeAddress(address)} ${token_id}`
110+
])
111+
}
112+
else {
113+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'key', conseiljs.ConseilOperator.EQ, [
114+
`0x${conseiljs.TezosMessageUtils.writeAddress(address)}`
115+
])
116+
}
117+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(tokenBalanceQuery, 'value', conseiljs.ConseilOperator.EQ, [0], true)
118+
tokenBalanceQuery = conseiljs.ConseilQueryBuilder.setLimit(tokenBalanceQuery, 1)
119+
120+
let balance = 0
121+
122+
try {
123+
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', tokenBalanceQuery);
124+
balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
125+
} catch (error) {
126+
console.log(`getTokenBalance failed for ${JSON.stringify(tokenBalanceQuery)} with ${error}`)
127+
}
128+
129+
return balance
130+
}
131+
132+
133+
const getTezBalanceForAddress = async (address) => {
134+
let accountQuery = conseiljs.ConseilQueryBuilder.blankQuery();
135+
accountQuery = conseiljs.ConseilQueryBuilder.addFields(accountQuery, 'balance');
136+
accountQuery = conseiljs.ConseilQueryBuilder.addPredicate(accountQuery, 'account_id', conseiljs.ConseilOperator.EQ, [address], false);
137+
accountQuery = conseiljs.ConseilQueryBuilder.setLimit(accountQuery, 1);
138+
139+
try {
140+
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'accounts', accountQuery);
141+
balance = balanceResult[0]['balance'] // TODO: consider bigNumber here, for the moment there is no reason for it
142+
} catch (error) {
143+
console.log(`getTezBalanceForAddress failed for ${JSON.stringify(accountQuery)} with ${error}`)
144+
}
145+
146+
return balance
147+
}
148+
149+
150+
const gethDAOPerTez = async() => {
151+
const tezBalance = await(getTezBalanceForAddress(mainnet.hDaoSwap))
152+
const hdaoBalance = await(gethDaoBalanceForAddress(mainnet.hDaoSwap))
153+
return hdaoBalance / tezBalance
154+
}
155+
156+
const getKolibriPerTez = async() => {
157+
const tezBalance = await(getTezBalanceForAddress(mainnet.kolibriSwap))
158+
//console.log("Tez balance", tezBalance)
159+
var kolibriBalance = await(getTokenBalance(mainnet.kolibriLedger, mainnet.kolibriSwap))
160+
kolibriBalance = parseInt(kolibriBalance.replace("Pair {} ", "")) / (10**((18 - 6)))
161+
//console.log(kolibriBalance)
162+
//console.log(typeof(kolibriBalance))
163+
//console.log("Kol balance", kolibriBalance)
164+
return kolibriBalance / tezBalance
165+
}
166+
167+
168+
const gethDaoBalances = async () => {
169+
let hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.blankQuery();
170+
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addFields(hDaoBalanceQuery, 'key', 'value');
171+
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'big_map_id', conseiljs.ConseilOperator.EQ, [mainnet.daoLedger])
172+
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.addPredicate(hDaoBalanceQuery, 'value', conseiljs.ConseilOperator.EQ, [0], true)
173+
hDaoBalanceQuery = conseiljs.ConseilQueryBuilder.setLimit(hDaoBalanceQuery, 500_000)
174+
175+
let balance = 0
176+
let hdaoMap = {}
177+
178+
try {
179+
const balanceResult = await conseiljs.TezosConseilClient.getTezosEntityData({ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' }, 'mainnet', 'big_map_contents', hDaoBalanceQuery);
180+
181+
182+
balanceResult.forEach(row => {
183+
hdaoMap[conseiljs.TezosMessageUtils.readAddress(row['key'].toString().replace(/^Pair 0x([0-9a-z]{1,}) [0-9]+/, '$1'))] = row['value']
184+
})
185+
//#balance = balanceResult[0]['value'] // TODO: consider bigNumber here, for the moment there is no reason for it
186+
} catch (error) {
187+
console.log(`gethDaoBalanceForAddress failed for ${JSON.stringify(hDaoBalanceQuery)} with ${error}`)
188+
}
189+
190+
191+
return hdaoMap
192+
193+
}
194+
195+
const getObjektMintingsLastWeek = async () => {
196+
var d = new Date();
197+
d.setDate(d.getDate()-5);
198+
let mintOperationQuery = conseiljs.ConseilQueryBuilder.blankQuery();
199+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addFields(mintOperationQuery, 'source');
200+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'kind', conseiljs.ConseilOperator.EQ, ['transaction'])
201+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'timestamp', conseiljs.ConseilOperator.AFTER, [d.getTime()]) // 2021 Feb 1
202+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'status', conseiljs.ConseilOperator.EQ, ['applied'])
203+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'destination', conseiljs.ConseilOperator.EQ, [mainnet.protocol])
204+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addPredicate(mintOperationQuery, 'parameters_entrypoints', conseiljs.ConseilOperator.EQ, ['mint_OBJKT'])
205+
mintOperationQuery = conseiljs.ConseilQueryBuilder.addOrdering(mintOperationQuery, 'block_level', conseiljs.ConseilSortDirection.DESC)
206+
mintOperationQuery = conseiljs.ConseilQueryBuilder.setLimit(mintOperationQuery, 900_000) // TODO: this is hardwired and will not work for highly productive artists
207+
208+
const mintOperationResult = await conseiljs.TezosConseilClient.getTezosEntityData(
209+
{ url: conseilServer, apiKey: conseilApiKey, network: 'mainnet' },
210+
'mainnet',
211+
'operations',
212+
mintOperationQuery);
213+
214+
const mints = mintOperationResult.map(r => r['source'])
215+
216+
var initialValue = {}
217+
var reducer = function(minters, mintOp) {
218+
if (!minters[mintOp]) {
219+
minters[mintOp] = 1;
220+
} else {
221+
minters[mintOp] = minters[mintOp] + 1;
222+
}
223+
return minters;
224+
}
225+
return mints.reduce(reducer, initialValue)
226+
}
227+
228+
104229
/**
105230
* 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.
106231
*
@@ -239,6 +364,46 @@ const getArtisticUniverse = async (max_time) => {
239364
return universe
240365
}
241366

367+
368+
const getFeaturedArtisticUniverse = async(max_time) => {
369+
370+
hdaoMap = await gethDaoBalances()
371+
372+
mintsPerCreator = await getObjektMintingsLastWeek()
373+
374+
artisticUniverse = await getArtisticUniverse(max_time)
375+
376+
hdaoPerTez = await gethDAOPerTez()
377+
378+
// Cost to be on feed per objekt last 7 days shouldn't be higher than:
379+
// 0.1tez
380+
// 1 hDAO
381+
// But not lower than:
382+
// 0.01 hDAO
383+
//
384+
// We should probably add more thresholds like $, € and yen
385+
// It should be cheap but not too cheap and it shouldn't be
386+
// affected by tez or hDAO volatility
387+
388+
thresholdHdao = Math.min(1_000_000, Math.max(100_000 * hdaoPerTez, 10_000))
389+
390+
return artisticUniverse.filter(function (o) {
391+
return ((hdaoMap[o.minter] || 0) / Math.max(mintsPerCreator[o.minter] || 1, 1)) > thresholdHdao
392+
})
393+
}
394+
395+
const getRecommendedCurateDefault = async() => {
396+
hdaoPerTez = await gethDAOPerTez()
397+
kolPerTez = await getKolibriPerTez()
398+
//console.log("hdaoPerTez", hdaoPerTez)
399+
//console.log("kolPerTez", kolPerTez)
400+
hdaoPerKol = hdaoPerTez / kolPerTez
401+
//console.log("hdaoPerKol", hdaoPerKol)
402+
403+
//Minimum of $0.1, 0.1 hDAO, and 0.1tez, in hDAO
404+
return Math.floor(Math.min(hdaoPerKol * 0.1, 0.1, 0.1 * hdaoPerTez) * 1_000_000)
405+
}
406+
242407
/**
243408
* Returns object ipfs hash and swaps if any
244409
*
@@ -304,5 +469,7 @@ module.exports = {
304469
getArtisticOutputForAddress,
305470
getObjectById,
306471
getArtisticUniverse,
307-
hDAOFeed
472+
getFeaturedArtisticUniverse,
473+
hDAOFeed,
474+
getRecommendedCurateDefault
308475
}

index.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,19 @@ const randomFeed = async (counter, res) => {
156156
})
157157
}
158158

159-
const getFeed = async (counter, res) => {
159+
const getFeed = async (counter, res, featured) => {
160160

161161
/* const now_time = Date.now()
162162
const immutable = (typeof max_time !== 'undefined') && (max_time < now_time)
163163
max_time = (typeof max_time !== 'undefined') ? max_time : customFloor(now_time, ONE_MINUTE_MILLIS)
164164
*/
165-
console.log('feed')
166-
var arr = await conseilUtil.getArtisticUniverse(0)
165+
console.log(`feed, featured: ${featured}`)
166+
var arr
167+
if (featured) {
168+
arr = await conseilUtil.getFeaturedArtisticUniverse(0)
169+
} else {
170+
arr = await conseilUtil.getArtisticUniverse(0)
171+
}
167172

168173
var feed = offset(desc(arr), counter)
169174
console.log(feed)
@@ -315,6 +320,18 @@ const app = express()
315320
app.use(express.json())
316321
app.use(cors({ origin: '*' }))
317322

323+
app.post('/featured', async (req, res) => {
324+
/*
325+
var counter = req.query.counter
326+
var max_time = req.query.hasOwnProperty('time') ? customFloor(req.query.time, ONE_MINUTE_MILLIS) : null
327+
const now_time_qt = customFloor(Date.now(), ONE_MINUTE_MILLIS)
328+
if (max_time != null & max_time > now_time_qt) {
329+
max_time = null
330+
}
331+
*/
332+
await getFeed(req.body.counter, res, true)
333+
})
334+
318335
app.post('/feed', async (req, res) => {
319336
/*
320337
var counter = req.query.counter
@@ -324,7 +341,7 @@ app.post('/feed', async (req, res) => {
324341
max_time = null
325342
}
326343
*/
327-
await getFeed(req.body.counter, res)
344+
await getFeed(req.body.counter, res, false)
328345
})
329346

330347
app.post('/random', async (req, res) => {
@@ -356,6 +373,11 @@ app.post('/objkt', async (req, res) => {
356373
res.json({ result: await getObjktById(req.body.objkt_id) })
357374
})
358375

376+
app.get('/recommend_curate', async (req, res) => {
377+
const amt = await conseilUtil.getRecommendedCurateDefault()
378+
res.json({ amount: amt })
379+
})
380+
359381
app.post('/hdao', async (req, res) => {
360382
await hDAOFeed(parseInt(req.body.counter), res)
361383
})

0 commit comments

Comments
 (0)