LendeXe

CertiKFiles named in the report

CertiK reviewed LendeXe Finance (LendeXe) on 28 Apr 2023: 28 findings, 1 critical; 25 marked fixed, 3 open.

1 critical finding, fixed
Open the reportOpen LendeXe Finance’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings28

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

SeverityOpenAcknowledgedFixed
Critical001
High305
Medium004
Low0015
CriticalIncorrect Function VisibilityFixed

The visibility of the function `transfer()` is public, which allows anyone to transfer tokens from the `from` address to the `to` address. The visibility of the function `approve()` is public, which allows anyone to set allowance of `spender` over the `owner's` token. The visibility of the function `spendAllowance()` is public, which allows anyone to update the `owner's` allowance for `spender` based on the spent `amount`.

HighCentralization Risks in the Function `fixBadAccruFixed

In the history of the compound protocol, the calculation of the Comp rewards becomes incorrect after proposal 062(`Split COMP rewards distribution and bug fixes`) is executed. In response to this problem, the function `fixBadAccruals()` has been temporarily added in proposal 065(`Correct Over-Accrued COMP`) and removed later. The function `fixBadAccruals()` is a centralized function. It will update the users' unclaimed Comp rewards to decrease the incorrect rewards, and record the COMP debt in the variable `compReceivable` for the users whose unclaimed Comp rewards are not enough to decrease. The function `fixBadAccruals()` is only used to handle the error caused by this upgrade migration in the Compound protocol, which is useless in the newly deployed Compound forks. Refer to: - https://compound.finance/governance/proposals/62 - https://compound.finance/governance/proposals/65

HighLack of Access Control Over `_setBorrowCapGuardiaFixed

The function `_setSwapHelperAddress()` is `external` and can be called by anyone as long as the contract is deployed.

HighLack of Access Control Over `_setSwapRouter()` FuFixed

The function `_setSwapRouter()` is `external` and can be called by anyone as long as the contract is deployed.

HighLack Validation For `nftAmount`Fixed

The input variable `nftAmount` is not validated in the function `supply()`, so the user can input the arbitrary `nft` amount to mint the maximum allowed XSD tokens.

HighPotential Arbitrage AttackFixed

The user can provide assets by calling the function provide() in the contract `UltimateLoan` and then directly call the function `burn()`/`burnShares()` in the contract `XSDStabilizer` to perform an arbitrage attack. The steps can be as follows, 1. call function `supply()` to lock xLEXE tokens as collateral, then receive XSD tokens, which is 150% of locked XLEXE tokens in value. 2. call the function `burn()`/`burnShares()` in the contract `XSDStabilizer` instead of the function `repay()` to burn XSD tokens will eventually receive 50% of the locked XLEXE tokens value in profit. ```solidity=240 (uint256 mintAmount, ) = stabilizer.mintShares(xsdAmount, supplyTokens); //UL will gain double the xsd amount that was set as input because it locks an equal amount of LEXE uint256 portionOfOwnerMint = mul_(mintAmount, Exp({mantissa: 0.75e18})); ``` In an extreme case, the user can give up repaying the loan, and receive 50% of the locked `xLEXE` tokens value in profit.

MediumInvalid ValidationFixed

The contract deployer can input himself as the admin to pass the validation. ```solidity=349 constructor( XSDStabilizer XSDStabilizer_, address lexeVault_, address payable admin_, Lexe lexe_, XToken xLEXE_, XToken[] memory supplyTokens_, uint256[] memory supplyTokenShares_, XSDInterface xsd_, PriceOracle oracle_ ) public { // Creator of the contract is admin during initialization admin = msg.sender; require(msg.sender == admin, "only admin may initialize the UL"); // Set initial exchange rate ULPercentage = uint256(50); stabilizer = XSDStabilizer(XSDStabilizer_); ... ```

MediumLack Of Input Validation for `lockEndBlocks`Fixed

There is no validation to ensure the `lockEndBlocks[i]` is larger than the `_lockingPeriodEndBlock`.

MediumLack of Sanity Check For `lockTime`Fixed

There's no sanity check to validate if a `lockTime` is existing. If the `lockTime` does not exist, the user who locked assets will not get the rewards.

MediumPotential Unable to Mint XSD TokensFixed

The user has locked their `xLEXE` tokens in the contract `UltimateLoanLock` for at least 3 months, with the expectation of minting `XDS` tokens through the function `supply()` in the contract `UltimateLoan` at the end of the lock period. However, if XDS's market share exceeds a certain threshold after three months, they may be unable to mint additional `XDS` tokens. - Contract `XSDStabilizer.sol` ```solidity=242 require( getHypotheticalLendexeShare(amount) < 0.6e18, "Market share of XSD is too high!" );

Show 10 more
Low`lexeVault` Address Can Acquire `xLEXE`Fixed

The `liquidate()` function calls the `xLEXE.transferFrom()` function with the `to` address specified as `lexeVault` for acquiring the `xLEXE` tokens. As a result, over time the `lexeVault` address will accumulate a significant portion of `xLEXE` tokens. If the `lexeVault` is an EOA (Externally Owned Account), the mishandling of its private key can have devastating consequences for the project as a whole.

LowDelete `poolShares`Fixed

