-
Notifications
You must be signed in to change notification settings - Fork 391
/
Copy pathGaugeActions.sol
154 lines (129 loc) · 6.14 KB
/
GaugeActions.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
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "@balancer-labs/v2-interfaces/contracts/liquidity-mining/IBalancerMinter.sol";
import "@balancer-labs/v2-interfaces/contracts/liquidity-mining/IStakingLiquidityGauge.sol";
import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/SafeERC20.sol";
import "./IBaseRelayerLibrary.sol";
/**
* @title GaugeActions
* @dev All functions must be payable so they can be called from a multicall involving ETH
*/
abstract contract GaugeActions is IBaseRelayerLibrary {
using SafeERC20 for IERC20;
IBalancerMinter private immutable _balancerMinter;
bool private immutable _canCallUserCheckpoint;
/**
* @dev The zero address may be passed as balancerMinter to safely disable features
* which only exist on mainnet
*/
constructor(IBalancerMinter balancerMinter, bool canCallUserCheckpoint) {
_balancerMinter = balancerMinter;
_canCallUserCheckpoint = canCallUserCheckpoint;
}
/**
* @notice Returns true if the relayer is configured to checkpoint gauges directly via `user_checkpoint`.
* @dev This method is not expected to be called inside `multicall` so it is not marked as `payable`.
*/
function canCallUserCheckpoint() external view returns (bool) {
return _canCallUserCheckpoint;
}
function gaugeDeposit(
IStakingLiquidityGauge gauge,
address sender,
address recipient,
uint256 amount
) external payable {
// We can query which token to pull and approve from the wrapper contract.
IERC20 bptToken = gauge.lp_token();
amount = _resolveAmountPullTokenAndApproveSpender(bptToken, address(gauge), amount, sender);
gauge.deposit(amount, recipient);
}
function gaugeWithdraw(
IStakingLiquidityGauge gauge,
address sender,
address recipient,
uint256 amount
) external payable {
amount = _resolveAmountAndPullToken(gauge, amount, sender);
// No approval is needed here, as the gauge Tokens are burned directly from the relayer's account.
gauge.withdraw(amount);
// Gauge does not support withdrawing BPT to another address atomically.
// If intended recipient is not the relayer then forward the withdrawn BPT on to the recipient.
if (recipient != address(this)) {
IERC20 bptToken = gauge.lp_token();
bptToken.safeTransfer(recipient, amount);
}
}
function gaugeMint(address[] calldata gauges, uint256 outputReference) external payable {
uint256 balMinted = _balancerMinter.mintManyFor(gauges, msg.sender);
_setChainedReference(outputReference, balMinted);
}
function gaugeSetMinterApproval(
bool approval,
address user,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable {
_balancerMinter.setMinterApprovalWithSignature(address(this), approval, user, deadline, v, r, s);
}
function gaugeClaimRewards(IStakingLiquidityGauge[] calldata gauges) external payable {
uint256 numGauges = gauges.length;
for (uint256 i; i < numGauges; ++i) {
gauges[i].claim_rewards(msg.sender);
}
}
/**
* @notice Perform a user checkpoint for the given user on the given set of gauges.
* @dev Both mainnet and child chain gauges are supported.
*/
function gaugeCheckpoint(address user, IStakingLiquidityGauge[] calldata gauges) external payable {
if (_canCallUserCheckpoint) {
_checkpointGaugesViaUserCheckpoint(user, gauges);
} else {
_checkpointGaugesViaUserBalance(user, gauges);
}
}
function _checkpointGaugesViaUserCheckpoint(address user, IStakingLiquidityGauge[] calldata gauges) internal {
uint256 numGauges = gauges.length;
// In L2s (child chain gauges), `user_checkpoint` is not permissioned, so we can just call it directly.
for (uint256 i = 0; i < numGauges; ++i) {
gauges[i].user_checkpoint(user);
}
}
function _checkpointGaugesViaUserBalance(address user, IStakingLiquidityGauge[] calldata gauges) internal {
uint256 numGauges = gauges.length;
IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](numGauges);
// In mainnet, `user_checkpoint` is permissioned for liquidity gauges, so we cannot call it directly.
// However, some non-permissioned actions cause the gauge to checkpoint a user as a side effect,
// even if the operation itself is a no-op. The simplest of these is a gauge token transfer, which we
// perform here. Since the Vault has an unlimited allowance for gauge tokens, and user balance
// operations use the Vault allowance, no approvals are necessary.
// The amount has to be greater than 0 for the checkpoint to take place, so we use 1 wei.
// There is no actual value transfer since the sender and the recipient are the same.
for (uint256 i = 0; i < numGauges; ++i) {
// We first prepare all the transfer operations for each of the gauges.
ops[i] = IVault.UserBalanceOp({
asset: IAsset(address(gauges[i])),
amount: 1,
sender: user,
recipient: payable(address(user)),
kind: IVault.UserBalanceOpKind.TRANSFER_EXTERNAL
});
}
// And we execute all of them at once.
getVault().manageUserBalance(ops);
}
}