The initialSwap function can revert because of a division by zero.
When the function initialSwap is called in order to compute the amount out the function call getPremiumDiscountFactor
UsdTokenSwapConfig:93 this function returns the premium or discount to be applied to the amount out of a swap, based on the vault's debt and the system configured premium / discount curve parameters..
You can run this POC by copy pasting this code in the performUpkeep.t.sol in the usd-token-swap folder of the integration folder in the test and run forge test --mt test_divisionByZero
function test_divisionByZero(
)
external
givenInitializeContract
{
uint256 vaultId = 0;
uint256 assetsToDeposit=0;
VaultConfig memory fuzzVaultConfig = getFuzzVaultConfig(vaultId);
TestFuzz_GivenCallPerformUpkeepFunction_Context memory ctx;
ctx.assetsToDeposit = bound({ x: assetsToDeposit, min: 1e18, max: fuzzVaultConfig.depositCap });
deal({
token: address(fuzzVaultConfig.asset),
to: fuzzVaultConfig.indexToken,
give: fuzzVaultConfig.depositCap
});
ctx.usdTokenSwapKeeper = usdTokenSwapKeepers[fuzzVaultConfig.asset];
changePrank({ msgSender: users.owner.account });
UsdTokenSwapKeeper(ctx.usdTokenSwapKeeper).setForwarder(users.keepersForwarder.account);
marketMakingEngine.configureSystemKeeper(ctx.usdTokenSwapKeeper, true);
changePrank({ msgSender: users.naruto.account });
ctx.mockPrice = IPriceAdapter(fuzzVaultConfig.priceAdapter).getPrice().intoUint256();
UD60x18 assetAmountX18 = ud60x18(IERC4626(fuzzVaultConfig.indexToken).totalAssets());
ctx.amountInUsd = assetAmountX18.mul(ud60x18(ctx.mockPrice)).intoUint256();
deal({ token: address(usdToken), to: users.naruto.account, give: ctx.amountInUsd });
IERC20(usdToken).approve(address(marketMakingEngine), ctx.amountInUsd);
ctx.minAmountOut = 0;
initiateUsdSwap(fuzzVaultConfig.vaultId, ctx.amountInUsd, ctx.minAmountOut);
bytes memory mockSignedReport = getMockedSignedReport(fuzzVaultConfig.streamId, ctx.mockPrice);
bytes memory performData = abi.encode(mockSignedReport, abi.encode(users.naruto.account, 1));
ctx.requestId = 1;
UsdTokenSwapConfig.SwapRequest memory request =
marketMakingEngine.getSwapRequest(users.naruto.account, ctx.requestId);
ctx.amountOut = marketMakingEngine.getAmountOfAssetOut(
fuzzVaultConfig.vaultId, ud60x18(ctx.amountInUsd), ud60x18(ctx.mockPrice)
);
(ctx.baseFeeX18, ctx.swapFeeX18) =
marketMakingEngine.getFeesForAssetsAmountOut(ctx.amountOut, ud60x18(ctx.mockPrice));
ctx.amountOutAfterFee =
convertUd60x18ToTokenAmount(fuzzVaultConfig.asset, ctx.amountOut.sub(ctx.baseFeeX18.add(ctx.swapFeeX18)));
ctx.protocolSwapFee = ctx.swapFeeX18.mul(ud60x18(marketMakingEngine.exposed_getTotalFeeRecipientsShares()));
ctx.protocolReward =
convertUd60x18ToTokenAmount(fuzzVaultConfig.asset, ctx.baseFeeX18.add(ctx.protocolSwapFee));
vm.expectEmit({ emitter: address(marketMakingEngine) });
emit StabilityBranch.LogFulfillSwap(
users.naruto.account,
ctx.requestId,
fuzzVaultConfig.vaultId,
request.amountIn,
request.minAmountOut,
request.assetOut,
request.deadline,
ctx.amountOutAfterFee,
ctx.baseFeeX18.intoUint256(),
ctx.swapFeeX18.intoUint256(),
ctx.protocolReward
);
changePrank({ msgSender: users.keepersForwarder.account });
UsdTokenSwapKeeper(ctx.usdTokenSwapKeeper).performUpkeep(performData);
deal({
token: address(fuzzVaultConfig.asset),
to: fuzzVaultConfig.indexToken,
give: fuzzVaultConfig.depositCap
});
deal({ token: address(usdToken), to: users.naruto.account, give: ctx.amountInUsd });
IERC20(usdToken).approve(address(marketMakingEngine), ctx.amountInUsd);
ctx.minAmountOut = 0;
initiateUsdSwap(fuzzVaultConfig.vaultId, 1, 0);
}