-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBalancerQueries.sol
149 lines (126 loc) · 6.12 KB
/
BalancerQueries.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
// 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/solidity-utils/helpers/BalancerErrors.sol";
import "@balancer-labs/v2-interfaces/contracts/solidity-utils/misc/IWETH.sol";
import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
import "@balancer-labs/v2-interfaces/contracts/vault/IBasePool.sol";
import "@balancer-labs/v2-interfaces/contracts/standalone-utils/IBalancerQueries.sol";
import "@balancer-labs/v2-vault/contracts/AssetHelpers.sol";
import "@balancer-labs/v2-solidity-utils/contracts/helpers/InputHelpers.sol";
/**
* @dev This contract simply builds on top of the Balancer V2 architecture to provide useful helpers to users.
* It connects different functionalities of the protocol components to allow accessing information that would
* have required a more cumbersome setup if we wanted to provide these already built-in.
*/
contract BalancerQueries is IBalancerQueries, AssetHelpers {
IVault public immutable vault;
constructor(IVault _vault) AssetHelpers(_vault.WETH()) {
vault = _vault;
}
function querySwap(IVault.SingleSwap memory singleSwap, IVault.FundManagement memory funds)
external
override
returns (uint256)
{
// The Vault only supports batch swap queries, so we need to convert the swap call into an equivalent batch
// swap. The result will be identical.
// The main difference between swaps and batch swaps is that batch swaps require an assets array. We're going
// to place the asset in at index 0, and asset out at index 1.
IAsset[] memory assets = new IAsset[](2);
assets[0] = singleSwap.assetIn;
assets[1] = singleSwap.assetOut;
IVault.BatchSwapStep[] memory swaps = new IVault.BatchSwapStep[](1);
swaps[0] = IVault.BatchSwapStep({
poolId: singleSwap.poolId,
assetInIndex: 0,
assetOutIndex: 1,
amount: singleSwap.amount,
userData: singleSwap.userData
});
int256[] memory assetDeltas = vault.queryBatchSwap(singleSwap.kind, swaps, assets, funds);
// Batch swaps return the full Vault asset deltas, which in the special case of a single step swap contains more
// information than we need (as the amount in is known in a GIVEN_IN swap, and the amount out is known in a
// GIVEN_OUT swap). We extract the information we're interested in.
if (singleSwap.kind == IVault.SwapKind.GIVEN_IN) {
// The asset out will have a negative Vault delta (the assets are coming out of the Pool and the user is
// receiving them), so make it positive to match the `swap` interface.
_require(assetDeltas[1] <= 0, Errors.SHOULD_NOT_HAPPEN);
return uint256(-assetDeltas[1]);
} else {
// The asset in will have a positive Vault delta (the assets are going into the Pool and the user is
// sending them), so we don't need to do anything.
return uint256(assetDeltas[0]);
}
}
function queryBatchSwap(
IVault.SwapKind kind,
IVault.BatchSwapStep[] memory swaps,
IAsset[] memory assets,
IVault.FundManagement memory funds
) external override returns (int256[] memory assetDeltas) {
return vault.queryBatchSwap(kind, swaps, assets, funds);
}
function queryJoin(
bytes32 poolId,
address sender,
address recipient,
IVault.JoinPoolRequest memory request
) external override returns (uint256 bptOut, uint256[] memory amountsIn) {
(address pool, ) = vault.getPool(poolId);
(uint256[] memory balances, uint256 lastChangeBlock) = _validateAssetsAndGetBalances(poolId, request.assets);
IProtocolFeesCollector feesCollector = vault.getProtocolFeesCollector();
(bptOut, amountsIn) = IBasePool(pool).queryJoin(
poolId,
sender,
recipient,
balances,
lastChangeBlock,
feesCollector.getSwapFeePercentage(),
request.userData
);
}
function queryExit(
bytes32 poolId,
address sender,
address recipient,
IVault.ExitPoolRequest memory request
) external override returns (uint256 bptIn, uint256[] memory amountsOut) {
(address pool, ) = vault.getPool(poolId);
(uint256[] memory balances, uint256 lastChangeBlock) = _validateAssetsAndGetBalances(poolId, request.assets);
IProtocolFeesCollector feesCollector = vault.getProtocolFeesCollector();
(bptIn, amountsOut) = IBasePool(pool).queryExit(
poolId,
sender,
recipient,
balances,
lastChangeBlock,
feesCollector.getSwapFeePercentage(),
request.userData
);
}
function _validateAssetsAndGetBalances(bytes32 poolId, IAsset[] memory expectedAssets)
internal
view
returns (uint256[] memory balances, uint256 lastChangeBlock)
{
IERC20[] memory actualTokens;
IERC20[] memory expectedTokens = _translateToIERC20(expectedAssets);
(actualTokens, balances, lastChangeBlock) = vault.getPoolTokens(poolId);
InputHelpers.ensureInputLengthMatch(actualTokens.length, expectedTokens.length);
for (uint256 i = 0; i < actualTokens.length; ++i) {
IERC20 token = actualTokens[i];
_require(token == expectedTokens[i], Errors.TOKENS_MISMATCH);
}
}
}