diff --git a/connect/src/protocols/cctpTransfer.ts b/connect/src/protocols/cctpTransfer.ts index 64da812e5..2fa8ba32a 100644 --- a/connect/src/protocols/cctpTransfer.ts +++ b/connect/src/protocols/cctpTransfer.ts @@ -489,20 +489,31 @@ export class CircleTransfer const originTxs = xfer.txids.filter((txid) => txid.chain === xfer.transfer.from.chain); if (originTxs.length > 0) { - receipt = { ...receipt, state: TransferState.SourceInitiated, originTxs } as Partial< - SourceInitiatedTransferReceipt - >; + receipt = { + ...receipt, + state: TransferState.SourceInitiated, + originTxs, + } as SourceInitiatedTransferReceipt; } const att = xfer.attestations?.filter((a) => isWormholeMessageId(a.id)) ?? []; const ctt = xfer.attestations?.filter((a) => isCircleMessageId(a.id)) ?? []; const attestation = att.length > 0 ? att[0]! : ctt.length > 0 ? ctt[0]! : undefined; - if (attestation && attestation.attestation) { - receipt = { - ...receipt, - state: TransferState.Attested, - attestation: attestation, - } as AttestedTransferReceipt; + if (attestation) { + if (attestation.id) { + receipt = { + ...receipt, + state: TransferState.SourceFinalized, + attestation: attestation, + } as SourceFinalizedTransferReceipt; + } + + if (attestation.attestation) + receipt = { + ...receipt, + state: TransferState.Attested, + attestation: { id: attestation.id, attestation: attestation.attestation }, + } as AttestedTransferReceipt; } const destinationTxs = xfer.txids.filter((txid) => txid.chain === xfer.transfer.to.chain); @@ -511,7 +522,7 @@ export class CircleTransfer ...receipt, state: TransferState.DestinationInitiated, destinationTxs, - } as CompletedTransferReceipt; + } as CompletedTransferReceipt; } return receipt as TransferReceipt; @@ -543,11 +554,12 @@ export class CircleTransfer const initTx = receipt.originTxs[receipt.originTxs.length - 1]!; const xfermsg = await CircleTransfer.getTransferMessage(_fromChain, initTx.txid); - yield { + receipt = { ...receipt, attestation: { id: xfermsg }, state: TransferState.SourceFinalized, } as SourceFinalizedTransferReceipt; + yield receipt; } if (isSourceFinalized(receipt)) { @@ -564,21 +576,21 @@ export class CircleTransfer receipt.attestation.id as WormholeMessageId, leftover(start, timeout), ); - yield { + receipt = { ...receipt, attestation: { id: receipt.attestation.id, attestation: vaa }, state: TransferState.Attested, } as AttestedTransferReceipt; + yield receipt; } } } - if (isAttested(receipt)) { + // First try to grab the tx status from the API + // Note: this requires a subsequent async step on the backend + // to have the dest txid populated, so it may be delayed by some time + if (isAttested(receipt) || isSourceFinalized(receipt)) { if (!receipt.attestation) throw "Invalid state transition"; - - // First try to grab the tx status from the API - // Note: this requires a subsequent async step on the backend - // to have the dest txid populated, so it may be delayed by some time const txStatus = await wh.getTransactionStatus( receipt.attestation.id as WormholeMessageId, leftover(start, timeout), @@ -586,26 +598,30 @@ export class CircleTransfer if (txStatus && txStatus.globalTx?.destinationTx?.txHash) { const { chainId, txHash } = txStatus.globalTx.destinationTx; - yield { + receipt = { ...receipt, destinationTxs: [{ chain: toChain(chainId) as DC, txid: txHash }], state: TransferState.DestinationFinalized, } as CompletedTransferReceipt; + yield receipt; } + } - // Fall back to asking the destination chain if this VAA has been redeemed - // assuming we have the full attestation - if (isAttested(receipt)) { - yield { + // Fall back to asking the destination chain if this VAA has been redeemed + // assuming we have the full attestation + if (isAttested(receipt)) { + const isComplete = await CircleTransfer.isTransferComplete( + _toChain, + receipt.attestation.attestation, + ); + if (isComplete) { + receipt = { ...receipt, - state: (await CircleTransfer.isTransferComplete( - _toChain, - receipt.attestation.attestation, - )) - ? TransferState.DestinationFinalized - : TransferState.Attested, - } as AttestedTransferReceipt; + state: TransferState.DestinationFinalized, + destinationTxs: [], + } as CompletedTransferReceipt; } + yield receipt; } } } diff --git a/connect/src/protocols/tokenTransfer.ts b/connect/src/protocols/tokenTransfer.ts index e8592e27e..e73aa828b 100644 --- a/connect/src/protocols/tokenTransfer.ts +++ b/connect/src/protocols/tokenTransfer.ts @@ -35,6 +35,8 @@ import { Wormhole } from "../wormhole"; import { AttestedTransferReceipt, CompletedTransferReceipt, + SourceFinalizedTransferReceipt, + SourceInitiatedTransferReceipt, TransferQuote, TransferReceipt, TransferState, @@ -541,19 +543,32 @@ export class TokenTransfer const originTxs = xfer.txids.filter((txid) => txid.chain === transfer.from.chain); if (originTxs.length > 0) { - receipt = { ...receipt, state: TransferState.SourceInitiated, originTxs: originTxs }; + receipt = { + ...receipt, + state: TransferState.SourceInitiated, + originTxs: originTxs, + } as SourceInitiatedTransferReceipt; } const att = xfer.attestations && xfer.attestations.length > 0 ? xfer.attestations![0]! : undefined; - const attestation = - att && att.id.emitter ? { id: att.id, attestation: att.attestation } : undefined; - if (attestation && attestation.attestation) { - receipt = { - ...receipt, - state: TransferState.Attested, - attestation: attestation, - } as AttestedTransferReceipt; + const attestation = att && att.id ? { id: att.id, attestation: att.attestation } : undefined; + if (attestation) { + if (attestation.id) { + receipt = { + ...receipt, + state: TransferState.SourceFinalized, + attestation: { id: attestation.id }, + } as SourceFinalizedTransferReceipt; + } + + if (attestation.attestation) { + receipt = { + ...receipt, + state: TransferState.Attested, + attestation: { id: attestation.id, attestation: attestation.attestation }, + } as AttestedTransferReceipt; + } } const destinationTxs = xfer.txids.filter((txid) => txid.chain === transfer.to.chain); @@ -596,7 +611,12 @@ export class TokenTransfer txid, leftover(start, timeout), ); - yield { ...receipt, state: TransferState.SourceFinalized, attestation: { id: msg } }; + receipt = { + ...receipt, + state: TransferState.SourceFinalized, + attestation: { id: msg }, + } as SourceFinalizedTransferReceipt; + yield receipt; } // If the source is finalized, we need to fetch the signed attestation @@ -606,7 +626,12 @@ export class TokenTransfer if (!receipt.attestation.id) throw "Attestation id required to fetch attestation"; const { id } = receipt.attestation; const attestation = await TokenTransfer.getTransferVaa(wh, id, leftover(start, timeout)); - yield { ...receipt, attestation: { id, attestation }, state: TransferState.Attested }; + receipt = { + ...receipt, + attestation: { id, attestation }, + state: TransferState.Attested, + } as AttestedTransferReceipt; + yield receipt; } // First try to grab the tx status from the API @@ -638,13 +663,13 @@ export class TokenTransfer ); if (isComplete) { - yield { + receipt = { ...receipt, state: TransferState.DestinationFinalized, } as CompletedTransferReceipt; - } else { - yield receipt; } + + yield receipt; } yield receipt; diff --git a/connect/src/wormholeTransfer.ts b/connect/src/wormholeTransfer.ts index 24e495cf6..784f08889 100644 --- a/connect/src/wormholeTransfer.ts +++ b/connect/src/wormholeTransfer.ts @@ -90,7 +90,7 @@ export interface CompletedTransferReceipt< > extends BaseTransferReceipt { state: TransferState.DestinationInitiated | TransferState.DestinationFinalized; originTxs: TransactionId[]; - attestation: Required>; + attestation: AttestationReceipt; destinationTxs?: TransactionId[]; }