@avalabs/fusion-sdk 0.14.2 → 0.14.3

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.
@@ -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,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;
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({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 t.awaitOrAbort(a.getTransactionConfirmations({transactionReceipt:l}).then(e=>Math.max(Number(e),0)).catch(()=>Math.max(o,0)),i);if(u.status===`aborted`)return r;let d=u.value;return d<s?{...r,source:{...r.source,confirmationCount:d}}:{...r,source:{...r.source,confirmationCount:d},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"],"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
+ {"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 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 newConfirmationCountResult = await awaitOrAbort(\n sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Math.max(Number(count), 0))\n .catch(() => Math.max(confirmationCount, 0)),\n signal,\n );\n\n if (newConfirmationCountResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const newConfirmationCount = newConfirmationCountResult.value;\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,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};
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({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 t(a.getTransactionConfirmations({transactionReceipt:l}).then(e=>Math.max(Number(e),0)).catch(()=>Math.max(o,0)),r);if(u.status===`aborted`)return n;let d=u.value;return d<s?{...n,source:{...n.source,confirmationCount:d}}:{...n,source:{...n.source,confirmationCount:d},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, 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":"mPAwBA,SAAgB,EAAqB,CACnC,mBACA,UACgE,CAChE,OAAQ,CAAE,WAAU,oBAAqB,CACvC,IAAM,EAAK,IAAI,gBACT,MAAe,CACnB,EAAG,OAAO,EA8DZ,OA3DI,EAAS,SAAW,aAAe,EAAS,SAAW,SAClD,CACL,SACA,OAAQ,QAAQ,QAAQ,EAAS,CAClC,CAuDI,CACL,SACA,QAtDsB,SAA+B,CACrD,IAAI,EAA4B,gBAAgB,EAAS,CAEnD,EAAe,EAAqB,CAAE,MAAO,EAAS,YAAa,CAAC,CAE1E,KAAO,CAAC,EAAG,OAAO,SAAS,CACzB,IAAM,EAAuB,EAAgB,OAE7C,OAAQ,EAAgB,OAAxB,CACE,IAAK,iBACH,EAAkB,MAAM,EAAe,CACrC,kBACA,OAAQ,EAAG,OACX,eACD,CAAC,CAEG,EAAG,OAAO,SACb,EAAe,EAAgB,CAGjC,MAEF,IAAK,mBACL,IAAK,iBACH,EAAkB,MAAM,EAAe,CACrC,mBACA,SACA,kBACA,OAAQ,EAAG,OACZ,CAAC,CAEG,EAAG,OAAO,SACb,EAAe,EAAgB,CAGjC,MAEF,QACE,OAAO,EAIc,EAAgB,SAAW,GAGlD,MAAM,EAAsB,CAAE,UAAW,IAAgC,OAAQ,EAAG,OAAQ,CAAC,CAIjG,OAAO,KAKkB,CAC1B,EAoBL,eAAsB,EAAe,CACnC,kBACA,SACA,gBACiG,CACjG,GAAM,CAAE,oBAAmB,4BAA2B,UAAW,EAAgB,OAEjF,GAAI,CAAC,EAAO,EAAO,CASjB,MARuC,CACrC,GAAG,EACH,UAAW,EAAU,eACrB,YAAa,kCACb,WAAY,KAAK,KAAK,CACtB,OAAQ,SACT,CAKH,GAAI,GAAqB,EAQvB,MALiD,CAC/C,GAAG,EACH,OAAQ,mBACT,CAKH,GAAI,CACF,IAAM,EAAgB,MAAM,EAC1B,EAAa,0BAA0B,CACrC,cAAe,KAAK,IAAI,EAAoB,EAAG,EAA0B,CACzE,KAAM,EACP,CAAC,CACF,EACD,CAED,GAAI,EAAc,SAAW,UAC3B,OAAO,EAGT,IAAM,EAAqB,EAAc,MAEzC,GAAI,EAAmB,SAAW,WAChC,MAAO,CACL,GAAG,EACH,UAAW,EAAU,qBACrB,YAAa,kCACb,WAAY,KAAK,KAAK,CACtB,OAAQ,SACT,CAGH,IAAM,EAA+B,MAAM,EACxC,4BAA4B,CAC3B,qBACD,CAAC,CACD,KAAM,GAAU,OAAO,EAAM,CAAC,CAC9B,UAAY,EAAoB,EAAE,CAcrC,OAXI,EAAuB,EAClB,CACL,GAAG,EACH,OAAQ,CACN,GAAG,EAAgB,OACnB,kBAAmB,EACpB,CACF,CAII,CACL,GAAG,EACH,OAAQ,CACN,GAAG,EAAgB,OACnB,kBAAmB,EACpB,CACD,OAAQ,mBACT,MACK,CACN,OAAO,GAIX,eAAsB,EAAe,CACnC,mBACA,SACA,kBACA,UACqH,CACrH,OAAQ,EAAgB,OAAxB,CACE,IAAK,mBACH,GAAI,CACF,IAAM,EAAiB,MAAM,EAC3B,EAAqB,CAAE,QAAS,EAAgB,YAAa,IAAK,EAAO,IAAK,CAAC,CAC/E,EACD,CAED,GAAI,EAAe,SAAW,UAC5B,OAAO,EAGT,IAAM,EAAkB,EAAe,MAAM,KAC1C,GAAM,EAAE,OAAO,aAAa,GAAK,EAAgB,OAAO,OAAO,aAAa,CAC9E,CAiBD,OAfI,GAAiB,aAC4B,CAC7C,GAAG,EACH,OAAQ,iBACR,OAAQ,CACN,kBAAmB,EACnB,0BAA2B,EAC3B,YAAa,KAAK,KAAK,CACvB,OAAQ,EAAgB,aACzB,CACF,CAKI,OACD,CACN,OAAO,EAGX,QAEE,GAAI,CAAC,EAAgB,QAAQ,OAE3B,MAAO,CACL,GAAG,EACH,UAAW,EAAU,QACrB,YAAa,kCACb,WAAY,KAAK,KAAK,CACtB,OAAQ,SACT,CAGH,GAAI,CACF,IAAM,EAAgB,MAAM,EAC1B,EAAiB,eAAe,EAAgB,OAAO,OAAO,CAC9D,EACD,CAED,GAAI,EAAc,SAAW,UAC3B,OAAO,EAGT,IAAM,EAA0B,EAAc,MAAM,cAapD,OAXI,EAA0B,EAAgB,OAAO,0BAC5C,CACL,GAAG,EACH,OAAQ,CACN,GAAG,EAAgB,OACnB,kBAAmB,EACpB,CACF,CAII,CACL,GAAG,EACH,cAAe,KAAK,KAAK,CACzB,OAAQ,CACN,GAAG,EAAgB,OACnB,kBAAmB,EACpB,CACD,OAAQ,YACT,MACK,CACN,OAAO"}
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 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 newConfirmationCountResult = await awaitOrAbort(\n sourceClient\n .getTransactionConfirmations({\n transactionReceipt,\n })\n .then((count) => Math.max(Number(count), 0))\n .catch(() => Math.max(confirmationCount, 0)),\n signal,\n );\n\n if (newConfirmationCountResult.status === 'aborted') {\n return currentTransfer;\n }\n\n const newConfirmationCount = newConfirmationCountResult.value;\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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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@avalabs/fusion-sdk",
3
3
  "license": "Limited Ecosystem License",
4
- "version": "0.14.2",
4
+ "version": "0.14.3",
5
5
  "type": "module",
6
6
  "main": "./dist/mod.cjs",
7
7
  "module": "./dist/mod.js",
@@ -47,8 +47,8 @@
47
47
  "vitest": "4.0.6",
48
48
  "tsdown": "0.20.3",
49
49
  "zod": "4.1.12",
50
- "eslint-config-custom": "0.1.0",
51
- "@internal/tsdown-config": "0.0.1"
50
+ "@internal/tsdown-config": "0.0.1",
51
+ "eslint-config-custom": "0.1.0"
52
52
  },
53
53
  "sideEffects": false,
54
54
  "scripts": {