-
Notifications
You must be signed in to change notification settings - Fork 261
Expand file tree
/
Copy pathBatchLiquidator.sol
More file actions
140 lines (129 loc) · 4.93 KB
/
BatchLiquidator.sol
File metadata and controls
140 lines (129 loc) · 4.93 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: AGPLv3
pragma solidity ^0.8.23;
import {
ISuperfluid, ISuperAgreement, ISuperToken, ISuperfluidPool,
IConstantFlowAgreementV1, IGeneralDistributionAgreementV1
} from "../interfaces/superfluid/ISuperfluid.sol";
import { ERC20 } from "@openzeppelin-v5/contracts/token/ERC20/ERC20.sol";
/**
* @title Batch liquidator contract
* @author Superfluid
* @dev This contract allows to delete multiple flows in a single transaction.
* @notice Reduces calldata by having host and cfa hardcoded, this can make a significant difference in tx fees on L2s
*/
contract BatchLiquidator {
enum FlowType {
ConstantFlowAgreement,
GeneralDistributionAgreement
}
struct FlowLiquidationData {
FlowType agreementOperation;
address sender;
address receiver;
}
address public immutable host;
address public immutable cfa;
address public immutable gda;
constructor(address host_) {
host = host_;
cfa = address(
ISuperfluid(host).getAgreementClass(keccak256("org.superfluid-finance.agreements.ConstantFlowAgreement.v1"))
);
gda = address(
ISuperfluid(host).getAgreementClass(
keccak256("org.superfluid-finance.agreements.GeneralDistributionAgreement.v1")
)
);
}
/**
* @dev Delete flows in batch
* @param superToken - The super token the flows belong to.
* @param data - The array of flow data to be deleted.
*/
function deleteFlows(address superToken, FlowLiquidationData[] memory data) external {
for (uint256 i; i < data.length;) {
// We tolerate any errors occured during liquidations.
// It could be due to flow had been liquidated by others.
_deleteFlow(superToken, data[i]);
unchecked {
i++;
}
}
// If the liquidation(s) resulted in any super token
// rewards, send them all to the sender instead of having them
// locked in the contract
{
uint256 balance = ERC20(superToken).balanceOf(address(this));
if (balance > 0) {
// don't fail for non-transferrable tokens
try ERC20(superToken).transferFrom(address(this), msg.sender, balance)
// solhint-disable-next-line no-empty-blocks
{} catch {}
}
}
}
/**
* @dev Delete a single flow
* @param superToken - The super token the flow belongs to.
* @param data - The flow data to be deleted.
*/
function deleteFlow(address superToken, FlowLiquidationData memory data) external {
/* solhint-disable */
(bool success, bytes memory returndata) = _deleteFlow(superToken, data);
if (!success) {
if (returndata.length == 0) revert();
// solhint-disable
assembly {
revert(add(32, returndata), mload(returndata))
}
}
/* solhint-enable */
// If the liquidation(s) resulted in any super token
// rewards, send them all to the sender instead of having them
// locked in the contract
{
uint256 balance = ERC20(superToken).balanceOf(address(this));
if (balance > 0) {
try ERC20(superToken).transferFrom(address(this), msg.sender, balance)
// solhint-disable-next-line no-empty-blocks
{} catch {}
}
}
}
function _deleteFlow(address superToken, FlowLiquidationData memory data)
internal
returns (bool success, bytes memory returndata)
{
if (data.agreementOperation == FlowType.ConstantFlowAgreement) {
// solhint-disable-next-line avoid-low-level-calls
(success, returndata) = address(host).call(
abi.encodeCall(
ISuperfluid(host).callAgreement,
(
ISuperAgreement(cfa),
abi.encodeCall(
IConstantFlowAgreementV1(cfa).deleteFlow,
(ISuperToken(superToken), data.sender, data.receiver, new bytes(0))
),
new bytes(0)
)
)
);
} else {
// solhint-disable-next-line avoid-low-level-calls
(success, returndata) = address(host).call(
abi.encodeCall(
ISuperfluid(host).callAgreement,
(
ISuperAgreement(gda),
abi.encodeCall(
IGeneralDistributionAgreementV1(gda).distributeFlow,
(ISuperToken(superToken), data.sender, ISuperfluidPool(data.receiver), 0, new bytes(0))
),
new bytes(0)
)
)
);
}
}
}