@avalabs/fusion-sdk 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/transfer-service/_tracking-utilities.cjs +1 -1
- package/dist/transfer-service/_tracking-utilities.cjs.map +1 -1
- package/dist/transfer-service/_tracking-utilities.js +1 -1
- package/dist/transfer-service/_tracking-utilities.js.map +1 -1
- package/dist/transfer-service/_utils.cjs +1 -1
- package/dist/transfer-service/_utils.cjs.map +1 -1
- package/dist/transfer-service/_utils.js +1 -1
- package/dist/transfer-service/_utils.js.map +1 -1
- package/dist/transfer-service/_warden-config-schema.cjs.map +1 -1
- package/dist/transfer-service/_warden-config-schema.js.map +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/track-transfer.cjs +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/track-transfer.cjs.map +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/track-transfer.js +1 -1
- package/dist/transfer-service/avalanche-evm/_handlers/track-transfer.js.map +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/transfer-data.cjs +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/transfer-data.cjs.map +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/transfer-data.js +1 -1
- package/dist/transfer-service/avalanche-evm/_utils/transfer-data.js.map +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.cjs +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.cjs.map +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.js +1 -1
- package/dist/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.js.map +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.cjs +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.cjs.map +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.js +1 -1
- package/dist/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js +1 -1
- package/dist/transfer-service/markr/_handlers/estimate-native-fee.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.js +1 -1
- package/dist/transfer-service/markr/_handlers/track-transfer.js.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.cjs.map +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js +1 -1
- package/dist/transfer-service/markr/_handlers/transfer-asset.js.map +1 -1
- package/dist/transfer-service/markr/_schema.cjs +1 -1
- package/dist/transfer-service/markr/_schema.cjs.map +1 -1
- package/dist/transfer-service/markr/_schema.js +1 -1
- package/dist/transfer-service/markr/_schema.js.map +1 -1
- package/dist/transfer-service/markr/_solana-utils.cjs +2 -0
- package/dist/transfer-service/markr/_solana-utils.cjs.map +1 -0
- package/dist/transfer-service/markr/_solana-utils.js +2 -0
- package/dist/transfer-service/markr/_solana-utils.js.map +1 -0
- package/dist/types/transfer.d.cts +24 -5
- package/dist/types/transfer.d.ts +24 -5
- package/dist/utils/transfer-utils.cjs +1 -1
- package/dist/utils/transfer-utils.cjs.map +1 -1
- package/dist/utils/transfer-utils.js +1 -1
- package/dist/utils/transfer-utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ErrorCode as e}from"../../../errors.js";import{awaitOrAbort as t,
|
|
1
|
+
import{ErrorCode as e}from"../../../errors.js";import{awaitOrAbort as t,getEvmClientForChain as n}from"../../_utils.js";import{getTransferData as r}from"../_utils/transfer-data.js";import{isAddress as i,isHash as a}from"viem";const o=1024n;function s({config:t}){return({transfer:r,updateListener:i})=>{let o=new AbortController,s=()=>{o.abort()};if(r.status===`completed`||r.status===`failed`)return{cancel:s,result:Promise.resolve(r)};let{txHash:u}=r.source;if(!a(u)){let t={...r,errorCode:e.INVALID_PARAMS,errorReason:`Invalid source transaction hash`,failedAtMs:Date.now(),status:`failed`};return i(t),{cancel:s,result:Promise.resolve(t)}}let d=n({chain:r.sourceChain}),f=n({chain:r.targetChain}),m=structuredClone(r);return{cancel:s,result:(async()=>{for(;!o.signal.aborted;){let e=!1;switch(m.status){case`source-pending`:m=await c({currentTransfer:m,signal:o.signal,sourceClient:d}),i(m);break;case`target-pending`:m=await l({assetIn:m.sourceAsset,currentTransfer:m,evmConfig:t,signal:o.signal,sourceChainId:m.sourceChain.chainId,targetChainId:m.targetChain.chainId,targetClient:f}),e=m.status===`target-pending`&&!m.target?.txHash,i(m),e&&await p({signal:o.signal});break;default:return m}}return m})()}}}async function c({currentTransfer:n,signal:r,sourceClient:i}){let{confirmationCount:o,requiredConfirmationCount:s,txHash:c}=n.source;if(!a(c))return{...n,errorCode:e.INVALID_PARAMS,errorReason:`Invalid source transaction hash`,failedAtMs:Date.now(),status:`failed`};if(o>=s)return{...n,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:1,startedAtMs:Date.now()}};try{let a=await t(i.waitForTransactionReceipt({confirmations:Math.min(o+1,s),hash:c}),r);if(a.status===`aborted`)return n;let l=a.value;if(l.status===`reverted`)return{...n,errorCode:e.TRANSACTION_REVERTED,errorReason:`Source transaction was reverted`,failedAtMs:Date.now(),status:`failed`};let u=await i.getTransactionConfirmations({transactionReceipt:l}).then(e=>Number(e)).catch(()=>o+1);return u<s?{...n,source:{...n.source,confirmationCount:u}}:{...n,source:{...n.source,confirmationCount:u},status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:1,startedAtMs:Date.now()}}}catch{return n}}async function l({assetIn:n,currentTransfer:i,evmConfig:o,signal:s,sourceChainId:c,targetChainId:l,targetClient:f}){let p=i.target??{confirmationCount:0,requiredConfirmationCount:1,startedAtMs:Date.now()};if(p.confirmationCount>=p.requiredConfirmationCount)return{...i,status:`completed`,completedAtMs:Date.now()};if(!p.txHash){let{ethToAva:e,target:{token:{address:t}}}=r({assetIn:n,sourceChainId:c,targetChainId:l},o),a=typeof f.getBlockNumber==`function`?await f.getBlockNumber().catch(()=>void 0):void 0,s=await(e?u({contractAddress:t,latestBlockNumber:a,sourceTxHash:i.source.txHash,targetClient:f,targetStartBlockNumber:i.source.targetStartBlockNumber}):d({contractAddress:t,evmConfig:o,fromAddress:i.fromAddress,latestBlockNumber:a,targetClient:f,targetStartBlockNumber:i.source.targetStartBlockNumber})).catch(()=>null);return s?{...i,target:{...p,txHash:s}}:a===void 0?i:{...i,source:{...i.source,targetStartBlockNumber:a}}}if(!a(p.txHash))return{...i,errorCode:e.INVALID_PARAMS,errorReason:`Invalid target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let n=await t(f.waitForTransactionReceipt({confirmations:p.requiredConfirmationCount,hash:p.txHash}),s);if(n.status===`aborted`)return i;let r=n.value;return r.status===`reverted`?{...i,errorCode:e.TRANSACTION_REVERTED,errorReason:`Target transaction was reverted`,failedAtMs:Date.now(),status:`failed`}:{...i,completedAtMs:Date.now(),status:`completed`,target:{...p,confirmationCount:await f.getTransactionConfirmations({transactionReceipt:r}).then(e=>Number(e)).catch(()=>p.requiredConfirmationCount)}}}catch{return i}}async function u({contractAddress:e,latestBlockNumber:t,sourceTxHash:n,targetClient:r,targetStartBlockNumber:i}){if(i===void 0)return null;let{fromBlock:a,toBlock:o}=f({latestBlockNumber:t,targetStartBlockNumber:i}),s=(await r.getLogs({address:e,event:{inputs:[{indexed:!1,name:`to`,type:`address`},{indexed:!1,name:`amount`,type:`uint256`},{indexed:!1,name:`feeAddress`,type:`address`},{indexed:!1,name:`feeAmount`,type:`uint256`},{indexed:!1,name:`originTxId`,type:`bytes32`}],name:`Mint`,type:`event`},fromBlock:a,toBlock:o})).find(e=>e.args.originTxId===n);return s?s.transactionHash:null}async function d({contractAddress:e,evmConfig:t,fromAddress:n,latestBlockNumber:r,targetClient:a,targetStartBlockNumber:o}){if(o===void 0||!i(n))return null;let{fromBlock:s,toBlock:c}=f({latestBlockNumber:r,targetStartBlockNumber:o}),l=(await a.getLogs({address:e,args:{dst:n,src:t.walletAddresses.ethereum},event:{inputs:[{indexed:!0,name:`src`,type:`address`},{indexed:!0,name:`dst`,type:`address`},{indexed:!1,name:`wad`,type:`uint256`}],name:`Transfer`,type:`event`},fromBlock:s,toBlock:c})).at(-1);return l?l.transactionHash:null}function f({latestBlockNumber:e,targetStartBlockNumber:t}){let n=t>o?t-o:`earliest`,r=t+o;return{fromBlock:n,toBlock:e!==void 0&&r<e?r:`latest`}}async function p({signal:e}){await t(new Promise(e=>setTimeout(e,15e3)),e)}export{s as trackTransferFactory};
|
|
2
2
|
//# sourceMappingURL=track-transfer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../src/transfer-service/avalanche-evm/_handlers/track-transfer.ts"],"sourcesContent":["import { isAddress, isHash, type Hash } from 'viem';\nimport { ErrorCode } from '../../../errors';\nimport type { Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../types/transfer';\nimport type { TransferService } from '../../../types/service';\nimport { awaitOrAbort, getErrorCodeForViemError, getEvmClientForChain } from '../../_utils';\nimport type { EvmConfig } from '../_types';\nimport { getTransferData } from '../_utils/transfer-data';\n\nexport interface TrackTransferFactoryConfig {\n config: EvmConfig;\n}\n\nexport function trackTransferFactory({\n config: evmConfig,\n}: TrackTransferFactoryConfig): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n // Failed and completed transfers are terminal states.\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const { txHash } = transfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...transfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n failedAtMs: Date.now(),\n status: 'failed',\n };\n updateListener(failedTransfer);\n\n return {\n cancel,\n result: Promise.resolve(failedTransfer),\n };\n }\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const executeTracking = async (): Promise<Transfer> => {\n // Create a loop to poll for confirmations and update the transfer status until\n // failed or completed.\n while (!ac.signal.aborted) {\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n updateListener(currentTransfer);\n break;\n }\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n assetIn: currentTransfer.sourceAsset,\n currentTransfer,\n evmConfig,\n signal: ac.signal,\n sourceChainId: currentTransfer.sourceChain.chainId,\n targetChainId: currentTransfer.targetChain.chainId,\n targetClient,\n });\n updateListener(currentTransfer);\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n assetIn: Asset;\n currentTransfer: TargetPendingTransfer;\n evmConfig: EvmConfig;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<Transfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n failedAtMs: Date.now(),\n status: 'failed',\n };\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to target-pending\n\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1, // EVM txs need 1 confirmation on target chain.\n startedAtMs: Date.now(),\n },\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n // Either update confirmation count or move to target-pending\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n // hash: txHash,\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, just update count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to target-pending\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1, // EVM txs need 1 confirmation on target chain.\n startedAtMs: Date.now(),\n },\n } satisfies TargetPendingTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n assetIn,\n currentTransfer,\n evmConfig,\n signal,\n sourceChainId,\n targetChainId,\n targetClient,\n}: TrackTargetTxParams): Promise<Transfer> {\n const target = currentTransfer.target ?? {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n };\n\n if (target.confirmationCount >= target.requiredConfirmationCount) {\n // Move to completed\n const completedTransfer: Transfer = {\n ...currentTransfer,\n status: 'completed',\n completedAtMs: Date.now(),\n };\n return completedTransfer;\n }\n\n if (!target.txHash) {\n // Find the target tx hash via logs and then update the transfer.\n const {\n ethToAva,\n target: {\n token: { address: contractAddress },\n },\n } = getTransferData(\n {\n assetIn,\n sourceChainId,\n targetChainId,\n },\n evmConfig,\n );\n\n const targetTxHash = ethToAva\n ? await _getTargetTxHashEthToAva({\n contractAddress,\n sourceTxHash: currentTransfer.source.txHash,\n targetClient,\n targetStartBlockNumber: currentTransfer.source.targetStartBlockNumber,\n })\n : await _getTargetTxHashAvaToEth({\n contractAddress,\n evmConfig,\n fromAddress: currentTransfer.fromAddress,\n targetClient,\n targetStartBlockNumber: currentTransfer.source.targetStartBlockNumber,\n });\n\n if (!targetTxHash) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Target transaction hash could not be found',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n return {\n ...currentTransfer,\n target: {\n ...target,\n txHash: targetTxHash,\n },\n } satisfies TargetPendingTransfer;\n }\n\n if (!isHash(target.txHash)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n // Wait for all required confirmations (which should just be 1 on target).\n // This is a safety in case it's ever more than 1. We just won't \"poll\" multiple times\n // like we do for the source.\n confirmations: target.requiredConfirmationCount,\n hash: target.txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...target,\n confirmationCount: await targetClient\n .getTransactionConfirmations({\n // hash: target.txHash,\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => target.requiredConfirmationCount),\n },\n } satisfies CompletedTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n}\n\nexport async function _getTargetTxHashEthToAva({\n contractAddress,\n sourceTxHash,\n targetClient,\n targetStartBlockNumber,\n}: {\n contractAddress: Hash;\n sourceTxHash: string;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n targetStartBlockNumber: bigint | undefined;\n}): Promise<Hash | null> {\n if (targetStartBlockNumber === undefined) {\n return null;\n }\n\n const logs = await targetClient.getLogs({\n address: contractAddress,\n event: {\n inputs: [\n {\n indexed: false,\n name: 'to',\n type: 'address',\n },\n {\n indexed: false,\n name: 'amount',\n type: 'uint256',\n },\n {\n indexed: false,\n name: 'feeAddress',\n type: 'address',\n },\n {\n indexed: false,\n name: 'feeAmount',\n type: 'uint256',\n },\n {\n indexed: false,\n name: 'originTxId',\n type: 'bytes32',\n },\n ],\n name: 'Mint',\n type: 'event',\n },\n fromBlock: targetStartBlockNumber,\n toBlock: 'latest', // TODO: Consider limiting the range?\n });\n\n // OriginTxId is not indexed. So it cannot be part of the filter.\n const foundLog = logs.find((log) => log.args.originTxId === sourceTxHash); // TODO: Is my assumption correct here? ie that we want to compare the sourceTxHash?\n\n if (!foundLog) {\n return null;\n }\n\n return foundLog.transactionHash;\n}\n\nexport async function _getTargetTxHashAvaToEth({\n contractAddress,\n evmConfig,\n fromAddress,\n targetClient,\n targetStartBlockNumber,\n}: {\n contractAddress: Hash;\n evmConfig: EvmConfig;\n fromAddress: string;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n targetStartBlockNumber: bigint | undefined;\n}): Promise<Hash | null> {\n if (targetStartBlockNumber === undefined) {\n return null;\n }\n\n if (!isAddress(fromAddress)) {\n return null;\n }\n\n const logs = await targetClient.getLogs({\n address: contractAddress,\n args: {\n dst: fromAddress,\n src: evmConfig.walletAddresses.ethereum,\n },\n event: {\n inputs: [\n {\n indexed: true,\n name: 'src',\n type: 'address',\n },\n {\n indexed: true,\n name: 'dst',\n type: 'address',\n },\n {\n indexed: false,\n name: 'wad',\n type: 'uint256',\n },\n ],\n name: 'Transfer',\n type: 'event',\n },\n fromBlock: targetStartBlockNumber,\n toBlock: 'latest', // TODO: Consider limiting the range?\n });\n\n const foundLog = logs.at(0); // TODO: Is this correct? Original code was using -1. But we want \"oldest\" log right? ie the closest to the \"fromBlock\"?\n\n if (!foundLog) {\n return null;\n }\n\n return foundLog.transactionHash;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../src/transfer-service/avalanche-evm/_handlers/track-transfer.ts"],"sourcesContent":["import { isAddress, isHash, type Hash } from 'viem';\nimport { ErrorCode } from '../../../errors';\nimport type { Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../types/transfer';\nimport type { TransferService } from '../../../types/service';\nimport { awaitOrAbort, getEvmClientForChain } from '../../_utils';\nimport type { EvmConfig } from '../_types';\nimport { getTransferData } from '../_utils/transfer-data';\n\nconst MAX_LOG_BLOCK_RANGE = 1024n;\nconst TARGET_HASH_POLL_INTERVAL_MS = 15_000;\n\ntype LogSearchRange = {\n fromBlock: bigint | 'earliest';\n toBlock: bigint | 'latest';\n};\n\nexport interface TrackTransferFactoryConfig {\n config: EvmConfig;\n}\n\nexport function trackTransferFactory({\n config: evmConfig,\n}: TrackTransferFactoryConfig): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n // Failed and completed transfers are terminal states.\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const { txHash } = transfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...transfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n updateListener(failedTransfer);\n\n return {\n cancel,\n result: Promise.resolve(failedTransfer),\n };\n }\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const executeTracking = async (): Promise<Transfer> => {\n // Create a loop to poll for confirmations and update the transfer status until\n // failed or completed.\n while (!ac.signal.aborted) {\n let shouldDelayAfterUpdate = false;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n updateListener(currentTransfer);\n break;\n }\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n assetIn: currentTransfer.sourceAsset,\n currentTransfer,\n evmConfig,\n signal: ac.signal,\n sourceChainId: currentTransfer.sourceChain.chainId,\n targetChainId: currentTransfer.targetChain.chainId,\n targetClient,\n });\n shouldDelayAfterUpdate = currentTransfer.status === 'target-pending' && !currentTransfer.target?.txHash;\n updateListener(currentTransfer);\n\n if (shouldDelayAfterUpdate) {\n await _waitForNextTargetHashPollingCycle({ signal: ac.signal });\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n assetIn: Asset;\n currentTransfer: TargetPendingTransfer;\n evmConfig: EvmConfig;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<Transfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to target-pending\n\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1, // EVM txs need 1 confirmation on target chain.\n startedAtMs: Date.now(),\n },\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Source transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n // Either update confirmation count or move to target-pending\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n // hash: txHash,\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, just update count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to target-pending\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1, // EVM txs need 1 confirmation on target chain.\n startedAtMs: Date.now(),\n },\n } satisfies TargetPendingTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n assetIn,\n currentTransfer,\n evmConfig,\n signal,\n sourceChainId,\n targetChainId,\n targetClient,\n}: TrackTargetTxParams): Promise<Transfer> {\n const target = currentTransfer.target ?? {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n };\n\n if (target.confirmationCount >= target.requiredConfirmationCount) {\n // Move to completed\n const completedTransfer: Transfer = {\n ...currentTransfer,\n status: 'completed',\n completedAtMs: Date.now(),\n };\n return completedTransfer;\n }\n\n if (!target.txHash) {\n // Find the target tx hash via logs and then update the transfer.\n const {\n ethToAva,\n target: {\n token: { address: contractAddress },\n },\n } = getTransferData(\n {\n assetIn,\n sourceChainId,\n targetChainId,\n },\n evmConfig,\n );\n\n const latestBlockNumber =\n typeof targetClient.getBlockNumber === 'function'\n ? await targetClient.getBlockNumber().catch(() => undefined)\n : undefined;\n\n const targetTxHash = await (ethToAva\n ? _getTargetTxHashEthToAva({\n contractAddress,\n latestBlockNumber,\n sourceTxHash: currentTransfer.source.txHash,\n targetClient,\n targetStartBlockNumber: currentTransfer.source.targetStartBlockNumber,\n })\n : _getTargetTxHashAvaToEth({\n contractAddress,\n evmConfig,\n fromAddress: currentTransfer.fromAddress,\n latestBlockNumber,\n targetClient,\n targetStartBlockNumber: currentTransfer.source.targetStartBlockNumber,\n })\n ).catch(() => null);\n\n if (!targetTxHash) {\n if (latestBlockNumber === undefined) {\n return currentTransfer;\n }\n\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n targetStartBlockNumber: latestBlockNumber,\n },\n } satisfies TargetPendingTransfer;\n }\n\n return {\n ...currentTransfer,\n target: {\n ...target,\n txHash: targetTxHash,\n },\n } satisfies TargetPendingTransfer;\n }\n\n if (!isHash(target.txHash)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n // Wait for all required confirmations (which should just be 1 on target).\n // This is a safety in case it's ever more than 1. We just won't \"poll\" multiple times\n // like we do for the source.\n confirmations: target.requiredConfirmationCount,\n hash: target.txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Target transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...target,\n confirmationCount: await targetClient\n .getTransactionConfirmations({\n // hash: target.txHash,\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => target.requiredConfirmationCount),\n },\n } satisfies CompletedTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _getTargetTxHashEthToAva({\n contractAddress,\n latestBlockNumber,\n sourceTxHash,\n targetClient,\n targetStartBlockNumber,\n}: {\n contractAddress: Hash;\n latestBlockNumber: bigint | undefined;\n sourceTxHash: string;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n targetStartBlockNumber: bigint | undefined;\n}): Promise<Hash | null> {\n if (targetStartBlockNumber === undefined) {\n return null;\n }\n\n const { fromBlock, toBlock } = _getLogSearchRange({\n latestBlockNumber,\n targetStartBlockNumber,\n });\n\n const logs = await targetClient.getLogs({\n address: contractAddress,\n event: {\n inputs: [\n {\n indexed: false,\n name: 'to',\n type: 'address',\n },\n {\n indexed: false,\n name: 'amount',\n type: 'uint256',\n },\n {\n indexed: false,\n name: 'feeAddress',\n type: 'address',\n },\n {\n indexed: false,\n name: 'feeAmount',\n type: 'uint256',\n },\n {\n indexed: false,\n name: 'originTxId',\n type: 'bytes32',\n },\n ],\n name: 'Mint',\n type: 'event',\n },\n fromBlock,\n toBlock,\n });\n\n // OriginTxId is not indexed. So it cannot be part of the filter.\n const foundLog = logs.find((log) => log.args.originTxId === sourceTxHash); // TODO: Is my assumption correct here? ie that we want to compare the sourceTxHash?\n\n if (!foundLog) {\n return null;\n }\n\n return foundLog.transactionHash;\n}\n\nexport async function _getTargetTxHashAvaToEth({\n contractAddress,\n evmConfig,\n fromAddress,\n latestBlockNumber,\n targetClient,\n targetStartBlockNumber,\n}: {\n contractAddress: Hash;\n evmConfig: EvmConfig;\n fromAddress: string;\n latestBlockNumber: bigint | undefined;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n targetStartBlockNumber: bigint | undefined;\n}): Promise<Hash | null> {\n if (targetStartBlockNumber === undefined) {\n return null;\n }\n\n if (!isAddress(fromAddress)) {\n return null;\n }\n\n const { fromBlock, toBlock } = _getLogSearchRange({\n latestBlockNumber,\n targetStartBlockNumber,\n });\n\n const logs = await targetClient.getLogs({\n address: contractAddress,\n args: {\n dst: fromAddress,\n src: evmConfig.walletAddresses.ethereum,\n },\n event: {\n inputs: [\n {\n indexed: true,\n name: 'src',\n type: 'address',\n },\n {\n indexed: true,\n name: 'dst',\n type: 'address',\n },\n {\n indexed: false,\n name: 'wad',\n type: 'uint256',\n },\n ],\n name: 'Transfer',\n type: 'event',\n },\n fromBlock,\n toBlock,\n });\n\n const foundLog = logs.at(-1);\n\n if (!foundLog) {\n return null;\n }\n\n return foundLog.transactionHash;\n}\n\nfunction _getLogSearchRange({\n latestBlockNumber,\n targetStartBlockNumber,\n}: {\n latestBlockNumber: bigint | undefined;\n targetStartBlockNumber: bigint;\n}): LogSearchRange {\n const fromBlock =\n targetStartBlockNumber > MAX_LOG_BLOCK_RANGE ? targetStartBlockNumber - MAX_LOG_BLOCK_RANGE : 'earliest';\n const highestBlockNumber = targetStartBlockNumber + MAX_LOG_BLOCK_RANGE;\n const toBlock =\n latestBlockNumber !== undefined && highestBlockNumber < latestBlockNumber ? highestBlockNumber : 'latest';\n\n return {\n fromBlock,\n toBlock,\n };\n}\n\nasync function _waitForNextTargetHashPollingCycle({ signal }: { signal: AbortSignal }): Promise<void> {\n await awaitOrAbort(new Promise<void>((resolve) => setTimeout(resolve, TARGET_HASH_POLL_INTERVAL_MS)), signal);\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../errors.cjs`),t=require(`../../../type-guards.cjs`),n=require(`../../../_utils/chain.cjs`),r=require(`./validations.cjs`),i=require(`../_constants.cjs`);let a=require(`viem`);const o=e=>({source:e.nativeTokenConfig.ethereumToken,target:e.nativeTokenConfig.avalancheToken,ethToAva:!0}),s=(e,t)=>(0,a.isAddressEqual)(e.address,t.address),c=(n,r)=>{if(!t.isErc20Asset(r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Wrong token type: ${r.type}`);let i=n.ethereumTokens[r.address];if(!i||!s(i.token,r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to find matching ethereum token in config: ${r.symbol}`);let a=n.avalancheTokens[i.target.tokenAddress];if(!a)throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to get avalanche token in config: ${r.symbol}`);return{source:i,target:a,ethToAva:!0}},l=(n,r)=>{if(!t.isErc20Asset(r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Wrong token type: ${r.type}`);let i=n.avalancheTokens[r.address];if(!i||!s(i.token,r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to find matching avalanche token in config: ${r.symbol}`);let a=n.ethereumTokens[i.target.tokenAddress];if(!a)throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to get ethereum token in config: ${r.symbol}`);return{source:i,target:a,ethToAva:!1}},u=({assetIn:a,sourceChainId:s,targetChainId:u},d)=>{if(s===u)throw new e.InvalidParamsError(e.ErrorReason.IDENTICAL_CHAINS_PROVIDED,`SourceChain and TargetChain need to be different network. But found the same chainId: ${s}`);if(!r.isValidChainCombination(s,u))throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`SourceChain and TargetChain combination is not correct or the chains provided are not supported`);return t.isNativeAsset(a)&&a.symbol===i.ETH_TOKEN.symbol&&n.isCaip2EthereumChainId(s)?o(d):n.isCaip2EthereumChainId(s)?c(d,a):l(d,a)};exports.getTransferData=u;
|
|
1
|
+
require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../errors.cjs`),t=require(`../../../type-guards.cjs`),n=require(`../../../_utils/chain.cjs`),r=require(`./validations.cjs`),i=require(`../_constants.cjs`);let a=require(`viem`);const o=e=>({source:e.nativeTokenConfig.ethereumToken,target:e.nativeTokenConfig.avalancheToken,ethToAva:!0}),s=(e,t)=>(0,a.isAddressEqual)(e.address,t.address),c=(n,r)=>{if(!t.isErc20Asset(r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Wrong token type: ${r.type}`);let i=n.ethereumTokens[r.address.toLowerCase()];if(!i||!s(i.token,r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to find matching ethereum token in config: ${r.symbol}`);let a=n.avalancheTokens[i.target.tokenAddress];if(!a)throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to get avalanche token in config: ${r.symbol}`);return{source:i,target:a,ethToAva:!0}},l=(n,r)=>{if(!t.isErc20Asset(r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Wrong token type: ${r.type}`);let i=n.avalancheTokens[r.address.toLowerCase()];if(!i||!s(i.token,r))throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to find matching avalanche token in config: ${r.symbol}`);let a=n.ethereumTokens[i.target.tokenAddress];if(!a)throw new e.InvalidParamsError(e.ErrorReason.ASSET_NOT_SUPPORTED,`Unable to get ethereum token in config: ${r.symbol}`);return{source:i,target:a,ethToAva:!1}},u=({assetIn:a,sourceChainId:s,targetChainId:u},d)=>{if(s===u)throw new e.InvalidParamsError(e.ErrorReason.IDENTICAL_CHAINS_PROVIDED,`SourceChain and TargetChain need to be different network. But found the same chainId: ${s}`);if(!r.isValidChainCombination(s,u))throw new e.InvalidParamsError(e.ErrorReason.INVALID_PARAMS,`SourceChain and TargetChain combination is not correct or the chains provided are not supported`);return t.isNativeAsset(a)&&a.symbol===i.ETH_TOKEN.symbol&&n.isCaip2EthereumChainId(s)?o(d):n.isCaip2EthereumChainId(s)?c(d,a):l(d,a)};exports.getTransferData=u;
|
|
2
2
|
//# sourceMappingURL=transfer-data.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-data.cjs","names":["isErc20Asset","InvalidParamsError","ErrorReason","isValidChainCombination","isNativeAsset","ETH_TOKEN","isCaip2EthereumChainId"],"sources":["../../../../src/transfer-service/avalanche-evm/_utils/transfer-data.ts"],"sourcesContent":["import type { AvaToEthTransferData, EthToAvaTransferData, EvmConfig, TransferData } from '../_types';\nimport { ErrorReason, InvalidParamsError } from '../../../errors';\nimport { isErc20Asset, isNativeAsset } from '../../../type-guards';\nimport type { Asset, Erc20Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { isCaip2EthereumChainId } from '../../../_utils/chain';\nimport { ETH_TOKEN } from '../_constants';\nimport { isAddressEqual } from 'viem';\nimport { isValidChainCombination } from './validations';\n\nconst ethHandler = (config: EvmConfig) => {\n return {\n source: config.nativeTokenConfig.ethereumToken,\n target: config.nativeTokenConfig.avalancheToken,\n ethToAva: true,\n };\n};\n\nconst assetsMatch = (a: Erc20Asset, b: Erc20Asset) => {\n return isAddressEqual(a.address, b.address);\n};\n\nconst prepEthToAvaTransferData = (config: EvmConfig, asset: Asset): EthToAvaTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const ethToken = config.ethereumTokens[asset.address];\n if (!ethToken || !assetsMatch(ethToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching ethereum token in config: ${asset.symbol}`,\n );\n }\n\n const avaToken = config.avalancheTokens[ethToken.target.tokenAddress];\n\n if (!avaToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get avalanche token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: ethToken,\n target: avaToken,\n ethToAva: true,\n };\n};\n\nconst prepAvaToEthTransferData = (config: EvmConfig, asset: Asset): AvaToEthTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const avaToken = config.avalancheTokens[asset.address];\n if (!avaToken || !assetsMatch(avaToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching avalanche token in config: ${asset.symbol}`,\n );\n }\n\n const ethToken = config.ethereumTokens[avaToken.target.tokenAddress];\n\n if (!ethToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get ethereum token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: avaToken,\n target: ethToken,\n ethToAva: false,\n };\n};\n\nexport const getTransferData = (\n {\n assetIn,\n sourceChainId,\n targetChainId,\n }: {\n assetIn: Asset;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n },\n config: EvmConfig,\n): TransferData => {\n if (sourceChainId === targetChainId) {\n throw new InvalidParamsError(\n ErrorReason.IDENTICAL_CHAINS_PROVIDED,\n `SourceChain and TargetChain need to be different network. But found the same chainId: ${sourceChainId}`,\n );\n }\n\n if (!isValidChainCombination(sourceChainId, targetChainId)) {\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `SourceChain and TargetChain combination is not correct or the chains provided are not supported`,\n );\n }\n\n if (isNativeAsset(assetIn) && assetIn.symbol === ETH_TOKEN.symbol && isCaip2EthereumChainId(sourceChainId)) {\n return ethHandler(config);\n }\n\n if (isCaip2EthereumChainId(sourceChainId)) {\n return prepEthToAvaTransferData(config, assetIn);\n } else {\n return prepAvaToEthTransferData(config, assetIn);\n }\n};\n"],"mappings":"2PAUA,MAAM,EAAc,IACX,CACL,OAAQ,EAAO,kBAAkB,cACjC,OAAQ,EAAO,kBAAkB,eACjC,SAAU,GACX,EAGG,GAAe,EAAe,KAClC,EAAA,EAAA,gBAAsB,EAAE,QAAS,EAAE,QAAQ,CAGvC,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAACA,EAAAA,aAAa,EAAM,CACtB,MAAM,IAAIC,EAAAA,mBAAmBC,EAAAA,YAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,eAAe,EAAM,
|
|
1
|
+
{"version":3,"file":"transfer-data.cjs","names":["isErc20Asset","InvalidParamsError","ErrorReason","isValidChainCombination","isNativeAsset","ETH_TOKEN","isCaip2EthereumChainId"],"sources":["../../../../src/transfer-service/avalanche-evm/_utils/transfer-data.ts"],"sourcesContent":["import type { AvaToEthTransferData, EthToAvaTransferData, EvmConfig, TransferData } from '../_types';\nimport { ErrorReason, InvalidParamsError } from '../../../errors';\nimport { isErc20Asset, isNativeAsset } from '../../../type-guards';\nimport type { Asset, Erc20Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { isCaip2EthereumChainId } from '../../../_utils/chain';\nimport { ETH_TOKEN } from '../_constants';\nimport { isAddressEqual } from 'viem';\nimport { isValidChainCombination } from './validations';\n\nconst ethHandler = (config: EvmConfig) => {\n return {\n source: config.nativeTokenConfig.ethereumToken,\n target: config.nativeTokenConfig.avalancheToken,\n ethToAva: true,\n };\n};\n\nconst assetsMatch = (a: Erc20Asset, b: Erc20Asset) => {\n return isAddressEqual(a.address, b.address);\n};\n\nconst prepEthToAvaTransferData = (config: EvmConfig, asset: Asset): EthToAvaTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const ethToken = config.ethereumTokens[asset.address.toLowerCase()];\n if (!ethToken || !assetsMatch(ethToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching ethereum token in config: ${asset.symbol}`,\n );\n }\n\n const avaToken = config.avalancheTokens[ethToken.target.tokenAddress];\n\n if (!avaToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get avalanche token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: ethToken,\n target: avaToken,\n ethToAva: true,\n };\n};\n\nconst prepAvaToEthTransferData = (config: EvmConfig, asset: Asset): AvaToEthTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const avaToken = config.avalancheTokens[asset.address.toLowerCase()];\n if (!avaToken || !assetsMatch(avaToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching avalanche token in config: ${asset.symbol}`,\n );\n }\n\n const ethToken = config.ethereumTokens[avaToken.target.tokenAddress];\n\n if (!ethToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get ethereum token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: avaToken,\n target: ethToken,\n ethToAva: false,\n };\n};\n\nexport const getTransferData = (\n {\n assetIn,\n sourceChainId,\n targetChainId,\n }: {\n assetIn: Asset;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n },\n config: EvmConfig,\n): TransferData => {\n if (sourceChainId === targetChainId) {\n throw new InvalidParamsError(\n ErrorReason.IDENTICAL_CHAINS_PROVIDED,\n `SourceChain and TargetChain need to be different network. But found the same chainId: ${sourceChainId}`,\n );\n }\n\n if (!isValidChainCombination(sourceChainId, targetChainId)) {\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `SourceChain and TargetChain combination is not correct or the chains provided are not supported`,\n );\n }\n\n if (isNativeAsset(assetIn) && assetIn.symbol === ETH_TOKEN.symbol && isCaip2EthereumChainId(sourceChainId)) {\n return ethHandler(config);\n }\n\n if (isCaip2EthereumChainId(sourceChainId)) {\n return prepEthToAvaTransferData(config, assetIn);\n } else {\n return prepAvaToEthTransferData(config, assetIn);\n }\n};\n"],"mappings":"2PAUA,MAAM,EAAc,IACX,CACL,OAAQ,EAAO,kBAAkB,cACjC,OAAQ,EAAO,kBAAkB,eACjC,SAAU,GACX,EAGG,GAAe,EAAe,KAClC,EAAA,EAAA,gBAAsB,EAAE,QAAS,EAAE,QAAQ,CAGvC,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAACA,EAAAA,aAAa,EAAM,CACtB,MAAM,IAAIC,EAAAA,mBAAmBC,EAAAA,YAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,eAAe,EAAM,QAAQ,aAAa,EAClE,GAAI,CAAC,GAAY,CAAC,EAAY,EAAS,MAAO,EAAM,CAClD,MAAM,IAAID,EAAAA,mBACRC,EAAAA,YAAY,oBACZ,qDAAqD,EAAM,SAC5D,CAGH,IAAM,EAAW,EAAO,gBAAgB,EAAS,OAAO,cAExD,GAAI,CAAC,EACH,MAAM,IAAID,EAAAA,mBACRC,EAAAA,YAAY,oBACZ,4CAA4C,EAAM,SACnD,CAGH,MAAO,CACL,OAAQ,EACR,OAAQ,EACR,SAAU,GACX,EAGG,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAACF,EAAAA,aAAa,EAAM,CACtB,MAAM,IAAIC,EAAAA,mBAAmBC,EAAAA,YAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,gBAAgB,EAAM,QAAQ,aAAa,EACnE,GAAI,CAAC,GAAY,CAAC,EAAY,EAAS,MAAO,EAAM,CAClD,MAAM,IAAID,EAAAA,mBACRC,EAAAA,YAAY,oBACZ,sDAAsD,EAAM,SAC7D,CAGH,IAAM,EAAW,EAAO,eAAe,EAAS,OAAO,cAEvD,GAAI,CAAC,EACH,MAAM,IAAID,EAAAA,mBACRC,EAAAA,YAAY,oBACZ,2CAA2C,EAAM,SAClD,CAGH,MAAO,CACL,OAAQ,EACR,OAAQ,EACR,SAAU,GACX,EAGU,GACX,CACE,UACA,gBACA,iBAMF,IACiB,CACjB,GAAI,IAAkB,EACpB,MAAM,IAAID,EAAAA,mBACRC,EAAAA,YAAY,0BACZ,yFAAyF,IAC1F,CAGH,GAAI,CAACC,EAAAA,wBAAwB,EAAe,EAAc,CACxD,MAAM,IAAIF,EAAAA,mBACRC,EAAAA,YAAY,eACZ,kGACD,CAUD,OAPEE,EAAAA,cAAc,EAAQ,EAAI,EAAQ,SAAWC,EAAAA,UAAU,QAAUC,EAAAA,uBAAuB,EAAc,CACjG,EAAW,EAAO,CAGvBA,EAAAA,uBAAuB,EAAc,CAChC,EAAyB,EAAQ,EAAQ,CAEzC,EAAyB,EAAQ,EAAQ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ErrorReason as e,InvalidParamsError as t}from"../../../errors.js";import{isErc20Asset as n,isNativeAsset as r}from"../../../type-guards.js";import{isCaip2EthereumChainId as i}from"../../../_utils/chain.js";import{isValidChainCombination as a}from"./validations.js";import{ETH_TOKEN as o}from"../_constants.js";import{isAddressEqual as s}from"viem";const c=e=>({source:e.nativeTokenConfig.ethereumToken,target:e.nativeTokenConfig.avalancheToken,ethToAva:!0}),l=(e,t)=>s(e.address,t.address),u=(r,i)=>{if(!n(i))throw new t(e.ASSET_NOT_SUPPORTED,`Wrong token type: ${i.type}`);let a=r.ethereumTokens[i.address];if(!a||!l(a.token,i))throw new t(e.ASSET_NOT_SUPPORTED,`Unable to find matching ethereum token in config: ${i.symbol}`);let o=r.avalancheTokens[a.target.tokenAddress];if(!o)throw new t(e.ASSET_NOT_SUPPORTED,`Unable to get avalanche token in config: ${i.symbol}`);return{source:a,target:o,ethToAva:!0}},d=(r,i)=>{if(!n(i))throw new t(e.ASSET_NOT_SUPPORTED,`Wrong token type: ${i.type}`);let a=r.avalancheTokens[i.address];if(!a||!l(a.token,i))throw new t(e.ASSET_NOT_SUPPORTED,`Unable to find matching avalanche token in config: ${i.symbol}`);let o=r.ethereumTokens[a.target.tokenAddress];if(!o)throw new t(e.ASSET_NOT_SUPPORTED,`Unable to get ethereum token in config: ${i.symbol}`);return{source:a,target:o,ethToAva:!1}},f=({assetIn:n,sourceChainId:s,targetChainId:l},f)=>{if(s===l)throw new t(e.IDENTICAL_CHAINS_PROVIDED,`SourceChain and TargetChain need to be different network. But found the same chainId: ${s}`);if(!a(s,l))throw new t(e.INVALID_PARAMS,`SourceChain and TargetChain combination is not correct or the chains provided are not supported`);return r(n)&&n.symbol===o.symbol&&i(s)?c(f):i(s)?u(f,n):d(f,n)};export{f as getTransferData};
|
|
1
|
+
import{ErrorReason as e,InvalidParamsError as t}from"../../../errors.js";import{isErc20Asset as n,isNativeAsset as r}from"../../../type-guards.js";import{isCaip2EthereumChainId as i}from"../../../_utils/chain.js";import{isValidChainCombination as a}from"./validations.js";import{ETH_TOKEN as o}from"../_constants.js";import{isAddressEqual as s}from"viem";const c=e=>({source:e.nativeTokenConfig.ethereumToken,target:e.nativeTokenConfig.avalancheToken,ethToAva:!0}),l=(e,t)=>s(e.address,t.address),u=(r,i)=>{if(!n(i))throw new t(e.ASSET_NOT_SUPPORTED,`Wrong token type: ${i.type}`);let a=r.ethereumTokens[i.address.toLowerCase()];if(!a||!l(a.token,i))throw new t(e.ASSET_NOT_SUPPORTED,`Unable to find matching ethereum token in config: ${i.symbol}`);let o=r.avalancheTokens[a.target.tokenAddress];if(!o)throw new t(e.ASSET_NOT_SUPPORTED,`Unable to get avalanche token in config: ${i.symbol}`);return{source:a,target:o,ethToAva:!0}},d=(r,i)=>{if(!n(i))throw new t(e.ASSET_NOT_SUPPORTED,`Wrong token type: ${i.type}`);let a=r.avalancheTokens[i.address.toLowerCase()];if(!a||!l(a.token,i))throw new t(e.ASSET_NOT_SUPPORTED,`Unable to find matching avalanche token in config: ${i.symbol}`);let o=r.ethereumTokens[a.target.tokenAddress];if(!o)throw new t(e.ASSET_NOT_SUPPORTED,`Unable to get ethereum token in config: ${i.symbol}`);return{source:a,target:o,ethToAva:!1}},f=({assetIn:n,sourceChainId:s,targetChainId:l},f)=>{if(s===l)throw new t(e.IDENTICAL_CHAINS_PROVIDED,`SourceChain and TargetChain need to be different network. But found the same chainId: ${s}`);if(!a(s,l))throw new t(e.INVALID_PARAMS,`SourceChain and TargetChain combination is not correct or the chains provided are not supported`);return r(n)&&n.symbol===o.symbol&&i(s)?c(f):i(s)?u(f,n):d(f,n)};export{f as getTransferData};
|
|
2
2
|
//# sourceMappingURL=transfer-data.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-data.js","names":[],"sources":["../../../../src/transfer-service/avalanche-evm/_utils/transfer-data.ts"],"sourcesContent":["import type { AvaToEthTransferData, EthToAvaTransferData, EvmConfig, TransferData } from '../_types';\nimport { ErrorReason, InvalidParamsError } from '../../../errors';\nimport { isErc20Asset, isNativeAsset } from '../../../type-guards';\nimport type { Asset, Erc20Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { isCaip2EthereumChainId } from '../../../_utils/chain';\nimport { ETH_TOKEN } from '../_constants';\nimport { isAddressEqual } from 'viem';\nimport { isValidChainCombination } from './validations';\n\nconst ethHandler = (config: EvmConfig) => {\n return {\n source: config.nativeTokenConfig.ethereumToken,\n target: config.nativeTokenConfig.avalancheToken,\n ethToAva: true,\n };\n};\n\nconst assetsMatch = (a: Erc20Asset, b: Erc20Asset) => {\n return isAddressEqual(a.address, b.address);\n};\n\nconst prepEthToAvaTransferData = (config: EvmConfig, asset: Asset): EthToAvaTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const ethToken = config.ethereumTokens[asset.address];\n if (!ethToken || !assetsMatch(ethToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching ethereum token in config: ${asset.symbol}`,\n );\n }\n\n const avaToken = config.avalancheTokens[ethToken.target.tokenAddress];\n\n if (!avaToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get avalanche token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: ethToken,\n target: avaToken,\n ethToAva: true,\n };\n};\n\nconst prepAvaToEthTransferData = (config: EvmConfig, asset: Asset): AvaToEthTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const avaToken = config.avalancheTokens[asset.address];\n if (!avaToken || !assetsMatch(avaToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching avalanche token in config: ${asset.symbol}`,\n );\n }\n\n const ethToken = config.ethereumTokens[avaToken.target.tokenAddress];\n\n if (!ethToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get ethereum token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: avaToken,\n target: ethToken,\n ethToAva: false,\n };\n};\n\nexport const getTransferData = (\n {\n assetIn,\n sourceChainId,\n targetChainId,\n }: {\n assetIn: Asset;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n },\n config: EvmConfig,\n): TransferData => {\n if (sourceChainId === targetChainId) {\n throw new InvalidParamsError(\n ErrorReason.IDENTICAL_CHAINS_PROVIDED,\n `SourceChain and TargetChain need to be different network. But found the same chainId: ${sourceChainId}`,\n );\n }\n\n if (!isValidChainCombination(sourceChainId, targetChainId)) {\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `SourceChain and TargetChain combination is not correct or the chains provided are not supported`,\n );\n }\n\n if (isNativeAsset(assetIn) && assetIn.symbol === ETH_TOKEN.symbol && isCaip2EthereumChainId(sourceChainId)) {\n return ethHandler(config);\n }\n\n if (isCaip2EthereumChainId(sourceChainId)) {\n return prepEthToAvaTransferData(config, assetIn);\n } else {\n return prepAvaToEthTransferData(config, assetIn);\n }\n};\n"],"mappings":"mWAUA,MAAM,EAAc,IACX,CACL,OAAQ,EAAO,kBAAkB,cACjC,OAAQ,EAAO,kBAAkB,eACjC,SAAU,GACX,EAGG,GAAe,EAAe,IAC3B,EAAe,EAAE,QAAS,EAAE,QAAQ,CAGvC,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAAC,EAAa,EAAM,CACtB,MAAM,IAAI,EAAmB,EAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,eAAe,EAAM,
|
|
1
|
+
{"version":3,"file":"transfer-data.js","names":[],"sources":["../../../../src/transfer-service/avalanche-evm/_utils/transfer-data.ts"],"sourcesContent":["import type { AvaToEthTransferData, EthToAvaTransferData, EvmConfig, TransferData } from '../_types';\nimport { ErrorReason, InvalidParamsError } from '../../../errors';\nimport { isErc20Asset, isNativeAsset } from '../../../type-guards';\nimport type { Asset, Erc20Asset } from '../../../types/asset';\nimport type { Caip2ChainId } from '../../../types/caip';\nimport { isCaip2EthereumChainId } from '../../../_utils/chain';\nimport { ETH_TOKEN } from '../_constants';\nimport { isAddressEqual } from 'viem';\nimport { isValidChainCombination } from './validations';\n\nconst ethHandler = (config: EvmConfig) => {\n return {\n source: config.nativeTokenConfig.ethereumToken,\n target: config.nativeTokenConfig.avalancheToken,\n ethToAva: true,\n };\n};\n\nconst assetsMatch = (a: Erc20Asset, b: Erc20Asset) => {\n return isAddressEqual(a.address, b.address);\n};\n\nconst prepEthToAvaTransferData = (config: EvmConfig, asset: Asset): EthToAvaTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const ethToken = config.ethereumTokens[asset.address.toLowerCase()];\n if (!ethToken || !assetsMatch(ethToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching ethereum token in config: ${asset.symbol}`,\n );\n }\n\n const avaToken = config.avalancheTokens[ethToken.target.tokenAddress];\n\n if (!avaToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get avalanche token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: ethToken,\n target: avaToken,\n ethToAva: true,\n };\n};\n\nconst prepAvaToEthTransferData = (config: EvmConfig, asset: Asset): AvaToEthTransferData => {\n if (!isErc20Asset(asset)) {\n throw new InvalidParamsError(ErrorReason.ASSET_NOT_SUPPORTED, `Wrong token type: ${asset.type}`);\n }\n const avaToken = config.avalancheTokens[asset.address.toLowerCase()];\n if (!avaToken || !assetsMatch(avaToken.token, asset)) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to find matching avalanche token in config: ${asset.symbol}`,\n );\n }\n\n const ethToken = config.ethereumTokens[avaToken.target.tokenAddress];\n\n if (!ethToken) {\n throw new InvalidParamsError(\n ErrorReason.ASSET_NOT_SUPPORTED,\n `Unable to get ethereum token in config: ${asset.symbol}`,\n );\n }\n\n return {\n source: avaToken,\n target: ethToken,\n ethToAva: false,\n };\n};\n\nexport const getTransferData = (\n {\n assetIn,\n sourceChainId,\n targetChainId,\n }: {\n assetIn: Asset;\n sourceChainId: Caip2ChainId;\n targetChainId: Caip2ChainId;\n },\n config: EvmConfig,\n): TransferData => {\n if (sourceChainId === targetChainId) {\n throw new InvalidParamsError(\n ErrorReason.IDENTICAL_CHAINS_PROVIDED,\n `SourceChain and TargetChain need to be different network. But found the same chainId: ${sourceChainId}`,\n );\n }\n\n if (!isValidChainCombination(sourceChainId, targetChainId)) {\n throw new InvalidParamsError(\n ErrorReason.INVALID_PARAMS,\n `SourceChain and TargetChain combination is not correct or the chains provided are not supported`,\n );\n }\n\n if (isNativeAsset(assetIn) && assetIn.symbol === ETH_TOKEN.symbol && isCaip2EthereumChainId(sourceChainId)) {\n return ethHandler(config);\n }\n\n if (isCaip2EthereumChainId(sourceChainId)) {\n return prepEthToAvaTransferData(config, assetIn);\n } else {\n return prepAvaToEthTransferData(config, assetIn);\n }\n};\n"],"mappings":"mWAUA,MAAM,EAAc,IACX,CACL,OAAQ,EAAO,kBAAkB,cACjC,OAAQ,EAAO,kBAAkB,eACjC,SAAU,GACX,EAGG,GAAe,EAAe,IAC3B,EAAe,EAAE,QAAS,EAAE,QAAQ,CAGvC,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAAC,EAAa,EAAM,CACtB,MAAM,IAAI,EAAmB,EAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,eAAe,EAAM,QAAQ,aAAa,EAClE,GAAI,CAAC,GAAY,CAAC,EAAY,EAAS,MAAO,EAAM,CAClD,MAAM,IAAI,EACR,EAAY,oBACZ,qDAAqD,EAAM,SAC5D,CAGH,IAAM,EAAW,EAAO,gBAAgB,EAAS,OAAO,cAExD,GAAI,CAAC,EACH,MAAM,IAAI,EACR,EAAY,oBACZ,4CAA4C,EAAM,SACnD,CAGH,MAAO,CACL,OAAQ,EACR,OAAQ,EACR,SAAU,GACX,EAGG,GAA4B,EAAmB,IAAuC,CAC1F,GAAI,CAAC,EAAa,EAAM,CACtB,MAAM,IAAI,EAAmB,EAAY,oBAAqB,qBAAqB,EAAM,OAAO,CAElG,IAAM,EAAW,EAAO,gBAAgB,EAAM,QAAQ,aAAa,EACnE,GAAI,CAAC,GAAY,CAAC,EAAY,EAAS,MAAO,EAAM,CAClD,MAAM,IAAI,EACR,EAAY,oBACZ,sDAAsD,EAAM,SAC7D,CAGH,IAAM,EAAW,EAAO,eAAe,EAAS,OAAO,cAEvD,GAAI,CAAC,EACH,MAAM,IAAI,EACR,EAAY,oBACZ,2CAA2C,EAAM,SAClD,CAGH,MAAO,CACL,OAAQ,EACR,OAAQ,EACR,SAAU,GACX,EAGU,GACX,CACE,UACA,gBACA,iBAMF,IACiB,CACjB,GAAI,IAAkB,EACpB,MAAM,IAAI,EACR,EAAY,0BACZ,yFAAyF,IAC1F,CAGH,GAAI,CAAC,EAAwB,EAAe,EAAc,CACxD,MAAM,IAAI,EACR,EAAY,eACZ,kGACD,CAUD,OAPE,EAAc,EAAQ,EAAI,EAAQ,SAAW,EAAU,QAAU,EAAuB,EAAc,CACjG,EAAW,EAAO,CAGvB,EAAuB,EAAc,CAChC,EAAyB,EAAQ,EAAQ,CAEzC,EAAyB,EAAQ,EAAQ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../errors.cjs`),t=require(`../../../_utils.cjs`),n=require(`../../constants.cjs`),r=require(`../../_utils/metadata.cjs`);let i=require(`viem`),a=require(`@lombard.finance/sdk`);function o({bitcoinFunctions:e,config:n}){return({transfer:r,updateListener:i})=>{let a=new AbortController,o=()=>{a.abort()};return r.status===`completed`||r.status===`failed`?{cancel:o,result:Promise.resolve(r)}:{cancel:o,result:(async()=>{let o=structuredClone(r),l=t.getEvmClientForChain({chain:r.targetChain});for(;!a.signal.aborted;){let r=o.status;switch(o.status){case`source-pending`:o=await s({bitcoinFunctions:e,currentTransfer:o,signal:a.signal}),a.signal.aborted||i(o);break;case`source-completed`:case`target-pending`:o=await c({config:n,currentTransfer:o,signal:a.signal,targetClient:l}),a.signal.aborted||i(o);break;default:return o}o.status===r&&await t.waitForTimeoutOrAbort({timeoutMs:3e4,signal:a.signal})}return o})()}}}async function s({bitcoinFunctions:e,currentTransfer:n,signal:r}){let i=await t.awaitOrAbort(e.getTransaction(n.source.txHash),r);if(i.status===`aborted`)return n;let{confirmations:a}=i.value;return a<n.source.requiredConfirmationCount?{...n,source:{...n.source,confirmationCount:a}}:{...n,source:{...n.source,confirmationCount:a},status:`source-completed`}}async function c({config:o,currentTransfer:s,signal:c,targetClient:l}){switch(s.status){case`source-completed`:{let i=await t.awaitOrAbort((0,a.getDepositsByAddress)({address:s.toAddress,env:o.env}),c);if(i.status===`aborted`)return s;let l=i.value.find(e=>e.txHash.toLowerCase()===s.source.txHash.toLowerCase());if(!l)return s;let{needsNotarizationUpdate:u,needsSessionStateUpdate:d}=r.getMetadataUpdates(s.metadata,l.notarizationStatus,l.sessionState);return u||d?{...s,metadata:{...s.metadata,...u&&{notarizationStatus:l.notarizationStatus},...d&&{sessionState:l.sessionState}}}:r.isDepositFailed(l)?{...s,errorCode:e.ErrorCode.NOTARIZATION_FAILED,failedAtMs:Date.now(),status:`failed`}:l.claimTxHash?{...s,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:n.EVM_CONFIRMATION_COUNT,startedAtMs:Date.now(),txHash:l.claimTxHash}}:s}default:{if(!s.target?.txHash)return{...s,errorCode:e.ErrorCode.UNKNOWN,errorReason:`Missing target transaction hash`,failedAtMs:Date.now(),status:`failed`};let{confirmationCount:n,requiredConfirmationCount:r,txHash:a}=s.target;if(!(0,i.isHash)(a))return{...s,errorCode:e.ErrorCode.UNKNOWN,errorReason:`Invalid target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let i=await t.awaitOrAbort(l.waitForTransactionReceipt({confirmations:Math.min(n+1,r),hash:a}),c);if(i.status===`aborted`)return s;let o=i.value;if(o.status===`reverted`)return{...s,errorCode:e.ErrorCode.TRANSACTION_REVERTED,failedAtMs:Date.now(),status:`failed`};let u=await l.getTransactionConfirmations({transactionReceipt:o}).then(e=>Number(e)).catch(()=>n+1);return u<r?{...s,target:{...s.target,confirmationCount:u}}:{...s,completedAtMs:Date.now(),status:`completed`,target:{...s.target,confirmationCount:u}}}catch
|
|
1
|
+
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../errors.cjs`),t=require(`../../../_utils.cjs`),n=require(`../../constants.cjs`),r=require(`../../_utils/metadata.cjs`);let i=require(`viem`),a=require(`@lombard.finance/sdk`);function o({bitcoinFunctions:e,config:n}){return({transfer:r,updateListener:i})=>{let a=new AbortController,o=()=>{a.abort()};return r.status===`completed`||r.status===`failed`?{cancel:o,result:Promise.resolve(r)}:{cancel:o,result:(async()=>{let o=structuredClone(r),l=t.getEvmClientForChain({chain:r.targetChain});for(;!a.signal.aborted;){let r=o.status;switch(o.status){case`source-pending`:o=await s({bitcoinFunctions:e,currentTransfer:o,signal:a.signal}),a.signal.aborted||i(o);break;case`source-completed`:case`target-pending`:o=await c({config:n,currentTransfer:o,signal:a.signal,targetClient:l}),a.signal.aborted||i(o);break;default:return o}o.status===r&&await t.waitForTimeoutOrAbort({timeoutMs:3e4,signal:a.signal})}return o})()}}}async function s({bitcoinFunctions:e,currentTransfer:n,signal:r}){try{let i=await t.awaitOrAbort(e.getTransaction(n.source.txHash),r);if(i.status===`aborted`)return n;let{confirmations:a}=i.value;return a<n.source.requiredConfirmationCount?{...n,source:{...n.source,confirmationCount:a}}:{...n,source:{...n.source,confirmationCount:a},status:`source-completed`}}catch{return n}}async function c({config:o,currentTransfer:s,signal:c,targetClient:l}){switch(s.status){case`source-completed`:try{let i=await t.awaitOrAbort((0,a.getDepositsByAddress)({address:s.toAddress,env:o.env}),c);if(i.status===`aborted`)return s;let l=i.value.find(e=>e.txHash.toLowerCase()===s.source.txHash.toLowerCase());if(!l)return s;let{needsNotarizationUpdate:u,needsSessionStateUpdate:d}=r.getMetadataUpdates(s.metadata,l.notarizationStatus,l.sessionState);return u||d?{...s,metadata:{...s.metadata,...u&&{notarizationStatus:l.notarizationStatus},...d&&{sessionState:l.sessionState}}}:r.isDepositFailed(l)?{...s,errorCode:e.ErrorCode.NOTARIZATION_FAILED,errorReason:`Deposit notarization failed`,failedAtMs:Date.now(),status:`failed`}:l.claimTxHash?{...s,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:n.EVM_CONFIRMATION_COUNT,startedAtMs:Date.now(),txHash:l.claimTxHash}}:s}catch{return s}default:{if(!s.target?.txHash)return{...s,errorCode:e.ErrorCode.UNKNOWN,errorReason:`Missing target transaction hash`,failedAtMs:Date.now(),status:`failed`};let{confirmationCount:n,requiredConfirmationCount:r,txHash:a}=s.target;if(!(0,i.isHash)(a))return{...s,errorCode:e.ErrorCode.UNKNOWN,errorReason:`Invalid target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let i=await t.awaitOrAbort(l.waitForTransactionReceipt({confirmations:Math.min(n+1,r),hash:a}),c);if(i.status===`aborted`)return s;let o=i.value;if(o.status===`reverted`)return{...s,errorCode:e.ErrorCode.TRANSACTION_REVERTED,errorReason:`Target transaction was reverted`,failedAtMs:Date.now(),status:`failed`};let u=await l.getTransactionConfirmations({transactionReceipt:o}).then(e=>Number(e)).catch(()=>n+1);return u<r?{...s,target:{...s.target,confirmationCount:u}}:{...s,completedAtMs:Date.now(),status:`completed`,target:{...s.target,confirmationCount:u}}}catch{return s}}}}exports.trackTransferFactory=o;
|
|
2
2
|
//# sourceMappingURL=track-transfer.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-transfer.cjs","names":["getEvmClientForChain","waitForTimeoutOrAbort","awaitOrAbort","getMetadataUpdates","isDepositFailed","ErrorCode","EVM_CONFIRMATION_COUNT","getErrorCodeForViemError"],"sources":["../../../../../src/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.ts"],"sourcesContent":["import { getDepositsByAddress } from '@lombard.finance/sdk';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport { awaitOrAbort, getErrorCodeForViemError, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport type { BtcToBtcbConfig } from '../../types';\nimport { isHash } from 'viem';\nimport { getMetadataUpdates, isDepositFailed } from '../../_utils/metadata';\nimport { EVM_CONFIRMATION_COUNT } from '../../constants';\n\n// Polling interval for BTC deposits (30 seconds - faster than BTC block time since we also poll Lombard API)\nconst DEPOSIT_POLLING_INTERVAL_MS = 1000 * 30;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcToBtcbConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n config,\n currentTransfer,\n signal: ac.signal,\n targetClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: DEPOSIT_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n currentTransfer: SourcePendingTransfer;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n config: BtcToBtcbConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const transactionResult = await awaitOrAbort(bitcoinFunctions.getTransaction(currentTransfer.source.txHash), signal);\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const { confirmations: confirmationCount } = transactionResult.value;\n\n if (confirmationCount < currentTransfer.source.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n}\n\nexport async function _trackTargetTx({\n config,\n currentTransfer,\n signal,\n targetClient,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n const depositsResult = await awaitOrAbort(\n getDepositsByAddress({ address: currentTransfer.toAddress, env: config.env }),\n signal,\n );\n\n if (depositsResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferDeposit = depositsResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (!transferDeposit) {\n // Source Completed but no deposit found yet on target\n return currentTransfer;\n }\n\n const { needsNotarizationUpdate, needsSessionStateUpdate } = getMetadataUpdates(\n currentTransfer.metadata,\n transferDeposit.notarizationStatus,\n transferDeposit.sessionState,\n );\n\n // Update metadata if needed\n if (needsNotarizationUpdate || needsSessionStateUpdate) {\n return {\n ...currentTransfer,\n metadata: {\n ...currentTransfer.metadata,\n ...(needsNotarizationUpdate && { notarizationStatus: transferDeposit.notarizationStatus }),\n ...(needsSessionStateUpdate && { sessionState: transferDeposit.sessionState }),\n },\n } satisfies SourceCompletedTransfer;\n }\n\n if (isDepositFailed(transferDeposit)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.NOTARIZATION_FAILED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n if (!transferDeposit.claimTxHash) {\n return currentTransfer;\n }\n\n return {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: EVM_CONFIRMATION_COUNT,\n startedAtMs: Date.now(),\n txHash: transferDeposit.claimTxHash,\n },\n } satisfies TargetPendingTransfer;\n }\n default: {\n // target-pending\n\n if (!currentTransfer.target?.txHash) {\n // Transfer missing target info, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.target;\n\n if (!isHash(txHash)) {\n // Invalid tx hash\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Invalid target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const transactionResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = transactionResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await targetClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies CompletedTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"track-transfer.cjs","names":["getEvmClientForChain","waitForTimeoutOrAbort","awaitOrAbort","getMetadataUpdates","isDepositFailed","ErrorCode","EVM_CONFIRMATION_COUNT"],"sources":["../../../../../src/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.ts"],"sourcesContent":["import { getDepositsByAddress } from '@lombard.finance/sdk';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport { awaitOrAbort, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport type { BtcToBtcbConfig } from '../../types';\nimport { isHash } from 'viem';\nimport { getMetadataUpdates, isDepositFailed } from '../../_utils/metadata';\nimport { EVM_CONFIRMATION_COUNT } from '../../constants';\n\n// Polling interval for BTC deposits (30 seconds - faster than BTC block time since we also poll Lombard API)\nconst DEPOSIT_POLLING_INTERVAL_MS = 1000 * 30;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcToBtcbConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n config,\n currentTransfer,\n signal: ac.signal,\n targetClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: DEPOSIT_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n currentTransfer: SourcePendingTransfer;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n config: BtcToBtcbConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n try {\n const transactionResult = await awaitOrAbort(\n bitcoinFunctions.getTransaction(currentTransfer.source.txHash),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const { confirmations: confirmationCount } = transactionResult.value;\n\n if (confirmationCount < currentTransfer.source.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n config,\n currentTransfer,\n signal,\n targetClient,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n try {\n const depositsResult = await awaitOrAbort(\n getDepositsByAddress({ address: currentTransfer.toAddress, env: config.env }),\n signal,\n );\n\n if (depositsResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferDeposit = depositsResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (!transferDeposit) {\n // Source Completed but no deposit found yet on target\n return currentTransfer;\n }\n\n const { needsNotarizationUpdate, needsSessionStateUpdate } = getMetadataUpdates(\n currentTransfer.metadata,\n transferDeposit.notarizationStatus,\n transferDeposit.sessionState,\n );\n\n // Update metadata if needed\n if (needsNotarizationUpdate || needsSessionStateUpdate) {\n return {\n ...currentTransfer,\n metadata: {\n ...currentTransfer.metadata,\n ...(needsNotarizationUpdate && { notarizationStatus: transferDeposit.notarizationStatus }),\n ...(needsSessionStateUpdate && { sessionState: transferDeposit.sessionState }),\n },\n } satisfies SourceCompletedTransfer;\n }\n\n if (isDepositFailed(transferDeposit)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.NOTARIZATION_FAILED,\n errorReason: 'Deposit notarization failed',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n if (!transferDeposit.claimTxHash) {\n return currentTransfer;\n }\n\n return {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: EVM_CONFIRMATION_COUNT,\n startedAtMs: Date.now(),\n txHash: transferDeposit.claimTxHash,\n },\n } satisfies TargetPendingTransfer;\n } catch {\n return currentTransfer;\n }\n }\n default: {\n // target-pending\n\n if (!currentTransfer.target?.txHash) {\n // Transfer missing target info, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.target;\n\n if (!isHash(txHash)) {\n // Invalid tx hash\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Invalid target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const transactionResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = transactionResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Target transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await targetClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies CompletedTransfer;\n } catch {\n return currentTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ErrorCode as e}from"../../../../errors.js";import{awaitOrAbort as t,
|
|
1
|
+
import{ErrorCode as e}from"../../../../errors.js";import{awaitOrAbort as t,getEvmClientForChain as n,waitForTimeoutOrAbort as r}from"../../../_utils.js";import{EVM_CONFIRMATION_COUNT as i}from"../../constants.js";import{getMetadataUpdates as a,isDepositFailed as o}from"../../_utils/metadata.js";import{isHash as s}from"viem";import{getDepositsByAddress as c}from"@lombard.finance/sdk";function l({bitcoinFunctions:e,config:t}){return({transfer:i,updateListener:a})=>{let o=new AbortController,s=()=>{o.abort()};return i.status===`completed`||i.status===`failed`?{cancel:s,result:Promise.resolve(i)}:{cancel:s,result:(async()=>{let s=structuredClone(i),c=n({chain:i.targetChain});for(;!o.signal.aborted;){let n=s.status;switch(s.status){case`source-pending`:s=await u({bitcoinFunctions:e,currentTransfer:s,signal:o.signal}),o.signal.aborted||a(s);break;case`source-completed`:case`target-pending`:s=await d({config:t,currentTransfer:s,signal:o.signal,targetClient:c}),o.signal.aborted||a(s);break;default:return s}s.status===n&&await r({timeoutMs:3e4,signal:o.signal})}return s})()}}}async function u({bitcoinFunctions:e,currentTransfer:n,signal:r}){try{let i=await t(e.getTransaction(n.source.txHash),r);if(i.status===`aborted`)return n;let{confirmations:a}=i.value;return a<n.source.requiredConfirmationCount?{...n,source:{...n.source,confirmationCount:a}}:{...n,source:{...n.source,confirmationCount:a},status:`source-completed`}}catch{return n}}async function d({config:n,currentTransfer:r,signal:l,targetClient:u}){switch(r.status){case`source-completed`:try{let s=await t(c({address:r.toAddress,env:n.env}),l);if(s.status===`aborted`)return r;let u=s.value.find(e=>e.txHash.toLowerCase()===r.source.txHash.toLowerCase());if(!u)return r;let{needsNotarizationUpdate:d,needsSessionStateUpdate:f}=a(r.metadata,u.notarizationStatus,u.sessionState);return d||f?{...r,metadata:{...r.metadata,...d&&{notarizationStatus:u.notarizationStatus},...f&&{sessionState:u.sessionState}}}:o(u)?{...r,errorCode:e.NOTARIZATION_FAILED,errorReason:`Deposit notarization failed`,failedAtMs:Date.now(),status:`failed`}:u.claimTxHash?{...r,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:i,startedAtMs:Date.now(),txHash:u.claimTxHash}}:r}catch{return r}default:{if(!r.target?.txHash)return{...r,errorCode:e.UNKNOWN,errorReason:`Missing target transaction hash`,failedAtMs:Date.now(),status:`failed`};let{confirmationCount:n,requiredConfirmationCount:i,txHash:a}=r.target;if(!s(a))return{...r,errorCode:e.UNKNOWN,errorReason:`Invalid target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let o=await t(u.waitForTransactionReceipt({confirmations:Math.min(n+1,i),hash:a}),l);if(o.status===`aborted`)return r;let s=o.value;if(s.status===`reverted`)return{...r,errorCode:e.TRANSACTION_REVERTED,errorReason:`Target transaction was reverted`,failedAtMs:Date.now(),status:`failed`};let c=await u.getTransactionConfirmations({transactionReceipt:s}).then(e=>Number(e)).catch(()=>n+1);return c<i?{...r,target:{...r.target,confirmationCount:c}}:{...r,completedAtMs:Date.now(),status:`completed`,target:{...r.target,confirmationCount:c}}}catch{return r}}}}export{l as trackTransferFactory};
|
|
2
2
|
//# sourceMappingURL=track-transfer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../../src/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.ts"],"sourcesContent":["import { getDepositsByAddress } from '@lombard.finance/sdk';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport { awaitOrAbort, getErrorCodeForViemError, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport type { BtcToBtcbConfig } from '../../types';\nimport { isHash } from 'viem';\nimport { getMetadataUpdates, isDepositFailed } from '../../_utils/metadata';\nimport { EVM_CONFIRMATION_COUNT } from '../../constants';\n\n// Polling interval for BTC deposits (30 seconds - faster than BTC block time since we also poll Lombard API)\nconst DEPOSIT_POLLING_INTERVAL_MS = 1000 * 30;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcToBtcbConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n config,\n currentTransfer,\n signal: ac.signal,\n targetClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: DEPOSIT_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n currentTransfer: SourcePendingTransfer;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n config: BtcToBtcbConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const transactionResult = await awaitOrAbort(bitcoinFunctions.getTransaction(currentTransfer.source.txHash), signal);\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const { confirmations: confirmationCount } = transactionResult.value;\n\n if (confirmationCount < currentTransfer.source.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n}\n\nexport async function _trackTargetTx({\n config,\n currentTransfer,\n signal,\n targetClient,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n const depositsResult = await awaitOrAbort(\n getDepositsByAddress({ address: currentTransfer.toAddress, env: config.env }),\n signal,\n );\n\n if (depositsResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferDeposit = depositsResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (!transferDeposit) {\n // Source Completed but no deposit found yet on target\n return currentTransfer;\n }\n\n const { needsNotarizationUpdate, needsSessionStateUpdate } = getMetadataUpdates(\n currentTransfer.metadata,\n transferDeposit.notarizationStatus,\n transferDeposit.sessionState,\n );\n\n // Update metadata if needed\n if (needsNotarizationUpdate || needsSessionStateUpdate) {\n return {\n ...currentTransfer,\n metadata: {\n ...currentTransfer.metadata,\n ...(needsNotarizationUpdate && { notarizationStatus: transferDeposit.notarizationStatus }),\n ...(needsSessionStateUpdate && { sessionState: transferDeposit.sessionState }),\n },\n } satisfies SourceCompletedTransfer;\n }\n\n if (isDepositFailed(transferDeposit)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.NOTARIZATION_FAILED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n if (!transferDeposit.claimTxHash) {\n return currentTransfer;\n }\n\n return {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: EVM_CONFIRMATION_COUNT,\n startedAtMs: Date.now(),\n txHash: transferDeposit.claimTxHash,\n },\n } satisfies TargetPendingTransfer;\n }\n default: {\n // target-pending\n\n if (!currentTransfer.target?.txHash) {\n // Transfer missing target info, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.target;\n\n if (!isHash(txHash)) {\n // Invalid tx hash\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Invalid target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const transactionResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = transactionResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await targetClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies CompletedTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../../src/transfer-service/lombard/btc-to-btcb/_handlers/track-transfer.ts"],"sourcesContent":["import { getDepositsByAddress } from '@lombard.finance/sdk';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport { awaitOrAbort, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport type { BtcToBtcbConfig } from '../../types';\nimport { isHash } from 'viem';\nimport { getMetadataUpdates, isDepositFailed } from '../../_utils/metadata';\nimport { EVM_CONFIRMATION_COUNT } from '../../constants';\n\n// Polling interval for BTC deposits (30 seconds - faster than BTC block time since we also poll Lombard API)\nconst DEPOSIT_POLLING_INTERVAL_MS = 1000 * 30;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcToBtcbConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const targetClient = getEvmClientForChain({ chain: transfer.targetChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n config,\n currentTransfer,\n signal: ac.signal,\n targetClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: DEPOSIT_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n currentTransfer: SourcePendingTransfer;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n config: BtcToBtcbConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n targetClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport async function _trackSourceTx({\n bitcoinFunctions,\n currentTransfer,\n signal,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n try {\n const transactionResult = await awaitOrAbort(\n bitcoinFunctions.getTransaction(currentTransfer.source.txHash),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const { confirmations: confirmationCount } = transactionResult.value;\n\n if (confirmationCount < currentTransfer.source.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n config,\n currentTransfer,\n signal,\n targetClient,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n try {\n const depositsResult = await awaitOrAbort(\n getDepositsByAddress({ address: currentTransfer.toAddress, env: config.env }),\n signal,\n );\n\n if (depositsResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferDeposit = depositsResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (!transferDeposit) {\n // Source Completed but no deposit found yet on target\n return currentTransfer;\n }\n\n const { needsNotarizationUpdate, needsSessionStateUpdate } = getMetadataUpdates(\n currentTransfer.metadata,\n transferDeposit.notarizationStatus,\n transferDeposit.sessionState,\n );\n\n // Update metadata if needed\n if (needsNotarizationUpdate || needsSessionStateUpdate) {\n return {\n ...currentTransfer,\n metadata: {\n ...currentTransfer.metadata,\n ...(needsNotarizationUpdate && { notarizationStatus: transferDeposit.notarizationStatus }),\n ...(needsSessionStateUpdate && { sessionState: transferDeposit.sessionState }),\n },\n } satisfies SourceCompletedTransfer;\n }\n\n if (isDepositFailed(transferDeposit)) {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.NOTARIZATION_FAILED,\n errorReason: 'Deposit notarization failed',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n if (!transferDeposit.claimTxHash) {\n return currentTransfer;\n }\n\n return {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: EVM_CONFIRMATION_COUNT,\n startedAtMs: Date.now(),\n txHash: transferDeposit.claimTxHash,\n },\n } satisfies TargetPendingTransfer;\n } catch {\n return currentTransfer;\n }\n }\n default: {\n // target-pending\n\n if (!currentTransfer.target?.txHash) {\n // Transfer missing target info, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.target;\n\n if (!isHash(txHash)) {\n // Invalid tx hash\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Invalid target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const transactionResult = await awaitOrAbort(\n targetClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (transactionResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = transactionResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Target transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await targetClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n status: 'completed',\n target: {\n ...currentTransfer.target,\n confirmationCount: newConfirmationCount,\n },\n } satisfies CompletedTransfer;\n } catch {\n return currentTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../errors.cjs`),t=require(`../../../_utils.cjs`);let n=require(`viem`),r=require(`@lombard.finance/sdk`);function i({bitcoinFunctions:e,config:n}){return({transfer:r,updateListener:i})=>{let s=new AbortController,c=()=>{s.abort()};return r.status===`completed`||r.status===`failed`?{cancel:c,result:Promise.resolve(r)}:{cancel:c,result:(async()=>{let c=structuredClone(r),l=t.getEvmClientForChain({chain:r.sourceChain});for(;!s.signal.aborted;){let r=c.status;switch(c.status){case`source-pending`:c=await a({currentTransfer:c,signal:s.signal,sourceClient:l}),s.signal.aborted||i(c);break;case`source-completed`:case`target-pending`:c=await o({bitcoinFunctions:e,config:n,currentTransfer:c,signal:s.signal}),s.signal.aborted||i(c);break;default:return c}c.status===r&&await t.waitForTimeoutOrAbort({timeoutMs:6e4,signal:s.signal})}return c})()}}}async function a({currentTransfer:r,signal:i,sourceClient:a}){let{confirmationCount:o,requiredConfirmationCount:s,txHash:c}=r.source;if(!(0,n.isHash)(c))return{...r,errorCode:e.ErrorCode.INVALID_PARAMS,errorReason:`Invalid source transaction hash`,failedAtMs:Date.now(),status:`failed`};if(o>=s)return{...r,status:`source-completed`};try{let n=await t.awaitOrAbort(a.waitForTransactionReceipt({confirmations:Math.min(o+1,s),hash:c}),i);if(n.status===`aborted`)return r;let l=n.value;if(l.status===`reverted`)return{...r,errorCode:e.ErrorCode.TRANSACTION_REVERTED,failedAtMs:Date.now(),status:`failed`};let u=await a.getTransactionConfirmations({transactionReceipt:l}).then(e=>Number(e)).catch(()=>o+1);return u<s?{...r,source:{...r.source,confirmationCount:u}}:{...r,source:{...r.source,confirmationCount:u},status:`source-completed`}}catch
|
|
1
|
+
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../errors.cjs`),t=require(`../../../_utils.cjs`);let n=require(`viem`),r=require(`@lombard.finance/sdk`);function i({bitcoinFunctions:e,config:n}){return({transfer:r,updateListener:i})=>{let s=new AbortController,c=()=>{s.abort()};return r.status===`completed`||r.status===`failed`?{cancel:c,result:Promise.resolve(r)}:{cancel:c,result:(async()=>{let c=structuredClone(r),l=t.getEvmClientForChain({chain:r.sourceChain});for(;!s.signal.aborted;){let r=c.status;switch(c.status){case`source-pending`:c=await a({currentTransfer:c,signal:s.signal,sourceClient:l}),s.signal.aborted||i(c);break;case`source-completed`:case`target-pending`:c=await o({bitcoinFunctions:e,config:n,currentTransfer:c,signal:s.signal}),s.signal.aborted||i(c);break;default:return c}c.status===r&&await t.waitForTimeoutOrAbort({timeoutMs:6e4,signal:s.signal})}return c})()}}}async function a({currentTransfer:r,signal:i,sourceClient:a}){let{confirmationCount:o,requiredConfirmationCount:s,txHash:c}=r.source;if(!(0,n.isHash)(c))return{...r,errorCode:e.ErrorCode.INVALID_PARAMS,errorReason:`Invalid source transaction hash`,failedAtMs:Date.now(),status:`failed`};if(o>=s)return{...r,status:`source-completed`};try{let n=await t.awaitOrAbort(a.waitForTransactionReceipt({confirmations:Math.min(o+1,s),hash:c}),i);if(n.status===`aborted`)return r;let l=n.value;if(l.status===`reverted`)return{...r,errorCode:e.ErrorCode.TRANSACTION_REVERTED,errorReason:`Source transaction was reverted`,failedAtMs:Date.now(),status:`failed`};let u=await a.getTransactionConfirmations({transactionReceipt:l}).then(e=>Number(e)).catch(()=>o+1);return u<s?{...r,source:{...r.source,confirmationCount:u}}:{...r,source:{...r.source,confirmationCount:u},status:`source-completed`}}catch{return r}}async function o({bitcoinFunctions:n,config:i,currentTransfer:a,signal:o}){switch(a.status){case`source-completed`:try{let e=await t.awaitOrAbort((0,r.getUnstakesByAddress)({address:a.fromAddress,env:i.env}),o);if(e.status===`aborted`)return a;let n=e.value.find(e=>e.txHash.toLowerCase()===a.source.txHash.toLowerCase());return n?.payoutTxHash?{...a,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:1,startedAtMs:Date.now(),txHash:n.payoutTxHash}}:a}catch{return a}default:if(!a.target?.txHash)return{...a,errorCode:e.ErrorCode.UNKNOWN,errorReason:`Missing target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let e=await t.awaitOrAbort(n.getTransaction(a.target.txHash),o);if(e.status===`aborted`)return a;let r=e.value.confirmations;return r<a.target.requiredConfirmationCount?{...a,target:{...a.target,confirmationCount:r}}:{...a,completedAtMs:Date.now(),target:{...a.target,confirmationCount:r},status:`completed`}}catch{return a}}}exports.trackTransferFactory=i;
|
|
2
2
|
//# sourceMappingURL=track-transfer.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-transfer.cjs","names":["getEvmClientForChain","waitForTimeoutOrAbort","ErrorCode","awaitOrAbort","getErrorCodeForViemError"],"sources":["../../../../../src/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.ts"],"sourcesContent":["import { isHash } from 'viem';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport type { BtcbToBtcConfig } from '../../types';\nimport { awaitOrAbort, getErrorCodeForViemError, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport { getUnstakesByAddress } from '@lombard.finance/sdk';\n\n// Polling interval for BTC redemptions (60 seconds - BTC confirmations are slow)\nconst REDEMPTION_POLLING_INTERVAL_MS = 1000 * 60;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: REDEMPTION_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to next stage\n\n const updatedTransfer: SourceCompletedTransfer = {\n ...currentTransfer,\n status: 'source-completed',\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to next stage\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n const unstakesResult = await awaitOrAbort(\n getUnstakesByAddress({ address: currentTransfer.fromAddress, env: config.env }),\n signal,\n );\n\n if (unstakesResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferUnstake = unstakesResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (transferUnstake?.payoutTxHash) {\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n txHash: transferUnstake.payoutTxHash,\n },\n };\n\n return updatedTransfer;\n }\n\n return currentTransfer;\n }\n default: {\n // target-pending\n if (!currentTransfer.target?.txHash) {\n // Fail if no target tx hash, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const receiptResult = await awaitOrAbort(bitcoinFunctions.getTransaction(currentTransfer.target.txHash), signal);\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const targetConfirmationCount = receiptResult.value.confirmations;\n\n if (targetConfirmationCount < currentTransfer.target.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n // Completed\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n status: 'completed',\n } satisfies CompletedTransfer;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"track-transfer.cjs","names":["getEvmClientForChain","waitForTimeoutOrAbort","ErrorCode","awaitOrAbort"],"sources":["../../../../../src/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.ts"],"sourcesContent":["import { isHash } from 'viem';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport type { BtcbToBtcConfig } from '../../types';\nimport { awaitOrAbort, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport { getUnstakesByAddress } from '@lombard.finance/sdk';\n\n// Polling interval for BTC redemptions (60 seconds - BTC confirmations are slow)\nconst REDEMPTION_POLLING_INTERVAL_MS = 1000 * 60;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: REDEMPTION_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to next stage\n\n const updatedTransfer: SourceCompletedTransfer = {\n ...currentTransfer,\n status: 'source-completed',\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Source transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to next stage\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n try {\n const unstakesResult = await awaitOrAbort(\n getUnstakesByAddress({ address: currentTransfer.fromAddress, env: config.env }),\n signal,\n );\n\n if (unstakesResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferUnstake = unstakesResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (transferUnstake?.payoutTxHash) {\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n txHash: transferUnstake.payoutTxHash,\n },\n };\n\n return updatedTransfer;\n }\n\n return currentTransfer;\n } catch {\n return currentTransfer;\n }\n }\n default: {\n // target-pending\n if (!currentTransfer.target?.txHash) {\n // Fail if no target tx hash, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n bitcoinFunctions.getTransaction(currentTransfer.target.txHash),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const targetConfirmationCount = receiptResult.value.confirmations;\n\n if (targetConfirmationCount < currentTransfer.target.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n // Completed\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n status: 'completed',\n } satisfies CompletedTransfer;\n } catch {\n return currentTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ErrorCode as e}from"../../../../errors.js";import{awaitOrAbort as t,
|
|
1
|
+
import{ErrorCode as e}from"../../../../errors.js";import{awaitOrAbort as t,getEvmClientForChain as n,waitForTimeoutOrAbort as r}from"../../../_utils.js";import{isHash as i}from"viem";import{getUnstakesByAddress as a}from"@lombard.finance/sdk";function o({bitcoinFunctions:e,config:t}){return({transfer:i,updateListener:a})=>{let o=new AbortController,l=()=>{o.abort()};return i.status===`completed`||i.status===`failed`?{cancel:l,result:Promise.resolve(i)}:{cancel:l,result:(async()=>{let l=structuredClone(i),u=n({chain:i.sourceChain});for(;!o.signal.aborted;){let n=l.status;switch(l.status){case`source-pending`:l=await s({currentTransfer:l,signal:o.signal,sourceClient:u}),o.signal.aborted||a(l);break;case`source-completed`:case`target-pending`:l=await c({bitcoinFunctions:e,config:t,currentTransfer:l,signal:o.signal}),o.signal.aborted||a(l);break;default:return l}l.status===n&&await r({timeoutMs:6e4,signal:o.signal})}return l})()}}}async function s({currentTransfer:n,signal:r,sourceClient:a}){let{confirmationCount:o,requiredConfirmationCount:s,txHash:c}=n.source;if(!i(c))return{...n,errorCode:e.INVALID_PARAMS,errorReason:`Invalid source transaction hash`,failedAtMs:Date.now(),status:`failed`};if(o>=s)return{...n,status:`source-completed`};try{let i=await t(a.waitForTransactionReceipt({confirmations:Math.min(o+1,s),hash:c}),r);if(i.status===`aborted`)return n;let l=i.value;if(l.status===`reverted`)return{...n,errorCode:e.TRANSACTION_REVERTED,errorReason:`Source transaction was reverted`,failedAtMs:Date.now(),status:`failed`};let u=await a.getTransactionConfirmations({transactionReceipt:l}).then(e=>Number(e)).catch(()=>o+1);return u<s?{...n,source:{...n.source,confirmationCount:u}}:{...n,source:{...n.source,confirmationCount:u},status:`source-completed`}}catch{return n}}async function c({bitcoinFunctions:n,config:r,currentTransfer:i,signal:o}){switch(i.status){case`source-completed`:try{let e=await t(a({address:i.fromAddress,env:r.env}),o);if(e.status===`aborted`)return i;let n=e.value.find(e=>e.txHash.toLowerCase()===i.source.txHash.toLowerCase());return n?.payoutTxHash?{...i,status:`target-pending`,target:{confirmationCount:0,requiredConfirmationCount:1,startedAtMs:Date.now(),txHash:n.payoutTxHash}}:i}catch{return i}default:if(!i.target?.txHash)return{...i,errorCode:e.UNKNOWN,errorReason:`Missing target transaction hash`,failedAtMs:Date.now(),status:`failed`};try{let e=await t(n.getTransaction(i.target.txHash),o);if(e.status===`aborted`)return i;let r=e.value.confirmations;return r<i.target.requiredConfirmationCount?{...i,target:{...i.target,confirmationCount:r}}:{...i,completedAtMs:Date.now(),target:{...i.target,confirmationCount:r},status:`completed`}}catch{return i}}}export{o as trackTransferFactory};
|
|
2
2
|
//# sourceMappingURL=track-transfer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../../src/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.ts"],"sourcesContent":["import { isHash } from 'viem';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport type { BtcbToBtcConfig } from '../../types';\nimport { awaitOrAbort, getErrorCodeForViemError, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport { getUnstakesByAddress } from '@lombard.finance/sdk';\n\n// Polling interval for BTC redemptions (60 seconds - BTC confirmations are slow)\nconst REDEMPTION_POLLING_INTERVAL_MS = 1000 * 60;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: REDEMPTION_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to next stage\n\n const updatedTransfer: SourceCompletedTransfer = {\n ...currentTransfer,\n status: 'source-completed',\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to next stage\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch (error) {\n return {\n ...currentTransfer,\n errorCode: getErrorCodeForViemError(error),\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n const unstakesResult = await awaitOrAbort(\n getUnstakesByAddress({ address: currentTransfer.fromAddress, env: config.env }),\n signal,\n );\n\n if (unstakesResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferUnstake = unstakesResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (transferUnstake?.payoutTxHash) {\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n txHash: transferUnstake.payoutTxHash,\n },\n };\n\n return updatedTransfer;\n }\n\n return currentTransfer;\n }\n default: {\n // target-pending\n if (!currentTransfer.target?.txHash) {\n // Fail if no target tx hash, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const receiptResult = await awaitOrAbort(bitcoinFunctions.getTransaction(currentTransfer.target.txHash), signal);\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const targetConfirmationCount = receiptResult.value.confirmations;\n\n if (targetConfirmationCount < currentTransfer.target.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n // Completed\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n status: 'completed',\n } satisfies CompletedTransfer;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"track-transfer.js","names":[],"sources":["../../../../../src/transfer-service/lombard/btcb-to-btc/_handlers/track-transfer.ts"],"sourcesContent":["import { isHash } from 'viem';\nimport { ErrorCode } from '../../../../errors';\nimport type { BitcoinFunctions } from '../../../../types/bitcoin';\nimport type { TransferService } from '../../../../types/service';\nimport type {\n CompletedTransfer,\n FailedTransfer,\n SourceCompletedTransfer,\n SourcePendingTransfer,\n TargetPendingTransfer,\n Transfer,\n} from '../../../../types/transfer';\nimport type { BtcbToBtcConfig } from '../../types';\nimport { awaitOrAbort, getEvmClientForChain, waitForTimeoutOrAbort } from '../../../_utils';\nimport { getUnstakesByAddress } from '@lombard.finance/sdk';\n\n// Polling interval for BTC redemptions (60 seconds - BTC confirmations are slow)\nconst REDEMPTION_POLLING_INTERVAL_MS = 1000 * 60;\n\nexport interface TrackTransferFactoryOptions {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n}\n\nexport function trackTransferFactory({\n bitcoinFunctions,\n config,\n}: TrackTransferFactoryOptions): TransferService['trackTransfer'] {\n return ({ transfer, updateListener }) => {\n const ac = new AbortController();\n const cancel = () => {\n ac.abort();\n };\n\n if (transfer.status === 'completed' || transfer.status === 'failed') {\n return {\n cancel,\n result: Promise.resolve(transfer),\n };\n }\n\n const executeTracking = async (): Promise<Transfer> => {\n let currentTransfer: Transfer = structuredClone(transfer);\n\n const sourceClient = getEvmClientForChain({ chain: transfer.sourceChain });\n\n while (!ac.signal.aborted) {\n const statusBeforeTracking = currentTransfer.status;\n\n switch (currentTransfer.status) {\n case 'source-pending': {\n currentTransfer = await _trackSourceTx({\n currentTransfer,\n signal: ac.signal,\n sourceClient,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n case 'source-completed':\n case 'target-pending': {\n currentTransfer = await _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal: ac.signal,\n });\n\n if (!ac.signal.aborted) {\n updateListener(currentTransfer);\n }\n\n break;\n }\n default: {\n return currentTransfer;\n }\n }\n\n const hasStatusChanged = currentTransfer.status !== statusBeforeTracking;\n\n if (!hasStatusChanged) {\n await waitForTimeoutOrAbort({ timeoutMs: REDEMPTION_POLLING_INTERVAL_MS, signal: ac.signal });\n }\n }\n\n return currentTransfer;\n };\n\n return {\n cancel,\n result: executeTracking(),\n };\n };\n}\n\nexport interface TrackBaseParams {\n currentTransfer: Transfer;\n signal: AbortSignal;\n}\n\nexport interface TrackSourceTxParams extends TrackBaseParams {\n currentTransfer: SourcePendingTransfer;\n sourceClient: ReturnType<typeof getEvmClientForChain>;\n}\n\nexport interface TrackTargetTxParams extends TrackBaseParams {\n bitcoinFunctions: BitcoinFunctions;\n config: BtcbToBtcConfig;\n currentTransfer: SourceCompletedTransfer | TargetPendingTransfer;\n}\n\nexport async function _trackSourceTx({\n currentTransfer,\n signal,\n sourceClient,\n}: TrackSourceTxParams): Promise<SourcePendingTransfer | SourceCompletedTransfer | FailedTransfer> {\n const { confirmationCount, requiredConfirmationCount, txHash } = currentTransfer.source;\n\n if (!isHash(txHash)) {\n const failedTransfer: FailedTransfer = {\n ...currentTransfer,\n errorCode: ErrorCode.INVALID_PARAMS,\n errorReason: 'Invalid source transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n };\n\n return failedTransfer;\n }\n\n if (confirmationCount >= requiredConfirmationCount) {\n // Move to next stage\n\n const updatedTransfer: SourceCompletedTransfer = {\n ...currentTransfer,\n status: 'source-completed',\n };\n\n return updatedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n sourceClient.waitForTransactionReceipt({\n confirmations: Math.min(confirmationCount + 1, requiredConfirmationCount),\n hash: txHash,\n }),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transactionReceipt = receiptResult.value;\n\n if (transactionReceipt.status === 'reverted') {\n return {\n ...currentTransfer,\n errorCode: ErrorCode.TRANSACTION_REVERTED,\n errorReason: 'Source transaction was reverted',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n const newConfirmationCount: number = await sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Number(count))\n .catch(() => confirmationCount + 1);\n\n // If still not enough confirmations, update the count\n if (newConfirmationCount < requiredConfirmationCount) {\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n } satisfies SourcePendingTransfer;\n }\n\n // Move to next stage\n return {\n ...currentTransfer,\n source: {\n ...currentTransfer.source,\n confirmationCount: newConfirmationCount,\n },\n status: 'source-completed',\n } satisfies SourceCompletedTransfer;\n } catch {\n return currentTransfer;\n }\n}\n\nexport async function _trackTargetTx({\n bitcoinFunctions,\n config,\n currentTransfer,\n signal,\n}: TrackTargetTxParams): Promise<SourceCompletedTransfer | TargetPendingTransfer | FailedTransfer | CompletedTransfer> {\n switch (currentTransfer.status) {\n case 'source-completed': {\n try {\n const unstakesResult = await awaitOrAbort(\n getUnstakesByAddress({ address: currentTransfer.fromAddress, env: config.env }),\n signal,\n );\n\n if (unstakesResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const transferUnstake = unstakesResult.value.find(\n (d) => d.txHash.toLowerCase() === currentTransfer.source.txHash.toLowerCase(),\n );\n\n if (transferUnstake?.payoutTxHash) {\n const updatedTransfer: TargetPendingTransfer = {\n ...currentTransfer,\n status: 'target-pending',\n target: {\n confirmationCount: 0,\n requiredConfirmationCount: 1,\n startedAtMs: Date.now(),\n txHash: transferUnstake.payoutTxHash,\n },\n };\n\n return updatedTransfer;\n }\n\n return currentTransfer;\n } catch {\n return currentTransfer;\n }\n }\n default: {\n // target-pending\n if (!currentTransfer.target?.txHash) {\n // Fail if no target tx hash, should not reach here\n return {\n ...currentTransfer,\n errorCode: ErrorCode.UNKNOWN,\n errorReason: 'Missing target transaction hash',\n failedAtMs: Date.now(),\n status: 'failed',\n } satisfies FailedTransfer;\n }\n\n try {\n const receiptResult = await awaitOrAbort(\n bitcoinFunctions.getTransaction(currentTransfer.target.txHash),\n signal,\n );\n\n if (receiptResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const targetConfirmationCount = receiptResult.value.confirmations;\n\n if (targetConfirmationCount < currentTransfer.target.requiredConfirmationCount) {\n return {\n ...currentTransfer,\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n } satisfies TargetPendingTransfer;\n }\n\n // Completed\n return {\n ...currentTransfer,\n completedAtMs: Date.now(),\n target: {\n ...currentTransfer.target,\n confirmationCount: targetConfirmationCount,\n },\n status: 'completed',\n } satisfies CompletedTransfer;\n } catch {\n return currentTransfer;\n }\n }\n }\n}\n"],"mappings":"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"}
|