@@ -19,24 +19,26 @@ import {
1919 Status
2020} from '../types'
2121import { datelog , retryFetch , smartIsoDateFromTimestamp , snooze } from '../util'
22+ import { createTokenId , tokenTypes } from '../util/asEdgeTokenId'
23+ import { EVM_CHAIN_IDS , REVERSE_EVM_CHAIN_IDS } from '../util/chainIds'
2224
2325const PLUGIN_START_DATE = '2023-01-01T00:00:00.000Z'
2426const asStatuses = asMaybe ( asValue ( 'DONE' ) , 'other' )
2527const asToken = asObject ( {
26- // address: asString,
27- // chainId: asNumber,
28+ address : asOptional ( asString ) ,
29+ chainId : asOptional ( asNumber ) ,
2830 symbol : asString ,
29- decimals : asNumber
31+ decimals : asNumber ,
3032 // name: asString,
31- // coinKey: asString,
33+ coinKey : asOptional ( asString )
3234 // logoURI: asString,
3335 // priceUSD: asString
3436} )
3537
3638const asTransaction = asObject ( {
3739 txHash : asString ,
3840 // txLink: asString,
39- // amount: asString,
41+ amount : asString ,
4042 token : asOptional ( asToken ) ,
4143 // chainId: asNumber,
4244 // gasPrice: asString,
@@ -45,7 +47,7 @@ const asTransaction = asObject({
4547 // gasAmount: asString,
4648 // gasAmountUSD: asString,
4749 amountUSD : asOptional ( asString ) ,
48- value : asString ,
50+ // value: asString,
4951 timestamp : asOptional ( asNumber )
5052} )
5153
@@ -70,6 +72,7 @@ const asTransfersResult = asObject({
7072 transfers : asArray ( asUnknown )
7173} )
7274
75+ type Transfer = ReturnType < typeof asTransfer >
7376type PartnerStatuses = ReturnType < typeof asStatuses >
7477
7578const MAX_RETRIES = 5
@@ -160,7 +163,13 @@ export const lifi: PartnerPlugin = {
160163}
161164
162165export function processLifiTx ( rawTx : unknown ) : StandardTx {
163- const tx = asTransfer ( rawTx )
166+ let tx : Transfer
167+ try {
168+ tx = asTransfer ( rawTx )
169+ } catch ( e ) {
170+ datelog ( e )
171+ throw e
172+ }
164173 const txTimestamp = tx . receiving . timestamp ?? tx . sending . timestamp ?? 0
165174 if ( txTimestamp === 0 ) {
166175 throw new Error ( 'No timestamp' )
@@ -172,35 +181,171 @@ export function processLifiTx(rawTx: unknown): StandardTx {
172181 if ( depositToken == null || payoutToken == null ) {
173182 throw new Error ( 'Missing token details' )
174183 }
175- const depositAmount = Number ( tx . sending . value ) / 10 ** depositToken . decimals
176-
177- const payoutAmount = Number ( tx . receiving . value ) / 10 ** payoutToken . decimals
178-
179- const standardTx : StandardTx = {
180- status : statusMap [ tx . status ] ,
181- orderId : tx . sending . txHash ,
182- countryCode : null ,
183- depositTxid : tx . sending . txHash ,
184- depositAddress : undefined ,
185- depositCurrency : depositToken . symbol ,
186- depositChainPluginId : undefined ,
187- depositEvmChainId : undefined ,
188- depositTokenId : undefined ,
189- depositAmount,
190- direction : null ,
191- exchangeType : 'swap' ,
192- paymentType : null ,
193- payoutTxid : undefined ,
194- payoutAddress : tx . toAddress ,
195- payoutCurrency : payoutToken . symbol ,
196- payoutChainPluginId : undefined ,
197- payoutEvmChainId : undefined ,
198- payoutTokenId : undefined ,
199- payoutAmount,
200- timestamp,
201- isoDate,
202- usdValue : Number ( tx . receiving . amountUSD ?? tx . sending . amountUSD ?? '-1' ) ,
203- rawTx
184+ const depositAmount = Number ( tx . sending . amount ) / 10 ** depositToken . decimals
185+ const payoutAmount = Number ( tx . receiving . amount ) / 10 ** payoutToken . decimals
186+
187+ // Get the currencCode of the gasToken as we'll use this to determine if this is
188+ // a token swap. If there's not gasToken object, use the token object.
189+ const depositChainCodeUnmapped =
190+ tx . sending . gasToken ?. coinKey ??
191+ tx . sending . gasToken ?. symbol ??
192+ depositToken ?. coinKey ??
193+ depositToken ?. symbol
194+ const payoutChainCodeUnmappped =
195+ tx . receiving . gasToken ?. coinKey ??
196+ tx . receiving . gasToken ?. symbol ??
197+ payoutToken ?. coinKey ??
198+ payoutToken ?. symbol
199+
200+ // For some reason, some gasToken like Solana are given as "wSOL", so map them to SOL
201+ const depositChainCode =
202+ TOKEN_CODE_MAPPINGS [ depositChainCodeUnmapped ?? '' ] ??
203+ depositChainCodeUnmapped
204+ const payoutChainCode =
205+ TOKEN_CODE_MAPPINGS [ payoutChainCodeUnmappped ?? '' ] ??
206+ payoutChainCodeUnmappped
207+
208+ const depositTokenCode =
209+ tx . sending . token ?. coinKey ??
210+ tx . sending . token ?. symbol ??
211+ tx . sending . gasToken ?. coinKey ??
212+ tx . sending . gasToken ?. symbol
213+ const payoutTokenCode =
214+ tx . receiving . token ?. coinKey ??
215+ tx . receiving . token ?. symbol ??
216+ tx . receiving . gasToken ?. coinKey ??
217+ tx . receiving . gasToken ?. symbol
218+
219+ // If the token code and chain code match, this is a gas token so
220+ // tokenId = null
221+ const depositTokenAddress =
222+ depositTokenCode !== depositChainCode ? depositToken ?. address : null
223+ const payoutTokenAddress =
224+ payoutTokenCode !== payoutChainCode ? payoutToken ?. address : null
225+
226+ // Try to determine the EVM chain id from the token chain id. Lifi
227+ // has chainIds for non-EVM chains like Solana so we have to filter them out.
228+ let depositEvmChainId =
229+ REVERSE_EVM_CHAIN_IDS [ depositToken . chainId ?? 0 ] != null
230+ ? depositToken . chainId
231+ : undefined
232+ let payoutEvmChainId =
233+ REVERSE_EVM_CHAIN_IDS [ payoutToken . chainId ?? 0 ] != null
234+ ? payoutToken . chainId
235+ : undefined
236+
237+ // Determine the chain plugin id and token id.
238+ // Try using the gas token code first, then chain id if we have one.
239+ const depositChainPluginId =
240+ REVERSE_EVM_CHAIN_IDS [ depositEvmChainId ?? 0 ] ??
241+ MAINNET_CODE_TRANSCRIPTION [
242+ tx . sending . gasToken ?. coinKey ?? tx . sending . gasToken ?. symbol ?? ''
243+ ]
244+ const payoutChainPluginId =
245+ REVERSE_EVM_CHAIN_IDS [ payoutEvmChainId ?? 0 ] ??
246+ MAINNET_CODE_TRANSCRIPTION [
247+ tx . receiving . gasToken ?. coinKey ?? tx . receiving . gasToken ?. symbol ?? ''
248+ ]
249+
250+ if ( depositChainPluginId == null || payoutChainPluginId == null ) {
251+ throw new Error ( 'Missing chain plugin id' )
204252 }
205- return standardTx
253+
254+ // If we weren't able to determine an EVM chain id, try to get it from the
255+ // chain plugin id.
256+ depositEvmChainId =
257+ depositEvmChainId == null
258+ ? EVM_CHAIN_IDS [ depositChainPluginId ?? '' ]
259+ : depositEvmChainId
260+ payoutEvmChainId =
261+ payoutEvmChainId == null
262+ ? EVM_CHAIN_IDS [ payoutChainPluginId ?? '' ]
263+ : payoutEvmChainId
264+
265+ const depositTokenType = tokenTypes [ depositChainPluginId ?? '' ]
266+ const payoutTokenType = tokenTypes [ payoutChainPluginId ?? '' ]
267+
268+ if ( depositTokenType == null || payoutTokenType == null ) {
269+ throw new Error ( 'Missing token type' )
270+ }
271+
272+ try {
273+ const depositTokenId = createTokenId (
274+ depositTokenType ,
275+ depositToken . symbol ,
276+ depositTokenAddress ?? undefined
277+ )
278+ const payoutTokenId = createTokenId (
279+ payoutTokenType ,
280+ payoutToken . symbol ,
281+ payoutTokenAddress ?? undefined
282+ )
283+
284+ const standardTx : StandardTx = {
285+ status : statusMap [ tx . status ] ,
286+ orderId : tx . sending . txHash ,
287+ countryCode : null ,
288+ depositTxid : tx . sending . txHash ,
289+ depositAddress : undefined ,
290+ depositCurrency : depositToken . symbol ,
291+ depositChainPluginId,
292+ depositEvmChainId,
293+ depositTokenId,
294+ depositAmount,
295+ direction : null ,
296+ exchangeType : 'swap' ,
297+ paymentType : null ,
298+ payoutTxid : tx . receiving . txHash ,
299+ payoutAddress : tx . toAddress ,
300+ payoutCurrency : payoutToken . symbol ,
301+ payoutChainPluginId,
302+ payoutEvmChainId,
303+ payoutTokenId,
304+ payoutAmount,
305+ timestamp,
306+ isoDate,
307+ usdValue : Number ( tx . sending . amountUSD ?? tx . receiving . amountUSD ?? '-1' ) ,
308+ rawTx
309+ }
310+ if ( statusMap [ tx . status ] === 'complete' ) {
311+ const { orderId, depositCurrency, payoutCurrency } = standardTx
312+ console . log (
313+ `${ orderId } ${ depositCurrency } ${ depositChainPluginId } ${ depositEvmChainId } ${ depositTokenId ?. slice (
314+ 0 ,
315+ 6
316+ ) ??
317+ '' } ${ depositAmount } -> ${ payoutCurrency } ${ payoutChainPluginId } ${ payoutEvmChainId } ${ payoutTokenId ?. slice (
318+ 0 ,
319+ 6
320+ ) ?? '' } ${ payoutAmount } `
321+ )
322+ }
323+ return standardTx
324+ } catch ( e ) {
325+ datelog ( e )
326+ throw e
327+ }
328+ }
329+
330+ const MAINNET_CODE_TRANSCRIPTION : Record < string , string > = {
331+ ARBITRUM : 'arbitrum' ,
332+ AVAX : 'avalanche' ,
333+ BNB : 'binancesmartchain' ,
334+ CELO : 'celo' ,
335+ ETH : 'ethereum' ,
336+ FTM : 'fantom' ,
337+ HYPE : 'hyperevm' ,
338+ OP : 'optimism' ,
339+ POL : 'polygon' ,
340+ PLS : 'pulsechain' ,
341+ RBTC : 'rsk' ,
342+ SOL : 'solana' ,
343+ wSOL : 'solana' ,
344+ SUI : 'sui' ,
345+ SONIC : 'sonic' ,
346+ ZKSYNC : 'zksync'
347+ }
348+
349+ const TOKEN_CODE_MAPPINGS : Record < string , string > = {
350+ wSOL : 'SOL'
206351}
0 commit comments