@@ -34,7 +34,6 @@ import {
34
34
fetchTokenInfo ,
35
35
getCachedBlockForTimestamp ,
36
36
getCurrentTime ,
37
- getNetworkName ,
38
37
isDefined ,
39
38
mapAsync ,
40
39
paginatedEventQuery ,
@@ -75,17 +74,11 @@ type HubPoolEvent =
75
74
| "RootBundleExecuted"
76
75
| "CrossChainContractsSet" ;
77
76
78
- type L1TokensToDestinationTokens = {
79
- [ l1Token : string ] : { [ destinationChainId : number ] : Address } ;
80
- } ;
81
-
82
77
export type LpFeeRequest = Pick < Deposit , "originChainId" | "inputToken" | "inputAmount" | "quoteTimestamp" > & {
83
78
paymentChainId ?: number ;
84
79
} ;
85
80
86
81
export class HubPoolClient extends BaseAbstractClient {
87
- // L1Token -> destinationChainId -> destinationToken
88
- protected l1TokensToDestinationTokens : L1TokensToDestinationTokens = { } ;
89
82
protected l1Tokens : L1TokenInfo [ ] = [ ] ; // L1Tokens and their associated info.
90
83
// @dev `token` here is a 20-byte hex sting
91
84
protected lpTokens : { [ token : string ] : LpToken } = { } ;
@@ -192,87 +185,65 @@ export class HubPoolClient extends BaseAbstractClient {
192
185
l1Token : EvmAddress ,
193
186
destinationChainId : number ,
194
187
latestHubBlock = Number . MAX_SAFE_INTEGER
195
- ) : Address {
188
+ ) : Address | undefined {
196
189
if ( ! this . l1TokensToDestinationTokensWithBlock ?. [ l1Token . toEvmAddress ( ) ] ?. [ destinationChainId ] ) {
197
- const chain = getNetworkName ( destinationChainId ) ;
198
- const { symbol } = this . l1Tokens . find ( ( { address } ) => address . eq ( l1Token ) ) ?? { symbol : l1Token . toString ( ) } ;
199
- throw new Error ( `Could not find SpokePool mapping for ${ symbol } on ${ chain } and L1 token ${ l1Token } ` ) ;
190
+ return undefined ;
200
191
}
201
192
// Find the last mapping published before the target block.
202
- const l2Token : DestinationTokenWithBlock | undefined = sortEventsDescending (
203
- this . l1TokensToDestinationTokensWithBlock [ l1Token . toEvmAddress ( ) ] [ destinationChainId ]
204
- ) . find ( ( mapping : DestinationTokenWithBlock ) => mapping . blockNumber <= latestHubBlock ) ;
205
- if ( ! l2Token ) {
206
- const chain = getNetworkName ( destinationChainId ) ;
207
- const { symbol } = this . l1Tokens . find ( ( { address } ) => address . eq ( l1Token ) ) ?? { symbol : l1Token . toString ( ) } ;
208
- throw new Error (
209
- `Could not find SpokePool mapping for ${ symbol } on ${ chain } at or before HubPool block ${ latestHubBlock } !`
210
- ) ;
211
- }
212
- return l2Token . l2Token ;
193
+ const l2Token : DestinationTokenWithBlock | undefined = this . l1TokensToDestinationTokensWithBlock [
194
+ l1Token . toEvmAddress ( )
195
+ ] [ destinationChainId ] . find ( ( mapping : DestinationTokenWithBlock ) => mapping . blockNumber <= latestHubBlock ) ;
196
+
197
+ return ! l2Token || l2Token . l2Token . isZeroAddress ( ) ? undefined : l2Token . l2Token ;
213
198
}
214
199
215
200
// Returns the latest L1 token to use for an L2 token as of the input hub block.
216
201
getL1TokenForL2TokenAtBlock (
217
202
l2Token : Address ,
218
203
destinationChainId : number ,
219
204
latestHubBlock = Number . MAX_SAFE_INTEGER
220
- ) : EvmAddress {
221
- const l2Tokens = Object . keys ( this . l1TokensToDestinationTokensWithBlock )
222
- . filter ( ( l1Token ) => this . l2TokenEnabledForL1Token ( EvmAddress . from ( l1Token ) , destinationChainId ) )
223
- . map ( ( l1Token ) => {
224
- // Return all matching L2 token mappings that are equal to or earlier than the target block.
225
- // @dev Since tokens on L2s (like Solana) can have 32 byte addresses, filter on the lower 20 bytes of the token only.
226
- return this . l1TokensToDestinationTokensWithBlock [ l1Token ] [ destinationChainId ] . filter (
227
- ( dstTokenWithBlock ) =>
228
- dstTokenWithBlock . l2Token . truncateToBytes20 ( ) === l2Token . truncateToBytes20 ( ) &&
229
- dstTokenWithBlock . blockNumber <= latestHubBlock
230
- ) ;
231
- } )
232
- . flat ( ) ;
233
- if ( l2Tokens . length === 0 ) {
234
- const chain = getNetworkName ( destinationChainId ) ;
235
- throw new Error (
236
- `Could not find HubPool mapping for ${ l2Token } on ${ chain } at or before HubPool block ${ latestHubBlock } !`
205
+ ) : EvmAddress | undefined {
206
+ const l2Tokens = Object . keys ( this . l1TokensToDestinationTokensWithBlock ) . flatMap ( ( l1Token ) => {
207
+ // Get the latest L2 token mapping for the given L1 token.
208
+ // @dev Since tokens on L2s (like Solana) can have 32 byte addresses, filter on the lower 20 bytes of the token only.
209
+ const sortedL2Tokens = sortEventsDescending (
210
+ ( this . l1TokensToDestinationTokensWithBlock [ l1Token ] [ destinationChainId ] ?? [ ] ) . filter (
211
+ ( dstTokenWithBlock ) => dstTokenWithBlock . blockNumber <= latestHubBlock
212
+ )
237
213
) ;
238
- }
239
- // Find the last mapping published before the target block.
240
- return sortEventsDescending ( l2Tokens ) [ 0 ] . l1Token ;
214
+ // If the latest L2 token mapping is equal to the target L2 token, return it.
215
+ return sortedL2Tokens . length > 0 && sortedL2Tokens [ 0 ] . l2Token . truncateToBytes20 ( ) === l2Token . truncateToBytes20 ( )
216
+ ? sortedL2Tokens [ 0 ]
217
+ : [ ] ;
218
+ } ) ;
219
+
220
+ return l2Tokens . length === 0 ? undefined : sortEventsDescending ( l2Tokens ) [ 0 ] . l1Token ;
241
221
}
242
222
243
223
protected getL1TokenForDeposit (
244
224
deposit : Pick < DepositWithBlock , "originChainId" | "inputToken" | "quoteBlockNumber" >
245
- ) : EvmAddress {
225
+ ) : EvmAddress | undefined {
246
226
// L1-->L2 token mappings are set via PoolRebalanceRoutes which occur on mainnet,
247
227
// so we use the latest token mapping. This way if a very old deposit is filled, the relayer can use the
248
228
// latest L2 token mapping to find the L1 token counterpart.
249
229
return this . getL1TokenForL2TokenAtBlock ( deposit . inputToken , deposit . originChainId , deposit . quoteBlockNumber ) ;
250
230
}
251
231
252
232
l2TokenEnabledForL1Token ( l1Token : EvmAddress , destinationChainId : number ) : boolean {
253
- return this . l1TokensToDestinationTokens ?. [ l1Token . toEvmAddress ( ) ] ?. [ destinationChainId ] != undefined ;
233
+ return this . l2TokenEnabledForL1TokenAtBlock ( l1Token , destinationChainId , Number . MAX_SAFE_INTEGER ) ;
254
234
}
255
235
256
236
l2TokenEnabledForL1TokenAtBlock ( l1Token : EvmAddress , destinationChainId : number , hubBlockNumber : number ) : boolean {
257
237
// Find the last mapping published before the target block.
258
238
const l2Token : DestinationTokenWithBlock | undefined = sortEventsDescending (
259
239
this . l1TokensToDestinationTokensWithBlock ?. [ l1Token . toEvmAddress ( ) ] ?. [ destinationChainId ] ?? [ ]
260
240
) . find ( ( mapping : DestinationTokenWithBlock ) => mapping . blockNumber <= hubBlockNumber ) ;
261
- return l2Token !== undefined ;
241
+ return l2Token !== undefined && ! l2Token . l2Token . isZeroAddress ( ) ;
262
242
}
263
243
264
244
l2TokenHasPoolRebalanceRoute ( l2Token : Address , l2ChainId : number , hubPoolBlock = this . latestHeightSearched ) : boolean {
265
- return Object . values ( this . l1TokensToDestinationTokensWithBlock ) . some ( ( destinationTokenMapping ) => {
266
- return Object . entries ( destinationTokenMapping ) . some ( ( [ _l2ChainId , setPoolRebalanceRouteEvents ] ) => {
267
- return setPoolRebalanceRouteEvents . some ( ( e ) => {
268
- return (
269
- e . blockNumber <= hubPoolBlock &&
270
- e . l2Token . truncateToBytes20 ( ) === l2Token . truncateToBytes20 ( ) &&
271
- Number ( _l2ChainId ) === l2ChainId
272
- ) ;
273
- } ) ;
274
- } ) ;
275
- } ) ;
245
+ const l1Token = this . getL1TokenForL2TokenAtBlock ( l2Token , l2ChainId , hubPoolBlock ) ;
246
+ return l1Token !== undefined ;
276
247
}
277
248
278
249
/**
@@ -407,9 +378,11 @@ export class HubPoolClient extends BaseAbstractClient {
407
378
const hubPoolTokens : { [ k : string ] : EvmAddress } = { } ;
408
379
const getHubPoolToken = ( deposit : LpFeeRequest , quoteBlockNumber : number ) : EvmAddress | undefined => {
409
380
const tokenKey = `${ deposit . originChainId } -${ deposit . inputToken } ` ;
410
- if ( this . l2TokenHasPoolRebalanceRoute ( deposit . inputToken , deposit . originChainId , quoteBlockNumber ) ) {
411
- return ( hubPoolTokens [ tokenKey ] ??= this . getL1TokenForDeposit ( { ...deposit , quoteBlockNumber } ) ) ;
412
- } else return undefined ;
381
+ const l1Token = this . getL1TokenForDeposit ( { ...deposit , quoteBlockNumber } ) ;
382
+ if ( ! l1Token ) {
383
+ return undefined ;
384
+ }
385
+ return ( hubPoolTokens [ tokenKey ] ??= l1Token ) ;
413
386
} ;
414
387
const getHubPoolTokens = ( ) : EvmAddress [ ] => dedupArray ( Object . values ( hubPoolTokens ) . filter ( isDefined ) ) ;
415
388
@@ -548,14 +521,14 @@ export class HubPoolClient extends BaseAbstractClient {
548
521
// Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
549
522
const l1TokenA = this . getL1TokenForL2TokenAtBlock ( tokenA , chainIdA , hubPoolBlock ) ;
550
523
const l1TokenB = this . getL1TokenForL2TokenAtBlock ( tokenB , chainIdB , hubPoolBlock ) ;
551
- if ( ! l1TokenA . eq ( l1TokenB ) ) {
524
+ if ( ! l1TokenA || ! l1TokenB || ! l1TokenA . eq ( l1TokenB ) ) {
552
525
return false ;
553
526
}
554
527
555
528
// Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
556
529
const _tokenA = this . getL2TokenForL1TokenAtBlock ( l1TokenA , chainIdA , hubPoolBlock ) ;
557
530
const _tokenB = this . getL2TokenForL1TokenAtBlock ( l1TokenB , chainIdB , hubPoolBlock ) ;
558
- return tokenA . eq ( _tokenA ) && tokenB . eq ( _tokenB ) ;
531
+ return _tokenA !== undefined && _tokenB !== undefined && tokenA . eq ( _tokenA ) && tokenB . eq ( _tokenB ) ;
559
532
}
560
533
561
534
getSpokeActivationBlockForChain ( chainId : number ) : number {
@@ -995,24 +968,22 @@ export class HubPoolClient extends BaseAbstractClient {
995
968
destinationToken = svmUsdc ;
996
969
}
997
970
998
- // If the destination token is set to the zero address in an event, then this means Across should no longer
999
- // rebalance to this chain.
1000
- if ( ! destinationToken . isZeroAddress ( ) ) {
1001
- assign ( this . l1TokensToDestinationTokens , [ args . l1Token , args . destinationChainId ] , destinationToken ) ;
1002
- assign (
1003
- this . l1TokensToDestinationTokensWithBlock ,
1004
- [ args . l1Token , args . destinationChainId ] ,
1005
- [
1006
- {
1007
- l1Token : EvmAddress . from ( args . l1Token ) ,
1008
- l2Token : destinationToken ,
1009
- blockNumber : args . blockNumber ,
1010
- txnIndex : args . txnIndex ,
1011
- logIndex : args . logIndex ,
1012
- txnRef : args . txnRef ,
1013
- } ,
1014
- ]
1015
- ) ;
971
+ const newRoute : DestinationTokenWithBlock = {
972
+ l1Token : EvmAddress . from ( args . l1Token ) ,
973
+ l2Token : destinationToken ,
974
+ blockNumber : args . blockNumber ,
975
+ txnIndex : args . txnIndex ,
976
+ logIndex : args . logIndex ,
977
+ txnRef : args . txnRef ,
978
+ } ;
979
+ if ( this . l1TokensToDestinationTokensWithBlock [ args . l1Token ] ?. [ args . destinationChainId ] ) {
980
+ // Events are most likely coming in descending orders already but just in case we sort them again.
981
+ this . l1TokensToDestinationTokensWithBlock [ args . l1Token ] [ args . destinationChainId ] = sortEventsDescending ( [
982
+ ...this . l1TokensToDestinationTokensWithBlock [ args . l1Token ] [ args . destinationChainId ] ,
983
+ newRoute ,
984
+ ] ) ;
985
+ } else {
986
+ assign ( this . l1TokensToDestinationTokensWithBlock , [ args . l1Token , args . destinationChainId ] , [ newRoute ] ) ;
1016
987
}
1017
988
}
1018
989
}
0 commit comments