@@ -43,9 +43,9 @@ describe('graph-builder', () => {
4343
4444 it ( 'should discover connections at depth 1' , async ( ) => {
4545 mockTrace . mockResolvedValue ( { success : true , data : {
46- connections : [
47- { address : '0xA' , volume_usd : 1000 , tx_count : 5 , direction : 'out' } ,
48- { address : '0xB' , volume_usd : 2000 , tx_count : 3 , direction : 'in' } ,
46+ edges : [
47+ { from : '0xseed' , to : '0xA' , volume_usd : 1000 , tx_count : 5 , direction : 'out' } ,
48+ { from : '0xB' , to : '0xseed ', volume_usd : 2000 , tx_count : 3 , direction : 'in' } ,
4949 ] ,
5050 } } ) ;
5151 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
@@ -55,17 +55,17 @@ describe('graph-builder', () => {
5555
5656 it ( 'should not revisit nodes' , async ( ) => {
5757 mockTrace . mockImplementation ( async ( addr : string ) => {
58- if ( addr === '0xseed' ) return { success : true , data : { connections : [ { address : '0xA' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ] } } ;
59- if ( addr === '0xA' ) return { success : true , data : { connections : [ { address : '0xseed' , volume_usd : 100 , tx_count : 1 , direction : 'in' } ] } } ;
60- return { success : true , data : { connections : [ ] } } ;
58+ if ( addr === '0xseed' ) return { success : true , data : { edges : [ { from : '0xseed' , to : '0xA' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ] } } ;
59+ if ( addr === '0xA' ) return { success : true , data : { edges : [ { from : '0xseed' , to : '0xA ', volume_usd : 100 , tx_count : 1 , direction : 'in' } ] } } ;
60+ return { success : true , data : { edges : [ ] } } ;
6161 } ) ;
6262 const g = await buildGraph ( { ...baseOpts , depth : 3 } ) ;
6363 expect ( g . nodes . length ) . toBe ( 2 ) ;
6464 } ) ;
6565
6666 it ( 'should respect depth guard' , async ( ) => {
6767 mockTrace . mockResolvedValue ( { success : true , data : {
68- connections : [ { address : '0xdeep' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] ,
68+ edges : [ { from : '0xseed' , to : '0xdeep' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] ,
6969 } } ) ;
7070 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
7171 const deepNode = g . nodes . find ( n => n . id === '0xdeep' ) ;
@@ -74,9 +74,9 @@ describe('graph-builder', () => {
7474
7575 it ( 'should filter by minVolume' , async ( ) => {
7676 mockTrace . mockResolvedValue ( { success : true , data : {
77- connections : [
78- { address : '0xbig' , volume_usd : 50000 , tx_count : 10 , direction : 'out' } ,
79- { address : '0xsmall' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ,
77+ edges : [
78+ { from : '0xseed' , to : '0xbig' , volume_usd : 50000 , tx_count : 10 , direction : 'out' } ,
79+ { from : '0xseed' , to : '0xsmall' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ,
8080 ] ,
8181 } } ) ;
8282 const g = await buildGraph ( { ...baseOpts , depth : 1 , minVolume : 1000 } ) ;
@@ -87,7 +87,7 @@ describe('graph-builder', () => {
8787 it ( 'should fallback to counterparties when trace fails' , async ( ) => {
8888 mockTrace . mockResolvedValue ( { success : false } ) ;
8989 mockCp . mockResolvedValue ( { success : true , data : [
90- { address : '0xcp1' , volume_usd : 3000 , tx_count : 2 , direction : 'both' , label : 'Binance' } ,
90+ { counterparty_address : '0xcp1' , total_volume_usd : 3000 , interaction_count : 2 , direction : 'both' , counterparty_address_label : [ 'Binance' ] } ,
9191 ] } ) ;
9292 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
9393 expect ( g . nodes . length ) . toBe ( 2 ) ;
@@ -96,9 +96,9 @@ describe('graph-builder', () => {
9696
9797 it ( 'should deduplicate edges' , async ( ) => {
9898 mockTrace . mockImplementation ( async ( addr : string ) => {
99- if ( addr === '0xseed' ) return { success : true , data : { connections : [ { address : '0xA' , volume_usd : 1000 , tx_count : 5 , direction : 'out' } ] } } ;
100- if ( addr === '0xa' ) return { success : true , data : { connections : [ { address : '0xseed' , volume_usd : 1000 , tx_count : 5 , direction : 'in' } ] } } ;
101- return { success : true , data : { connections : [ ] } } ;
99+ if ( addr === '0xseed' ) return { success : true , data : { edges : [ { from : '0xseed' , to : '0xA' , volume_usd : 1000 , tx_count : 5 , direction : 'out' } ] } } ;
100+ if ( addr === '0xa' ) return { success : true , data : { edges : [ { from : '0xseed' , to : '0xA ', volume_usd : 1000 , tx_count : 5 , direction : 'in' } ] } } ;
101+ return { success : true , data : { edges : [ ] } } ;
102102 } ) ;
103103 const g = await buildGraph ( { ...baseOpts , depth : 2 } ) ;
104104 const edgeCount = g . links . filter ( e =>
@@ -108,15 +108,15 @@ describe('graph-builder', () => {
108108 } ) ;
109109
110110 it ( 'should enrich SM nodes' , async ( ) => {
111- mockTrace . mockResolvedValue ( { success : true , data : { connections : [ { address : '0xsm' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
111+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [ { from : '0xseed' , to : '0xsm' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
112112 mockEnrich . mockResolvedValue ( { labels : [ 'Fund' ] , sm_labels : [ 'Smart Money' ] , balance_usd : 1e6 , pnl_30d : 50000 , defi_protocols : 3 } ) ;
113113 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
114114 const smNode = g . nodes . find ( n => n . id === '0xsm' ) ;
115115 expect ( smNode ?. type ) . toBe ( 'smart-money' ) ;
116116 } ) ;
117117
118118 it ( 'should set label from enrichment' , async ( ) => {
119- mockTrace . mockResolvedValue ( { success : true , data : { connections : [ { address : '0xlabel' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
119+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [ { from : '0xseed' , to : '0xlabel' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
120120 mockEnrich . mockImplementation ( async ( addr : string ) => {
121121 if ( addr === '0xlabel' ) return { labels : [ 'Binance' ] , sm_labels : [ ] , balance_usd : 0 , pnl_30d : 0 , defi_protocols : 0 } ;
122122 return { labels : [ ] , sm_labels : [ ] , balance_usd : 0 , pnl_30d : 0 , defi_protocols : 0 } ;
@@ -127,7 +127,7 @@ describe('graph-builder', () => {
127127 } ) ;
128128
129129 it ( 'should classify contract nodes' , async ( ) => {
130- mockTrace . mockResolvedValue ( { success : true , data : { connections : [ { address : '0xc' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
130+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [ { from : '0xseed' , to : '0xc' , volume_usd : 1000 , tx_count : 1 , direction : 'out' } ] } } ) ;
131131 mockEnrich . mockImplementation ( async ( addr : string ) => {
132132 if ( addr === '0xc' ) return { labels : [ 'Uniswap Router' ] , sm_labels : [ ] , balance_usd : 0 , pnl_30d : 0 , defi_protocols : 0 } ;
133133 return { labels : [ ] , sm_labels : [ ] , balance_usd : 0 , pnl_30d : 0 , defi_protocols : 0 } ;
@@ -146,10 +146,10 @@ describe('graph-builder', () => {
146146 } ) ;
147147
148148 it ( 'should normalize edge directions' , async ( ) => {
149- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
150- { address : '0xin' , volume_usd : 100 , tx_count : 1 , direction : 'in' } ,
151- { address : '0xout' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ,
152- { address : '0xboth' , volume_usd : 100 , tx_count : 1 , direction : 'both' } ,
149+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
150+ { from : '0xin' , to : '0xseed ', volume_usd : 100 , tx_count : 1 , direction : 'in' } ,
151+ { from : '0xseed' , to : '0xout' , volume_usd : 100 , tx_count : 1 , direction : 'out' } ,
152+ { from : '0xseed' , to : '0xboth' , volume_usd : 100 , tx_count : 1 , direction : 'both' } ,
153153 ] } } ) ;
154154 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
155155 expect ( g . links . find ( e => e . target === '0xin' ) ?. direction ) . toBe ( 'inflow' ) ;
@@ -166,8 +166,8 @@ describe('graph-builder', () => {
166166 } ) ;
167167
168168 it ( 'should apply conn.label to node' , async ( ) => {
169- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
170- { address : '0xlbl' , volume_usd : 100 , tx_count : 1 , direction : 'out' , label : 'Known Whale' } ,
169+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
170+ { from : '0xseed' , to : '0xlbl' , volume_usd : 100 , tx_count : 1 , direction : 'out' , label : 'Known Whale' } ,
171171 ] } } ) ;
172172 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
173173 const node = g . nodes . find ( n => n . id === '0xlbl' ) ;
@@ -178,53 +178,65 @@ describe('graph-builder', () => {
178178 it ( 'should fallback counterparty fields to defaults when missing' , async ( ) => {
179179 mockTrace . mockResolvedValue ( { success : false } ) ;
180180 mockCp . mockResolvedValue ( { success : true , data : [
181- { address : '0xno_fields' } , // Missing volume_usd, tx_count, direction
181+ { counterparty_address : '0xno_fields' } , // Missing volume_usd, tx_count, direction
182+ { address : '0xcp_address_fallback' , volume_usd : 100 , tx_count : 1 , direction : 'out' } // tests c.address fallback on line 158
182183 ] } ) ;
183184 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
184185 const edge = g . links . find ( e => e . target === '0xno_fields' ) ;
185186 expect ( edge ?. volume_usd ) . toBe ( 0 ) ;
186187 expect ( edge ?. tx_count ) . toBe ( 0 ) ;
187188 expect ( edge ?. direction ) . toBe ( 'bidirectional' ) ;
189+
190+ // Check fallback
191+ expect ( g . nodes . find ( n => n . id === '0xcp_address_fallback' ) ) . toBeDefined ( ) ;
188192 } ) ;
189193
190194 it ( 'should handle trace connections with missing optional fields' , async ( ) => {
191- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
192- { address : '0xbare' } , // No volume_usd, tx_count, direction
195+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
196+ { from : '0xseed' , to : '0xbare' } , // No volume_usd, tx_count, direction
197+ { from : '0xother' , to : '0xseed' } , // isFrom is false, tests line 145 'in'
198+ { address : '0xnofrom' , to : '0xseed' } // e.from is undefined, tests line 140 fallback
193199 ] } } ) ;
194200 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
195- const edge = g . links . find ( e => e . target === '0xbare' ) ;
196- expect ( edge ?. volume_usd ) . toBe ( 0 ) ;
197- expect ( edge ?. tx_count ) . toBe ( 0 ) ;
198- expect ( edge ?. direction ) . toBe ( 'bidirectional' ) ;
201+ const edgeOut = g . links . find ( e => e . target === '0xbare' ) ;
202+ expect ( edgeOut ?. volume_usd ) . toBe ( 0 ) ;
203+ expect ( edgeOut ?. tx_count ) . toBe ( 0 ) ;
204+ expect ( edgeOut ?. direction ) . toBe ( 'outflow' ) ;
205+
206+ const edgeIn = g . links . find ( e => e . target === '0xother' ) ;
207+ expect ( edgeIn ?. direction ) . toBe ( 'inflow' ) ;
208+
209+ const edgeNoFrom = g . links . find ( e => e . target === '0xnofrom' ) ;
210+ expect ( edgeNoFrom ?. direction ) . toBe ( 'inflow' ) ;
199211 } ) ;
200212
201213 it ( 'should normalize "inflow" direction alias' , async ( ) => {
202- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
203- { address : '0xinflow' , volume_usd : 100 , tx_count : 1 , direction : 'inflow' } ,
214+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
215+ { from : '0xinflow' , to : '0xseed ', volume_usd : 100 , tx_count : 1 , direction : 'inflow' } ,
204216 ] } } ) ;
205217 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
206218 expect ( g . links . find ( e => e . target === '0xinflow' ) ?. direction ) . toBe ( 'inflow' ) ;
207219 } ) ;
208220
209221 it ( 'should normalize "outflow" direction alias' , async ( ) => {
210- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
211- { address : '0xoutflow' , volume_usd : 100 , tx_count : 1 , direction : 'outflow' } ,
222+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
223+ { from : '0xseed' , to : '0xoutflow' , volume_usd : 100 , tx_count : 1 , direction : 'outflow' } ,
212224 ] } } ) ;
213225 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
214226 expect ( g . links . find ( e => e . target === '0xoutflow' ) ?. direction ) . toBe ( 'outflow' ) ;
215227 } ) ;
216228
217229 it ( 'should pass primary_token through to edge' , async ( ) => {
218- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
219- { address : '0xtoken' , volume_usd : 100 , tx_count : 1 , direction : 'out' , primary_token : 'USDC' } ,
230+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
231+ { from : '0xseed' , to : '0xtoken' , volume_usd : 100 , tx_count : 1 , direction : 'out' , primary_token : 'USDC' } ,
220232 ] } } ) ;
221233 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
222234 expect ( g . links . find ( e => e . target === '0xtoken' ) ?. primary_token ) . toBe ( 'USDC' ) ;
223235 } ) ;
224236
225237 it ( 'should not override label if enrichment gives labels but label is already custom' , async ( ) => {
226- mockTrace . mockResolvedValue ( { success : true , data : { connections : [
227- { address : '0xcustom' , volume_usd : 100 , tx_count : 1 , direction : 'out' , label : 'My Label' } ,
238+ mockTrace . mockResolvedValue ( { success : true , data : { edges : [
239+ { from : '0xseed' , to : '0xcustom' , volume_usd : 100 , tx_count : 1 , direction : 'out' , label : 'My Label' } ,
228240 ] } } ) ;
229241 mockEnrich . mockImplementation ( async ( addr : string ) => {
230242 if ( addr === '0xcustom' ) return { labels : [ 'Enriched' ] , sm_labels : [ ] , balance_usd : 0 , pnl_30d : 0 , defi_protocols : 0 } ;
@@ -239,7 +251,7 @@ describe('graph-builder', () => {
239251 it ( 'should handle trace data without connections array' , async ( ) => {
240252 mockTrace . mockResolvedValue ( { success : true , data : { result : 'ok' } } ) ; // No connections key
241253 mockCp . mockResolvedValue ( { success : true , data : [
242- { address : '0xfallback' , volume_usd : 500 , tx_count : 2 , direction : 'out' } ,
254+ { counterparty_address : '0xfallback' , total_volume_usd : 500 , interaction_count : 2 , direction : 'out' } ,
243255 ] } ) ;
244256 const g = await buildGraph ( { ...baseOpts , depth : 1 } ) ;
245257 expect ( g . nodes . find ( n => n . id === '0xfallback' ) ) . toBeDefined ( ) ;
0 commit comments