Lendle
Smart Contract Audit Report
Audit Summary
Lendle is releasing a new decentralized lending platform where users can borrow assets after depositing collateral. Users can additionally earn rewards by staking $LEND tokens.
For this audit, we reviewed the following contracts on the Mantle Mainnet:
- AToken contract at 0xeC3414058620E118D2258F8D9765F6c8b8320694
- StableDebtToken contract at 0xd8A36c0E6148fFB374C6726d4c60Bbd55B745407
- VariableDebtToken contract at 0xB3f838d219A0cFba73193453C2023090277d6Af5
- LendleToken contract at 0x25356aeca4210eF7553140edb9b8026089E49396
- ChefIncentivesController implementation contract at 0x201673Fe5b93d92B9a036Cb4e2E444D352413B32 behind its proxy contract at 0x79e2fd1c484EB9EE45001A98Ce31F28918F27C41
- TokenVesting contract at 0xA7f784Dc0EC287342B0B84e63961eFfA541f7E6f
- MultiFeeDistribution implementation contract at 0xfA12Aaa98bB6f301B5a95383E4F43d5873dE522b behind its proxy contract at 0x5C75A733656c3E42E44AFFf1aCa1913611F49230
- MasterChef implementation contract at 0x2186a94dF0f06a378999c22CFb6A160B798Cd56B behind its proxy contract at 0xC90C10c7e3B2F14870cC870A046Bd099CCDDEe12
- MerkleDistributor contract at 0xB57f32d28E098Cd2d72EAFc7a4ECfC54F3589296
- StakingConfigurator implementation contract at 0xf4e565954356Ff2b32C792Bd685b989A03528B2f behind its proxy contract at 0xE5F9fFc0D0D70EED59364b44B1F11900B39dB37B
- LendingPool implementation contract at 0x13e9761c037f382472cE765556c3dA2aF29d9EC7 behind its proxy contract at 0xCFa5aE7c2CE8Fadc6426C1ff872cA45378Fb7cF3
- WETHGateway contract at 0xEc831f8710C6286a91a348928600157f07aC55c2
- LendingPoolCollateralManager contract at 0x7D350354Dd9D1E48Ab1810f1F1b139309e9394Cc
- LendingPoolConfigurator implementation contract at 0xFE91d9901DfaAF939A3bb8b444f5E141bb7dD0c1 behind its proxy contract at 0x30d990834539e1ce8be816631b73a534e5044856
- AaveOracle contract at 0x870c9692Ab04944C86ec6FEeF63F261226506EfC
- LendingPoolAddressesProvider contract at 0xAb94Bedd21ae3411eB2698945dfCab1D5C19C3d4
- LendingPoolAddressesProviderRegistry contract at 0xb92Bffee2DE49B6e87Ef3260337B676a2811b868
- LendingRateOracle contract at 0xc7F65C6b94A8A1C0977add58b6799ad456D72392
- PythPriceFeed contract at 0x7099bB3a72B3ed19509ccF844Fbaf33001fEA137
Audit Findings
No findings were identified, though some centralized aspects are present.
Date: September 8th, 2023.
Updated: September 25th, 2023 to reflect deployment on Mantle Mainnet.
Contracts Overview
AToken Contract:StableDebtToken Contract:
- aTokens are interest bearing tokens that correspond to an underlying asset. For example an lvWETH aToken would correspond to WETH.
- aTokens are minted to the user when they deposit the underlying asset into a Lending Pool.
- When withdrawing from the Lending Pool, the tokens are burned and the corresponding asset is returned to the user.
- When tokens are minted, burned, or transferred, rewards are minted as Staking Tokens in a MultiFeeDistribution contract. The amount of rewards earned is dependent on the amount of tokens and the time since rewards were last earned.
- The Lending Pool uses this contract to mint tokens as interest to the Treasury and Second Treasury addresses; 80% of the interest is sent to the Treasury and the remaining 20% is sent to the Second Treasury.
- This contract complies with the ERC-20 standard.
- Other than the ERC20 and EIP2612 functions, all functions that affect the state of the token can only be called by the Lending Pool.
VariableDebtToken Contract:
- StableDebtTokens are created when a user borrows from the Lending Pool.
- As the name suggests, StableDebtTokens represent a borrowing position that has a stable interest rate.
- Tokens are minted when the borrower takes the position, and burned when they repay their position.
- When tokens are minted or burned, rewards are minted as Staking Tokens in a MultiFeeDistribution contract. The amount of rewards earned is dependent on the amount of tokens and the time since rewards were last earned.
- These tokens are modeled after the ERC20 standard, but do not fully implement the standard. As debt is non-transferable, there are no transfer functions for StableDebtTokens.
- Similar to the aToken, all non-ERC20 public functions that affect state are only able to be called by the Lending Pool.
ChefIncentivesController Contract:
- VariableDebtTokens are created when a user borrows from the Lending Pool.
- As the name suggests, VariableDebtTokens represent a borrowing position that has a variable interest rate.
- The variable interest rate is constantly changing based on the Utilization rate. The utilization rate is a measure of the liquidity of the corresponding asset existing in the Lending Pool.
- Tokens are minted when the borrower takes the position, and burned when they repay their position.
- When tokens are minted or burned, rewards are minted as Staking Tokens in a MultiFeeDistribution contract. The amount of rewards earned is dependent on the amount of tokens and the time since rewards were last earned.
- These tokens are modeled after the ERC20 standard, but do not fully implement the standard.
- As debt is non-transferable, there are no transfer functions for VariableDebtTokens.
- Similar to the aToken, all non-ERC20 public functions that affect state are only able to be called by the Lending Pool.
LendleToken Contract:
- This contract is used to provide rewards for IncentivizedERC20 token holders.
- It is configured in the LendingPoolConfigurator contract.
- Users are able to claim rewards which are minted in the MultiFeeDistribution contract and locked for a specified time; these earned rewards can be withdrawn before the lock time with a penalty.
- The claimable rewards are only minted upon claiming. Since there is a mint cap, it is possible that if the user has not claimed for a while, some claimable rewards will not be able to be minted if the mint cap is reached.
- Reward rates are variable and set by the owner on deployment. Each reward period may have a different rewards emission rate.
- Rewards are determined based on the time elapsed, how many allocation points are given to the pool, and the user's token balance.
- The owner can start the rewards and set the start time only once on deployment.
- The owner can change the allocation points for the token pools at any time.
- The owner can change the receiver of rewards for any user at any time.
- The team must exercise caution when adding tokens to avoid fee-on-transfer and ERC777-compliant tokens.
TokenVesting Contract:
- The total supply of the token is set on deployment.
- The minter address, which can be set only once, has the ability to mint tokens to any address as long as it does not result in the total supply exceeding the max total supply.
- There are no burning functionalities present, however a user can send their tokens to any burn address to reduce circulating supply, if desired.
- The contract complies with the ERC-20 token standard.
MultiFeeDistribution Contract:
- This contract takes a list of recipients and amounts and unlocks these amounts in a MultiFeeDistribution contract over time.
- User amounts vest linearly over the span of a custom duration specified by the owner. For example, if 10% of the duration has passed, 10% of funds will be available to claim.
- When funds are claimed in this contract, they are minted as unlocked tokens for the user in the MultiFeeDistribution contract.
- The owner must execute the start function in order for the vesting time to begin.
MasterChef Contract:
- This contract allows users to stake tokens to earn rewards in various tokens.
- Users must deposit staking tokens with a lock in order to earn rewards in the same staking tokens. Tokens that are locked may not be withdrawn until the locked period is over. Unlocked tokens do not earn rewards in staking tokens, but will still accrue other rewards.
- The lock duration is 13 weeks and cannot be changed.
- Earned rewards are able to be withdrawn with a penalty before the lock duration is over.
- Users are able to withdraw unlocked tokens and rewards at anytime.
- The owner can disable reward claiming at any time, which will
- The owner can set valid addresses for minters only once.
- The owner can to add a token as a reward at anytime.
- The owner can withdraw any non-staking and non-rewards token in the contract at any time.
- The team must exercise caution that the Staking Token used is not fee-on-transfer.
MerkleDistributor Contract:
- Users can stake designated tokens in the contract to earn rewards.
- Rewards are determined based on the time elapsed, how many allocation points are given to the pool, and how much the user has staked.
- Reward rates are variable and set by the owner on deployment. Each reward period may have a different rewards emission rate.
- Users are able to designate where their rewards are sent.
- Staking tokens are minted as rewards in an external MultiFeeDistribution contract as long as they do not exceed the max total amount that can be minted.
- Minted rewards are locked at a time set in the MultiFeeDistribution contract. They can be withdrawn before the lock time at a penalty.
- Rewards are claimed when both depositing and withdrawing, but they are not minted until claimed. Users can call the claim function to mint any pending rewards.
- The claimable rewards are only minted upon claiming. Since there is a mint cap, it is possible that if the user has not claimed for a while, some claimable rewards will not be able to be minted if the mint cap is reached.
- There is also the option to emergency withdraw which will send the user all of their deposited tokens without claiming any rewards.
- The owner can choose to start the rewards only once after the contract is deployed.
- The owner can add a new pool to the contract at any time. The team must exercise caution when adding tokens to avoid fee-on-transfer and ERC777-compliant tokens.
- The owner can change the allocation points for the token pools at any time.
- The owner can change the receiver of rewards for any user at any time.
StakingConfigurator Contract:
- The owner of this contract has the ability to create Claim Records with custom durations and amounts. A recipient is not specified.
- The duration must be at least 1 week, and cannot be modified after the Claim Record is created.
- Any user can redeem a Claim Record until its specified duration has passed.
- When a user makes a claim, the amount claimed is minted and locked in the MultiFeeDistribution contract.
- The total amount of claimable tokens cannot exceed the maximum claimable amount, which decreases over the course of a year to a base amount.
- Claim Record data is secured and verified through the implementation of a Merkle Tree.
PythPriceFeed Contract:
- This contract is used to maintain a list of various addresses used throughout the protocol.
- The owner may use this contract to update the implementation of a registered Proxy address at any time.
- The owner may update the MultiFeeDistribution, ChefIncentivesController, MasterChef, and ProtocolRevenueDistribution addresses at any time.
AaveOracle Contract:
- This contract uses Pyth to determine the current price of an asset.
- The oracle is considered to have failed to fetch an accurate price if there is a failed response, or the timeout period has passed since the last price fetch.
- In the event that Pyth is failing to return an accurate price, the contract defaults to the last good price that the contract received.
LendingPoolAddressesProvider Contract:
- This contract is used to track the price of various assets determined by Sources.
- The current asset price is determined by the price reported by the asset's Source.
- The Owner may set the Source for any asset at any time.
LendingPoolAddressesProviderRegistry Contract:
- This contract is used to maintain a list of various addresses used throughout the protocol.
- The owner may use this contract to update the implementation of a registered Proxy address at any time.
- The owner may update the address corresponding to a market ID at any time.
- The owner may update the LendingPool, LendingPoolConfigurator, LendingPoolCollateralManager, PriceOracle, LendingRateOracle, and StakingConfigurator addresses at any time.
- The owner may update a specified Pool's Pool Admin and Emergency Admin at any time.
LendingRateOracle Contract:
- This contract is used to maintain a list of LendingPoolAddressesProvider contracts.
- The owner may register and unregister an address as an AddressProvider at any time.
LendingPool Contract:
- This contract is used to track the borrow rate and liquidity rate for various assets.
- The owner may set the borrow rate for any asset at any time.
- The owner may set the liquidity rate for any asset at any time.
WETHGateway Contract:
- Users are able to deposit various supported tokens into this contract.
- Upon depositing, aTokens corresponding to the token are minted to the user. When withdrawing, these aTokens are burned.
- Users can also deposit and withdraw on behalf of another address, if desired. This means that aTokens can be minted to a different address upon deposit, and a different address can receive withdrawn funds.
- A user can borrow an amount of an asset from its reserve, provided that the user has deposited enough funds to be used as collateral.
- Users must manually set their funds to be used as collateral before borrowing. The collateralized loans are then locked and cannot be withdrawn.
- When borrowing, users either choose to use a stable or variable interest rate model, where corresponding StableDebtTokens or VariableDebtTokens will be minted to the borrower to track debt.
- Users can swap between interest rate models at any time.
- Funds have no deadline to be paid back, however more interest will be accrued as time passes. This can lead to a decrease in the loan's health factor, which is based on the liquidation threshold and the value of the borrowed assets.
- If the health factor drops below a certain threshold, another user can pay a portion of the funds borrowed and receive a portion of the collateral plus an additional percentage of the collateral as a bonus.
- The user performing the liquidation can select to receive the collateral aTokens or the actual underlying asset back.
- The health factor can be increased by either repaying borrowed funds or by depositing futher collateral.
- Users can specify a different address to receive debt, provided they have been given a credit delegation allowance. StableDebtTokens/VariableDebtTokens are then minted and burned to or from this different address.
- The owner of the LendingPoolAddressProvider contract can update this contract's LendingPoolCollateralManager address at any time.
- This contract also implements flashloan functionality, where anyone can borrow the all of the assets in any liquidity pool, as long as they are returned within the transaction. A premium of up to .09% of each pool used in the flash loan is charged as a fee.
- When depositing, borrowing, or taking out a flashloan, users are able to specify a referral address.
- A Price Oracle contract is used to calculate price data, and can be changed at any time by the Pool Admin of the LendingPoolAddressProvider address.
- The Lending Pool Configurator of the LendingPoolAddressesProvider contract has the ability to add new assets at any time, as long as the number of assets does not exceed 128.
- The owner of the LendingPoolAddressProvider contract has the ability to update the LendingPool contract implementation at any time.
LendingPoolConfigurator Contract:
- This contract allows users to wrap and unwrap the blockchain's native token to facilitate standard LendingPool functionality.
- The blockchain's native token will be wrapped when depositing and repaying, and unwrapped when withdrawing and borrowing from LendingPools.
- When repaying, any excess tokens will be returned to the user.
- The owner can withdraw any tokens or ETH from this contract at any time.
- A Pool Admin, defined in the LendingPoolAddressesProvider contract, has power to make various configuration updates to the lending pools through this contract.
- An Emergency Admin, also defined in the LendingPoolAddressesProvider contract, has the ability to pause all transactions through this contract.
- The Pool Admin and Emergency Admin roles can be changed at any time by the owner of the LendingPoolAddressesProvider contract.
- A Pool Admin has the ability to make any of the following changes at any time:
- Update the implementation of any aToken.
- Update the implementation of the StableDebtToken or VariableDebtToken.
- Allow or disallow any kind of borrowing on any reserve.
- Configure the loan to value ratio of an asset used as collateral.
- Update the bonus percentage earned when liquidating an asset.
- Update an asset's health factor threshold to enable liquidations.
- Activate or deactivate any reserve.
- Freeze or unfreeze any reserve, disallowing/allowing deposits, borrows, and rate swaps.
- Change a reserve's strategy contract used to generate interest.
- Update the reserve factor of an asset, which is the factor of how much of an asset is stored in a separate contract as reserve.
Audit Results
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Jump/Storage Write | N/A | PASS |
Centralization of Control |
|
WARNING |
Compiler Issues | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Ether/Token Theft | N/A | PASS |
Flash Loans | N/A | PASS |
Front Running | N/A | PASS |
Improper Events | N/A | PASS |
Improper Authorization Scheme | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | N/A | PASS |
Oracle Issues | N/A | PASS |
Outdated Compiler Version | N/A | PASS |
Race Conditions | N/A | PASS |
Reentrancy | N/A | PASS |
Signature Issues | N/A | PASS |
Sybil Attack | N/A | PASS |
Unbounded Loops | N/A | PASS |
Unused Code | N/A | PASS |
Overall Contract Safety | PASS |
Contract Source Summary and Visualizations
Name |
Address/Source Code |
Visualized |
AToken |
||
StableDebtToken |
||
VariableDebtToken |
||
LendleToken |
||
ChefIncentivesController |
||
TokenVesting |
||
MultiFeeDistribution |
||
MasterChef |
||
MerkleDistributor |
||
StakingConfigurator |
||
LendingPool |
||
WETHGateway |
||
LendingPoolCollateralManager |
||
LendingPoolConfigurator |
||
PythPriceFeed |
||
AaveOracle |
||
LendingPoolAddressesProvider |
||
LendingPoolAddressesProviderRegistry |
||
LendingRateOracle |
About SourceHat
SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!
Contact us today to get a free quote for a smart contract audit of your project!
What is a SourceHat Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.
How Do I Interpret the Findings?
Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:
- High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
- Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
- Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
- Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.