Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: re-add BalancerPoolTokenLiquidator #52

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/liquidators/BalancerPoolTokenLiquidator.sol
1 change: 0 additions & 1 deletion .solhitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
node_modules
lib

src/liquidators/BalancerPoolTokenLiquidator.sol
113 changes: 113 additions & 0 deletions src/liquidators/BalancerPoolTokenLiquidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* prettier-ignore */

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";

import "../external/balancer/IBalancerPool.sol";
import "../external/uniswap/IUniswapV2Router02.sol";

import "./IRedemptionStrategy.sol";

/**
* @title BalancerPoolTokenLiquidator
* @notice Exchanges seized Balancer Pool Token (BPT) collateral for underlying tokens for use as a step in a liquidation.
* @author David Lucid <[email protected]> (https://github.com/davidlucid)
*/
contract BalancerPoolTokenLiquidator is IRedemptionStrategy {
using SafeERC20Upgradeable for IERC20Upgradeable;

/**
* @dev Internal function to approve unlimited tokens of `erc20Contract` to `to`.
*/
function safeApprove(
IERC20Upgradeable token,
address to,
uint256 minAmount
) private {
uint256 allowance = token.allowance(address(this), to);

if (allowance < minAmount) {
if (allowance > 0) token.safeApprove(to, 0);
token.safeApprove(to, uint256(-1));
}
}

/**
* @notice Redeems custom collateral `token` for an underlying token.
* @param inputToken The input wrapped token to be redeemed for an underlying token.
* @param inputAmount The amount of the input wrapped token to be redeemed for an underlying token.
* @param strategyData The ABI-encoded data to be used in the redemption strategy logic.
* @return outputToken The underlying ERC20 token outputted.
* @return outputAmount The quantity of underlying tokens outputted.
*/
function redeem(
IERC20Upgradeable inputToken,
uint256 inputAmount,
bytes memory strategyData
)
external
override
returns (IERC20Upgradeable outputToken, uint256 outputAmount)
{
// Exit Balancer pool
IBalancerPool balancerPool = IBalancerPool(address(inputToken));
address[] memory tokens = balancerPool.getFinalTokens();
uint256[] memory minAmountsOut = new uint256[](tokens.length);
balancerPool.exitPool(inputAmount, minAmountsOut);

// Swap underlying tokens
(IUniswapV2Router02 uniswapV2Router, address[][] memory swapPaths) = abi.decode(strategyData, (IUniswapV2Router02, address[][]));
require(
swapPaths.length == tokens.length,
"Swap paths array length must match the number of underlying tokens in the Balancer pool."
);
for (uint256 i = 1; i < swapPaths.length; i++)
require(
(
swapPaths[0].length > 0
? swapPaths[0][swapPaths[0].length - 1]
: tokens[0]
) ==
(
swapPaths[i].length > 0
? swapPaths[i][swapPaths[i].length - 1]
: tokens[i]
),
"All underlying token swap paths must output the same token."
);

for (uint256 i = 0; i < swapPaths.length; i++)
if (
swapPaths[i].length > 0 &&
swapPaths[i][swapPaths[i].length - 1] != tokens[i]
) {
uint256 swapAmountIn = IERC20Upgradeable(tokens[i]).balanceOf(
address(this)
);
safeApprove(
IERC20Upgradeable(tokens[i]),
address(uniswapV2Router),
swapAmountIn
);
uniswapV2Router.swapExactTokensForTokens(
swapAmountIn,
0,
swapPaths[i],
address(this),
block.timestamp
);
}

// Get new collateral
outputToken = IERC20Upgradeable(
swapPaths[0].length > 0
? swapPaths[0][swapPaths[0].length - 1]
: tokens[0]
);
outputAmount = outputToken.balanceOf(address(this));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.8.10;

interface BalancerPoolTokenLiquidator {
function redeem(
address inputToken,
uint256 inputAmount,
bytes memory strategyData
) external returns (address outputToken, uint256 outputAmount);
}