11import { IResponse , successResponse , errorResponse } from "../utils/lambda-response" ;
22import wrap from "../utils/wrap" ;
33import { getCurrentUnixTimestamp , getTimestampAtStartOfDay } from "../utils/date" ;
4- import {
5- queryAggregatedDailyTimestampRange ,
6- queryAggregatedTokensInRange ,
7- queryConfig ,
8- } from "../utils/wrappa/postgres/query" ;
4+ import { queryAggregatedTokenStatsTop30 } from "../utils/wrappa/postgres/query" ;
95import { getLlamaPrices } from "../utils/prices" ;
106import { importBridgeNetwork } from "../data/importBridgeNetwork" ;
11- import BigNumber from "bignumber.js" ;
127import { normalizeChain , normlizeTokenSymbol } from "../utils/normalizeChain" ;
138import { getCache } from "../utils/cache" ;
149
15- const numberOfTokensToReturn = 30 ; // also determines # of addresses returned
16-
17- // the following 2 types should probably be combined
1810type TokenRecord = {
1911 [ token : string ] : {
2012 amount : string ;
@@ -24,234 +16,122 @@ type TokenRecord = {
2416 } ;
2517} ;
2618
27- type TokenRecordBn = {
28- [ token : string ] : {
29- amountBn : BigNumber ;
30- } ;
31- } ;
32-
3319type AddressRecord = {
3420 [ address : string ] : {
3521 usdValue : number ;
3622 txs : number ;
3723 } ;
3824} ;
3925
40- interface IAggregatedData {
41- bridge_id : string ;
42- ts : Date ;
43- total_tokens_deposited : string [ ] ;
44- total_tokens_withdrawn : string [ ] ;
45- total_deposited_usd : string ;
46- total_withdrawn_usd : string ;
47- total_deposit_txs : number ;
48- total_withdrawal_txs : number ;
49- total_address_deposited : string [ ] ;
50- total_address_withdrawn : string [ ] ;
51- }
52-
53- const sumTokenTxs = async (
54- tokenTotals : string [ ] ,
55- dailyTokensRecord : TokenRecord ,
56- prices : Record < string , any > ,
57- lzSymbols : Record < string , string >
58- ) => {
59- if ( ! tokenTotals ) return ;
60- let dailyTokensRecordBn = { } as TokenRecordBn ;
61-
62-
63-
64- tokenTotals . map ( ( tokenString ) => {
65- const tokenData = tokenString . replace ( / [ ( ' " ) ] / g, "" ) . split ( "," ) ;
66- const token = tokenData [ 0 ] ;
67-
68- const amountBn = BigNumber ( tokenData [ 1 ] ) ;
69- const usdValue = parseFloat ( tokenData [ 2 ] ) ;
70- dailyTokensRecordBn [ token ] = dailyTokensRecordBn [ token ] || { } ;
71- dailyTokensRecordBn [ token ] . amountBn = dailyTokensRecordBn [ token ] . amountBn
72- ? dailyTokensRecordBn [ token ] . amountBn . plus ( amountBn )
73- : BigNumber ( amountBn ) ;
74- dailyTokensRecord [ token ] = dailyTokensRecord [ token ] || { } ;
75- dailyTokensRecord [ token ] . usdValue = ( dailyTokensRecord [ token ] . usdValue ?? 0 ) + usdValue ;
76- } ) ;
77- Object . entries ( dailyTokensRecordBn ) . map ( ( [ token , tokenData ] ) => {
78- dailyTokensRecord [ token ] . amount = tokenData . amountBn ?. toFixed ( ) ?? "0" ;
79- const key = token ?. toLowerCase ?.( ) ?? token ;
80- const priceEntry = prices ?. [ key ] ?? prices ?. [ token ] ;
81- const addrOnly = key . includes ( ":" ) ? key . split ( ":" ) [ 1 ] : key ;
82- const fallbackSymbol = lzSymbols ?. [ addrOnly ] ?? lzSymbols ?. [ key ] ?? "" ;
83- const symbol = priceEntry ?. symbol ?? fallbackSymbol ?? "" ;
84- dailyTokensRecord [ token ] . symbol = normlizeTokenSymbol ( symbol ) ;
85- const decimalsVal = priceEntry ?. decimals ;
86- dailyTokensRecord [ token ] . decimals = typeof decimalsVal === "string" ? Number ( decimalsVal ) : decimalsVal ?? 0 ;
87- } ) ;
26+ type StatsRow = {
27+ kind : "dt" | "wt" | "da" | "wa" ;
28+ key : string ;
29+ amount : string | null ;
30+ usd_value : string ;
31+ txs : number | null ;
8832} ;
8933
90- const sumAddressTxs = ( addressTotals : string [ ] , dailyAddresssRecord : AddressRecord ) => {
91- if ( ! addressTotals ) return ;
92- addressTotals . map ( ( addressString ) => {
93- const addressData = addressString . replace ( / [ ( ' " ) ] / g, "" ) . split ( "," ) ;
94- const address = addressData [ 0 ] ;
95- const usdValue = parseFloat ( addressData [ 1 ] ) ;
96- const txs = parseInt ( addressData [ 2 ] ) ;
97- dailyAddresssRecord [ address ] = dailyAddresssRecord [ address ] || { } ;
98- dailyAddresssRecord [ address ] . usdValue = ( dailyAddresssRecord [ address ] . usdValue ?? 0 ) + usdValue ;
99- dailyAddresssRecord [ address ] . txs = ( dailyAddresssRecord [ address ] . txs ?? 0 ) + txs ;
100- } ) ;
34+ const isValidPriceId = ( id ?: string ) => {
35+ if ( ! id ) return false ;
36+ if ( id . includes ( "\\" ) || id . includes ( "/" ) || id . includes ( " " ) ) return false ;
37+ const parts = id . split ( ":" ) ;
38+ if ( parts . length !== 2 ) return false ;
39+ if ( ! parts [ 0 ] || ! parts [ 1 ] ) return false ;
40+ if ( parts [ 1 ] === "\\N" || parts [ 1 ] === "\\n" || parts [ 1 ] . toLowerCase ( ) === "null" || parts [ 1 ] . toLowerCase ( ) === "undefined" ) return false ;
41+ return true ;
10142} ;
10243
103- // can also return total deposit/withdraw USD, deposit/withdraw #txs here if needed
104- // don't let chain be 'all'
10544const getBridgeStatsOnDay = async ( timestamp : string = "0" , chain : string , bridgeId ?: string ) => {
10645 let bridgeDbName = undefined as any ;
10746 const queryChain = chain === "" ? "" : normalizeChain ( chain ) ;
108- if ( ! bridgeId ) {
109- bridgeDbName = undefined ;
110- } else {
47+ if ( bridgeId ) {
11148 try {
11249 const bridgeNetwork = importBridgeNetwork ( undefined , parseInt ( bridgeId ) ) ;
11350 if ( ! bridgeNetwork ) {
11451 throw new Error ( "No bridge network found." ) ;
11552 }
11653 ( { bridgeDbName } = bridgeNetwork ) ;
11754 } catch ( e ) {
118- return errorResponse ( {
119- message : "Invalid bridgeId entered." ,
120- } ) ;
55+ return errorResponse ( { message : "Invalid bridgeId entered." } ) ;
12156 }
12257 }
12358
124- const sourceChainConfigs = ( await queryConfig ( undefined , undefined , queryChain ) ) . filter ( ( config ) => {
125- if ( bridgeId ) {
126- return config . bridge_name === bridgeDbName ;
127- }
128- return true ;
129- } ) ;
130-
13159 const queryTimestamp = getTimestampAtStartOfDay ( parseInt ( timestamp ) ) ;
13260 const maxEndTimestamp = queryTimestamp + 86400 ;
13361 const currentTimestamp = getCurrentUnixTimestamp ( ) ;
13462 const endTimestamp = Math . max ( queryTimestamp , Math . min ( maxEndTimestamp , currentTimestamp ) ) ;
13563
136- let sourceChainsDailyData = [ ] as IAggregatedData [ ] ;
137- await Promise . all (
138- sourceChainConfigs . map ( async ( config ) => {
139- const sourceChainData = await queryAggregatedDailyTimestampRange (
140- queryTimestamp ,
141- endTimestamp ,
142- config . chain ,
143- config . bridge_name
144- ) ;
145- sourceChainsDailyData . push ( ...sourceChainData ) ;
146- } )
147- ) ;
64+ const rows = ( await queryAggregatedTokenStatsTop30 (
65+ queryTimestamp ,
66+ endTimestamp ,
67+ queryChain ,
68+ bridgeDbName
69+ ) ) as StatsRow [ ] ;
70+
71+ const dt = rows . filter ( ( r ) => r . kind === "dt" ) ;
72+ const wt = rows . filter ( ( r ) => r . kind === "wt" ) ;
73+ const da = rows . filter ( ( r ) => r . kind === "da" ) ;
74+ const wa = rows . filter ( ( r ) => r . kind === "wa" ) ;
75+
76+ const tokenIds = new Set < string > ( ) ;
77+ for ( const r of dt ) {
78+ const id = r . key ?. toLowerCase ( ) ;
79+ if ( isValidPriceId ( id ) ) tokenIds . add ( id ) ;
80+ }
81+ for ( const r of wt ) {
82+ const id = r . key ?. toLowerCase ( ) ;
83+ if ( isValidPriceId ( id ) ) tokenIds . add ( id ) ;
84+ }
14885
149- const dailyData = await queryAggregatedTokensInRange ( queryTimestamp , endTimestamp , queryChain , bridgeDbName ) ;
150- let dailyTokensDeposited = { } as TokenRecord ;
151- let dailyTokensWithdrawn = { } as TokenRecord ;
152- let dailyAddressesDeposited = { } as AddressRecord ;
153- let dailyAddressesWithdrawn = { } as AddressRecord ;
15486 const lzSymbols = ( ( await getCache ( "lz_token_symbols" ) ) || { } ) as Record < string , string > ;
155-
156- const allTokenIds = new Set < string > ( ) ;
157- const isValidPriceId = ( id ?: string ) => {
158- if ( ! id ) return false ;
159- if ( id . includes ( "\\" ) || id . includes ( "/" ) || id . includes ( " " ) ) return false ;
160- const parts = id . split ( ":" ) ;
161- if ( parts . length !== 2 ) return false ;
162- if ( ! parts [ 0 ] || ! parts [ 1 ] ) return false ;
163- if ( parts [ 1 ] === "\\N" || parts [ 1 ] === "\\n" || parts [ 1 ] . toLowerCase ( ) === "null" || parts [ 1 ] . toLowerCase ( ) === "undefined" ) return false ;
164- return true ;
165- } ;
166- const collectTokenIds = ( tokenTotals ?: string [ ] ) => {
167- if ( ! tokenTotals ) return ;
168- tokenTotals . forEach ( ( tokenString ) => {
169- const tokenData = tokenString . replace ( / [ ( ' \" ) ] / g, "" ) . split ( "," ) ;
170- const tokenId = ( tokenData [ 0 ] || "" ) . toLowerCase ( ) ;
171- if ( isValidPriceId ( tokenId ) ) allTokenIds . add ( tokenId ) ;
172- } ) ;
173- } ;
174- dailyData . forEach ( ( { total_tokens_deposited, total_tokens_withdrawn } ) => {
175- collectTokenIds ( total_tokens_deposited ) ;
176- collectTokenIds ( total_tokens_withdrawn ) ;
177- } ) ;
178- sourceChainsDailyData . forEach ( ( { total_tokens_deposited, total_tokens_withdrawn } ) => {
179- collectTokenIds ( total_tokens_deposited ) ;
180- collectTokenIds ( total_tokens_withdrawn ) ;
181- } ) ;
182-
18387 let prices : Record < string , any > = { } ;
18488 try {
18589 prices = ( await Promise . race ( [
186- getLlamaPrices ( Array . from ( allTokenIds ) ) ,
90+ getLlamaPrices ( Array . from ( tokenIds ) ) ,
18791 new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( { } ) , 5000 ) ) ,
18892 ] ) ) as Record < string , any > ;
18993 } catch {
19094 prices = { } ;
19195 }
19296
193- const dailyDataPromises = Promise . all (
194- dailyData . map ( async ( dayData ) => {
195- const { total_tokens_deposited, total_tokens_withdrawn, total_address_deposited, total_address_withdrawn } =
196- dayData ;
197- await sumTokenTxs ( total_tokens_deposited , dailyTokensDeposited , prices , lzSymbols ) ;
198- await sumTokenTxs ( total_tokens_withdrawn , dailyTokensWithdrawn , prices , lzSymbols ) ;
199- await sumAddressTxs ( total_address_deposited , dailyAddressesDeposited ) ;
200- await sumAddressTxs ( total_address_withdrawn , dailyAddressesWithdrawn ) ;
201- } )
202- ) ;
203- await dailyDataPromises ;
204-
205- const sourceChainsPromises = Promise . all (
206- sourceChainsDailyData . map ( async ( dayData ) => {
207- const { total_tokens_deposited, total_tokens_withdrawn, total_address_deposited, total_address_withdrawn } =
208- dayData ;
209- await sumTokenTxs ( total_tokens_deposited , dailyTokensWithdrawn , prices , lzSymbols ) ;
210- await sumTokenTxs ( total_tokens_withdrawn , dailyTokensDeposited , prices , lzSymbols ) ;
211- await sumAddressTxs ( total_address_deposited , dailyAddressesWithdrawn ) ;
212- await sumAddressTxs ( total_address_withdrawn , dailyAddressesDeposited ) ;
213- } )
214- ) ;
215- await sourceChainsPromises ;
97+ const buildTokenRecord = ( xs : typeof dt ) : TokenRecord => {
98+ const out : TokenRecord = { } ;
99+ for ( const r of xs ) {
100+ const token = r . key ;
101+ const lower = token ?. toLowerCase ?.( ) ?? token ;
102+ const priceEntry = prices ?. [ lower ] ?? prices ?. [ token ] ;
103+ const addrOnly = lower . includes ( ":" ) ? lower . split ( ":" ) [ 1 ] : lower ;
104+ const fallbackSymbol = lzSymbols ?. [ addrOnly ] ?? lzSymbols ?. [ lower ] ?? "" ;
105+ const symbol = priceEntry ?. symbol ?? fallbackSymbol ?? "" ;
106+ const decimalsVal = priceEntry ?. decimals ;
107+ out [ token ] = {
108+ amount : r . amount ?? "0" ,
109+ usdValue : Number ( r . usd_value ) ,
110+ symbol : normlizeTokenSymbol ( symbol ) ,
111+ decimals : typeof decimalsVal === "string" ? Number ( decimalsVal ) : decimalsVal ?? 0 ,
112+ } ;
113+ }
114+ return out ;
115+ } ;
216116
217- const sortedDailyTokensDeposited = Object . fromEntries (
218- Object . entries ( dailyTokensDeposited )
219- . sort ( ( a , b ) => {
220- return b [ 1 ] . usdValue - a [ 1 ] . usdValue ;
221- } )
222- . slice ( 0 , numberOfTokensToReturn )
223- ) ;
224- const sortedDailyTokensWithdrawn = Object . fromEntries (
225- Object . entries ( dailyTokensWithdrawn )
226- . sort ( ( a , b ) => {
227- return b [ 1 ] . usdValue - a [ 1 ] . usdValue ;
228- } )
229- . slice ( 0 , numberOfTokensToReturn )
230- ) ;
231- const sortedDailyAddressesDeposited = Object . fromEntries (
232- Object . entries ( dailyAddressesDeposited )
233- . sort ( ( a , b ) => {
234- return b [ 1 ] . usdValue - a [ 1 ] . usdValue ;
235- } )
236- . slice ( 0 , numberOfTokensToReturn )
237- ) ;
238- const sortedDailyAddressesWithdrawn = Object . fromEntries (
239- Object . entries ( dailyAddressesWithdrawn )
240- . sort ( ( a , b ) => {
241- return b [ 1 ] . usdValue - a [ 1 ] . usdValue ;
242- } )
243- . slice ( 0 , numberOfTokensToReturn )
244- ) ;
117+ const buildAddressRecord = ( xs : typeof da ) : AddressRecord => {
118+ const out : AddressRecord = { } ;
119+ for ( const r of xs ) {
120+ out [ r . key ] = {
121+ usdValue : Number ( r . usd_value ) ,
122+ txs : r . txs ?? 0 ,
123+ } ;
124+ }
125+ return out ;
126+ } ;
245127
246- const response = {
128+ return {
247129 date : queryTimestamp ,
248- totalTokensDeposited : sortedDailyTokensDeposited ,
249- totalTokensWithdrawn : sortedDailyTokensWithdrawn ,
250- totalAddressDeposited : sortedDailyAddressesDeposited ,
251- totalAddressWithdrawn : sortedDailyAddressesWithdrawn ,
130+ totalTokensDeposited : buildTokenRecord ( dt ) ,
131+ totalTokensWithdrawn : buildTokenRecord ( wt ) ,
132+ totalAddressDeposited : buildAddressRecord ( da ) ,
133+ totalAddressWithdrawn : buildAddressRecord ( wa ) ,
252134 } ;
253-
254- return response ;
255135} ;
256136
257137const handler = async ( event : AWSLambda . APIGatewayEvent ) : Promise < IResponse > => {
0 commit comments