Skip to content
Open
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
110 changes: 105 additions & 5 deletions contracts/BAMM.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//SPDX-License-Identifier: Unlicense
pragma solidity >= 0.6.6;

import '@uniswap/v2-periphery/contracts/libraries/SafeMath.sol';
import "./QuickswapLPManager.sol";
import "./CropJoinAdapter.sol";
import '@uniswap/v2-periphery/contracts/libraries/SafeMath.sol';
import "@openzeppelin/contracts/access/Ownable.sol";


Expand All @@ -13,6 +13,11 @@ contract BAMM is UniswapLPManager, CropJoinAdapter, Ownable {
// mapping from token to price feed
PriceFeed immutable collateralFeed;

uint constant public PRECISION = 1e18;

event UserDeposit(address indexed user, uint lusdAmount, uint numShares);
event UserWithdraw(address indexed user, uint collateralAmount, uint lpAmount, uint token0Amount, uint token1Amount, uint shares);

constructor(
IERC20 _lpToken,
IERC20 _token0,
Expand All @@ -31,23 +36,118 @@ contract BAMM is UniswapLPManager, CropJoinAdapter, Ownable {
require(_collateralToken != _lpToken, "collateral cannot be lp token");
require(_collateralToken != _token0, "collateral cannot be token 0");
require(_collateralToken != _token1, "collateral cannot be token 1");
require(_collateralToken != _cropToken, "collateral cannot be cropToken");

collateralToken = _collateralToken;
collateralFeed = _collateralFeed;
}

function getUSDValue() public returns(uint backstop, uint collateral) {
function getUSDValue() public returns(uint backstop, uint collateral, uint staked) {
backstop = getUSDBalance();
collateral = collateralToken.balanceOf(address(this)).mul(collateralFeed.getPrice(token0)) / (uint(10) ** collateralToken.decimals());
//staked = quickswapStaking.balanceOf(address(this));
}

function deposit(uint lpTokenAmount) public {
// TODO
// get the total usd value
(uint backstop, uint collateral, uint staked) = getUSDValue();
uint totalUsdValue = backstop.add(collateral);
// get the USD deposit amount
uint depositAmountUSD = lpTokenAmount.mul(getLPValue());

require(feed.getPrice(token0) > 0 && feed.getPrice(token1) > 0, "deposit: price feed is down");
require(totalUsdValue > 0 || totalSupply() == 0, "deposit: system is rekt");

// caclulate the share
uint newShare = PRECISION;
if(totalSupply() > 0) newShare = totalSupply().mul(depositAmountUSD) / totalUsdValue;

// deposit
require(lpToken.transferFrom(msg.sender, address(this), lpTokenAmount), "deposit: transferFrom failed");

// update LP token
mint(msg.sender, newShare);

//stake the LP token for rewards
lpToken.approve(address(quickswapStaking), lpTokenAmount);
quickswapStaking.stake(lpTokenAmount);

emit UserDeposit(msg.sender, lpTokenAmount, newShare);
}

function withdraw(uint shares) public {
// TODO
uint totalSupply = totalSupply();
uint bammLp = IERC20(address(quickswapStaking)).balanceOf(address(this));
uint lpAmount = bammLp.mul(shares) / totalSupply;
uint token1Amount = token1.balanceOf(address(this)).mul(shares) / totalSupply;
uint token0Amount = token0.balanceOf(address(this)).mul(shares) / totalSupply;
uint collAmount = collateralToken.balanceOf(address(this)).mul(shares) / totalSupply;

burn(msg.sender, shares);

if(collAmount > 0){
require(collateralToken.transfer(msg.sender, collAmount), "withdraw: collateralToken transfer failed");
}

if(lpAmount > 0){
quickswapStaking.withdraw(lpAmount);
require(lpToken.transfer(msg.sender, lpAmount), "withdraw: lpToken transfer failed");
}

if(token0Amount > 0){
require(token0.transfer(msg.sender, token0Amount), "withdraw: token0 transfer failed");
}

if(token1Amount > 0){
require(token1.transfer(msg.sender, token1Amount), "withdraw: token0 transfer failed");
}

emit UserWithdraw(msg.sender, collAmount, lpAmount, token0Amount, token1Amount, shares);
}

/*
function getSwapEthAmount(uint lusdQty) public view returns(uint ethAmount) {
uint lusdBalance = LUSD.balanceOf(address(this));
uint ethBalance = address(this).balance;

uint eth2usdPrice = fetchPrice();
if(eth2usdPrice == 0) return 0; // chainlink is down

uint ethUsdValue = ethBalance.mul(eth2usdPrice) / PRECISION;
uint maxReturn = addBps(lusdQty.mul(PRECISION) / eth2usdPrice, int(maxDiscount));

uint xQty = lusdQty;
uint xBalance = lusdBalance;
uint yBalance = lusdBalance.add(ethUsdValue.mul(2));

uint usdReturn = getReturn(xQty, xBalance, yBalance, A);
uint basicEthReturn = usdReturn.mul(PRECISION) / eth2usdPrice;

if(ethBalance < basicEthReturn) basicEthReturn = ethBalance; // cannot give more than balance
if(maxReturn < basicEthReturn) basicEthReturn = maxReturn;

ethAmount = basicEthReturn;
}
*/

function getSwapAmount(address token, uint amount) public view returns(uint swapAmount) {
require(token == address(token0) || token == address(token1), "getSwapAmount faild: swap can only be made between the pair tokens");
// check which token is greater to permit swap only for the one we want
uint tokenAmount = IERC20(token).balanceOf(address(this));
require(tokenAmount > 0, "getSwapAmount faild: token supply is 0");

address otherToken = token == address(token0) ? address(token1) : address(token0);
uint otherTokenAmount = IERC20(otherToken).balanceOf(address(this));
require(tokenAmount > otherTokenAmount, "getSwapAmount faild: token balance low try to swap the other token in the token pair");
// todo fetch prices
uint tokenInUsd = tokenAmount.mul(feed.getPrice(IERC20(token))) / IERC20(token).decimals();
uint otherTokenInUsd = otherTokenAmount.mul(feed.getPrice(IERC20(otherToken))) / IERC20(otherToken).decimals();
// calculate the swap amount
uint swapAmountUsd = tokenInUsd.sub(otherTokenInUsd) / 2;
swapAmount = swapAmountUsd / feed.getPrice(IERC20(otherToken));
}


function swap () public {

}
}
94 changes: 94 additions & 0 deletions contracts/fix.3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
pragma solidity =0.6.6;

import '@uniswap/v2-periphery/contracts/libraries/SafeMath.sol';

contract ERC201 {
using SafeMath for uint;

string public constant name = 'Test Token';
string public constant symbol = 'TT';
uint8 public constant decimals = 7;
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;

bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint) public nonces;

event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);

constructor(uint _totalSupply) public {
uint chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);
_mint(msg.sender, _totalSupply);
}

function _mint(address to, uint value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}

function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}

function _approve(address owner, address spender, uint value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}

function _transfer(address from, address to, uint value) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}

function approve(address spender, uint value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}

function transfer(address to, uint value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}

function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}

function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'EXPIRED');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'INVALID_SIGNATURE');
_approve(owner, spender, value);
}
}
Loading