Skip to content

Aave adapter #55

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

Open
wants to merge 5 commits into
base: ctoken_as_backstop
Choose a base branch
from
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
96 changes: 96 additions & 0 deletions packages/contracts/contracts/B.Protocol/APriceFeed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

interface BAMMLike {
function cBorrow() external view returns(address);
}

interface AaveOracleLike {
function getAssetPrice(address underlying) external view returns (uint);
}

interface AaveLendingPoolAddressesProviderLike {
function getPriceOracle() view external returns(AaveOracleLike);
}

interface AaveToCTokenAdapterLike {
function underlying() external view returns (address);
function decimals() external view returns(uint);
}

interface APriceFeedLike {
function decimals() view external returns(uint);
function getPriceFromBamm(address sender, address dst) view external returns(uint);
}

interface IAToken {
function UNDERLYING_ASSET_ADDRESS() view external returns(address);
function decimals() external view returns(uint);
}

contract ADegenFeed {
APriceFeedLike immutable priceFeed;
address immutable ctoken;

constructor(address _priceFeed, address _ctoken) public {
priceFeed = APriceFeedLike(_priceFeed);
ctoken = _ctoken;
}

function decimals() view public returns(uint) {
return priceFeed.decimals();
}

function latestRoundData() external view returns
(
uint80 /* roundId */,
int256 answer,
uint256 /* startedAt */,
uint256 timestamp,
uint80 /* answeredInRound */
)
{
answer = int(priceFeed.getPriceFromBamm(msg.sender, ctoken));
timestamp = block.timestamp;
}
}
/*
price feed for ctokens. a single feed for all ctokens.
*/
contract APriceFeed {
AaveLendingPoolAddressesProviderLike immutable provider;
uint public constant decimals = 18;

constructor(AaveLendingPoolAddressesProviderLike _provider) public {
provider = _provider;
}

function getPrice(address src, address dst) public view returns(uint) {
AaveOracleLike oracle = provider.getPriceOracle();

address underlyingSrc = AaveToCTokenAdapterLike(src).underlying();
address underlyingDst = IAToken(dst).UNDERLYING_ASSET_ADDRESS();

uint srcUnderlyingPrice = oracle.getAssetPrice(underlyingSrc);
uint dstUnderlyingPrice = oracle.getAssetPrice(underlyingDst);

uint price = (10 ** decimals) * dstUnderlyingPrice / srcUnderlyingPrice;


return price;
}

function getPriceFromBamm(address sender, address dst) public view returns(uint) {
address src = BAMMLike(sender).cBorrow();
return getPrice(src, dst);
}

function generateDegenFeed(address dstCToken) public returns(address) {
ADegenFeed degenFeed = new ADegenFeed(address(this), dstCToken);
return address(degenFeed);
}
}



65 changes: 65 additions & 0 deletions packages/contracts/contracts/B.Protocol/AaveBAMM.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.6.11;

import "./HundredBAMM.sol";
import "./AaveToCTokenAdapter.sol";

contract AaveBAMM is HundredBAMM {
constructor(
address _cBorrow,
bool _isCETH,
uint _maxDiscount,
address payable _feePool)
public
HundredBAMM(_cBorrow, _isCETH, _maxDiscount, _feePool)
{
// give allowance to cBorrow
IERC20(AaveToCTokenAdapter(_cBorrow).aToken()).safeApprove(_cBorrow, uint(-1));
}

function addCollateral(ICToken ctoken, AggregatorV3Interface feed) override public onlyOwner {
IERC20(address(ctoken)).safeApprove(address(ctoken), uint(-1));

super.addCollateral(ctoken, feed);
}

function removeCollateral(ICToken ctoken) override public onlyOwner {
IERC20(address(ctoken)).safeApprove(address(ctoken), uint(0));

super.removeCollateral(ctoken);
}

function canLiquidate(
ICToken cTokenBorrowed,
ICToken cTokenCollateral,
uint repayAmount
)
external
view
override
returns(bool)
{
if(address(cTokenBorrowed) != address(cBorrow.underlying())) return false;
bool validCollateral = false;
for(uint i = 0 ; i < collaterals.length ; i++) {
if(address(cTokenCollateral) == IAToken(address(collaterals[i])).UNDERLYING_ASSET_ADDRESS()) {
validCollateral = true;
break;
}
}

if(! validCollateral) return false;

// check if there is sufficient balance at the backstop
(uint err, uint ctokenBalance, /* borrow balance */, uint exchangeRateMantissa) = cBorrow.getAccountSnapshot(address(this));
if(err != 0) return false;

uint underlyingBalance = ctokenBalance.mul(exchangeRateMantissa) / 1e18;

if(repayAmount > underlyingBalance) return false;
if(repayAmount > cBorrow.getCash()) return false;

return true;
}
}
127 changes: 127 additions & 0 deletions packages/contracts/contracts/B.Protocol/AaveToCTokenAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.6.11;

import { ICToken } from "./BAMM.sol";
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./../Dependencies/Ownable.sol";

interface ILendingPoolAddressesProvider {
function getLendingPool() external view returns(address);
}

interface ILendingPool {
function withdraw(
address asset,
uint256 amount,
address to
) external;

function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;
}

interface IAToken is IERC20 {
function UNDERLYING_ASSET_ADDRESS() view external returns(address);
function decimals() view external returns(uint8);
function symbol() view external returns(string memory);
}


