Skip to content

Commit 30376ee

Browse files
committed
Update lifi to provide chainId, pluginId, and tokenId
1 parent f756b6b commit 30376ee

3 files changed

Lines changed: 355 additions & 37 deletions

File tree

src/partners/lifi.ts

Lines changed: 182 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,26 @@ import {
1919
Status
2020
} from '../types'
2121
import { 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

2325
const PLUGIN_START_DATE = '2023-01-01T00:00:00.000Z'
2426
const asStatuses = asMaybe(asValue('DONE'), 'other')
2527
const 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

3638
const 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>
7376
type PartnerStatuses = ReturnType<typeof asStatuses>
7477

7578
const MAX_RETRIES = 5
@@ -160,7 +163,13 @@ export const lifi: PartnerPlugin = {
160163
}
161164

162165
export 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

Comments
 (0)