function fulfillSwap(
address user,
uint128 requestId,
bytes calldata priceData,
address engine
)
external
onlyRegisteredSystemKeepers
{
UsdTokenSwapConfig.SwapRequest storage request = UsdTokenSwapConfig.load().swapRequests[user][requestId];
if (request.processed) {
revert Errors.RequestAlreadyProcessed(user, requestId);
}
FulfillSwapContext memory ctx;
ctx.deadline = request.deadline;
if (ctx.deadline < block.timestamp) {
revert Errors.SwapRequestExpired(user, requestId, ctx.deadline);
}
request.processed = true;
MarketMakingEngineConfiguration.Data storage marketMakingEngineConfiguration =
MarketMakingEngineConfiguration.load();
ctx.vaultId = request.vaultId;
Vault.Data storage vault = Vault.loadLive(ctx.vaultId);
ctx.usdToken = UsdToken(marketMakingEngineConfiguration.usdTokenOfEngine[engine]);
StabilityConfiguration.Data storage stabilityConfiguration = StabilityConfiguration.load();
ctx.priceX18 = stabilityConfiguration.verifyOffchainPrice(priceData);
ctx.amountIn = request.amountIn;
ctx.amountOutBeforeFeesX18 = getAmountOfAssetOut(ctx.vaultId, ud60x18(ctx.amountIn), ctx.priceX18);
(ctx.baseFeeX18, ctx.swapFeeX18) = getFeesForAssetsAmountOut(ctx.amountOutBeforeFeesX18, ctx.priceX18);
ctx.asset = vault.collateral.asset;
Collateral.Data storage collateral = Collateral.load(ctx.asset);
ctx.amountOut =
collateral.convertUd60x18ToTokenAmount(ctx.amountOutBeforeFeesX18.sub(ctx.baseFeeX18.add(ctx.swapFeeX18)));
ctx.minAmountOut = request.minAmountOut;
if (ctx.amountOut < ctx.minAmountOut) {
revert Errors.SlippageCheckFailed(ctx.minAmountOut, ctx.amountOut);
}
ctx.protocolSwapFeeX18 = ctx.swapFeeX18.mul(ud60x18(marketMakingEngineConfiguration.totalFeeRecipientsShares));
ctx.protocolReward = collateral.convertUd60x18ToTokenAmount(ctx.baseFeeX18.add(ctx.protocolSwapFeeX18));
vault.marketsRealizedDebtUsd -= int128(ctx.amountIn);
ctx.usdToken.burn(ctx.amountIn);
IERC20(ctx.asset).safeTransferFrom(vault.indexToken, address(this), ctx.amountOut + ctx.protocolReward);
marketMakingEngineConfiguration.distributeProtocolAssetReward(ctx.asset, ctx.protocolReward);
IERC20(ctx.asset).safeTransfer(user, ctx.amountOut);
emit LogFulfillSwap(
user,
requestId,
ctx.vaultId,
ctx.amountIn,
ctx.minAmountOut,
request.assetOut,
ctx.deadline,
ctx.amountOut,
ctx.baseFeeX18.intoUint256(),
ctx.swapFeeX18.intoUint256(),
ctx.protocolReward
);
}
CEI pattern not followed.
No Reentrancy Guard.
The keeper might interact with a malicious ERC20 token. This might reenter the contract and drain out funds.
More than one valid withdrawals.
Loss of funds from the vault.