xWin Finance (Audit 2)

CertiKFiles named in the report

CertiK reviewed xWIN Finance (xWin Finance (Audit 2)) on 13 Jun 2023: 66 findings, 2 critical; 57 marked fixed, 1 acknowledged, 8 open.

2 critical findings, fixed
Open the reportOpen xWIN Finance’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings66

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical002
High015
Medium009
Low8041
LowCheck Price Fluctuations Before ReTradeOpen

The `systemReTrade()` function can be executed when there is a remaining buy or close-buy, but the `systemReTrade()` function does not take into account price fluctuations before reTrade. This can lead to a situation where the function is executed without considering the current market conditions, potentially resulting in unfavorable trades. ```solidity function systemReTrade() external onlyExecutor nonReentrant { TradeQueue memory tr = tradeQueue; require(tr.nextTrade && tr.nextTradeBlock <= block.number, "not allow to retrade yet"); if(tr.nextTrade){ _systemTrade(tr.tradeType); } } ```

LowDivide Before MultiplyOpen

Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=125 uPerBlock = uPerBlock / (blocksPerDay * 365); ``` ```solidity=133 pendingMFee = pendingMFee + (totalblock * uPerBlock); ``` --- ```solidity=128 uint256 uPerBlockPlatform = supply / (10000 - platformFee) * 10000; ``` --- ```solidity=130 uPerBlockPlatform = uPerBlockPlatform / (blocksPerDay * 365); ``` ```solidity=134 pendingPFee = pendingPFee + (totalblock * uPerBlockPlatform); ``` --- ```solidity=225 uint256 redeemratio = amount * 1e18 / getFundTotalSupply(); ``` ```solidity=229 uint256 entitled = redeemratio * totalBase / 1e18; ``` --- ```solidity=262 uint256 redeemratio = redeemUnit * 1e18 / getFundTotalSupply(); ``` ```solidity=265 uint256 totalOutput = redeemratio * getBalance(baseCcy) / 1e18; ``` --- ```solidity=675 uint256 ratioNoReg = notRecognizedShare * 10000 / _shares; ``` ```solidity=680 uint256 partialProfit = ratioNoReg * (profitPerUnit / _unitprice) * swapOutput / 10000; ``` --- ```solidity=687 uint256 anotherProfit = ratioReg * profitPerUnit / _unitprice * swapOutput / 10000; ``` --- ```solidity=266 uint redeemratio = _shares * 1e18 / totalSupply(); ``` ```solidity=272 withdrawStable = redeemratio * stableBal / 1e18; ``` --- ```solidity=93 uint amountPerblock = stablecoinBal / swapDuration; ``` ```solidity=94 uint toSwapQty = amountPerblock * blockDiff; ``` --- ```solidity=186 uint redeemratio = _shares * 1e18 / totalSupply(); ``` ```solidity=187 uint totalRefund = stableBalB4 * redeemratio / 1e18; ``` --- ```solidity=202 uint redeemratio = _shares * 1e18 / totalSupply(); ``` ```solidity=203 uint totalRefund = stableBalB4 * redeemratio / 1e18; ``` --- ```solidity=314 uint redeemratio = (_shares * 1e18) / totalSupply(); ``` ```solidity=320 withdrawStable = (redeemratio * stableBal) / 1e18; ``` --- ```solidity=358 uint256 estimatedDailyReward = emmission * blocksPerDay * user.shares / totalShares; ``` ```solidity=359 estimatedDailyRate = estimatedDailyReward * 1e18 / user.amount; ``` --- ```solidity=369 uint256 estimatedReward = emmission * 365 * blocksPerDay * user.lockedShares / totalLockedShares; ``` ```solidity=370 apr = estimatedReward * 10000 / user.amount; ``` --- ```solidity=381 newShares = _amount * totalShares / totalXWINBalance(); ``` ```solidity=385 uint256 estimatedDailyReward = emmissionCompound * blocksPerDay * newShares / (totalShares + newShares); ``` --- ```solidity=392 uint256 estimatedReward = emmissionBonus * 365 * blocksPerDay * newLockedShares / (totalLockedShares + newLockedShares); ``` ```solidity=394 estimatedBonusApr = estimatedReward * 10000 / _amount; ``` --- ```solidity=178 uint256 cakeReward = multiplier * xwinPerBlock * pool.allocPoint / totalAllocPoint; ``` ```solidity=179 accCakePerShare = accCakePerShare + (cakeReward * 1e18 / lpSupply); ``` --- ```solidity=225 uint256 cakeReward = multiplier * xwinPerBlock * pool.allocPoint / totalAllocPoint; ``` ```solidity=229 pool.accCakePerShare = pool.accCakePerShare + (cakeReward * 1e18 / lpSupply); ``` --- ```solidity=394 uint xwinReward = xwinPerBlock * pool.allocPoint / totalAllocPoint; ``` ```solidity=395 uint accCakePerShare = xwinReward * 1e18 / lpBal; ``` --- ```solidity=403 uint256 userRatio = user.amount * 1e18 / pool.totalDeposit; ``` ```solidity=404 uint256 rewardPerBlockUser = xwinPerBlock * pool.allocPoint * userRatio / totalAllocPoint; ``` --- ```solidity=107 uint256 redeemratio = _amount * 1e18 / totalSupply(); ``` ```solidity=110 uint totalRefund = redeemratio * targetBalB4 / 1e18; ``` --- ```solidity=149 uint balanceStakingTokenInUSD = balanceStakingToken == 0 ? 0 : (balanceStakingToken * exchangeRateStaking / 1e18) * exRateTargetBase / getDecimals(address(targetToken)); ``` --- ```solidity=168 uint accruedCompInUSD = exRateLendingBase * accruedComp / getDecimals(address(stablecoinUSDAddr)); ``` ```solidity=169 uint percentage = accruedCompInUSD * 10000 / fundValueInUSD; ``` --- ```solidity=107 uint256 redeemratio = _amount * 1e18 / totalSupply(); ``` ```solidity=110 uint totalRefund = redeemratio * targetBalB4 / 1e18; ``` --- ```solidity=149 uint balanceStakingTokenInUSD = balanceStakingToken == 0 ? 0 : (balanceStakingToken * exchangeRateStaking / 1e18) * exRateTargetBase / getDecimals(address(targetToken)); ``` --- ```solidity=168 uint accruedCompInUSD = exRateLendingBase * accruedComp / getDecimals(address(stablecoinUSDAddr)); ``` ```solidity=169 uint percentage = accruedCompInUSD * 10000 / fund

