Lendroid Whalestreet security record

Ethereumlendroid.com

As of 3 Nov 2026, Lendroid Whalestreet has 2 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 26 Nov 2020. Those reports list no open critical findings.

Critical findings
No open critical findings
Across 2 reports.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Nov 2026. Trustblock does not review code.

Audits2

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Lendroid WhalestreetSecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com
Lendroid Protocol version 1.0Security firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com

Findings24

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

SeverityOpenAcknowledgedFixed
Critical000
High003
Medium019
Low407
HighGas Intensive Loop UsageLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

If a user stakes at epoch `X`, and claims his reward at epoch `Y` using `BasePool.unstakeAndClaim` or `BasePool.claim`, and if `Y` is higher enough than `X`, transactions will most likely throw due to running out of gas or will hit the block gas limit. As a result, users will lose all their rewards. This issue is due to the use of a loop inside `BasePool.earned` function that iterates over all previous epochs from `lastEpochRewardsClaimed` until the current epoch relative to the block timestamp. Note that `lastEpochRewardsClaimed's` default value is zero; hence, late stakers will face the same issue, since the iteration necessarily starts from epoch zero.

Open the report
HighIncorrect Series 2 RewardLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

Total rewards between epochs 85 and 336 is almost doubled of what it should be. According to the specification and documentation in the code, the total rewards between epochs 85 and 336 should be 21.6M, but it is currently set to 41,850,000: ``` // UNIV2SHRIMPPool.sol function totalRewardsInEpoch(uint256 epoch) … { . . . else if (epoch > 84 && epoch <= 336) { totalRewards = 41850000 * (10 ** 18);// 21.6 M } . . . } ```

Open the report
HighIncorrect Staking LogicLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

The users staked amount at epoch X will not allow them any reward at epoch Y if X < Y , meaning that their staked LP tokens are not accumulated when computing the rewards for each epoch.

Open the report
Medium`starttime` Value Is Inconsistent With SpecificationLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

Following the `$HRIMP` distribution per epoch table as stated in the specification, the start time is set to be December 4th, 2020. However, in `BasePool.sol` (L30), `starttime` is set to `HEARTBEATSTARTTIME`, which in turn, is equal to the `1602288000` timestamp (`2020-10-10 00:00:00 (UTC UTC +00:00)`). Hence, the start time in the code is inconsistent with the spec.

Open the report
MediumTotal Reward AdjustmentLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

In `totalRewardsInEpoch`, L88-92 adjusts `totalRewards` using a logic that is not currently documented and therefore could not be validated. As such, its effect is currently undetermined. As a consequence to this issue the total distributed reward in a series will be way less than the original `totalRewards` amounts since the number of epochs multiplied by the final reward value will be less than the original documented and set `totalRewards` values.

Open the report
MediumUndistributed Epoch Zero RewardLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

Epoch zero reward will never be distributed since `BasePool.stake` will be allowed to execute only after `starttime` where `starttime` is equal to `HEARTBEATSTARTTIME`. In the opposite `_currentEpoch` will return zero only if the block timestamp is lower than `HEARTBEATSTARTTIME`. At any given timestamp the stated condition will not be satisfied simultaneously, meaning that when staking will be allowed the current epoch will be epoch 1.

Open the report
MediumClashing Position HashLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

Added as part of the second report. position_hash() can be called for two or more separate positions with the same arguments. This method mitigates this possibility by adding a _nonce value but the validation is insufficient. Exploit Scenario: 1. User calls fill_kernel() once with _is_creator_lender set as true 2. open_position() is called with _kernel_creator = lender 3. L280 _nonce is validated for the wrangler and the creator (lender) 4. User calls fill_kernel() again with the same arguments but with _is_creator_lender set as false 5. open_position() is called with the same arguments except for _kernel_creator = borrower 6. L280 _nonce is validated for the wrangler and the creator (borrower), allowing for the same _nonce value to pass 7. This results in the position_hash being the same for both positions 8. Anything that relies on position_hash will be unable to distinguish between multiple positions

Open the report
MediumDebugging Code Is Present While Real Logic Is NotLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

L452, 453 In cancel_kernel, debugging code is active, rendering the function useless if deployed as it is.

Open the report
MediumIncorrect Logic When Calculating Filled or Cancelled Loan AmountLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

The same value self.kernels_filled[_kernel_hash] is being added twice, whereas the function should be taking the sum of the filled and cancelled loan amounts.

Open the report
MediumIncorrect Logic When Calculating Total InterestLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

