11const conseiljs = require ( 'conseiljs' )
22const fetch = require ( 'node-fetch' )
33const log = require ( 'loglevel' )
4+ const BigNumber = require ( 'bignumber.js' )
45
56const logger = log . getLogger ( 'conseiljs' )
67logger . setLevel ( 'error' , false )
@@ -35,7 +36,7 @@ const hDAOFeed = async () => {
3536 */
3637const getCollectionForAddress = async ( address ) => {
3738 let collectionQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
38- collectionQuery = conseiljs . ConseilQueryBuilder . addFields ( collectionQuery , 'key' , 'value' ) ;
39+ collectionQuery = conseiljs . ConseilQueryBuilder . addFields ( collectionQuery , 'key' , 'value' , 'operation_group_id' ) ;
3940 collectionQuery = conseiljs . ConseilQueryBuilder . addPredicate ( collectionQuery , 'big_map_id' , conseiljs . ConseilOperator . EQ , [ mainnet . nftLedger ] )
4041 collectionQuery = conseiljs . ConseilQueryBuilder . addPredicate ( collectionQuery , 'key' , conseiljs . ConseilOperator . STARTSWITH , [
4142 `Pair 0x${ conseiljs . TezosMessageUtils . writeAddress ( address ) } ` ,
@@ -45,10 +46,14 @@ const getCollectionForAddress = async (address) => {
4546
4647 const collectionResult = await conseiljs . TezosConseilClient . getTezosEntityData ( { url : conseilServer , apiKey : conseilApiKey , network : 'mainnet' } , 'mainnet' , 'big_map_contents' , collectionQuery ) ;
4748 let collection = collectionResult . map ( ( i ) => {
48- return { piece : i . key . toString ( ) . replace ( / .* ( [ 0 - 9 ] { 1 , } $ ) / , '$1' ) , amount : Number ( i . value ) }
49+ return {
50+ piece : i [ 'key' ] . toString ( ) . replace ( / .* ( [ 0 - 9 ] { 1 , } $ ) / , '$1' ) ,
51+ amount : Number ( i [ 'value' ] ) ,
52+ opId : i [ 'operation_group_id' ]
53+ }
4954 } )
5055
51- const queryChunks = chunkArray ( collection . map ( i => i . piece ) , 20 ) // NOTE: consider increasing this number somewhat
56+ const queryChunks = chunkArray ( collection . map ( i => i . piece ) , 50 ) // NOTE: consider increasing this number somewhat
5257 const makeObjectQuery = ( keys ) => {
5358 let mintedObjectsQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
5459 mintedObjectsQuery = conseiljs . ConseilQueryBuilder . addFields ( mintedObjectsQuery , 'key_hash' , 'value' ) ;
@@ -70,17 +75,84 @@ const getCollectionForAddress = async (address) => {
7075 objectIpfsMap [ objectId ] = ipfsHash
7176 } ) ) ) )
7277
73- collection = collection . map ( i => { return {
74- ipfsHash : objectIpfsMap [ i . piece . toString ( ) ] ,
75- ...i
78+ const operationGroupIds = collectionResult . map ( ( r ) => r . operation_group_id )
79+ const priceQueryChunks = chunkArray ( operationGroupIds , 30 )
80+ const makeLastPriceQuery = ( opIds ) => {
81+ let lastPriceQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
82+ lastPriceQuery = conseiljs . ConseilQueryBuilder . addFields ( lastPriceQuery , 'timestamp' , 'amount' , 'operation_group_hash' , 'parameters_entrypoints' , 'parameters' ) ;
83+ lastPriceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( lastPriceQuery , 'kind' , conseiljs . ConseilOperator . EQ , [ 'transaction' ] ) ;
84+ lastPriceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( lastPriceQuery , 'status' , conseiljs . ConseilOperator . EQ , [ 'applied' ] ) ;
85+ lastPriceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( lastPriceQuery , 'internal' , conseiljs . ConseilOperator . EQ , [ 'false' ] ) ;
86+ lastPriceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( lastPriceQuery , 'operation_group_hash' , opIds . length > 1 ? conseiljs . ConseilOperator . IN : conseiljs . ConseilOperator . EQ , opIds ) ;
87+ lastPriceQuery = conseiljs . ConseilQueryBuilder . setLimit ( lastPriceQuery , opIds . length ) ;
88+
89+ return lastPriceQuery ;
90+ }
91+
92+ const priceQueries = priceQueryChunks . map ( ( c ) => makeLastPriceQuery ( c ) )
93+ const priceMap = { } ;
94+ await Promise . all (
95+ priceQueries . map (
96+ async ( q ) =>
97+ await conseiljs . TezosConseilClient . getTezosEntityData ( { url : conseilServer , apiKey : conseilApiKey , network : 'mainnet' } , 'mainnet' , 'operations' , q ) . then ( ( result ) =>
98+ result . map ( ( row ) => {
99+ let amount = 0 ;
100+ const action = row . parameters_entrypoints ;
101+
102+ if ( action === 'collect' ) {
103+ amount = Number ( row . parameters . toString ( ) . replace ( / ^ P a i r ( [ 0 - 9 ] + ) [ 0 - 9 ] + / , '$1' ) ) ;
104+ } else if ( action === 'transfer' ) {
105+ amount = Number (
106+ row . parameters
107+ . toString ( )
108+ . replace (
109+ / [ { ] P a i r \" [ 1 - 9 A - H J - N P - Z a - k m - z ] { 36 } \" [ { ] P a i r \" [ 1 - 9 A - H J - N P - Z a - k m - z ] { 36 } \" [ ( ] P a i r [ 0 - 9 ] + [ 0 - 9 ] + [ ) ] [ } ] [ } ] / ,
110+ '$1'
111+ )
112+ ) ;
113+ }
114+
115+ priceMap [ row . operation_group_hash ] = {
116+ price : new BigNumber ( row . amount ) ,
117+ amount,
118+ timestamp : row . timestamp ,
119+ action,
120+ } ;
121+ } )
122+ )
123+ )
124+ )
125+
126+ collection = collection . map ( i => {
127+ let price = 0
128+ let receivedOn = new Date ( )
129+ let action = ''
130+
131+ try {
132+ const priceRecord = priceMap [ i . opId ]
133+ price = priceRecord . price . dividedToIntegerBy ( priceRecord . amount ) . toNumber ( )
134+ receivedOn = new Date ( priceRecord . timestamp )
135+ action = priceRecord . action === 'collect' ? 'Purchased' : 'Received'
136+ } catch {
137+ //
138+ }
139+
140+ delete i . opId
141+
142+ return {
143+ price : isNaN ( price ) ? 0 : price ,
144+ receivedOn,
145+ action,
146+ ipfsHash : objectIpfsMap [ i . piece . toString ( ) ] ,
147+ ...i
76148 } } )
77149
78- return collection . sort ( ( a , b ) => parseInt ( b . piece ) - parseInt ( a . piece ) ) // sort descending by id – most-recently minted art first
150+ return collection . sort ( ( a , b ) => b . receivedOn . getTime ( ) - a . receivedOn . getTime ( ) ) // sort descending by date – most-recently acquired art first
79151}
80152
81153const gethDaoBalanceForAddress = async ( address ) => {
82- let hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
83- hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . addFields ( hDaoBalanceQuery , 'value' ) ;
154+ let hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . blankQuery ( )
155+ hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . addFields ( hDaoBalanceQuery , 'value' )
84156 hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( hDaoBalanceQuery , 'big_map_id' , conseiljs . ConseilOperator . EQ , [ mainnet . daoLedger ] )
85157 hDaoBalanceQuery = conseiljs . ConseilQueryBuilder . addPredicate ( hDaoBalanceQuery , 'key' , conseiljs . ConseilOperator . EQ , [
86158 `Pair 0x${ conseiljs . TezosMessageUtils . writeAddress ( address ) } 0`
@@ -91,7 +163,7 @@ const gethDaoBalanceForAddress = async (address) => {
91163 let balance = 0
92164
93165 try {
94- const balanceResult = await conseiljs . TezosConseilClient . getTezosEntityData ( { url : conseilServer , apiKey : conseilApiKey , network : 'mainnet' } , 'mainnet' , 'big_map_contents' , hDaoBalanceQuery ) ;
166+ const balanceResult = await conseiljs . TezosConseilClient . getTezosEntityData ( { url : conseilServer , apiKey : conseilApiKey , network : 'mainnet' } , 'mainnet' , 'big_map_contents' , hDaoBalanceQuery )
95167 balance = balanceResult [ 0 ] [ 'value' ] // TODO: consider bigNumber here, for the moment there is no reason for it
96168 } catch ( error ) {
97169 console . log ( `gethDaoBalanceForAddress failed for ${ JSON . stringify ( hDaoBalanceQuery ) } with ${ error } ` )
@@ -233,8 +305,8 @@ const getObjektMintingsLastWeek = async () => {
233305 * @returns
234306 */
235307const getArtisticOutputForAddress = async ( address ) => {
236- let mintOperationQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
237- mintOperationQuery = conseiljs . ConseilQueryBuilder . addFields ( mintOperationQuery , 'operation_group_hash' ) ;
308+ let mintOperationQuery = conseiljs . ConseilQueryBuilder . blankQuery ( )
309+ mintOperationQuery = conseiljs . ConseilQueryBuilder . addFields ( mintOperationQuery , 'operation_group_hash' )
238310 mintOperationQuery = conseiljs . ConseilQueryBuilder . addPredicate ( mintOperationQuery , 'kind' , conseiljs . ConseilOperator . EQ , [ 'transaction' ] )
239311 mintOperationQuery = conseiljs . ConseilQueryBuilder . addPredicate ( mintOperationQuery , 'timestamp' , conseiljs . ConseilOperator . AFTER , [ 1612240919000 ] ) // 2021 Feb 1
240312 mintOperationQuery = conseiljs . ConseilQueryBuilder . addPredicate ( mintOperationQuery , 'status' , conseiljs . ConseilOperator . EQ , [ 'applied' ] )
@@ -248,14 +320,14 @@ const getArtisticOutputForAddress = async (address) => {
248320 { url : conseilServer , apiKey : conseilApiKey , network : 'mainnet' } ,
249321 'mainnet' ,
250322 'operations' ,
251- mintOperationQuery ) ;
323+ mintOperationQuery )
252324
253325 const operationGroupIds = mintOperationResult . map ( r => r [ 'operation_group_hash' ] )
254326 const queryChunks = chunkArray ( operationGroupIds , 30 )
255327
256328 const makeObjectQuery = ( opIds ) => {
257- let mintedObjectsQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
258- mintedObjectsQuery = conseiljs . ConseilQueryBuilder . addFields ( mintedObjectsQuery , 'key_hash' , 'value' ) ;
329+ let mintedObjectsQuery = conseiljs . ConseilQueryBuilder . blankQuery ( )
330+ mintedObjectsQuery = conseiljs . ConseilQueryBuilder . addFields ( mintedObjectsQuery , 'key_hash' , 'value' )
259331 mintedObjectsQuery = conseiljs . ConseilQueryBuilder . addPredicate ( mintedObjectsQuery , 'big_map_id' , conseiljs . ConseilOperator . EQ , [ mainnet . nftMetadataMap ] )
260332 mintedObjectsQuery = conseiljs . ConseilQueryBuilder . addPredicate ( mintedObjectsQuery , 'operation_group_id' , ( opIds . length > 1 ? conseiljs . ConseilOperator . IN : conseiljs . ConseilOperator . EQ ) , opIds )
261333 mintedObjectsQuery = conseiljs . ConseilQueryBuilder . setLimit ( mintedObjectsQuery , opIds . length )
@@ -306,7 +378,7 @@ const getArtisticUniverse = async (max_time) => {
306378 } )
307379
308380 let swapsQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
309- swapsQuery = conseiljs . ConseilQueryBuilder . addFields ( swapsQuery , 'key' , 'value' ) ;
381+ swapsQuery = conseiljs . ConseilQueryBuilder . addFields ( swapsQuery , 'key' , 'value' )
310382 swapsQuery = conseiljs . ConseilQueryBuilder . addPredicate ( swapsQuery , 'big_map_id' , conseiljs . ConseilOperator . EQ , [ mainnet . nftSwapMap ] )
311383 swapsQuery = conseiljs . ConseilQueryBuilder . setLimit ( swapsQuery , 30_000 ) // NOTE, limited to 30_000
312384
@@ -410,7 +482,6 @@ const getRecommendedCurateDefault = async() => {
410482 * @param {number } objectId
411483 * @returns
412484 */
413-
414485const getObjectById = async ( objectId ) => {
415486 let objectQuery = conseiljs . ConseilQueryBuilder . blankQuery ( ) ;
416487 objectQuery = conseiljs . ConseilQueryBuilder . addFields ( objectQuery , 'value' ) ;
0 commit comments