@@ -11,7 +11,6 @@ import {
1111 fromAssetId ,
1212 optimismAssetId ,
1313} from '@shapeshiftoss/caip'
14- import { isEvmChainId } from '@shapeshiftoss/chain-adapters'
1514import type { Asset } from '@shapeshiftoss/types'
1615import {
1716 createThrottle ,
@@ -92,6 +91,8 @@ const chunkArray = <T>(array: T[], chunkSize: number) => {
9291 return result
9392}
9493
94+ const PLASMA_USDT0_ASSET_ID = 'eip155:9745/erc20:0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb'
95+
9596const getZerionRelatedAssetIds = async (
9697 assetId : AssetId ,
9798 assetData : Record < AssetId , PartialFields < Asset , 'relatedAssetKey' > > ,
@@ -106,9 +107,9 @@ const getZerionRelatedAssetIds = async (
106107 } ,
107108 }
108109
109- const { chainId , assetReference } = fromAssetId ( assetId )
110+ const { assetReference } = fromAssetId ( assetId )
110111
111- if ( ! isEvmChainId ( chainId ) || FEE_ASSET_IDS . includes ( assetId ) ) return
112+ if ( FEE_ASSET_IDS . includes ( assetId ) ) return
112113
113114 const filter = { params : { 'filter[implementation_address]' : assetReference } }
114115 const url = '/fungibles'
@@ -131,11 +132,14 @@ const getZerionRelatedAssetIds = async (
131132
132133 const implementations = firstEntry . attributes . implementations
133134
134- // Use all assetIds actually present in the dataset
135+ // Use all assetIds actually present in the dataset, excluding Plasma USDT0 (corrupt CoinGecko data)
135136 const allRelatedAssetIds = implementations
136137 ?. map ( zerionImplementationToMaybeAssetId )
137138 . filter ( isSome )
138- . filter ( relatedAssetId => assetData [ relatedAssetId ] !== undefined )
139+ . filter ( relatedAssetId => {
140+ return assetData [ relatedAssetId ] !== undefined
141+ } )
142+ . filter ( relatedAssetId => relatedAssetId !== PLASMA_USDT0_ASSET_ID )
139143
140144 if ( ! allRelatedAssetIds || allRelatedAssetIds . length <= 1 ) {
141145 return
@@ -161,11 +165,12 @@ const getCoingeckoRelatedAssetIds = async (
161165
162166 const platforms = data . platforms
163167
164- // Use all assetIds actually present in the dataset
168+ // Use all assetIds actually present in the dataset, excluding Plasma USDT0 (corrupt CoinGecko data)
165169 const allRelatedAssetIds = Object . entries ( platforms )
166170 ?. map ( coingeckoPlatformDetailsToMaybeAssetId )
167171 . filter ( isSome )
168172 . filter ( relatedAssetId => assetData [ relatedAssetId ] !== undefined )
173+ . filter ( relatedAssetId => relatedAssetId !== PLASMA_USDT0_ASSET_ID )
169174
170175 if ( allRelatedAssetIds . length <= 1 ) {
171176 return
@@ -185,12 +190,11 @@ const processRelatedAssetIds = async (
185190 assetId : AssetId ,
186191 assetData : Record < AssetId , PartialFields < Asset , 'relatedAssetKey' > > ,
187192 relatedAssetIndex : Record < AssetId , AssetId [ ] > ,
188- coingeckoPlatformsByAssetId : Record < AssetId , number > ,
189193 throttle : ( ) => Promise < void > ,
190194) : Promise < void > => {
191195 // Skip related asset generation for Plasma usdt0 - Coingecko has corrupt data claiming
192196 // it shares the same Arbitrum/Polygon contracts as real USDT, which corrupts groupings
193- if ( assetId === 'eip155:9745/erc20:0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb' ) {
197+ if ( assetId === PLASMA_USDT0_ASSET_ID ) {
194198 assetData [ assetId ] . relatedAssetKey = null
195199 await throttle ( )
196200 return
@@ -202,18 +206,6 @@ const processRelatedAssetIds = async (
202206 return
203207 }
204208
205- // For assets with relatedAssetKey: null or undefined, check if CoinGecko now has multiple platforms
206- // This optimization skips expensive API calls for assets that only exist on one chain
207- if ( existingRelatedAssetKey === null || existingRelatedAssetKey === undefined ) {
208- const platformCount = coingeckoPlatformsByAssetId [ assetId ]
209- if ( platformCount === undefined || platformCount <= 1 ) {
210- assetData [ assetId ] . relatedAssetKey = null
211- // Still no related assets upstream, skip expensive API calls
212- await throttle ( )
213- return
214- }
215- }
216-
217209 console . log ( `Processing related assetIds for ${ assetId } ` )
218210
219211 // Check if this asset is already in the relatedAssetIndex
@@ -225,7 +217,7 @@ const processRelatedAssetIds = async (
225217 )
226218 assetData [ assetId ] . relatedAssetKey = key
227219 }
228- return // Else, early return as this asset is already processed
220+ return // Early return - asset already processed and grouped
229221 }
230222 }
231223
@@ -264,22 +256,37 @@ const processRelatedAssetIds = async (
264256 relatedAssetIds : [ ] ,
265257 }
266258
267- const relatedAssetKey =
259+ let relatedAssetKey =
268260 manualRelatedAssetsResult ?. relatedAssetKey ||
269261 zerionRelatedAssetsResult ?. relatedAssetKey ||
270262 coingeckoRelatedAssetsResult ?. relatedAssetKey ||
271263 assetId
272264
265+ // If the relatedAssetKey itself points to another key, follow the chain to find the actual key
266+ // This handles the case where Tron WETH -> ETH WETH, but ETH WETH -> Arbitrum WETH
267+ const relatedAssetKeyData = assetData [ relatedAssetKey ] ?. relatedAssetKey
268+ if ( relatedAssetKeyData ) {
269+ relatedAssetKey = relatedAssetKeyData
270+ }
271+
272+ // If the relatedAssetKey is Plasma USDT0, reject this entire grouping
273+ if ( relatedAssetKey === PLASMA_USDT0_ASSET_ID ) {
274+ assetData [ assetId ] . relatedAssetKey = null
275+ await throttle ( )
276+ return
277+ }
278+
273279 const zerionRelatedAssetIds = zerionRelatedAssetsResult ?. relatedAssetIds ?? [ ]
274280 const coingeckoRelatedAssetIds = coingeckoRelatedAssetsResult ?. relatedAssetIds ?? [ ]
281+
275282 const mergedRelatedAssetIds = Array . from (
276283 new Set ( [
277284 ...manualRelatedAssetIds ,
278285 ...zerionRelatedAssetIds ,
279286 ...coingeckoRelatedAssetIds ,
280287 assetId ,
281288 ] ) ,
282- )
289+ ) . filter ( id => id !== PLASMA_USDT0_ASSET_ID ) // Filter out Plasma USDT0 from final merged array
283290
284291 // Has zerion-provided related assets, or manually added ones
285292 const hasRelatedAssets = mergedRelatedAssetIds . length > 1
@@ -290,13 +297,17 @@ const processRelatedAssetIds = async (
290297 const isAlreadyGrouped = existingGroup && existingGroup . includes ( assetId )
291298
292299 if ( ! isAlreadyGrouped ) {
293- // This is the first asset in this group to be processed, set up the group
294- relatedAssetIndex [ relatedAssetKey ] = mergedRelatedAssetIds
300+ // Merge with existing group instead of replacing it
301+ const currentGroup = relatedAssetIndex [ relatedAssetKey ] || [ ]
302+ relatedAssetIndex [ relatedAssetKey ] = Array . from (
303+ new Set ( [ ...currentGroup , ...mergedRelatedAssetIds ] ) ,
304+ )
295305 }
296306
297307 // Always ensure all assets in the group have the correct relatedAssetKey
298308 // This handles both new groups and updates from parallel processing
299- for ( const relatedAssetId of mergedRelatedAssetIds ) {
309+ const allAssetsInGroup = relatedAssetIndex [ relatedAssetKey ]
310+ for ( const relatedAssetId of allAssetsInGroup ) {
300311 if ( assetData [ relatedAssetId ] ) {
301312 assetData [ relatedAssetId ] . relatedAssetKey = relatedAssetKey
302313 }
@@ -321,26 +332,6 @@ export const generateRelatedAssetIndex = async () => {
321332 const { assetData : generatedAssetData , sortedAssetIds } = decodeAssetData ( encodedAssetData )
322333 const relatedAssetIndex = decodeRelatedAssetIndex ( encodedRelatedAssetIndex , sortedAssetIds )
323334
324- const coingeckoData = await adapters . fetchCoingeckoData ( adapters . coingeckoUrl )
325-
326- const coingeckoPlatformsByAssetId = coingeckoData . reduce < Record < AssetId , number > > ( ( acc , coin ) => {
327- const supportedPlatforms = Object . entries ( coin . platforms )
328- . map ( ( [ platform , address ] ) => {
329- try {
330- return coingeckoPlatformDetailsToMaybeAssetId ( [ platform , address ] )
331- } catch {
332- return undefined
333- }
334- } )
335- . filter ( isSome )
336-
337- for ( const assetId of supportedPlatforms ) {
338- acc [ assetId ] = supportedPlatforms . length
339- }
340-
341- return acc
342- } , { } )
343-
344335 // Remove stale related asset data from the assetData where:
345336 // a) the primary related asset no longer exists in the dataset
346337 // b) the related asset key is Plasma usdt0 (corrupt Coingecko data)
@@ -376,18 +367,13 @@ export const generateRelatedAssetIndex = async () => {
376367 drainPerInterval : 25 , // Adjusted drain rate to replenish at a sustainable pace
377368 intervalMs : 2000 ,
378369 } )
370+
379371 const chunks = chunkArray ( Object . keys ( generatedAssetData ) , BATCH_SIZE )
380372 for ( const [ i , batch ] of chunks . entries ( ) ) {
381373 console . log ( `Processing chunk: ${ i } of ${ chunks . length } ` )
382374 await Promise . all (
383375 batch . map ( async assetId => {
384- await processRelatedAssetIds (
385- assetId ,
386- generatedAssetData ,
387- relatedAssetIndex ,
388- coingeckoPlatformsByAssetId ,
389- throttle ,
390- )
376+ await processRelatedAssetIds ( assetId , generatedAssetData , relatedAssetIndex , throttle )
391377 return
392378 } ) ,
393379 )
0 commit comments