Skip to content

Commit

Permalink
Merge pull request #1 from Max-3-7/master
Browse files Browse the repository at this point in the history
Balancerlike Adapter
  • Loading branch information
metadept authored Jan 17, 2022
2 parents 495db64 + bc3a3c4 commit 88b5ccf
Show file tree
Hide file tree
Showing 11 changed files with 638 additions and 4 deletions.
226 changes: 226 additions & 0 deletions contracts/adapters/BalancerlikeAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// ╟╗ ╔╬
// ╞╬╬ ╬╠╬
// ╔╣╬╬╬ ╠╠╠╠╦
// ╬╬╬╬╬╩ ╘╠╠╠╠╬
// ║╬╬╬╬╬ ╘╠╠╠╠╬
// ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ╒╬╬╬╬╬╬╬╜ ╠╠╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╠
// ╙╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╕ ╬╬╬╬╬╬╬╜ ╣╠╠╬╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╬╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╩
// ╙╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ╔╬╬╬╬╬╬╬ ╔╠╠╠╬╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬╬╬╬╬╠╠╠╠╝╙
// ╘╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ╒╠╠╠╬╠╬╩╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬╣╬╬╬╬╬╬╬╙
// ╣╬╬╬╬╬╬╬╬╬╬╠╣ ╣╬╠╠╠╬╩ ╚╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
// ╣╬╬╬╬╬╬╬╬╬╣ ╣╬╠╠╠╬╬ ╣╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
// ╟╬╬╬╬╬╬╬╩ ╬╬╠╠╠╠╬╬╬╬╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬╠╬╬╬╬╬╬╬
// ╬╬╬╬╬╬╬ ╒╬╬╠╠╬╠╠╬╬╬╬╬╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬
// ╬╬╬╬╬╬╬ ╬╬╬╠╠╠╠╝╝╝╝╝╝╝╠╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╚╬╬╬╬╬╬╬╬
// ╬╬╬╬╬╬╬ ╣╬╬╬╬╠╠╩ ╘╬╬╬╬╬╬╬ ╠╬╬╬╬╬╬╬ ╙╬╬╬╬╬╬╬╬
//

// Supports Balancerlike pools

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.7.0;
pragma abicoder v2;

import "../lib/SafeERC20.sol";
import "../lib/SafeMath.sol";
import "../YakAdapter.sol";
import "../interface/IVault.sol";
import "../interface/IBasePool.sol";
import "../interface/IMinimalSwapInfoPool.sol";

contract BalancerlikeAdapter is YakAdapter {
using SafeERC20 for IERC20;
using SafeMath for uint256;

address public vault;

mapping(address => mapping(address => uint128)) internal poolToTokenIndex;
mapping(address => mapping(address => address[])) internal tokensToPools;

constructor(
string memory _name,
address _vault,
address[] memory _pools,
uint256 _swapGasEstimate
) {
name = _name;
vault = _vault;
addPools(_pools);
setSwapGasEstimate(_swapGasEstimate);
}

function addPools(address[] memory _pools) public onlyOwner {
for (uint128 i = 0; i < _pools.length; i++) {
address pool = _pools[i];
bytes32 poolId = IBasePool(pool).getPoolId();
(IERC20[] memory tokens, , ) = IVault(vault).getPoolTokens(poolId);
for (uint128 j = 0; j < tokens.length; j++) {
address token = address(tokens[j]);
poolToTokenIndex[pool][token] = j;
for (uint128 k = 0; k < tokens.length; k++) {
if (j != k) {
tokensToPools[token][address(tokens[k])].push(pool);
}
}
}
}
}

function removePools(address[] memory _pools) public onlyOwner {
for (uint256 i = 0; i < _pools.length; i++) {
address pool = _pools[i];
bytes32 poolId = IBasePool(pool).getPoolId();
(IERC20[] memory tokens, , ) = IVault(vault).getPoolTokens(poolId);
for (uint128 j = 0; j < tokens.length; j++) {
address token = address(tokens[j]);
for (uint128 k = 0; k < tokens.length; k++) {
if (j != k) {
address[] memory currentPools = tokensToPools[token][
address(tokens[k])
];
for (uint128 l = 0; l < currentPools.length; l++) {
if (currentPools[l] == pool) {
delete currentPools[l];
}
}
tokensToPools[token][address(tokens[k])] = currentPools;
}
}
}
}
}

function getPools(address tokenIn, address tokenOut)
public
view
returns (address[] memory)
{
return tokensToPools[tokenIn][tokenOut];
}

function setAllowances() public override onlyOwner {}

function _approveIfNeeded(address _tokenIn, uint256 _amount)
internal
override
{
uint256 allowance = IERC20(_tokenIn).allowance(address(this), vault);
if (allowance < _amount) {
IERC20(_tokenIn).safeApprove(vault, _amount);
}
}

function _query(
uint256 _amountIn,
address _tokenIn,
address _tokenOut
) internal view override returns (uint256) {
if (_amountIn == 0 || _tokenIn == _tokenOut) {
return 0;
}

address[] memory pools = getPools(_tokenIn, _tokenOut);
if (pools.length == 0) {
return 0;
}

(, uint256 amountOut) = _getBestPoolForSwap(
pools,
_tokenIn,
_tokenOut,
_amountIn
);
return amountOut;
}

function _swap(
uint256 _amountIn,
uint256 _amountOut,
address _tokenIn,
address _tokenOut,
address to
) internal override {
address[] memory pools = getPools(_tokenIn, _tokenOut);

require(pools.length > 0, "No pools for swapping");

(address pool, ) = _getBestPoolForSwap(
pools,
_tokenIn,
_tokenOut,
_amountIn
);

require(pool != address(0), "Undefined pool");

IVault.SingleSwap memory swap;
swap.poolId = IBasePool(pool).getPoolId();
swap.kind = IVault.SwapKind.GIVEN_IN;
swap.assetIn = IAsset(_tokenIn);
swap.assetOut = IAsset(_tokenOut);
swap.amount = _amountIn;
swap.userData = "0x";

IVault.FundManagement memory fund;
fund.sender = address(this);
fund.recipient = payable(to);
fund.fromInternalBalance = false;
fund.toInternalBalance = false;

IVault(vault).swap(swap, fund, _amountOut, block.timestamp);
}

function _getBestPoolForSwap(
address[] memory pools,
address _tokenIn,
address _tokenOut,
uint256 _amountIn
) internal view returns (address bestPool, uint256 amountOut) {
amountOut = 0;
bestPool = address(0);
for (uint128 i; i < pools.length; i++) {
address pool = pools[i];

if (pool == address(0)) {
continue;
}

IPoolSwapStructs.SwapRequest memory request;
request.poolId = IBasePool(pool).getPoolId();
request.kind = IVault.SwapKind.GIVEN_IN;
request.tokenIn = IERC20(_tokenIn);
request.tokenOut = IERC20(_tokenOut);
request.amount = _amountIn;
request.userData = "0x";

uint256 newAmountOut = _getAmountOut(request, pool);
if (newAmountOut > amountOut) {
amountOut = newAmountOut;
bestPool = pool;
}
}
}

function _getAmountOut(
IPoolSwapStructs.SwapRequest memory request,
address pool
) internal view returns (uint256 amountOut) {
// Based on https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/vault/contracts/Swaps.sol#L275
(, uint256[] memory balances, ) = IVault(vault).getPoolTokens(
request.poolId
);

uint256 tokenInTotal = balances[
poolToTokenIndex[pool][address(request.tokenIn)]
];
uint256 tokenOutTotal = balances[
poolToTokenIndex[pool][address(request.tokenOut)]
];

amountOut = IMinimalSwapInfoPool(pool).onSwap(
request,
tokenInTotal,
tokenOutTotal
);
}
}
6 changes: 6 additions & 0 deletions contracts/interface/IAsset.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
9 changes: 9 additions & 0 deletions contracts/interface/IBasePool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;
pragma abicoder v2;