L247 _position_duration_in_days is not taken into account when _total_interest is calculated. This doesn't match up with how loanAmountOwed is calculated in SimpleLoan.sol, the Solidity implementation that we used as a reference.

Open the report
Show 9 more
MediumIncorrect Logic When Cancelling KernelLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

L459 checks if collateralAmount - filledOrCancelledLoanAmount >= cancelledLoanTokenAmount. This should instead be loanAmountOffered - filledOrCancelledLoanAmount >= cancelledLoanTokenAmount according to the Solidity implementation found in Kernel.sol

Open the report
MediumLending and Borrowing Positions Can Be OverwrittenLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

borrow_positions (L209) and lend_positions (L211) can be unexpectedly overwritten because the indices (last_borrow_position_index and last_lend_position_index) are not incremented/decremented correctly. Exploit Scenario: 1. User borrows twice by calling open_position(). Assume the resulting _position_hashs are 0x123 and 0x456 2. Both positions get recorded in self.borrow_positions[_borrower]. eg ({1 : 0x123, 2: 0x456}) 3. At this point, self.last_borrow_position_index[_borrower] == 2 4. User closes position 0x123 by calling close_position() 5. remove_position() L217 gets called, decrementing self.last_borrow_position_index[_borrower] 6. At this point, self.last_borrow_position_index[_borrower] == 1 7. User borrows again, self.borrow_positions[user_address][2] is overwritten with the new borrow position. The borrowing data for 0x456 is lost

Open the report
LowInput ValidationLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

The address parameters received as input parameters do not have any sanity checks. Example: in the constructor of `LPTokenWrapper.sol`, the `lpTokenAddress` could be `0x0`. If so, the underlying `lpToken` will also be `0x0`, which will lead to all operations dependent on it to fail.

Open the report
LowStaking ManipulationLendroid Whalestreet · Quantstamp · 26 Nov 2020Fixed

A user staking right before the end of an epoch will still be able to get the reward for the ongoing reward even if he staked for only one block.

Open the report
LowCreator Signature Is Not VerifiedLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

L429 assert _kernel_creator == ecrecover(_k_hash,...) is commented out. Exploit Scenario: Malicious actors may pose as a creator

Open the report
LowOther Code Defects/SuggestionsLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

The following is a list of code defects that are bugs, do not adhere to best practices, affect gas use, or are deprecated/obsolete.

Open the report
LowReentrancyLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

A reentrancy vulnerability is a scenario where an attacker can repeatedly call a function from itself, unexpectedly leading to potentially disastrous results. Here's a basic example representing the very attack which impacted The DAO in 2016: function withdraw_with_reentrancy(uint256 _amount) public { msg.sender.call.value(_amount)(); // ATTACKER CAN CALL AGAIN BEFORE // FUNCTION TERMINATES because `call` // allows msg.sender to execute any code using fallback function balances[msg.sender] -= _amount; // Balance only updated AFTER funds withdrawn } Exploit Scenario: 1. L331 When calling liquidate_position() the function checks that the position status is POSITION_STATUS_OPEN 2. L333 The transfer() method on an external ERC20 contract is called 3. There could be a re-entrancy exploit that calls liquidate_position() again and transfer tokens 4. L337 Position is finally updated to POSITION_STATUS_LIQUIDATED

Open the report
LowRemoved AssertionsLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

Added as part of the second report Assertions have been removed with an added comment that refers to the error message "May not call non-constant function function_name within an assertion". The assertions should however be kept. In order to resolve the error message, return values should be saved in a temporary variable and used for the assertion afterwards.

Open the report
LowWorking With Inconsistent ERC20 ImplementationsLendroid Protocol version 1.0 · Quantstamp · 18 Feb 2019Fixed

The ERC20 standard stipulates that the transfer() function returns true on success. The Lendroid protocol makes external calls to transfer() in functions open_position() topup_position() liquidate_position() and close_position() asserting that the result is true (the transactions will fail otherwise). An adversarial implementation of the ERC20 token can consistently or occasionally return false on transfer() calls, which will make these methods fails. The Ethereum network does not provide the Lendroid protocol with any means of checking that an external token implements the ERC20 standard correctly. It is up to the users to verify the source codes of ERC20 tokens that they intend to use the Lendroid protocol with, however, the Lendroid team needs to inform the users about these limitations. Exploit Scenario: 1. User puts up token A as collateral and borrows a malicious Token B. 2. User tries to retrieve his deposit by calling close_position() and returning token B 3. Token B is programmed so that it fails for all transfers coming from user B 4. The lender waits until the position expires and liquidates the position, successfully collecting user B's deposit

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="lendroid-whalestreet"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

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