fluid-contracts-public/contracts/mocks/mockProtocol.sol

134 lines
5.0 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { FluidLiquidityUserModule } from "../liquidity/userModule/main.sol";
import { IFluidLiquidity } from "../liquidity/interfaces/iLiquidity.sol";
/// @title Mock Protocol
/// @notice Mock protocol for testing, implements:
/// function liquidityCallback(address token_, uint256 amount_, bytes calldata data_) external;
/// This callback method MUST transferFrom data_ decoded from address to the liquidity contract
contract MockProtocol {
using SafeERC20 for IERC20;
/// @dev address that is mapped to the chain native token
address internal constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address public immutable liquidityContract;
/// @notice thrown when liquidity callback is called for a native token operation
error MockProtocol__UnexpectedLiquidityCallback();
bool transferInsufficientMode = false;
bool transferExcessMode = false;
bool reentrancyFromCallback = false;
address transferFromAddress;
/// @notice Construct a new MockProtocol
/// @param liquidityContract_ The address of the liquidity contract
constructor(address liquidityContract_) {
liquidityContract = liquidityContract_;
}
receive() external payable {}
function setTransferInsufficientMode(bool transferInsufficientMode_) public {
transferInsufficientMode = transferInsufficientMode_;
}
function setTransferExcessMode(bool transferExcessMode_) public {
transferExcessMode = transferExcessMode_;
}
function setReentrancyFromCallback(bool reentrancyFromCallback_) public {
reentrancyFromCallback = reentrancyFromCallback_;
}
function setTransferFromAddress(address transferFromAddress_) public {
transferFromAddress = transferFromAddress_;
}
/// @notice Mock liquidity callback
/// @param token_ The token being transferred
/// @param amount_ The amount being transferred
function liquidityCallback(address token_, uint256 amount_, bytes memory data_) external {
if (reentrancyFromCallback) {
// call operate with some random values (should not matter as it reverts anyway)
IFluidLiquidity(liquidityContract).operate(token_, 10, 0, address(0), address(0), abi.encode(address(this)));
}
if (token_ == NATIVE_TOKEN_ADDRESS) {
revert MockProtocol__UnexpectedLiquidityCallback();
}
address from_;
if (transferFromAddress == address(0)) {
// take the last 20 bytes of data_ and decode them to address. Gives more flexibility in type of
// data that can be passed in to Liquidity at mock calls while ensuring mock Protocol can do what it
// is supposed to do: transfer amount of token to liquidity.
assembly {
from_ := mload(
add(
// add padding for length as present for dynamic arrays in memory
add(data_, 32),
// assembly expects address with leading zeros / left padded so need to use 32 as length here
sub(mload(data_), 32)
)
)
}
} else {
from_ = transferFromAddress;
}
if (amount_ > 0) {
if (transferExcessMode) {
amount_ += 1e6;
} else if (transferInsufficientMode) {
amount_ -= 1;
}
}
if (from_ == address(this)) {
// use approve and transferFrom for more consistent testing of methods called
// (always transferFrom instead of transfer)
IERC20(token_).safeApprove(address(this), amount_);
IERC20(token_).safeTransferFrom(address(this), liquidityContract, amount_);
} else {
IERC20(token_).safeTransferFrom(from_, liquidityContract, amount_);
}
}
/// @notice Proxy method for executing `operate` on the liquidity contract
function operate(
address token_,
int256 supplyAmount_,
int256 borrowAmount_,
address withdrawTo_,
address borrowTo_,
bytes calldata callbackData_
) external payable returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {
uint256 valueAmount = msg.value;
if (valueAmount > 0) {
if (transferExcessMode) {
valueAmount += 1e9 + 1;
} else if (transferInsufficientMode) {
valueAmount -= 1;
}
}
return
FluidLiquidityUserModule(liquidityContract).operate{ value: valueAmount }(
token_,
supplyAmount_,
borrowAmount_,
withdrawTo_,
borrowTo_,
callbackData_
);
}
}