LowImplementation of Price Oracle for UniswapV3Open

In the `xWinSwapV3` contract, the `internalSwap` currently uses `priceMaster.getPrice()` to fetch the price for token swaps on Uniswap V3. However, it's not clear which price oracle is being used behind this operation since the codebase currently doesn't seem to include a contract or logic implementing a Time-Weighted Average Price (TWAP) oracle for the Uniswap v3 pool.

LowNon-guaranteed Reward DistributionOpen

The `safeTokenTransfer()` function transfers the reward token to users. Users may receive fewer reward tokens than they are entitled to according to the code `_amount = bal < _amount ? bal : _amount`. However, there is no way to enable users to claim their missing tokens at a later time. ```solidity=216 function safeTokenTransfer(IERC20Upgradeable rewardToken, address _to, uint256 _amount) internal { uint bal = rewardToken.balanceOf(address(this)); _amount = bal < _amount ? bal : _amount; uint devFeeTotal = _amount * devFee / 10000; rewardToken.safeTransfer(devAddress, devFeeTotal); rewardToken.safeTransfer(_to, _amount - devFeeTotal); } ```

LowPotential Price Manipulation To Enforce A SystemOpen

The function `performUpkeep()` performs a trade if `getTriggerStopLoss() == true`, where `getTriggerStopLoss()` checks the current price of `targetToken` and compares it with `stopLossPrice`. ```solidity function performUpkeep(bytes calldata /*performData*/) external override { require(getTriggerStopLoss(), "no stop loss condition found"); ... _systemTrade(1); } ``` ```solidity function getTriggerStopLoss() public view returns (bool) { if ((targetToken.balanceOf(address(this)) > 0) && ReadyToTrade()){ uint rate = xWinPriceMaster.getPrice(address(targetToken), address(stablecoin)); return rate <= stopLossPrice; } return false; } ``` As the oracle used in `xWinPriceMaster.getPrice()` may be susceptible to price manipulations, any user can force a system trade.

