@@ -2,6 +2,92 @@ import * as AbiStore from '../abi-store.js'
22import { Effect , Layer } from 'effect'
33import { SqlClient } from '@effect/sql'
44
5+ // Utility function to build query conditions for a single key
6+ const buildQueryForKey = (
7+ sql : SqlClient . SqlClient ,
8+ { address, signature, event, chainID } : { address : string ; signature ?: string ; event ?: string ; chainID : number } ,
9+ ) => {
10+ const addressQuery = sql . and ( [
11+ sql `address = ${ address . toLowerCase ( ) } ` ,
12+ sql `chain = ${ chainID } ` ,
13+ sql `type = 'address'` ,
14+ ] )
15+
16+ const signatureQuery = signature ? sql . and ( [ sql `signature = ${ signature } ` , sql `type = 'func'` ] ) : undefined
17+ const eventQuery = event ? sql . and ( [ sql `event = ${ event } ` , sql `type = 'event'` ] ) : undefined
18+
19+ return signature == null && event == null
20+ ? addressQuery
21+ : sql . or ( [ addressQuery , signatureQuery , eventQuery ] . filter ( Boolean ) )
22+ }
23+
24+ // Convert database items to result format
25+ const createResult = ( items : readonly any [ ] , address : string , chainID : number ) : AbiStore . ContractAbiResult => {
26+ const successItems = items . filter ( ( item ) => item . status === 'success' )
27+
28+ const item =
29+ successItems . find ( ( item ) => {
30+ // Prioritize address over fragments
31+ return item . type === 'address'
32+ } ) ?? successItems [ 0 ]
33+
34+ if ( item != null ) {
35+ return {
36+ status : 'success' ,
37+ result : {
38+ type : item . type ,
39+ event : item . event ,
40+ signature : item . signature ,
41+ address,
42+ chainID,
43+ abi : item . abi ,
44+ } ,
45+ } as AbiStore . ContractAbiResult
46+ } else if ( items [ 0 ] != null && items [ 0 ] . status === 'not-found' ) {
47+ return {
48+ status : 'not-found' ,
49+ result : null ,
50+ }
51+ }
52+
53+ return {
54+ status : 'empty' ,
55+ result : null ,
56+ }
57+ }
58+
59+ // Build single lookup map with prefixed keys
60+ const buildLookupMap = ( allItems : readonly any [ ] ) => {
61+ const lookupMap = new Map < string , any [ ] > ( )
62+
63+ const addToMap = ( key : string , item : any ) => {
64+ if ( ! lookupMap . has ( key ) ) lookupMap . set ( key , [ ] )
65+ lookupMap . get ( key ) ?. push ( item )
66+ }
67+
68+ for ( const item of allItems ) {
69+ // Address-based lookup: "addr:address_chain" (with same type check as original)
70+ if ( typeof item . address === 'string' && typeof item . chain === 'number' ) {
71+ const addressKey = `addr:${ item . address . toLowerCase ( ) } _${ item . chain } `
72+ addToMap ( addressKey , item )
73+ }
74+
75+ // Signature-based lookup: "sig:signature"
76+ if ( item . signature && item . type === 'func' ) {
77+ const signatureKey = `sig:${ item . signature } `
78+ addToMap ( signatureKey , item )
79+ }
80+
81+ // Event-based lookup: "event:event"
82+ if ( item . event && item . type === 'event' ) {
83+ const eventKey = `event:${ item . event } `
84+ addToMap ( eventKey , item )
85+ }
86+ }
87+
88+ return lookupMap
89+ }
90+
591export const make = ( strategies : AbiStore . AbiStore [ 'strategies' ] ) =>
692 Layer . scoped (
793 AbiStore . AbiStore ,
@@ -89,55 +175,56 @@ export const make = (strategies: AbiStore.AbiStore['strategies']) =>
89175
90176 get : ( { address, signature, event, chainID } ) =>
91177 Effect . gen ( function * ( ) {
92- const addressQuery = sql . and ( [
93- sql `address = ${ address . toLowerCase ( ) } ` ,
94- sql `chain = ${ chainID } ` ,
95- sql `type = 'address'` ,
96- ] )
97-
98- const signatureQuery = signature ? sql . and ( [ sql `signature = ${ signature } ` , sql `type = 'func'` ] ) : undefined
99- const eventQuery = event ? sql . and ( [ sql `event = ${ event } ` , sql `type = 'event'` ] ) : undefined
100- const query =
101- signature == null && event == null
102- ? addressQuery
103- : sql . or ( [ addressQuery , signatureQuery , eventQuery ] . filter ( Boolean ) )
178+ const query = buildQueryForKey ( sql , { address, signature, event, chainID } )
104179
105180 const items = yield * sql ` SELECT * FROM ${ table } WHERE ${ query } ` . pipe (
106181 Effect . tapError ( Effect . logError ) ,
107182 Effect . catchAll ( ( ) => Effect . succeed ( [ ] ) ) ,
108183 )
109184
110- const successItems = items . filter ( ( item ) => item . status === 'success' )
111-
112- const item =
113- successItems . find ( ( item ) => {
114- // Prioritize address over fragments
115- return item . type === 'address'
116- } ) ?? successItems [ 0 ]
117-
118- if ( item != null ) {
119- return {
120- status : 'success' ,
121- result : {
122- type : item . type ,
123- event : item . event ,
124- signature : item . signature ,
125- address,
126- chainID,
127- abi : item . abi ,
128- } ,
129- } as AbiStore . ContractAbiResult
130- } else if ( items [ 0 ] != null && items [ 0 ] . status === 'not-found' ) {
131- return {
132- status : 'not-found' ,
133- result : null ,
185+ return createResult ( items , address , chainID )
186+ } ) ,
187+
188+ getMany : ( keys ) =>
189+ Effect . gen ( function * ( ) {
190+ if ( keys . length === 0 ) return [ ]
191+
192+ // Single database query for all keys
193+ const conditions = keys . map ( ( key ) => buildQueryForKey ( sql , key ) )
194+ const batchQuery = sql . or ( conditions )
195+
196+ const allItems = yield * sql `SELECT * FROM ${ table } WHERE ${ batchQuery } ` . pipe (
197+ Effect . tapError ( Effect . logError ) ,
198+ Effect . catchAll ( ( ) => Effect . succeed ( [ ] ) ) ,
199+ )
200+
201+ // Build efficient lookup map once
202+ const lookupMap = buildLookupMap ( allItems )
203+
204+ // Process results for each key using lookup map
205+ return keys . map ( ( { address, signature, event, chainID } ) => {
206+ const keyItems : any [ ] = [ ]
207+
208+ // Get address-based matches
209+ const addressKey = `addr:${ address . toLowerCase ( ) } _${ chainID } `
210+ const addressItems = lookupMap . get ( addressKey ) || [ ]
211+ keyItems . push ( ...addressItems )
212+
213+ // Get signature-based matches
214+ if ( signature ) {
215+ const signatureKey = `sig:${ signature } `
216+ const signatureItems = lookupMap . get ( signatureKey ) || [ ]
217+ keyItems . push ( ...signatureItems )
134218 }
135- }
136219
137- return {
138- status : 'empty' ,
139- result : null ,
140- }
220+ // Get event-based matches
221+ if ( event ) {
222+ const eventKey = `event:${ event } `
223+ const eventItems = lookupMap . get ( eventKey ) || [ ]
224+ keyItems . push ( ...eventItems )
225+ }
226+ return createResult ( keyItems , address , chainID )
227+ } )
141228 } ) ,
142229 } )
143230 } ) ,
0 commit comments