文件 3 的 3:lending.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
interface IEtherVistaPair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function setMetadata(string calldata website, string calldata image, string calldata description, string calldata chat, string calldata social) external;
function websiteUrl() external view returns (string memory);
function imageUrl() external view returns (string memory);
function tokenDescription() external view returns (string memory);
function chatUrl() external view returns (string memory);
function socialUrl() external view returns (string memory);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function updateProvider(address user) external;
function euler(uint) external view returns (uint256);
function viewShare() external view returns (uint256 share);
function claimShare() external;
function poolBalance() external view returns (uint);
function totalCollected() external view returns (uint);
function setProtocol(address) external;
function protocol() external view returns (address);
function payableProtocol() external view returns (address payable origin);
function creator() external view returns (address);
function renounce() external;
function setFees() external;
function updateFees(uint8, uint8, uint8, uint8) external;
function buyLpFee() external view returns (uint8);
function sellLpFee() external view returns (uint8);
function buyProtocolFee() external view returns (uint8);
function sellProtocolFee() external view returns (uint8);
function buyTotalFee() external view returns (uint8);
function sellTotalFee() external view returns (uint8);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function first_mint(address to, uint8 buyLp, uint8 sellLp, uint8 buyProtocol, uint8 sellProtocol, address protocolAddress) external returns (uint liquidity);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address _token0, address _token1) external;
}
interface IEtherVistaRouter {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
uint deadline
) external returns (uint amountETH);
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function launch(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
uint8 buyLpFee,
uint8 sellLpFee,
uint8 buyProtocolFee,
uint8 sellProtocolFee,
address protocolAddress
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function updateSelf(address _token) external;
function safeTransferLp(address _token, address to, uint256 _amount) external;
function hardstake(address _contract, address _token, uint256 _amount) external;
function usdcToEth(uint256 usdcAmount) external view returns (uint256);
}
interface IVistaFactory {
function getPair(address tokenA, address tokenB) external view returns (address);
function router() external view returns (address);
}
interface ITWAPOracle {
function validatePriceAndGetMinAmounts(
address tokenAddress,
uint256 lpAmount
) external view returns (uint256 tokenMin, uint256 ethMin);
function updatePrice(address pair) external;
function getRequiredETHAmount(
address tokenAddress,
uint256 amountTokens
) external view returns (uint256 amountETH);
}
interface IFlashloan {
function borrow(bytes memory data) external payable;
}
contract TokenLending is ReentrancyGuard {
address private owner;
address private weth;
uint256 private bigNumber = 10**20;
uint256 public slippage = 200;
mapping(address => bool) public whitelisted;
uint256 public lockTime = 0;
uint256 public minimumETHBorrow = 5;
uint256 public minimumETHActive = 5;
uint256 public minimumETHDeposit = 5;
uint256 public lenderShare = 40;
uint256 public borrowerShare = 60;
IVistaFactory private factory;
ITWAPOracle private oracle;
constructor(address _oracle) {
owner = msg.sender;
weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
oracle = ITWAPOracle(_oracle);
factory = IVistaFactory(0x9a27cb5ae0B2cEe0bb71f9A85C0D60f3920757B4);
}
struct LenderInfo {
uint256 ETHAvailable;
uint256 totalLent;
uint256 euler0;
bool lock;
}
uint256 public totalRewards = 0;
uint256 public totalSupply = 0;
uint256[] private euler;
mapping(address => LenderInfo) public lenders;
LenderETH[] public activeLenders;
mapping(address => uint256) public activeLenderIndex;
function updateEuler(uint256 Fee) internal {
if (euler.length == 0){
euler.push((Fee*bigNumber)/totalSupply);
}else{
euler.push(euler[euler.length - 1] + (Fee*bigNumber)/totalSupply);
}
}
struct LenderETH {
address lender;
uint256 ethAvailable;
}
function isActiveLender(address lender) public view returns (bool) {
return activeLenderIndex[lender] != 0;
}
function _addActiveLender(address lender) internal {
activeLenders.push(LenderETH({
lender: lender,
ethAvailable: lenders[lender].ETHAvailable
}));
activeLenderIndex[lender] = activeLenders.length;
}
function _removeActiveLender(address lender) internal {
uint256 indexPlusOne = activeLenderIndex[lender];
if (indexPlusOne != 0) {
uint256 actualIndex = indexPlusOne - 1;
uint256 lastLenderIndex = activeLenders.length - 1;
if (actualIndex != lastLenderIndex) {
LenderETH memory lastLenderInfo = activeLenders[lastLenderIndex];
activeLenders[actualIndex] = lastLenderInfo;
activeLenderIndex[lastLenderInfo.lender] = indexPlusOne;
}
activeLenders.pop();
delete activeLenderIndex[lender];
}
}
function _updateLenderETH(address lender) internal {
uint256 indexPlusOne = activeLenderIndex[lender];
if (indexPlusOne != 0) {
activeLenders[indexPlusOne - 1].ethAvailable = lenders[lender].ETHAvailable;
}
}
function getActiveLenders() public view returns (LenderETH[] memory) {
return activeLenders;
}
struct BorrowInfo {
uint256 unlockTimestamp;
address tokenAddress;
bool isActive;
uint256 amountETHBorrowed;
uint256 amountTokenBorrowed;
uint256 liquidity;
uint256 euler0;
}
mapping(address => uint256) public totalRewardsLp;
mapping(address => uint256) public totalSupplies;
mapping(address => uint256[]) private eulers;
mapping(address => mapping(address => BorrowInfo)) public borrows;
function updateEulerLp(address token, uint256 Fee) internal {
uint256[] storage eulerLp = eulers[token];
uint256 totalSupplyLp = totalSupplies[token];
if (eulerLp.length == 0) {
eulerLp.push((Fee * bigNumber) / totalSupplyLp);
} else {
eulerLp.push(eulerLp[eulerLp.length - 1] + (Fee * bigNumber) / totalSupplyLp);
}
}
mapping(address => address[]) public userLoans;
mapping(address => mapping(address => uint256)) private loanIndexes;
mapping(address => address[]) public userBorrows;
mapping(address => mapping(address => uint256)) private borrowIndexes;
function _addBorrowRelationship(address lender, address borrower) internal {
if (loanIndexes[lender][borrower] == 0) {
userLoans[lender].push(borrower);
loanIndexes[lender][borrower] = userLoans[lender].length;
}
if (borrowIndexes[borrower][lender] == 0) {
userBorrows[borrower].push(lender);
borrowIndexes[borrower][lender] = userBorrows[borrower].length;
}
}
function _removeBorrowRelationship(address lender, address borrower) internal {
uint256 loanIndex = loanIndexes[lender][borrower];
if (loanIndex != 0) {
uint256 actualIndex = loanIndex - 1;
address[] storage lenderArray = userLoans[lender];
uint256 lastIndex = lenderArray.length - 1;
if (actualIndex != lastIndex) {
address lastBorrower = lenderArray[lastIndex];
lenderArray[actualIndex] = lastBorrower;
loanIndexes[lender][lastBorrower] = loanIndex;
}
lenderArray.pop();
delete loanIndexes[lender][borrower];
}
uint256 borrowIndex = borrowIndexes[borrower][lender];
if (borrowIndex != 0) {
uint256 actualIndex = borrowIndex - 1;
address[] storage borrowerArray = userBorrows[borrower];
uint256 lastIndex = borrowerArray.length - 1;
if (actualIndex != lastIndex) {
address lastLender = borrowerArray[lastIndex];
borrowerArray[actualIndex] = lastLender;
borrowIndexes[borrower][lastLender] = borrowIndex;
}
borrowerArray.pop();
delete borrowIndexes[borrower][lender];
}
}
function getBorrowers(address lender) public view returns (address[] memory) {
return userLoans[lender];
}
function getLenders(address borrower) public view returns (address[] memory) {
return userBorrows[borrower];
}
mapping(address => uint256) lendingRewards;
mapping(address => uint256) lenderLpRewards;
mapping(address => uint256) borrowerLpRewards;
event ETHDeposited(address indexed lender, uint256 ETHAvailable, uint256 totalLent);
event ETHBorrowed(address indexed lender, address indexed borrower, uint256 amountETH, address indexed tokenAddress, uint256 ETHAvailable, uint256 liquidity, uint256 unlockTimestamp);
event ETHWithdrawn(address indexed lender, uint256 amount, uint256 totalLent);
event BorrowClosed(address indexed lender,address indexed borrower, address indexed token, uint256 totalLent);
receive() external payable {
if (msg.sender == 0x3DBaA8d14Df63164a7aEa6e1d4d62e163376eac2) {
totalRewards += msg.value;
updateEuler(msg.value);
}
}
function depositETH(address _randomPair)
public
payable
nonReentrant
{
require(msg.value > IEtherVistaRouter(factory.router()).usdcToEth(minimumETHDeposit), "Must deposit enough ETH");
oracle.updatePrice(_randomPair);
LenderInfo storage lender = lenders[msg.sender];
if (euler.length == 0) {
lender.euler0 = 0;
}else{
uint256 share = (lender.totalLent * (euler[euler.length - 1] - lender.euler0))/bigNumber;
lender.euler0 = euler[euler.length - 1];
if (share > 0) {
lendingRewards[msg.sender] += share;
}
}
totalSupply += msg.value;
lender.ETHAvailable += msg.value;
lender.totalLent += msg.value;
if (lender.ETHAvailable >= IEtherVistaRouter(factory.router()).usdcToEth(minimumETHActive) && !isActiveLender(msg.sender)) {
_addActiveLender(msg.sender);
} else if (lender.ETHAvailable >= IEtherVistaRouter(factory.router()).usdcToEth(minimumETHActive)) {
_updateLenderETH(msg.sender);
}
emit ETHDeposited(msg.sender, lender.ETHAvailable, lender.totalLent);
}
function withdrawAvailableETH(address _randomPair)
public
nonReentrant
{
LenderInfo storage lender = lenders[msg.sender];
uint256 amount = lender.ETHAvailable;
require(amount > 0, "No ETH available to withdraw");
oracle.updatePrice(_randomPair);
if (euler.length == 0) {
lender.euler0 = 0;
}else{
uint256 share = (lender.totalLent * (euler[euler.length - 1] - lender.euler0))/bigNumber;
lender.euler0 = euler[euler.length - 1];
if (share > 0) {
lendingRewards[msg.sender] += share;
}
}
totalSupply -= amount;
lender.ETHAvailable = 0;
lender.totalLent -= amount;
_removeActiveLender(msg.sender);
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "ETH transfer failed");
emit ETHWithdrawn(msg.sender, amount, lender.totalLent);
}
function borrowETH(
address _lender,
uint256 _amountToken,
address _tokenAddress
)
public
nonReentrant
{
require(_amountToken > 0 && _lender != msg.sender, "Cannot lend to self");
require(whitelisted[_tokenAddress], "Token is not whitelisted");
LenderInfo storage lender = lenders[_lender];
require(!lender.lock, "Lender has disabled borrows");
require(!free);
uint256 ethAmountRequired = oracle.getRequiredETHAmount(_tokenAddress, _amountToken);
require(ethAmountRequired >= IEtherVistaRouter(factory.router()).usdcToEth(minimumETHBorrow) && lender.ETHAvailable >= ethAmountRequired, "Insufficient ETH borrowed or available" );
BorrowInfo storage borrow = borrows[_lender][msg.sender];
require(!borrow.isActive, "Active borrow exists");
borrow.isActive = true;
IERC20(_tokenAddress).transferFrom(msg.sender, address(this), _amountToken);
IERC20(_tokenAddress).approve(factory.router(), 0);
IERC20(_tokenAddress).approve(factory.router(), _amountToken);
(uint256 amountToken, uint256 amountETH, uint256 liquidity) = IEtherVistaRouter(factory.router()).addLiquidityETH{value: ethAmountRequired}(
_tokenAddress,
_amountToken,
_amountToken*(10000-slippage)/10000,
ethAmountRequired*(10000-slippage)/10000,
block.timestamp + 200
);
if (_amountToken > amountToken) {
IERC20(_tokenAddress).transfer(msg.sender, _amountToken - amountToken);
}
borrow.unlockTimestamp = block.timestamp + lockTime;
borrow.tokenAddress = _tokenAddress;
_addBorrowRelationship(_lender, msg.sender);
lender.ETHAvailable -= amountETH;
if (lender.ETHAvailable < IEtherVistaRouter(factory.router()).usdcToEth(minimumETHActive)) {
_removeActiveLender(_lender);
} else {
_updateLenderETH(_lender);
}
borrow.amountETHBorrowed = amountETH;
borrow.amountTokenBorrowed = amountToken;
borrow.liquidity = liquidity;
IEtherVistaPair pair = IEtherVistaPair(factory.getPair(weth, _tokenAddress));
oracle.updatePrice(address(pair));
uint256 share = pair.viewShare();
if (share > 0) {
pair.claimShare();
totalRewardsLp[_tokenAddress] += share;
updateEulerLp(_tokenAddress, share);
}
totalSupplies[_tokenAddress] += liquidity;
uint256[] storage eulerLp = eulers[_tokenAddress];
if (eulerLp.length == 0){
borrow.euler0 = 0;
} else {
borrow.euler0 = eulerLp[eulerLp.length - 1];
}
emit ETHBorrowed(
_lender,
msg.sender,
amountETH,
_tokenAddress,
lender.ETHAvailable,
liquidity,
borrow.unlockTimestamp
);
}
function closeBorrow(address _lender, address _borrower) public nonReentrant {
require(msg.sender == _lender || msg.sender == _borrower, "Unauthorized");
BorrowInfo storage borrow = borrows[_lender][_borrower];
if (!free) {
require(block.timestamp >= borrow.unlockTimestamp, "Lock period not expired");
}
address tokenAddress = borrow.tokenAddress;
uint256 lpAmount = borrow.liquidity;
uint256 ethBorrowed = borrow.amountETHBorrowed;
setShareLp(_lender, _borrower);
totalSupplies[tokenAddress] -= lpAmount;
_removeBorrowRelationship(_lender, _borrower);
delete borrows[_lender][_borrower];
uint256 amountETH = _handleLiquidityRemoval(tokenAddress, lpAmount, _borrower);
_updateLenderState(_lender, amountETH, ethBorrowed);
emit BorrowClosed(_lender, _borrower, tokenAddress, lenders[_lender].totalLent);
}
function _handleLiquidityRemoval(
address tokenAddress,
uint256 lpAmount,
address borrower
) internal nonReentrant returns (uint256) {
IERC20 token = IERC20(tokenAddress);
uint256 balanceBefore = token.balanceOf(address(this));
(uint256 tokenMin, uint256 ethMin) = oracle.validatePriceAndGetMinAmounts(
tokenAddress,
lpAmount
);
uint256 amountETH = IEtherVistaRouter(factory.router())
.removeLiquidityETHSupportingFeeOnTransferTokens(
tokenAddress,
lpAmount,
tokenMin,
ethMin,
block.timestamp
);
uint256 amountTokenReceived = token.balanceOf(address(this)) - balanceBefore;
token.transfer(borrower, amountTokenReceived);
return amountETH;
}
function _updateLenderState(
address _lender,
uint256 amountETH,
uint256 ethBorrowed
) internal nonReentrant {
LenderInfo storage lender = lenders[_lender];
if (euler.length > 0) {
uint256 share = (lender.totalLent * (euler[euler.length - 1] - lender.euler0))/bigNumber;
if (share > 0) {
lender.euler0 = euler[euler.length - 1];
lendingRewards[_lender] += share;
}
}
lender.ETHAvailable += amountETH;
if (amountETH > ethBorrowed) {
uint256 gain = amountETH - ethBorrowed;
lender.totalLent += gain;
totalSupply += gain;
} else {
uint256 loss = ethBorrowed - amountETH;
lender.totalLent -= loss;
totalSupply -= loss;
}
if (lender.ETHAvailable >= IEtherVistaRouter(factory.router()).usdcToEth(minimumETHActive) && !isActiveLender(msg.sender)) {
_addActiveLender(msg.sender);
} else if (lender.ETHAvailable >= IEtherVistaRouter(factory.router()).usdcToEth(minimumETHActive)) {
_updateLenderETH(msg.sender);
}
}
function setShareLp(address _lender, address _borrower) internal nonReentrant {
BorrowInfo storage borrow = borrows[_lender][_borrower];
IEtherVistaPair pair = IEtherVistaPair(factory.getPair(weth, borrow.tokenAddress));
oracle.updatePrice(address(pair));
uint256 contractShare = pair.viewShare();
if (contractShare > 0) {
pair.claimShare();
totalRewardsLp[borrow.tokenAddress] += contractShare;
updateEulerLp(borrow.tokenAddress, contractShare);
}
uint256[] memory eulerLp = eulers[borrow.tokenAddress];
if (eulerLp.length > 0) {
uint256 balance = borrow.liquidity;
uint256 share = (balance * (eulerLp[eulerLp.length - 1] - borrow.euler0)/bigNumber);
borrow.euler0 = eulerLp[eulerLp.length - 1];
uint256 devShare = share * 15/1000;
uint256 treasuryShare = share * 35/1000;
(bool devSent,) = payable(0x53D1912a2E1cb45eEa1DE08763F8EAf624f7EaFB).call{value: devShare}("");
require(devSent, "Dev transfer failed");
(bool treasurySent,) = payable(owner).call{value: treasuryShare}("");
require(treasurySent, "Treasury transfer failed");
uint256 remainingShare = share - devShare - treasuryShare;
lenderLpRewards[_lender] += (remainingShare * lenderShare)/100;
borrowerLpRewards[_borrower] += (remainingShare * borrowerShare)/100;
}
}
function viewShareBorrow(address _lender, address _borrower) public view returns (uint256 share) {
BorrowInfo memory borrow = borrows[_lender][_borrower];
address token = borrow.tokenAddress;
IEtherVistaPair pair = IEtherVistaPair(factory.getPair(weth, token));
uint256[] memory eulerLp = eulers[token];
uint256 contractShare = pair.viewShare();
uint256 totalSupplyLp = totalSupplies[borrow.tokenAddress];
if (contractShare == 0 && eulerLp.length == 0) {
share = 0;
} else if (contractShare == 0 && eulerLp.length > 0) {
share = borrow.liquidity * (eulerLp[eulerLp.length - 1] - borrow.euler0)/bigNumber;
} else if (contractShare > 0 && eulerLp.length == 0) {
uint256 euler_n = contractShare * bigNumber / totalSupplyLp;
share = borrow.liquidity * (euler_n - borrow.euler0)/bigNumber;
} else if (contractShare > 0 && eulerLp.length > 0) {
uint256 euler_n = eulerLp[eulerLp.length - 1] + (contractShare * bigNumber) / totalSupplyLp;
share = borrow.liquidity * (euler_n - borrow.euler0)/bigNumber;
}
}
function claimShareLp(address _randomPair) public nonReentrant {
oracle.updatePrice(_randomPair);
uint256 share = lenderLpRewards[msg.sender] + borrowerLpRewards[msg.sender];
require(share > 0, "Nothing to claim");
lenderLpRewards[msg.sender] = 0;
borrowerLpRewards[msg.sender] = 0;
(bool sent,) = payable(msg.sender).call{value: share}("");
require(sent, "Failed to send Ether");
}
function viewShareLp() public view returns (uint256 share) {
return lenderLpRewards[msg.sender] + borrowerLpRewards[msg.sender];
}
function claimShareLending(address _randomPair) public nonReentrant {
require(euler.length > 0, 'EtherVista: Nothing to Claim');
oracle.updatePrice(_randomPair);
LenderInfo storage lender = lenders[msg.sender];
uint256 share = (lender.totalLent * (euler[euler.length - 1] - lender.euler0))/bigNumber + lendingRewards[msg.sender];
lender.euler0 = euler[euler.length - 1];
lendingRewards[msg.sender] = 0;
(bool sent,) = payable(msg.sender).call{value: share}("");
require(sent, "Failed to send Ether");
}
function viewShareLending() public view returns (uint256 share) {
if (euler.length == 0){
return 0;
}else{
return (lenders[msg.sender].totalLent * (euler[euler.length - 1] - lenders[msg.sender].euler0)/bigNumber) + lendingRewards[msg.sender];
}
}
function setLock(bool _lock, address _lender) public {
require(msg.sender == owner);
lenders[_lender].lock = _lock;
if (_lock == true) {
_removeActiveLender(msg.sender);
}
}
bool private free = false;
function setFree(bool _free) public {
require(msg.sender == owner);
free = _free;
}
function setSlippage(uint256 _slippage) public {
require(msg.sender == owner);
slippage = _slippage;
}
function setWhitelisted(address _token, bool _whitelist) public {
require(msg.sender == owner);
whitelisted[_token] = _whitelist;
}
function setTime(uint256 _lockTime) public {
require(msg.sender == owner);
lockTime = _lockTime;
}
function setMinAmounts(uint256 _minimumETHBorrow, uint256 _minimumETHActive, uint256 _minimumETHDeposit) public {
require(msg.sender == owner);
minimumETHBorrow = _minimumETHBorrow;
minimumETHActive = _minimumETHActive;
minimumETHDeposit = _minimumETHDeposit;
}
function setShares(uint256 _lenderShare, uint256 _borrowerShare) public {
require(msg.sender == owner);
require(_lenderShare + _borrowerShare == 100, "Invalid percentages");
lenderShare = _lenderShare;
borrowerShare = _borrowerShare;
}
function setOracle(address _oracle) public {
require(msg.sender == owner);
oracle = ITWAPOracle(_oracle);
}
function flashloan(
address callback,
uint256 amount,
bytes calldata data
) public nonReentrant {
uint256 balanceBefore = address(this).balance;
IFlashloan(callback).borrow{value: amount}(data);
require(address(this).balance >= balanceBefore, "Flashloan not repaid");
}
}