Powerful Honeysuckle Anteater
High
During a dCDS withdrawal, if the user has opted in for liquidations, there are cases where collateral (ETH/rsETH/wETH) needs to be retrieved from another chain. However, the protocol attempts to transfer it back to the user before it has been received, which could lead to a transaction revert.
When a dCDS depositor has opted in for liquidations, the protocol may need to retrieve ETH from another chain to cover the owed amount.
In CDSLib.sol
, the liquidation calculation logic is implemented here - CDSLib.sol#L624-L777.
The protocol attempts to retrieve the funds from the other chain and send them to the treasury on the current chain:
interfaces.globalVariables.oftOrCollateralReceiveFromOtherChains{value: msg.value - params.fee}(
IGlobalVariables.FunctionToDo(
// Call getLzFunctionToDo in cds library to get, which action needs to do in dst chain
getLzFunctionToDo(params.optionsFeesToGetFromOtherChain, collateralToGetFromOtherChain)
),
IGlobalVariables.USDaOftTransferData(
address(interfaces.treasury), params.optionsFeesToGetFromOtherChain
),
IGlobalVariables.CollateralTokenTransferData(
@>> address(interfaces.treasury)
@>> ethAmountFromOtherChain,
@>> weETHAmountFromOtherChain,
@>> rsETHAmountFromOtherChain
),
IGlobalVariables.CallingFunction.CDS_WITHDRAW,
msg.sender
);
if (rsETHAmountFromOtherChain > 0) {
interfaces.treasury.wrapRsETH(rsETHAmountFromOtherChain);
}
params.ethAmount = ethAmountFromOtherChain + params.ethAmount;
weETHAmount = weETHAmountFromOtherChain + weETHAmount;
rsETHAmount = rsETHAmountFromOtherChain + rsETHAmount;
LayerZero needs technical time to process the transaction, send it to the other chain, and wait for the other chain to respond and send the requesting chain the collateral, this is impossible to happen in the same call of this transaction.
Thus later when we do the transfers, we could have a revert, as we won't have enough collateral on the current chain:
if (params.ethAmount != 0) {
params.omniChainData.collateralProfitsOfLiquidators -= totalWithdrawCollateralAmountInETH;
// Call transferEthToCdsLiquidators to tranfer eth
interfaces.treasury.transferEthToCdsLiquidators(msg.sender, params.ethAmount);
}
if (weETHAmount != 0) {
interfaces.treasury.approveTokens(IBorrowing.AssetName.WeETH, address(interfaces.cds), weETHAmount);
bool sent = IERC20(interfaces.borrowing.assetAddress(IBorrowing.AssetName.WeETH)).transferFrom(
address(interfaces.treasury), msg.sender, weETHAmount
); // transfer amount to msg.sender
if (!sent) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.WeETH);
}
if (rsETHAmount != 0) {
interfaces.treasury.approveTokens(IBorrowing.AssetName.WrsETH, address(interfaces.cds), rsETHAmount);
bool sent = IERC20(interfaces.borrowing.assetAddress(IBorrowing.AssetName.WrsETH)).transferFrom(
address(interfaces.treasury), msg.sender, rsETHAmount
); // transfer amount to msg.sender
if (!sent) revert CDSInterface.CDS_TransferFailed(IBorrowing.AssetName.WrsETH);
}
Liquidations have occurred during the deposit of the dCDS user, with some taking place on another chain. To cover the share amount owed to the depositor, additional ETH collateral must be retrieved from the other chain.
The shares are calculated based on global values, making it possible that collateral from the other chain is required.
- A normal dCDS depositor attempts to withdraw their position, which is opted in for liquidations.
- The transaction reverts because the ETH collateral has not yet been received from the other chain, and the current chain does not have enough to cover the owed amount.
The transaction reverts, preventing the user from withdrawing their funds from the dCDS.
interfaces.globalVariables.oftOrCollateralReceiveFromOtherChains{value: msg.value - params.fee}(
IGlobalVariables.FunctionToDo(
// Call getLzFunctionToDo in cds library to get, which action needs to do in dst chain
getLzFunctionToDo(params.optionsFeesToGetFromOtherChain, collateralToGetFromOtherChain)
),
IGlobalVariables.USDaOftTransferData(
address(interfaces.treasury), params.optionsFeesToGetFromOtherChain
),
IGlobalVariables.CollateralTokenTransferData(
- address(interfaces.treasury)
+ address(msg.sender)
ethAmountFromOtherChain,
weETHAmountFromOtherChain,
rsETHAmountFromOtherChain
),
IGlobalVariables.CallingFunction.CDS_WITHDRAW,
msg.sender
);
if (rsETHAmountFromOtherChain > 0) {
interfaces.treasury.wrapRsETH(rsETHAmountFromOtherChain);
}
- params.ethAmount = ethAmountFromOtherChain + params.ethAmount;
- weETHAmount = weETHAmountFromOtherChain + weETHAmount;
- rsETHAmount = rsETHAmountFromOtherChain + rsETHAmount;
As showed above, modify the receiver and don't include the amount required to be transferred from the other chain in the one we will transfer in the current transaction.