Files
kucherenko__jscpd/fixtures/solidity/file2.sol
apk 64e471e2b0 test: raise coverage to 96%+ on @jscpd/finder and 98%+ on apps/jscpd
Adds 92 new tests (178 → 270 total) with zero source-file modifications:

- packages/finder: 13 new test files covering all reporters, subscribers,
  validators, hooks, utils, options, cloneFound, getFilesToDetect, and
  InFilesDetector; shared buildClone() factory helper
- apps/jscpd: 3 new test files (options-unit, print, store) plus extensions
  to formats, modes, and options integration tests
- vitest.config.ts added to both packages to restrict coverage.include
  to src/** only, eliminating bin/ and prism.js noise
- Fixes 3 tokenizer bugs (trailing space in format lookup, tokenization
  edge cases) uncovered by the new format fixture suite
- Adds fixture files for 40+ language formats to support formats.test.ts
2026-05-09 15:00:38 +02:00

76 lines
2.8 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/// @title SimpleLending - Collateralized lending pool
contract SimpleLending is Ownable, ReentrancyGuard {
IERC20 public immutable stakingToken;
IERC20 public immutable rewardToken;
uint256 public rewardRate;
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
mapping(address => uint256) public stakedBalance;
uint256 public totalStaked;
uint256 public constant COLLATERAL_RATIO = 150; // 150% over-collateralized
mapping(address => uint256) public collateral;
mapping(address => uint256) public borrowed;
event Deposited(address indexed user, uint256 amount);
event Borrowed(address indexed user, uint256 amount);
event Repaid(address indexed user, uint256 amount);
constructor(address _stakingToken, address _rewardToken, uint256 _rewardRate) {
stakingToken = IERC20(_stakingToken);
rewardToken = IERC20(_rewardToken);
rewardRate = _rewardRate;
}
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = block.timestamp;
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
function rewardPerToken() public view returns (uint256) {
if (totalStaked == 0) return rewardPerTokenStored;
return rewardPerTokenStored +
((block.timestamp - lastUpdateTime) * rewardRate * 1e18) / totalStaked;
}
function earned(address account) public view returns (uint256) {
return (stakedBalance[account] *
(rewardPerToken() - userRewardPerTokenPaid[account])) /
1e18 + rewards[account];
}
function depositCollateral(uint256 amount) external nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot deposit 0");
totalStaked += amount;
stakedBalance[msg.sender] += amount;
collateral[msg.sender] += amount;
stakingToken.transferFrom(msg.sender, address(this), amount);
emit Deposited(msg.sender, amount);
}
function borrow(uint256 amount) external nonReentrant {
uint256 maxBorrow = (collateral[msg.sender] * 100) / COLLATERAL_RATIO;
require(borrowed[msg.sender] + amount <= maxBorrow, "Insufficient collateral");
borrowed[msg.sender] += amount;
rewardToken.transfer(msg.sender, amount);
emit Borrowed(msg.sender, amount);
}
}