In the function `liquidateStabilizerPool`, the `poolShares` is possibly deleted if the length of the array `_poolShares` is 1, and there is no function provided in the contract to reset the `poolShares`. Therefore, the function `liquidateStabilizerPool` will execute fail in the next call. We would like to confirm with the customer whether it is as intended.

LowDuplicated AssignmentFixed

`vt.lockEndBlock` is assigned the same value twice. ```solidity=111 for (uint256 i = 0; i < _lockTimes.length; i++) { uint8 distributionTime = uint8(totalSupply[i] / tokensPerDay); VestingModel storage vt = vestingModels[_lockTimes[i]]; vt.lockEndBlock = lockEndBlocks[i]; vt.totalSupply = totalSupply[i]; vt.lockEndBlock = lockEndBlocks[i]; vt.distributionTime = distributionTime; } ```

LowLogic Issue Of Function `setSupplyTokens()`Fixed

The array `supplyTokens` is not reset before new supply tokens are pushed, so it is impossible to update the supply tokens and shares if the function is called again. ```solidity=87 supplyTokens.push( supplyToken({ token: supplyTokens_[i], share: Exp({mantissa: supplyTokenShares_[i]}) }) ); ```

LowLogical issue on Function `calculateTokenPortionsFixed

The function `calculateTokenPortions()` in the contract `LockDrop` should be called successfully daily. Otherwise, users who locked assets will potentially lose their rewards. We want to check with the team for more detail about the mechanism that can ensure the function `calculateTokenPortions()` run successfully daily.

LowMissing Error MessagesFixed

The **require** can be used to check for conditions and throw an exception if the condition is not met. It is better to provide a string message containing details about the error that will be passed back to the caller.

LowMissing Import FileFixed

`EIP20NonStandardInterface` and `EIP20Interface` are used in the contract `XSDStabilizer`, but the import is missing.

LowMissing Validation for Array LengthFixed

There is no validation between `_lockTimes.length` , `lockEndBlocks.length` ,`totalSupply.length` and `_xTokenAddresses.length` in constructor. And there is no validation between `amounts.length` and `_xTokenAddresses.length` in function `lockAssets()`.

LowPotential Sandwich AttacksFixed

A sandwich attack may happen when an attacker observes a transaction swapping tokens or adding liquidity without setting restrictions on slippage or minimum output amount. The attacker can manipulate the exchange rate by frontrunning (executing before the target) a transaction to purchase one of the assets and make profits by backrunning (executing after the target) a transaction to sell the asset. The following functions are called without setting restrictions on slippage or minimum output amount, so transactions triggering these functions are vulnerable to sandwich attacks, especially when the input amount is large: - `swapRouter.exactInputSingle(ISwapRouter.ExactInputSingleParams()`

LowPotential Sandwich AttacksFixed

A sandwich attack might happen when an attacker observes a transaction swapping tokens or adding liquidity without setting restrictions on slippage or minimum output amount. The attacker can manipulate the exchange rate by frontrunning (before the transaction is attacked) a transaction to purchase one of the assets and make profits by backrunning (after the transaction is attacked) a transaction to sell the asset. The following functions are called without setting restrictions on slippage or minimum output amount, so transactions triggering these functions are vulnerable to sandwich attacks, especially when the input amount is large: - `swapRouter.exactInputSingle()` - `swapRouter.exactInput()`

Show 5 more
LowPotential Unable To Burn XSD TokensFixed

If the supply token status is marked as `BLACKLISTED` or `BANNED`, users are possibly unable to burn the XDS tokens to get the supply tokens.

LowSafeMath Not UsedFixed

These expressions in the contracts do not check arithmetic overflow. Such unsafe math operations may cause unexpected behavior if unusual parameters are given.

LowTransfer Balance Is Not Properly CalculatedFixed

The transfer amount of `xwbtc`/`supplyToken` from the `XSDStabilizer` contract is not properly calculated, this will transfer all `xwbtc`/`supplyToken` left in the current contract to the recipient. The token balance should be checked before and after the transfer. ```solidity=665 xwbtc.mint(underlyingSupplyTokenBalance); xwbtc.transfer( address(xsdInterface), xwbtc.balanceOf(address(this)) ); ``` ```solidity=682 supplyToken.mint(underlyingSupplyTokenBalance); supplyToken.transfer( address(xsdInterface), supplyToken.balanceOf(address(this)) ); ```

LowUnchecked Return ValueFixed

The linked functions invocations do not check the return value of the function call which returns a value in case of a proper call.

LowUnreasonable Fee CalculationFixed

As per the fee calculation logic, if the amount is less than 1, all the locked assets amount will be charged as fees. ```solidity=157 uint256 actualAmount = (amount * 99) / 100; uint256 fee = amount - actualAmount; ```

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 (8)

  • SwapHelper.sol
  • XSD.sol
  • XSDInterface.sol
  • UltimateLoanLock.sol
  • IUniswapV3SwapCallback.sol
  • UltimateLoan.sol
  • XSDStabilizer.sol
  • ISwapRouter.sol

Part of LendeXe Finance’s security record

LendeXe Finance
Audits1 audit by 1 firmLast audit Apr 2023, by CertiK.
Critical findingsNo open critical findingsIn that report, 1 critical finding was reported. It is marked fixed by the firm that reported it.
Open LendeXe 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