contract AaveToCTokenAdapter is ICToken, Ownable {
using SafeERC20 for IERC20;

ILendingPoolAddressesProvider public immutable lendingPoolAddressesProvider;
IAToken public immutable aToken;

constructor(IAToken _aToken, ILendingPoolAddressesProvider _lendingPoolAddressesProvider) public {
lendingPoolAddressesProvider = _lendingPoolAddressesProvider;
aToken = _aToken;
}

// read functions
function balanceOf(address a) external override view returns (uint) {
return aToken.balanceOf(a);
}

function underlying() public override view returns(IERC20) {
return IERC20(aToken.UNDERLYING_ASSET_ADDRESS());
}

function getCash() external override view returns(uint) {
return underlying().balanceOf(address(aToken));
}

function getAccountSnapshot(address account)
external
view
override
returns(uint err, uint cTokenBalance, uint borrowBalance, uint exchangeRateMantissa)
{
require(account == owner(), "getAccountSnapshot: only owner is supported");

err = 0;
cTokenBalance = aToken.balanceOf(account);
exchangeRateMantissa = 1e18; // 1, as balance returns the underlying value
borrowBalance = 0; // never borrow
}

function decimals() public view returns(uint8) {
return aToken.decimals();
}

function symbol() public view returns(string memory) {
return aToken.symbol();
}

// admin funcrion - this is called only once, as bamm won't be able to call it again
function setBAMM(address bamm) external onlyOwner {
transferOwnership(bamm);
}

// write functions
function redeemUnderlying(uint redeemAmount) external override onlyOwner returns (uint) {
IERC20(aToken).safeTransferFrom(msg.sender, address(this), redeemAmount);
address pool = lendingPoolAddressesProvider.getLendingPool();
IERC20(aToken).safeApprove(pool, redeemAmount);
ILendingPool(pool).withdraw(address(underlying()), redeemAmount, msg.sender);

return 0;
}

function liquidateBorrow(address borrower, uint amount, address collateral) external override onlyOwner returns (uint) {
address collateralUnderlying = address(IAToken(collateral).UNDERLYING_ASSET_ADDRESS());
address debt = address(underlying());

IERC20(debt).safeTransferFrom(msg.sender, address(this), amount);
address pool = lendingPoolAddressesProvider.getLendingPool();
IERC20(debt).safeApprove(pool, amount);

ILendingPool(pool).liquidationCall(collateralUnderlying, debt, borrower, amount, true);

// send collateral atoken to bamm
IERC20(collateral).safeTransfer(msg.sender, IERC20(collateral).balanceOf(address(this)));

return 0;
}

function transfer(address to, uint amount) external onlyOwner returns (bool) {
IERC20(aToken).safeTransferFrom(msg.sender, to, amount);

return true;
}

function transferFrom(address from, address to, uint tokens) external onlyOwner returns (bool) {
require(to == owner(), "transferFrom: only pulling by owner is supported");

IERC20(aToken).safeTransferFrom(from, address(this), tokens);
IERC20(aToken).safeTransfer(to, tokens);

return true;
}
}
5 changes: 3 additions & 2 deletions packages/contracts/contracts/B.Protocol/BAMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ contract BAMM is TokenAdapter, PriceFormula, Ownable, ReentrancyGuard {
emit ParamsSet(_A, _fee, _callerFee);
}

function addCollateral(ICToken ctoken, AggregatorV3Interface feed) external onlyOwner {
function addCollateral(ICToken ctoken, AggregatorV3Interface feed) public virtual onlyOwner {
IERC20 token = IERC20(address(ctoken));

// validations
Expand All @@ -113,7 +113,7 @@ contract BAMM is TokenAdapter, PriceFormula, Ownable, ReentrancyGuard {
cTokens[address(ctoken)] = true;
}

function removeCollateral(ICToken ctoken) external onlyOwner {
function removeCollateral(ICToken ctoken) public virtual onlyOwner {
IERC20 token = IERC20(address(ctoken));

for(uint i = 0 ; i < collaterals.length ; i++) {
Expand Down Expand Up @@ -316,6 +316,7 @@ contract BAMM is TokenAdapter, PriceFormula, Ownable, ReentrancyGuard {
)
external
view
virtual
returns(bool)
{
if(cTokenBorrowed != cBorrow) return false;
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts/contracts/B.Protocol/ChainlinkTestnet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ contract ChainlinkTestnet {
if(time == 0 ) timestamp = now;
else timestamp = time;
}

function latestAnswer() external view returns(int) {
return int(price);
}
}

contract FakePriceOracle {
Expand Down
52 changes: 52 additions & 0 deletions packages/contracts/contracts/B.Protocol/MockCToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,55 @@ contract MockUnitroller {

}
}


contract MockAave {
function getLendingPool() external pure returns (address) {
return address(0);
}

function getPriceOracle() external pure returns (address) {
return address(0);
}

function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external
{

}

function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external
{

}

function setAssetSources(address[] calldata assets, address[] calldata sources)
external
{

}

function owner() external pure returns(address) {
return address(0);
}

function getAssetPrice(address asset) external pure returns(uint) {
return 8;
}
}

contract ForceEthSend {
constructor(address payable dest) public payable {
selfdestruct(dest);
}
}
Loading