mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
d7a58e88ff
ARB: deploy protocols
251 lines
10 KiB
Solidity
251 lines
10 KiB
Solidity
//SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.21;
|
|
|
|
import "forge-std/Test.sol";
|
|
|
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
|
|
import { IERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
|
|
|
|
import { fToken } from "../../../contracts/protocols/lending/fToken/main.sol";
|
|
import { FluidLendingFactory } from "../../../contracts/protocols/lending/lendingFactory/main.sol";
|
|
import { IFluidLendingFactory } from "../../../contracts/protocols/lending/interfaces/iLendingFactory.sol";
|
|
import { IAllowanceTransfer } from "../../../contracts/protocols/lending/interfaces/permit2/iAllowanceTransfer.sol";
|
|
import { IFToken } from "../../../contracts/protocols/lending/interfaces/iFToken.sol";
|
|
import { Error } from "../../../contracts/protocols/lending/error.sol";
|
|
import { ErrorTypes } from "../../../contracts/protocols/lending/errorTypes.sol";
|
|
|
|
import { fTokenBaseSetUp } from "./fToken.t.sol";
|
|
import { UserModuleMock } from "./mocks/userModuleMock.sol";
|
|
|
|
abstract contract fTokenPermit2DepositsTestBase is fTokenBaseSetUp {
|
|
bytes32 public constant _PERMIT_DETAILS_TYPEHASH =
|
|
keccak256("PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)");
|
|
bytes32 public constant _PERMIT_SINGLE_TYPEHASH =
|
|
keccak256(
|
|
"PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
|
|
);
|
|
|
|
uint160 constant defaultAmount = 10 ** 18;
|
|
|
|
function _createToken(FluidLendingFactory lendingFactory_, IERC20 asset_) internal virtual override returns (IERC4626) {
|
|
vm.prank(admin);
|
|
factory.setFTokenCreationCode("fToken", type(fToken).creationCode);
|
|
vm.prank(admin);
|
|
return IERC4626(lendingFactory_.createToken(address(asset_), "fToken", false));
|
|
}
|
|
|
|
function createPermit() internal view returns (IAllowanceTransfer.PermitSingle memory) {
|
|
return
|
|
IAllowanceTransfer.PermitSingle({
|
|
details: IAllowanceTransfer.PermitDetails({
|
|
token: address(underlying),
|
|
amount: defaultAmount,
|
|
expiration: type(uint48).max,
|
|
nonce: 0
|
|
}),
|
|
spender: address(lendingFToken),
|
|
sigDeadline: block.timestamp
|
|
});
|
|
}
|
|
|
|
function getSignature(IAllowanceTransfer.PermitSingle memory permit) internal view returns (bytes memory) {
|
|
(, , , IAllowanceTransfer permit2, , , , , ) = lendingFToken.getData();
|
|
return getPermitSignature(permit, alicePrivateKey, permit2.DOMAIN_SEPARATOR());
|
|
}
|
|
|
|
|
|
function getPermitSignatureRaw(
|
|
IAllowanceTransfer.PermitSingle memory permit,
|
|
uint256 privateKey,
|
|
bytes32 domainSeparator
|
|
) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
|
|
bytes32 permitHash = keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details));
|
|
|
|
bytes32 msgHash = keccak256(
|
|
abi.encodePacked(
|
|
"\x19\x01",
|
|
domainSeparator,
|
|
keccak256(abi.encode(_PERMIT_SINGLE_TYPEHASH, permitHash, permit.spender, permit.sigDeadline))
|
|
)
|
|
);
|
|
|
|
(v, r, s) = vm.sign(privateKey, msgHash);
|
|
}
|
|
|
|
function getPermitSignature(
|
|
IAllowanceTransfer.PermitSingle memory permit,
|
|
uint256 privateKey,
|
|
bytes32 domainSeparator
|
|
) internal pure returns (bytes memory sig) {
|
|
(uint8 v, bytes32 r, bytes32 s) = getPermitSignatureRaw(permit, privateKey, domainSeparator);
|
|
return bytes.concat(r, s, bytes1(v));
|
|
}
|
|
}
|
|
|
|
contract fTokenPermit2DepositsTest is fTokenPermit2DepositsTestBase {
|
|
function setUp() public virtual override {
|
|
// native underlying tests must run in fork for WETH support
|
|
vm.createSelectFork(vm.envString("MAINNET_RPC_URL"));
|
|
|
|
super.setUp();
|
|
|
|
(, , , IAllowanceTransfer permit2, , , , , ) = lendingFToken.getData();
|
|
// approve permit2 contract for alice
|
|
_setApproval(underlying, address(permit2), alice);
|
|
}
|
|
|
|
function test_SetUpState() public {
|
|
(, , , IAllowanceTransfer permit2, , , , , ) = lendingFToken.getData();
|
|
assertEq(address(permit2), 0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
|
}
|
|
|
|
function test_depositWithSignature() public {
|
|
uint256 underlyingBalanceBefore = underlying.balanceOf(alice);
|
|
IAllowanceTransfer.PermitSingle memory permit = createPermit();
|
|
bytes memory signature = getSignature(permit);
|
|
|
|
vm.prank(alice);
|
|
uint256 shares = lendingFToken.depositWithSignature(defaultAmount, alice, 0, permit, signature);
|
|
|
|
assertEqDecimal(shares, defaultAmount, DEFAULT_DECIMALS);
|
|
assertEqDecimal(lendingFToken.balanceOf(alice), defaultAmount, DEFAULT_DECIMALS);
|
|
assertEq(underlyingBalanceBefore - underlying.balanceOf(alice), defaultAmount);
|
|
}
|
|
|
|
function test_depositWithSignature_RevertIfMinAmountOut() public {
|
|
IAllowanceTransfer.PermitSingle memory permit = createPermit();
|
|
bytes memory signature = getSignature(permit);
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert(abi.encodeWithSelector(Error.FluidLendingError.selector, ErrorTypes.fToken__MinAmountOut));
|
|
lendingFToken.depositWithSignature(defaultAmount, alice, defaultAmount + 1, permit, signature);
|
|
}
|
|
|
|
function test_mintWithSignature() public {
|
|
uint256 underlyingBalanceBefore = underlying.balanceOf(alice);
|
|
IAllowanceTransfer.PermitSingle memory permit = createPermit();
|
|
bytes memory signature = getSignature(permit);
|
|
|
|
vm.prank(alice);
|
|
uint256 shares = lendingFToken.mintWithSignature(defaultAmount, alice, defaultAmount, permit, signature);
|
|
|
|
assertEqDecimal(shares, defaultAmount, DEFAULT_DECIMALS);
|
|
assertEqDecimal(lendingFToken.balanceOf(alice), defaultAmount, DEFAULT_DECIMALS);
|
|
assertEq(underlyingBalanceBefore - underlying.balanceOf(alice), defaultAmount);
|
|
}
|
|
|
|
function test_mintWithSignature_RevertIfMaxAssetsIsSurpassed() public {
|
|
IAllowanceTransfer.PermitSingle memory permit = createPermit();
|
|
bytes memory signature = getSignature(permit);
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert(abi.encodeWithSelector(Error.FluidLendingError.selector, ErrorTypes.fToken__MaxAmount));
|
|
lendingFToken.mintWithSignature(defaultAmount, alice, defaultAmount - 1, permit, signature);
|
|
}
|
|
|
|
function test_liquidityCallback_RevertIfSenderIsNotLiquidityContract() public {
|
|
vm.prank(alice);
|
|
vm.expectRevert(abi.encodeWithSelector(Error.FluidLendingError.selector, ErrorTypes.fToken__Unauthorized));
|
|
lendingFToken.liquidityCallback(address(underlying), 0, new bytes(0));
|
|
}
|
|
|
|
function test_liquidityCallback_RevertIfSenderIsNotAssetContract() public {
|
|
vm.prank(address(liquidity));
|
|
vm.expectRevert(abi.encodeWithSelector(Error.FluidLendingError.selector, ErrorTypes.fToken__Unauthorized));
|
|
lendingFToken.liquidityCallback(address(0), 0, new bytes(0));
|
|
}
|
|
|
|
function test_liquidityCallback_RevertIfIsNotPermit2() public virtual {
|
|
address originalUserModule = liquidity.getSigsImplementation(UserModuleMock.operate.selector);
|
|
vm.prank(admin);
|
|
liquidity.removeImplementation(originalUserModule);
|
|
|
|
// this UserModuleMock returns abi.encode(from_, false) as a callback.
|
|
UserModuleMock userModuleMock = new UserModuleMock();
|
|
|
|
vm.prank(admin);
|
|
liquidity.addImplementation(address(userModuleMock), userSigs);
|
|
|
|
IAllowanceTransfer.PermitSingle memory permit = createPermit();
|
|
bytes memory signature = getSignature(permit);
|
|
|
|
vm.prank(alice);
|
|
vm.expectRevert(abi.encodeWithSelector(Error.FluidLendingError.selector, ErrorTypes.fToken__InvalidParams));
|
|
lendingFToken.depositWithSignature(defaultAmount, alice, defaultAmount, permit, signature);
|
|
}
|
|
}
|
|
|
|
interface IERC2612 is IERC20 {
|
|
function permit(
|
|
address owner,
|
|
address spender,
|
|
uint256 value,
|
|
uint256 deadline,
|
|
uint8 v,
|
|
bytes32 r,
|
|
bytes32 s
|
|
) external;
|
|
|
|
function nonces(address owner) external view returns (uint);
|
|
|
|
function DOMAIN_SEPARATOR() external view returns (bytes32);
|
|
}
|
|
|
|
contract fTokenPermit2DepositsEIP2612RevertsTest is fTokenPermit2DepositsTestBase {
|
|
function test_depositWithSignatureEIP2612_Revert() public {
|
|
uint256 deadline = block.timestamp;
|
|
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
|
alicePrivateKey,
|
|
_getPermitHash(
|
|
IERC2612(address(underlying)),
|
|
alice,
|
|
address(lendingFToken),
|
|
DEFAULT_AMOUNT,
|
|
0, // Nonce is always 0 because user is a fresh address.
|
|
deadline
|
|
)
|
|
);
|
|
bytes memory signature = abi.encodePacked(r, s, v);
|
|
vm.expectRevert();
|
|
vm.prank(alice);
|
|
lendingFToken.depositWithSignatureEIP2612(DEFAULT_AMOUNT, alice, 0, deadline, signature);
|
|
}
|
|
|
|
function test_mintWithSignatureEIP2612_Revert() public {
|
|
uint256 deadline = block.timestamp;
|
|
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
|
alicePrivateKey,
|
|
_getPermitHash(
|
|
IERC2612(address(underlying)),
|
|
alice,
|
|
address(lendingFToken),
|
|
DEFAULT_AMOUNT,
|
|
0, // Nonce is always 0 because user is a fresh address.
|
|
deadline
|
|
)
|
|
);
|
|
bytes memory signature = abi.encodePacked(r, s, v);
|
|
vm.expectRevert();
|
|
vm.prank(alice);
|
|
lendingFToken.depositWithSignatureEIP2612(DEFAULT_AMOUNT, alice, DEFAULT_AMOUNT, deadline, signature);
|
|
}
|
|
|
|
function _getPermitHash(
|
|
IERC2612 /* token */,
|
|
address owner,
|
|
address spender,
|
|
uint256 value,
|
|
uint256 nonce,
|
|
uint256 deadline
|
|
) private pure returns (bytes32 h) {
|
|
bytes32 domainHash = keccak256("token.DOMAIN_SEPARATOR()"); // for revert test just use some bytes32
|
|
bytes32 typeHash = keccak256(
|
|
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
|
);
|
|
bytes32 structHash = keccak256(abi.encode(typeHash, owner, spender, value, nonce, deadline));
|
|
return keccak256(abi.encodePacked("\x19\x01", domainHash, structHash));
|
|
}
|
|
}
|