import "./IPoolSwapStructs.sol";

interface IBasePool is IPoolSwapStructs {
function getPoolId() external view returns (bytes32);
}
13 changes: 13 additions & 0 deletions contracts/interface/IMinimalSwapInfoPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;
pragma abicoder v2;

import "./IBasePool.sol";

interface IMinimalSwapInfoPool is IBasePool {
function onSwap(
SwapRequest memory swapRequest,
uint256 currentBalanceTokenIn,
uint256 currentBalanceTokenOut
) external view returns (uint256 amount);
}
19 changes: 19 additions & 0 deletions contracts/interface/IPoolSwapStructs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;
pragma abicoder v2;

import "./IVault.sol";

interface IPoolSwapStructs {
struct SwapRequest {
IVault.SwapKind kind;
IERC20 tokenIn;
IERC20 tokenOut;
uint256 amount;
bytes32 poolId;
uint256 lastChangeBlock;
address from;
address to;
bytes userData;
}
}
57 changes: 57 additions & 0 deletions contracts/interface/IVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;
pragma abicoder v2;

import "./IAsset.sol";
import "./IERC20.sol";

interface IVault {
enum SwapKind { GIVEN_IN, GIVEN_OUT }

struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IAsset assetIn;
IAsset assetOut;
uint256 amount;
bytes userData;
}

struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}

function swap(
SingleSwap memory singleSwap,
FundManagement memory funds,
uint256 limit,
uint256 deadline
) external payable returns (uint256);

function getPoolTokens(bytes32 poolId)
external
view
returns (
IERC20[] memory tokens,
uint256[] memory balances,
uint256 lastChangeBlock
);

struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}

function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] memory swaps,
IAsset[] memory assets,
FundManagement memory funds
) external returns (int256[] memory assetDeltas);
}
33 changes: 33 additions & 0 deletions deploy/adapters/embr/embr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { balancerlikePools, balancerlikeVaults } = require("../../../test/addresses.json")

module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy, log } = deployments
const { deployer } = await getNamedAccounts()

const NAME = 'EmbrAdapterV0'
const VAULT = balancerlikeVaults.embr
const POOLS = Object.values(balancerlikePools)
const GAS_ESTIMATE = 258000

log(NAME)
const deployResult = await deploy(NAME, {
from: deployer,
contract: "BalancerlikeAdapter",
gas: 4000000,
args: [
NAME,
VAULT,
POOLS,
GAS_ESTIMATE
],
skipIfAlreadyDeployed: true
});

if (deployResult.newlyDeployed) {
log(`- ${deployResult.contractName} deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`);
} else {
log(`- Deployment skipped, using previous deployment at: ${deployResult.address}`)
}
}

module.exports.tags = ['V0', 'adapter', 'embr', 'ausd', 'wavax'];
2 changes: 1 addition & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ module.exports = {
chainId: 43114,
forking: {
url: AVALANCHE_FORK_RPC,
blockNumber: 8487440
blockNumber: 9607002
},
accounts: {
accountsBalance: "10000000000000000000000000",
Expand Down
Loading

0 comments on commit 88b5ccf

Please sign in to comment.