LowPotential Rounding Error In `calculateFavor()`Open

The `calculateFavor()` function may contain a rounding error, particularly during the calculation of `tmp`. This is because the values of amount and `diff` could potentially be smaller than `1e18`, which may lead to truncation of the decimal places during integer division, resulting in a loss of precision. ```solidity=341 function calculateFavor(uint256 amount, uint256 startTimestamp, uint256 endTimeStamp) private pure returns (uint256 favor) { uint256 diff = 1 + (endTimeStamp - startTimestamp) / 86400; if (diff > 365) { diff = 365; } uint256 tmp = amount * diff * diff / 1e18; tmp = (Babylonian.sqrt(tmp) * 1e18) * 5000 / 81600; favor = tmp / 1e18; if (favor > 5000) { favor = 5000; } } ```

LowPotential Unintended Revert In `_convertFrom18()`Open

In the `_convertFrom18()` and `_convertTo18()` functions, if the token used in these two functions has more than 18 decimals, the functions will unintentionally revert. ```solidity function _convertFrom18(uint value, address token) internal view returns (uint){ if(value == 0) return 0; uint diffDecimal = 18 - ERC20Upgradeable(token).decimals(); return diffDecimal > 0 ? (value / (10**diffDecimal)) : value; } ``` ```solidity function _convertTo18(uint value, address token) internal view returns (uint){ uint diffDecimal = 18 - ERC20Upgradeable(token).decimals(); return diffDecimal > 0 ? (value * (10**diffDecimal)) : value; } ```

LowRecommendation for Chainlink Price OracleOpen

The `xWinPriceMaster` contract will utilize the Chainlink Price Oracle for specific tokens. The audit team recommends clients carefully validate the `price` and `updatedAt` results. The best practice for Chainlink Price Oracle can be to configure the application to detect and respond to extreme price volatility or prices outside your acceptable limits. For example, the data feed aggregator includes both `minAnswer` and `maxAnswer` values. These variables prevent the aggregator from updating the `latestAnswer` outside the agreed range of acceptable values. If the actual is lower than `minAnswer` or higher than `maxAnswer`, the chainlink oracle will not be accurate anymore.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (25)

  • contracts/fundV2Factory.sol
  • contracts/UniSwapV2TWAPOracle.sol
  • contracts/strategies/xWinDollarCostAverage.sol
  • contracts/xWinSystemExecutor.sol
  • contracts/xWinStrategyInteractor.sol
  • contracts/Library/TransferHelper.sol
  • contracts/strategies/xWinBBMA.sol
  • contracts/strategies/xWinERC20Alpha.sol
  • contracts/xWinStrategyWithFee.sol
  • contracts/xWinStrategy.sol
  • contracts/xWinSwapV3.sol
  • contracts/strategies/xWinSingleAssetAave.sol
  • contracts/strategies/xWinSingleAsset.sol
  • contracts/xWinEmitEvent.sol
  • contracts/strategies/xWinBuddyChef.sol
  • contracts/xWinSwap.sol
  • contracts/strategies/xWinSingleAssetAlpaca.sol
  • contracts/strategies/xWinMasterChef.sol
  • contracts/strategies/xWinSingleAssetOla.sol
  • contracts/Library/xWinLib.sol
  • contracts/strategies/xWinIRT.sol
  • contracts/fundV2.sol
  • contracts/xWinPriceMaster.sol
  • contracts/xWinSplitFeeWallet.sol
  • contracts/strategies/xWinLockedStake.sol

Part of xWIN Finance’s security record

xWIN Finance
Audits2 audits by 1 firmLast audit Jun 2023, by CertiK.
Critical findingsNo open critical findingsAcross the 2 reports, 2 critical findings were reported. Each is marked fixed by the firm that reported it.
IncidentIncident 24 Jun 2021Source: De.Fi incident database.
Open xWIN Finance’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us