@@ -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,141 +175,55 @@ 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 ,
134- }
135- }
136-
137- return {
138- status : 'empty' ,
139- result : null ,
140- }
185+ return createResult ( items , address , chainID )
141186 } ) ,
142187
143188 getMany : ( keys ) =>
144189 Effect . gen ( function * ( ) {
145- if ( keys . length === 0 ) {
146- return [ ]
147- }
148-
149- // Build a batch query for all the keys
150- const conditions = keys . map ( ( { address, signature, event, chainID } ) => {
151- const addressQuery = sql . and ( [
152- sql `address = ${ address . toLowerCase ( ) } ` ,
153- sql `chain = ${ chainID } ` ,
154- sql `type = 'address'` ,
155- ] )
156-
157- const signatureQuery = signature
158- ? sql . and ( [ sql `signature = ${ signature } ` , sql `type = 'func'` ] )
159- : undefined
160- const eventQuery = event ? sql . and ( [ sql `event = ${ event } ` , sql `type = 'event'` ] ) : undefined
161-
162- return signature == null && event == null
163- ? addressQuery
164- : sql . or ( [ addressQuery , signatureQuery , eventQuery ] . filter ( Boolean ) )
165- } )
190+ if ( keys . length === 0 ) return [ ]
166191
192+ // Single database query for all keys
193+ const conditions = keys . map ( ( key ) => buildQueryForKey ( sql , key ) )
167194 const batchQuery = sql . or ( conditions )
168195
169196 const allItems = yield * sql `SELECT * FROM ${ table } WHERE ${ batchQuery } ` . pipe (
170197 Effect . tapError ( Effect . logError ) ,
171198 Effect . catchAll ( ( ) => Effect . succeed ( [ ] ) ) ,
172199 )
173200
174- // Process results for each key
201+ // Build efficient lookup map once
202+ const lookupMap = buildLookupMap ( allItems )
203+
204+ // Process results for each key using lookup map
175205 return keys . map ( ( { address, signature, event, chainID } ) => {
176- const keyItems = allItems . filter ( ( item ) => {
177- // Match by address and chain
178- if (
179- typeof item . address === 'string' &&
180- item . address . toLowerCase ( ) === address . toLowerCase ( ) &&
181- item . chain === chainID
182- ) {
183- return true
184- }
185- // Match by signature
186- if ( signature && item . signature === signature && item . type === 'func' ) {
187- return true
188- }
189- // Match by event
190- if ( event && item . event === event && item . type === 'event' ) {
191- return true
192- }
193- return false
194- } )
195-
196- const successItems = keyItems . filter ( ( item ) => item . status === 'success' )
197-
198- const item =
199- successItems . find ( ( item ) => {
200- // Prioritize address over fragments
201- return item . type === 'address'
202- } ) ?? successItems [ 0 ]
203-
204- if ( item != null ) {
205- return {
206- status : 'success' ,
207- result : {
208- type : item . type ,
209- event : item . event ,
210- signature : item . signature ,
211- address,
212- chainID,
213- abi : item . abi ,
214- } ,
215- } as AbiStore . ContractAbiResult
216- } else if ( keyItems [ 0 ] != null && keyItems [ 0 ] . status === 'not-found' ) {
217- return {
218- status : 'not-found' ,
219- result : null ,
220- }
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 )
221218 }
222219
223- return {
224- status : 'empty' ,
225- result : null ,
220+ // Get event-based matches
221+ if ( event ) {
222+ const eventKey = `event:${ event } `
223+ const eventItems = lookupMap . get ( eventKey ) || [ ]
224+ keyItems . push ( ...eventItems )
226225 }
226+ return createResult ( keyItems , address , chainID )
227227 } )
228228 } ) ,
229229 } )
0 commit comments