Skip to content

Commit

Permalink
shot at required fields based on transfer state
Browse files Browse the repository at this point in the history
  • Loading branch information
barnjamin committed Dec 31, 2023
1 parent 5a79d2c commit 3794d75
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 160 deletions.
112 changes: 59 additions & 53 deletions connect/src/protocols/cctpTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,22 @@ export class CircleTransfer<N extends Network = Network>
): TransferReceipt<CircleTransferProtocol> {
const { from, to } = xfer.transfer;

// This attestation may be either the auto relay vaa or the circle attestation
// depending on the request

let receipt: Partial<TransferReceipt<CircleTransferProtocol>> = {
protocol: xfer.transfer.automatic ? "AutomaticCircleBridge" : "CircleBridge",
request: xfer.transfer,
from: from.chain,
to: to.chain,
state: TransferState.Created,
};

const originTxs = xfer.txids.filter((txid) => txid.chain === xfer.transfer.from.chain);
if (originTxs.length > 0) {
receipt = { ...receipt, state: TransferState.SourceInitiated, originTxs };
}

const att = xfer.attestations.filter((a) =>
isWormholeMessageId(a.id),
) as AttestationReceipt<"AutomaticCircleBridge">[];
Expand All @@ -481,27 +497,17 @@ export class CircleTransfer<N extends Network = Network>
isCircleMessageId(a.id),
) as AttestationReceipt<"CircleBridge">[];

// This attestation may be either the auto relay vaa or the circle attestation
// depending on the request
const attestation = att.length > 0 ? att[0]! : ctt.length > 0 ? ctt[0]! : undefined;
if (attestation && attestation.attestation) {
receipt = { ...receipt, state: TransferState.Attested, attestation: attestation };
}

const receipt: TransferReceipt<CircleTransferProtocol> = {
protocol: xfer.transfer.automatic ? "AutomaticCircleBridge" : "CircleBridge",
from: from.chain,
to: to.chain,
state: TransferState.Created,
originTxs: xfer.txids.filter((txid) => txid.chain === xfer.transfer.from.chain),
destinationTxs: xfer.txids.filter((txid) => txid.chain === xfer.transfer.to.chain),
request: xfer.transfer,
attestation,
};

if (receipt.originTxs.length > 0) receipt.state = TransferState.SourceInitiated;
if (receipt.attestation && receipt.attestation.attestation)
receipt.state = TransferState.Attested;
if (receipt.destinationTxs.length > 0) receipt.state = TransferState.DestinationInitiated;
const destinationTxs = xfer.txids.filter((txid) => txid.chain === xfer.transfer.to.chain);
if (destinationTxs.length > 0) {
receipt = { ...receipt, state: TransferState.DestinationInitiated, destinationTxs };
}

return receipt;
return receipt as TransferReceipt<CircleTransferProtocol>;
}

// AsyncGenerator fn that produces status updates through an async generator
Expand All @@ -522,83 +528,83 @@ export class CircleTransfer<N extends Network = Network>
_fromChain = _fromChain ?? wh.getChain(receipt.from);
_toChain = _toChain ?? wh.getChain(receipt.to);

type R<TS extends TransferState = typeof receipt.state> = TransferReceipt<
typeof receipt.protocol,
typeof receipt.from,
typeof receipt.to,
TS
>;

// Check the source chain for initiation transaction
// and capture the message id
if (receipt.state === TransferState.SourceInitiated) {
if (receipt.originTxs.length === 0)
const _receipt = receipt as R<TransferState.SourceInitiated>;
if (_receipt.originTxs.length === 0)
throw "Invalid state transition: no originating transactions";

if (!receipt.attestation || !receipt.attestation.id) {
const initTx = receipt.originTxs[receipt.originTxs.length - 1]!;
const xfermsg = await CircleTransfer.getTransferMessage(_fromChain, initTx.txid);
receipt.attestation = { id: xfermsg };
receipt.state = TransferState.SourceFinalized;
yield receipt;
}
const initTx = _receipt.originTxs[_receipt.originTxs.length - 1]!;
const xfermsg = await CircleTransfer.getTransferMessage(_fromChain, initTx.txid);
receipt = { ..._receipt, attestation: { id: xfermsg }, state: TransferState.SourceFinalized };
yield receipt;
}

if (receipt.state == TransferState.SourceFinalized) {
if (!receipt.attestation) throw "Invalid state transition: no attestation id";
const _receipt = receipt as R<TransferState.SourceFinalized>;
if (!_receipt.attestation) throw "Invalid state transition: no attestation id";

if (receipt.protocol === "AutomaticCircleBridge") {
if (_receipt.protocol === "AutomaticCircleBridge") {
// we need to get the attestation so we can deliver it
// we can use the message id we parsed out of the logs, if we have them
// or try to fetch it from the last origin transaction
let vaa = receipt.attestation.attestation ? receipt.attestation.attestation : undefined;
let vaa = _receipt.attestation.attestation ? _receipt.attestation.attestation : undefined;
if (!vaa) {
vaa = await CircleTransfer.getTransferVaa(
wh,
receipt.attestation.id as WormholeMessageId,
_receipt.attestation.id as WormholeMessageId,
leftover(start, timeout),
);
receipt.attestation.attestation = vaa;
receipt.state = TransferState.Attested;
receipt = {
..._receipt,
attestation: { id: _receipt.attestation.id, attestation: vaa },
state: TransferState.Attested,
};
yield receipt;
}
}
}

if (receipt.state == TransferState.Attested) {
if (!receipt.attestation) throw "Invalid state transition";
const _receipt = receipt as R<TransferState.SourceFinalized>;
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,
_receipt.attestation.id as WormholeMessageId,
leftover(start, timeout),
);
if (!txStatus) {
yield receipt;
return;
}

if (txStatus.globalTx?.destinationTx?.txHash) {
if (txStatus && txStatus.globalTx?.destinationTx?.txHash) {
const { chainId, txHash } = txStatus.globalTx.destinationTx;

receipt.destinationTxs = [
{
chain: toChain(chainId),
txid: txHash,
},
];

receipt.state = TransferState.DestinationFinalized;
receipt = {
...receipt,
destinationTxs: [{ chain: toChain(chainId), txid: txHash }],
state: TransferState.DestinationFinalized,
};
yield receipt;
}

// Fall back to asking the destination chain if this VAA has been redeemed
// assuming we have the full attestation
if (
receipt.attestation.attestation &&
(await CircleTransfer.isTransferComplete(_toChain, receipt.attestation.attestation),
_receipt.attestation.attestation &&
(await CircleTransfer.isTransferComplete(_toChain, _receipt.attestation.attestation),
leftover(start, timeout))
) {
receipt.state = TransferState.DestinationFinalized;
receipt = { ...receipt, state: TransferState.DestinationFinalized };
yield receipt;
}
}
yield receipt;
return;
}
}
Loading

0 comments on commit 3794d75

Please sign in to comment.