generated from PaulRBerg/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 3
/
GnosisSafeWrapper.sol
110 lines (89 loc) · 4.41 KB
/
GnosisSafeWrapper.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// SPDX-License-Identifier: MIT
// Thanks to ultrasecr.eth
pragma solidity ^0.8.19;
import { IGnosisSafe } from "./interfaces/IGnosisSafe.sol";
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Enum } from "./lib/Enum.sol";
import { BaseWrapper, IERC7399, IERC20 } from "../BaseWrapper.sol";
/// @dev Safe Gnosis Flash Lender that uses individual Gnosis Safe contracts as source of liquidity.
contract GnosisSafeWrapper is BaseWrapper, AccessControl, Initializable {
using SafeERC20 for IERC20;
error UnsupportedAsset(address asset);
error FailedTransfer(address asset, uint256 amount);
error InsufficientRepayment(address asset, uint256 amount);
event LendingDataSet(address indexed asset, uint248 fee, bool enabled);
event SafeSet(IGnosisSafe indexed safe);
struct LendingData {
uint248 fee; // 1 = 0.01%
bool enabled;
}
address public constant ALL_ASSETS = address(0);
IGnosisSafe public safe;
mapping(address asset => LendingData data) public lending;
function initialize(address _safe) public initializer {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
safe = IGnosisSafe(_safe);
emit SafeSet(safe);
}
/// @inheritdoc IERC7399
function maxFlashLoan(address asset) public view returns (uint256) {
if (lending[asset].enabled == true || lending[ALL_ASSETS].enabled == true) {
return IERC20(asset).balanceOf(address(safe));
} else {
return 0;
}
}
/// @inheritdoc IERC7399
function flashFee(address asset, uint256 amount) public view returns (uint256) {
uint256 max = maxFlashLoan(asset);
if (max == 0) revert UnsupportedAsset(asset); // TODO: Should we revert on tokens that are enabled but have zero
// liquidity?
if (amount >= max) {
return type(uint256).max;
} else {
uint256 fee = lending[ALL_ASSETS].enabled == true ? lending[ALL_ASSETS].fee : lending[asset].fee;
return amount * fee / 10_000;
}
}
/// @dev Serve a flash loan.
function _flashLoan(address asset, uint256 amount, bytes memory params) internal override {
Data memory decodedParams = abi.decode(params, (Data));
uint256 fee = flashFee(asset, amount); // Checks for unsupported assets
// Take assets from safe
bytes memory transferCall =
abi.encodeWithSignature("transfer(address,uint256)", decodedParams.loanReceiver, amount);
if (!safe.execTransactionFromModule(asset, 0, transferCall, Enum.Operation.Call)) {
revert FailedTransfer(asset, amount);
}
// Call callback
_bridgeToCallback(asset, amount, fee, params);
// Repay to the Safe. The assets are temporary held by this wrapper to support reentrancy.
if (IERC20(asset).balanceOf(address(this)) < amount + fee) {
revert InsufficientRepayment(asset, amount + fee);
} else {
IERC20(asset).safeTransfer(address(safe), amount + fee);
}
}
/// @dev Transfer the assets to the loan receiver.
/// Overriden because the provider can send the funds directly
// solhint-disable-next-line no-empty-blocks
function _transferAssets(address, uint256, address) internal override { }
/// @dev Set lending data for an asset.
/// @param asset Address of the asset.
/// @param fee Fee for the flash loan (FP 1e-4)
/// @param enabled Whether the asset is enabled for flash loans.
function lend(address asset, uint248 fee, bool enabled) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (asset == ALL_ASSETS) revert UnsupportedAsset(asset); // address(0) is reserved for the all assets override
lending[asset] = LendingData({ fee: fee, enabled: enabled });
emit LendingDataSet(asset, fee, enabled);
}
/// @dev Set a lending data override for all assets.
/// @param fee Fee for the flash loan (FP 1e-4)
/// @param enabled Whether the lending data override is enabled for flash loans.
function lendAll(uint248 fee, bool enabled) public onlyRole(DEFAULT_ADMIN_ROLE) {
lending[ALL_ASSETS] = LendingData({ fee: fee, enabled: enabled });
emit LendingDataSet(ALL_ASSETS, fee, enabled);
}
}