Skip to content

Instantly share code, notes, and snippets.

@suhailgme
Created June 25, 2020 19:32
Show Gist options
  • Save suhailgme/821da15f69b6e9d2f7b5a7b36e43d829 to your computer and use it in GitHub Desktop.
Save suhailgme/821da15f69b6e9d2f7b5a7b36e43d829 to your computer and use it in GitHub Desktop.
Balancer_ZapIn_General_V1.sol
// File: browser/Balancer_ZapIn_General_V1.sol
// Copyright (C) 2020 defizap, dipeshsukhani, nodarjanashia, suhailg, sumitrajput, apoorvlathey
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 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 Affero General Public License for more details.
//
// Visit <https://www.gnu.org/licenses/>for a copy of the GNU Affero General Public License
// File: localhost/defizap/node_modules/@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
///@author DeFiZap
///@notice this contract helps in investing in balancer pools through ETH or ERC20 tokens
pragma solidity 0.5.12;
import "../../node_modules/@openzeppelin/contracts/utils/Address.sol";
import "../../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../node_modules/@openzeppelin/contracts/ownership/Ownable.sol";
import "../../node_modules/@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
interface IBFactory_Balancer_ZapIn_General_V1 {
function isBPool(address b) external view returns (bool);
}
interface IBPool_Balancer_ZapIn_General_V1 {
function joinswapExternAmountIn(
address tokenIn,
uint256 tokenAmountIn,
uint256 minPoolAmountOut
) external payable returns (uint256 poolAmountOut);
function isBound(address t) external view returns (bool);
function getFinalTokens() external view returns (address[] memory tokens);
function totalSupply() external view returns (uint256);
function getDenormalizedWeight(address token)
external
view
returns (uint256);
function getTotalDenormalizedWeight() external view returns (uint256);
function getSwapFee() external view returns (uint256);
function calcPoolOutGivenSingleIn(
uint256 tokenBalanceIn,
uint256 tokenWeightIn,
uint256 poolSupply,
uint256 totalWeight,
uint256 tokenAmountIn,
uint256 swapFee
) external pure returns (uint256 poolAmountOut);
function getBalance(address token) external view returns (uint256);
}
interface IuniswapFactory_Balancer_ZapIn_General_V1 {
function getExchange(address token)
external
view
returns (address exchange);
}
interface Iuniswap_Balancer_ZapIn_General_V1 {
function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline)
external
payable
returns (uint256 tokens_bought);
// converting ERC20 to ERC20 and transfer
function tokenToTokenSwapInput(
uint256 tokens_sold,
uint256 min_tokens_bought,
uint256 min_eth_bought,
uint256 deadline,
address token_addr
) external returns (uint256 tokens_bought);
function getTokenToEthInputPrice(uint256 tokens_sold)
external
view
returns (uint256 eth_bought);
function getEthToTokenInputPrice(uint256 eth_sold)
external
view
returns (uint256 tokens_bought);
function balanceOf(address _owner) external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address from, address to, uint256 tokens)
external
returns (bool success);
}
interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
contract Balancer_ZapIn_General_V1 is ReentrancyGuard, Ownable {
using SafeMath for uint256;
using Address for address;
bool private stopped = false;
uint16 public goodwill;
address public dzgoodwillAddress;
uint256 public defaultSlippage;
IuniswapFactory_Balancer_ZapIn_General_V1 public UniSwapFactoryAddress = IuniswapFactory_Balancer_ZapIn_General_V1(
0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95
);
IBFactory_Balancer_ZapIn_General_V1 BalancerFactory = IBFactory_Balancer_ZapIn_General_V1(
0x9424B1412450D0f8Fc2255FAf6046b98213B76Bd
);
address wethTokenAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
event Zapin(
address _toWhomToIssue,
address _toBalancerPoolAddress,
uint256 _OutgoingBPT
);
constructor(
uint16 _goodwill,
address _dzgoodwillAddress,
uint256 _slippage
) public {
goodwill = _goodwill;
dzgoodwillAddress = _dzgoodwillAddress;
defaultSlippage = _slippage;
}
// circuit breaker modifiers
modifier stopInEmergency {
if (stopped) {
revert("Temporarily Paused");
} else {
_;
}
}
/**
@notice This function is used to invest in given balancer pool through ETH/ERC20 Tokens
@param _FromTokenContractAddress The token used for investment (address(0x00) if ether)
@param _ToBalancerPoolAddress The address of balancer pool to zapin
@param _amount The amount of ERC to invest
@param slippage slippage user wants
@return success or failure
*/
function EasyZapIn(
address _FromTokenContractAddress,
address _ToBalancerPoolAddress,
uint256 _amount,
uint256 slippage
) public payable nonReentrant stopInEmergency returns (uint256 tokensBought) {
require(
BalancerFactory.isBPool(_ToBalancerPoolAddress),
"Invalid Balancer Pool"
);
uint256 withSlippage = slippage > 0 && slippage < 10000
? slippage
: defaultSlippage;
if (_FromTokenContractAddress == address(0)) {
require(msg.value > 0, "ERR: No ETH sent");
address _ToTokenContractAddress = _getBestDeal(
_ToBalancerPoolAddress,
msg.value,
_FromTokenContractAddress
);
tokensBought = _performZapIn(
msg.sender,
_FromTokenContractAddress,
_ToBalancerPoolAddress,
msg.value,
_ToTokenContractAddress,
withSlippage
);
return tokensBought;
}
require(_amount > 0, "ERR: No ERC sent");
require(msg.value == 0, "ERR: ETH sent with tokens");
//transfer tokens to contract
require(
IERC20(_FromTokenContractAddress).transferFrom(
msg.sender,
address(this),
_amount
),
"Error in transferring ERC: 1"
);
address _ToTokenContractAddress = _getBestDeal(
_ToBalancerPoolAddress,
_amount,
_FromTokenContractAddress
);
tokensBought = _performZapIn(
msg.sender,
_FromTokenContractAddress,
_ToBalancerPoolAddress,
_amount,
_ToTokenContractAddress,
withSlippage
);
}
/**
@notice This function is used to invest in given balancer pool through ETH/ERC20 Tokens with interface
@param _toWhomToIssue The user address who want to invest
@param _FromTokenContractAddress The token used for investment (address(0x00) if ether)
@param _ToBalancerPoolAddress The address of balancer pool to zapin
@param _amount The amount of ERC to invest
@param _IntermediateToken The token for intermediate conversion before zapin
@param slippage slippage user wants
@return The quantity of Balancer Pool tokens returned
*/
function ZapIn(
address payable _toWhomToIssue,
address _FromTokenContractAddress,
address _ToBalancerPoolAddress,
uint256 _amount,
address _IntermediateToken,
uint256 slippage
) public payable nonReentrant stopInEmergency returns (uint256 tokensBought) {
uint256 withSlippage = slippage > 0 && slippage < 10000
? slippage
: defaultSlippage;
if (_FromTokenContractAddress == address(0)) {
require(msg.value > 0, "ERR: No ETH sent");
tokensBought = _performZapIn(
_toWhomToIssue,
_FromTokenContractAddress,
_ToBalancerPoolAddress,
msg.value,
_IntermediateToken,
withSlippage
);
return tokensBought;
}
require(_amount > 0, "ERR: No ERC sent");
require(msg.value == 0, "ERR: ETH sent with tokens");
//transfer tokens to contract
require(
IERC20(_FromTokenContractAddress).transferFrom(
_toWhomToIssue,
address(this),
_amount
),
"Error in transferring ERC: 2"
);
tokensBought = _performZapIn(
_toWhomToIssue,
_FromTokenContractAddress,
_ToBalancerPoolAddress,
_amount,
_IntermediateToken,
withSlippage
);
}
/**
@notice This function internally called by ZapIn() and EasyZapIn()
@param _toWhomToIssue The user address who want to invest
@param _FromTokenContractAddress The token used for investment (address(0x00) if ether)
@param _ToBalancerPoolAddress The address of balancer pool to zapin
@param _amount The amount of ETH/ERC to invest
@param _IntermediateToken The token for intermediate conversion before zapin
@param slippage slippage user wants
@return The quantity of Balancer Pool tokens returned
*/
function _performZapIn(
address _toWhomToIssue,
address _FromTokenContractAddress,
address _ToBalancerPoolAddress,
uint256 _amount,
address _IntermediateToken,
uint256 slippage
) internal returns (uint256 tokensBought) {
// check if isBound()
bool isBound = IBPool_Balancer_ZapIn_General_V1(_ToBalancerPoolAddress)
.isBound(_FromTokenContractAddress);
uint256 balancerTokens;
if (isBound) {
balancerTokens = _enter2Balancer(
_ToBalancerPoolAddress,
_FromTokenContractAddress,
_amount
);
} else {
// swap tokens or eth
uint256 tokenBought;
if (_FromTokenContractAddress == address(0)) {
tokenBought = _eth2Token(_IntermediateToken, slippage);
} else {
tokenBought = _token2Token(
_FromTokenContractAddress,
_IntermediateToken,
_amount,
slippage
);
}
//get BPT
balancerTokens = _enter2Balancer(
_ToBalancerPoolAddress,
_IntermediateToken,
tokenBought
);
}
//transfer goodwill
uint256 goodwillPortion = _transferGoodwill(
_ToBalancerPoolAddress,
balancerTokens
);
emit Zapin(
_toWhomToIssue,
_ToBalancerPoolAddress,
SafeMath.sub(balancerTokens, goodwillPortion)
);
//transfer tokens to user
IERC20(_ToBalancerPoolAddress).transfer(
_toWhomToIssue,
SafeMath.sub(balancerTokens, goodwillPortion)
);
return SafeMath.sub(balancerTokens, goodwillPortion);
}
/**
@notice This function is used to calculate and transfer goodwill
@param _tokenContractAddress Token in which goodwill is deducted
@param tokens2Trade The total amount of tokens to be zapped in
@return The quantity of goodwill deducted
*/
function _transferGoodwill(
address _tokenContractAddress,
uint256 tokens2Trade
) internal returns (uint256 goodwillPortion) {
goodwillPortion = SafeMath.div(
SafeMath.mul(tokens2Trade, goodwill),
10000
);
if (goodwillPortion == 0) {
return 0;
}
require(
IERC20(_tokenContractAddress).transfer(
dzgoodwillAddress,
goodwillPortion
),
"Error in transferring BPT:1"
);
}
/**
@notice This function is used to zapin to balancer pool
@param _ToBalancerPoolAddress The address of balancer pool to zap in
@param _FromTokenContractAddress The token used to zap in
@param tokens2Trade The amount of tokens to invest
@return The quantity of Balancer Pool tokens returned
*/
function _enter2Balancer(
address _ToBalancerPoolAddress,
address _FromTokenContractAddress,
uint256 tokens2Trade
) internal returns (uint256 poolTokensOut) {
require(
IBPool_Balancer_ZapIn_General_V1(_ToBalancerPoolAddress).isBound(
_FromTokenContractAddress
),
"Token not bound"
);
uint256 allowance = IERC20(_FromTokenContractAddress).allowance(
address(this),
_ToBalancerPoolAddress
);
if (allowance < tokens2Trade) {
IERC20(_FromTokenContractAddress).approve(
_ToBalancerPoolAddress,
uint256(-1)
);
}
poolTokensOut = IBPool_Balancer_ZapIn_General_V1(_ToBalancerPoolAddress)
.joinswapExternAmountIn(_FromTokenContractAddress, tokens2Trade, 1);
require(poolTokensOut > 0, "Error in entering balancer pool");
}
/**
@notice This function finds best token from the final tokens of balancer pool
@param _ToBalancerPoolAddress The address of balancer pool to zap in
@param _amount amount of eth/erc to invest
@param _FromTokenContractAddress the token address which is used to invest
@return The token address having max liquidity
*/
function _getBestDeal(
address _ToBalancerPoolAddress,
uint256 _amount,
address _FromTokenContractAddress
) internal view returns (address _token) {
//get token list
address[] memory tokens = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).getFinalTokens();
uint256 amount = _amount;
if (_FromTokenContractAddress != address(0)) {
// check if isBound()
bool isBound = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).isBound(_FromTokenContractAddress);
if (isBound) return _FromTokenContractAddress;
//get eth value for given token
Iuniswap_Balancer_ZapIn_General_V1 FromUniSwapExchangeContractAddress
= Iuniswap_Balancer_ZapIn_General_V1(
UniSwapFactoryAddress.getExchange(_FromTokenContractAddress)
);
//get qty of eth expected
amount = Iuniswap_Balancer_ZapIn_General_V1(
FromUniSwapExchangeContractAddress
).getTokenToEthInputPrice(_amount);
}
uint256 maxBPT;
for (uint256 index = 0; index < tokens.length; index++) {
Iuniswap_Balancer_ZapIn_General_V1 FromUniSwapExchangeContractAddress
= Iuniswap_Balancer_ZapIn_General_V1(
UniSwapFactoryAddress.getExchange(tokens[index])
);
if (address(FromUniSwapExchangeContractAddress) == address(0)) {
continue;
}
//get qty of tokens
uint256 expectedTokens = Iuniswap_Balancer_ZapIn_General_V1(
FromUniSwapExchangeContractAddress
).getEthToTokenInputPrice(amount);
//get bpt for given tokens
uint256 expectedBPT = getToken2BPT(
_ToBalancerPoolAddress,
expectedTokens,
tokens[index]
);
//get token giving max BPT
if (maxBPT < expectedBPT) {
maxBPT = expectedBPT;
_token = tokens[index];
}
}
}
/**
@notice Function gives the expected amount of pool tokens on investing
@param _ToBalancerPoolAddress Address of balancer pool to zapin
@param _IncomingERC The amount of ERC to invest
@param _FromToken Address of token to zap in with
@return Amount of BPT token
*/
function getToken2BPT(
address _ToBalancerPoolAddress,
uint256 _IncomingERC,
address _FromToken
) internal view returns (uint256 tokensReturned) {
uint256 totalSupply = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).totalSupply();
uint256 swapFee = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).getSwapFee();
uint256 totalWeight = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).getTotalDenormalizedWeight();
uint256 balance = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).getBalance(_FromToken);
uint256 denorm = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).getDenormalizedWeight(_FromToken);
tokensReturned = IBPool_Balancer_ZapIn_General_V1(
_ToBalancerPoolAddress
).calcPoolOutGivenSingleIn(
balance,
denorm,
totalSupply,
totalWeight,
_IncomingERC,
swapFee
);
}
/**
@notice This function is used to buy tokens from eth
@param _tokenContractAddress Token address which we want to buy
@return The quantity of token bought
*/
function _eth2Token(address _tokenContractAddress, uint256 slippage)
internal
returns (uint256 tokenBought)
{
if(_tokenContractAddress == wethTokenAddress) {
IWETH(wethTokenAddress).deposit.value(msg.value)();
return msg.value;
}
Iuniswap_Balancer_ZapIn_General_V1 FromUniSwapExchangeContractAddress
= Iuniswap_Balancer_ZapIn_General_V1(
UniSwapFactoryAddress.getExchange(_tokenContractAddress)
);
uint256 minTokenBought = FromUniSwapExchangeContractAddress
.getEthToTokenInputPrice(msg.value);
minTokenBought = SafeMath.div(
SafeMath.mul(minTokenBought, SafeMath.sub(10000, slippage)),
10000
);
tokenBought = FromUniSwapExchangeContractAddress
.ethToTokenSwapInput
.value(msg.value)(minTokenBought, SafeMath.add(now, 300));
}
/**
@notice This function is used to swap tokens
@param _FromTokenContractAddress The token address to swap from
@param _ToTokenContractAddress The token address to swap to
@param tokens2Trade The amount of tokens to swap
@return The quantity of tokens bought
*/
function _token2Token(
address _FromTokenContractAddress,
address _ToTokenContractAddress,
uint256 tokens2Trade,
uint256 slippage
) internal returns (uint256 tokenBought) {
Iuniswap_Balancer_ZapIn_General_V1 FromUniSwapExchangeContractAddress
= Iuniswap_Balancer_ZapIn_General_V1(
UniSwapFactoryAddress.getExchange(_FromTokenContractAddress)
);
Iuniswap_Balancer_ZapIn_General_V1 ToUniSwapExchangeContractAddress
= Iuniswap_Balancer_ZapIn_General_V1(
UniSwapFactoryAddress.getExchange(_ToTokenContractAddress)
);
IERC20(_FromTokenContractAddress).approve(
address(FromUniSwapExchangeContractAddress),
tokens2Trade
);
uint256 minEthBought = FromUniSwapExchangeContractAddress
.getTokenToEthInputPrice(tokens2Trade);
minEthBought = SafeMath.div(
SafeMath.mul(minEthBought, SafeMath.sub(10000, slippage)),
10000
);
uint256 minTokenBought = ToUniSwapExchangeContractAddress
.getEthToTokenInputPrice(minEthBought);
minTokenBought = SafeMath.div(
SafeMath.mul(minTokenBought, SafeMath.sub(10000, slippage)),
10000
);
tokenBought = FromUniSwapExchangeContractAddress.tokenToTokenSwapInput(
tokens2Trade,
minTokenBought,
minEthBought,
SafeMath.add(now, 1800),
_ToTokenContractAddress
);
require(tokenBought > 0, "Error in swapping ERC: 1");
}
function updateSlippage(uint256 _newSlippage) public onlyOwner {
require(
_newSlippage > 0 && _newSlippage < 10000,
"Slippage Value not allowed"
);
defaultSlippage = _newSlippage;
}
function set_new_goodwill(uint16 _new_goodwill) public onlyOwner {
require(
_new_goodwill >= 0 && _new_goodwill < 10000,
"GoodWill Value not allowed"
);
goodwill = _new_goodwill;
}
function set_new_dzgoodwillAddress(address _new_dzgoodwillAddress)
public
onlyOwner
{
dzgoodwillAddress = _new_dzgoodwillAddress;
}
function inCaseTokengetsStuck(IERC20 _TokenAddress) public onlyOwner {
uint256 qty = _TokenAddress.balanceOf(address(this));
_TokenAddress.transfer(owner(), qty);
}
// - to Pause the contract
function toggleContractActive() public onlyOwner {
stopped = !stopped;
}
// - to withdraw any ETH balance sitting in the contract
function withdraw() public onlyOwner {
uint256 contractBalance = address(this).balance;
address payable _to = owner().toPayable();
_to.transfer(contractBalance);
}
// - to kill the contract
function destruct() public onlyOwner {
address payable _to = owner().toPayable();
selfdestruct(_to);
}
function() external payable {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment