Merge pull request #57 from yaronvel/main

Add B.Protocol
This commit is contained in:
Thrilok kumar 2021-08-29 22:18:29 +05:30 committed by GitHub
commit 81a3ab22c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1996 additions and 0 deletions

View File

@ -0,0 +1,62 @@
pragma solidity ^0.7.0;
contract Events {
event LogDeposit(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogWithdraw(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogBorrow(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogPayback(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogDepositCToken(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 cTokenAmt,
uint256 getId,
uint256 setId
);
event LogWithdrawCToken(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 cTokenAmt,
uint256 getId,
uint256 setId
);
event LogLiquidate(
address indexed borrower,
address indexed tokenToPay,
address indexed tokenInReturn,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
}

View File

@ -0,0 +1,26 @@
pragma solidity ^0.7.0;
import { DSMath } from "./../../../common/math.sol";
import { Basic } from "./../../../common/basic.sol";
import { ComptrollerInterface, CompoundMappingInterface, BComptrollerInterface } from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev Compound Comptroller
*/
ComptrollerInterface internal constant troller = ComptrollerInterface(0x9dB10B9429989cC13408d7368644D4A1CB704ea3);
/**
* @dev Compound Mapping
*/
CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88);
/**
* @dev B.Compound Mapping
*/
function getMapping(string calldata tokenId) public returns(address token, address btoken) {
address ctoken;
(token, ctoken) = compMapping.getMapping(tokenId);
btoken = BComptrollerInterface(address(troller)).c2b(ctoken);
}
}

View File

@ -0,0 +1,40 @@
pragma solidity ^0.7.0;
interface CTokenInterface {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function borrow(uint borrowAmount) external returns (uint);
function repayBorrow(uint repayAmount) external returns (uint);
function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); // For ERC20
function liquidateBorrow(address borrower, uint repayAmount, address cTokenCollateral) external returns (uint);
function borrowBalanceCurrent(address account) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function exchangeRateCurrent() external returns (uint);
function balanceOf(address owner) external view returns (uint256 balance);
}
interface CETHInterface {
function mint() external payable;
function repayBorrow() external payable;
function repayBorrowBehalf(address borrower) external payable;
function liquidateBorrow(address borrower, address cTokenCollateral) external payable;
}
interface ComptrollerInterface {
function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
function exitMarket(address cTokenAddress) external returns (uint);
function getAssetsIn(address account) external view returns (address[] memory);
function getAccountLiquidity(address account) external view returns (uint, uint, uint);
function claimComp(address) external;
}
interface CompoundMappingInterface {
function cTokenMapping(string calldata tokenId) external view returns (address);
function getMapping(string calldata tokenId) external view returns (address, address);
}
interface BComptrollerInterface {
function c2b(address ctoken) external view returns(address);
}

View File

@ -0,0 +1,347 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title B.Compound.
* @dev Lending & Borrowing.
*/
import { TokenInterface } from "../../../common/interfaces.sol";
import { Stores } from "../../../common/stores.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
import { CETHInterface, CTokenInterface } from "./interface.sol";
abstract contract BCompoundResolver is Events, Helpers {
/**
* @dev Deposit ETH/ERC20_Token.
* @notice Deposit a token to Compound for lending / collaterization.
* @param token The address of the token to deposit. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited.
*/
function depositRaw(
address token,
address cToken,
uint256 amt,
uint256 getId,
uint256 setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
if (token == ethAddr) {
_amt = _amt == uint(-1) ? address(this).balance : _amt;
CETHInterface(cToken).mint{value: _amt}();
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
approve(tokenContract, cToken, _amt);
require(CTokenInterface(cToken).mint(_amt) == 0, "deposit-failed");
}
setUint(setId, _amt);
_eventName = "LogDeposit(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, _amt, getId, setId);
}
/**
* @dev Deposit ETH/ERC20_Token using the Mapping.
* @notice Deposit a token to Compound for lending / collaterization.
* @param tokenId The token id of the token to deposit.(For eg: ETH-A)
* @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited.
*/
function deposit(
string calldata tokenId,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = depositRaw(token, cToken, amt, getId, setId);
}
/**
* @dev Withdraw ETH/ERC20_Token.
* @notice Withdraw deposited token from Compound
* @param token The address of the token to withdraw. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param amt The amount of the token to withdraw. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens withdrawn.
*/
function withdrawRaw(
address token,
address cToken,
uint256 amt,
uint256 getId,
uint256 setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
CTokenInterface cTokenContract = CTokenInterface(cToken);
if (_amt == uint(-1)) {
TokenInterface tokenContract = TokenInterface(token);
uint initialBal = token == ethAddr ? address(this).balance : tokenContract.balanceOf(address(this));
require(cTokenContract.redeem(cTokenContract.balanceOf(address(this))) == 0, "full-withdraw-failed");
uint finalBal = token == ethAddr ? address(this).balance : tokenContract.balanceOf(address(this));
_amt = finalBal - initialBal;
} else {
require(cTokenContract.redeemUnderlying(_amt) == 0, "withdraw-failed");
}
setUint(setId, _amt);
_eventName = "LogWithdraw(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, _amt, getId, setId);
}
/**
* @dev Withdraw ETH/ERC20_Token using the Mapping.
* @notice Withdraw deposited token from Compound
* @param tokenId The token id of the token to withdraw.(For eg: ETH-A)
* @param amt The amount of the token to withdraw. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens withdrawn.
*/
function withdraw(
string calldata tokenId,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = withdrawRaw(token, cToken, amt, getId, setId);
}
/**
* @dev Borrow ETH/ERC20_Token.
* @notice Borrow a token using Compound
* @param token The address of the token to borrow. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param amt The amount of the token to borrow.
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens borrowed.
*/
function borrowRaw(
address token,
address cToken,
uint256 amt,
uint256 getId,
uint256 setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
require(CTokenInterface(cToken).borrow(_amt) == 0, "borrow-failed");
setUint(setId, _amt);
_eventName = "LogBorrow(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, _amt, getId, setId);
}
/**
* @dev Borrow ETH/ERC20_Token using the Mapping.
* @notice Borrow a token using Compound
* @param tokenId The token id of the token to borrow.(For eg: DAI-A)
* @param amt The amount of the token to borrow.
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens borrowed.
*/
function borrow(
string calldata tokenId,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = borrowRaw(token, cToken, amt, getId, setId);
}
/**
* @dev Payback borrowed ETH/ERC20_Token.
* @notice Payback debt owed.
* @param token The address of the token to payback. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param amt The amount of the token to payback. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens paid back.
*/
function paybackRaw(
address token,
address cToken,
uint256 amt,
uint256 getId,
uint256 setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
CTokenInterface cTokenContract = CTokenInterface(cToken);
_amt = _amt == uint(-1) ? cTokenContract.borrowBalanceCurrent(address(this)) : _amt;
if (token == ethAddr) {
require(address(this).balance >= _amt, "not-enough-eth");
CETHInterface(cToken).repayBorrow{value: _amt}();
} else {
TokenInterface tokenContract = TokenInterface(token);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
approve(tokenContract, cToken, _amt);
require(cTokenContract.repayBorrow(_amt) == 0, "repay-failed.");
}
setUint(setId, _amt);
_eventName = "LogPayback(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, _amt, getId, setId);
}
/**
* @dev Payback borrowed ETH/ERC20_Token using the Mapping.
* @notice Payback debt owed.
* @param tokenId The token id of the token to payback.(For eg: COMP-A)
* @param amt The amount of the token to payback. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens paid back.
*/
function payback(
string calldata tokenId,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = paybackRaw(token, cToken, amt, getId, setId);
}
/**
* @dev Deposit ETH/ERC20_Token.
* @notice Same as depositRaw. The only difference is this method stores cToken amount in set ID.
* @param token The address of the token to deposit. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of cTokens received.
*/
function depositCTokenRaw(
address token,
address cToken,
uint256 amt,
uint256 getId,
uint256 setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
CTokenInterface ctokenContract = CTokenInterface(cToken);
uint initialBal = ctokenContract.balanceOf(address(this));
if (token == ethAddr) {
_amt = _amt == uint(-1) ? address(this).balance : _amt;
CETHInterface(cToken).mint{value: _amt}();
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
approve(tokenContract, cToken, _amt);
require(ctokenContract.mint(_amt) == 0, "deposit-ctoken-failed.");
}
uint _cAmt;
{
uint finalBal = ctokenContract.balanceOf(address(this));
_cAmt = sub(finalBal, initialBal);
setUint(setId, _cAmt);
}
_eventName = "LogDepositCToken(address,address,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, _amt, _cAmt, getId, setId);
}
/**
* @dev Deposit ETH/ERC20_Token using the Mapping.
* @notice Same as deposit. The only difference is this method stores cToken amount in set ID.
* @param tokenId The token id of the token to depositCToken.(For eg: DAI-A)
* @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of cTokens received.
*/
function depositCToken(
string calldata tokenId,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = depositCTokenRaw(token, cToken, amt, getId, setId);
}
/**
* @dev Withdraw CETH/CERC20_Token using cToken Amt.
* @notice Same as withdrawRaw. The only difference is this method fetch cToken amount in get ID.
* @param token The address of the token to withdraw. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cToken The address of the corresponding cToken.
* @param cTokenAmt The amount of cTokens to withdraw
* @param getId ID to retrieve cTokenAmt
* @param setId ID stores the amount of tokens withdrawn.
*/
function withdrawCTokenRaw(
address token,
address cToken,
uint cTokenAmt,
uint getId,
uint setId
) public payable returns (string memory _eventName, bytes memory _eventParam) {
uint _cAmt = getUint(getId, cTokenAmt);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
CTokenInterface cTokenContract = CTokenInterface(cToken);
TokenInterface tokenContract = TokenInterface(token);
_cAmt = _cAmt == uint(-1) ? cTokenContract.balanceOf(address(this)) : _cAmt;
uint withdrawAmt;
{
uint initialBal = token != ethAddr ? tokenContract.balanceOf(address(this)) : address(this).balance;
require(cTokenContract.redeem(_cAmt) == 0, "redeem-failed");
uint finalBal = token != ethAddr ? tokenContract.balanceOf(address(this)) : address(this).balance;
withdrawAmt = sub(finalBal, initialBal);
}
setUint(setId, withdrawAmt);
_eventName = "LogWithdrawCToken(address,address,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(token, cToken, withdrawAmt, _cAmt, getId, setId);
}
/**
* @dev Withdraw CETH/CERC20_Token using cToken Amt & the Mapping.
* @notice Same as withdraw. The only difference is this method fetch cToken amount in get ID.
* @param tokenId The token id of the token to withdraw CToken.(For eg: ETH-A)
* @param cTokenAmt The amount of cTokens to withdraw
* @param getId ID to retrieve cTokenAmt
* @param setId ID stores the amount of tokens withdrawn.
*/
function withdrawCToken(
string calldata tokenId,
uint cTokenAmt,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
(address token, address cToken) = getMapping(tokenId);
(_eventName, _eventParam) = withdrawCTokenRaw(token, cToken, cTokenAmt, getId, setId);
}
}
contract ConnectV2BCompound is BCompoundResolver {
string public name = "B.Compound-v1.0";
}

View File

@ -0,0 +1,22 @@
pragma solidity ^0.7.6;
contract Events {
/* Stability Pool */
event LogStabilityDeposit(
address indexed borrower,
uint amount,
uint lqtyGain,
uint getDepositId,
uint setDepositId,
uint setLqtyGainId
);
event LogStabilityWithdraw(
address indexed borrower,
uint numShares,
uint lqtyGain,
uint getWithdrawId,
uint setWithdrawId,
uint setLqtyGainId
);
}

View File

@ -0,0 +1,18 @@
pragma solidity ^0.7.6;
import { DSMath } from "../../../common/math.sol";
import { Basic } from "../../../common/basic.sol";
import { TokenInterface } from "../../../common/interfaces.sol";
import {
StabilityPoolLike,
BAMMLike
} from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
StabilityPoolLike internal constant stabilityPool = StabilityPoolLike(0x66017D22b0f8556afDd19FC67041899Eb65a21bb);
TokenInterface internal constant lqtyToken = TokenInterface(0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D);
TokenInterface internal constant lusdToken = TokenInterface(0x5f98805A4E8be255a32880FDeC7F6728C6568bA0);
BAMMLike internal constant BAMM = BAMMLike(0x0d3AbAA7E088C2c82f54B2f47613DA438ea8C598);
}

View File

@ -0,0 +1,17 @@
pragma solidity ^0.7.6;
interface StabilityPoolLike {
function provideToSP(uint _amount, address _frontEndTag) external;
function withdrawFromSP(uint _amount) external;
function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external;
function getDepositorETHGain(address _depositor) external view returns (uint);
function getDepositorLQTYGain(address _depositor) external view returns (uint);
function getCompoundedLUSDDeposit(address _depositor) external view returns (uint);
}
interface BAMMLike {
function deposit(uint lusdAmount) external;
function withdraw(uint numShares) external;
function balanceOf(address a) external view returns(uint);
function totalSupply() external view returns(uint);
}

View File

@ -0,0 +1,86 @@
pragma solidity ^0.7.6;
/**
* @title B.Liquity.
* @dev Lending & Borrowing.
*/
import {
StabilityPoolLike,
BAMMLike
} from "./interface.sol";
import { Stores } from "../../../common/stores.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
abstract contract BLiquityResolver is Events, Helpers {
/* Begin: Stability Pool */
/**
* @dev Deposit LUSD into Stability Pool
* @notice Deposit LUSD into Stability Pool
* @param amount Amount of LUSD to deposit into Stability Pool
* @param getDepositId Optional storage slot to retrieve the amount of LUSD from
* @param setDepositId Optional storage slot to store the final amount of LUSD deposited
* @param setLqtyGainId Optional storage slot to store any LQTY gains in
*/
function deposit(
uint amount,
uint getDepositId,
uint setDepositId,
uint setLqtyGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
amount = getUint(getDepositId, amount);
amount = amount == uint(-1) ? lusdToken.balanceOf(address(this)) : amount;
uint lqtyBalanceBefore = lqtyToken.balanceOf(address(this));
lusdToken.approve(address(BAMM), amount);
BAMM.deposit(amount);
uint lqtyBalanceAfter = lqtyToken.balanceOf(address(this));
uint lqtyGain = sub(lqtyBalanceAfter, lqtyBalanceBefore);
setUint(setDepositId, amount);
setUint(setLqtyGainId, lqtyGain);
_eventName = "LogStabilityDeposit(address,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), amount,lqtyGain, getDepositId, setDepositId, setLqtyGainId);
}
/**
* @dev Withdraw user deposited LUSD from Stability Pool
* @notice Withdraw LUSD from Stability Pool
* @param numShares amount of shares to withdraw from the BAMM
* @param getWithdrawId Optional storage slot to retrieve the amount of LUSD to withdraw from
* @param setWithdrawId Optional storage slot to store the withdrawn LUSD
* @param setLqtyGainId Optional storage slot to store any LQTY gains in
*/
function withdraw(
uint numShares,
uint getWithdrawId,
uint setWithdrawId,
uint setLqtyGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
numShares = getUint(getWithdrawId, numShares);
numShares = numShares == uint(-1) ? BAMM.balanceOf(address(this)) : numShares;
uint lqtyBalanceBefore = lqtyToken.balanceOf(address(this));
BAMM.withdraw(numShares);
uint lqtyBalanceAfter = lqtyToken.balanceOf(address(this));
uint lqtyGain = sub(lqtyBalanceAfter, lqtyBalanceBefore);
setUint(setWithdrawId, numShares);
setUint(setLqtyGainId, lqtyGain);
_eventName = "LogStabilityWithdraw(address,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), numShares, lqtyGain, getWithdrawId, setWithdrawId, setLqtyGainId);
}
}
contract ConnectV2BLiquity is BLiquityResolver {
string public name = "B.Liquity-v1";
}

View File

@ -0,0 +1,26 @@
pragma solidity ^0.7.0;
contract Events {
event LogOpen(uint256 indexed vault, bytes32 indexed ilk);
event LogClose(uint256 indexed vault, bytes32 indexed ilk);
event LogTransfer(uint256 indexed vault, bytes32 indexed ilk, address newOwner);
event LogDeposit(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogWithdraw(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogBorrow(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogPayback(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogWithdrawLiquidated(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogExitDai(uint256 indexed vault, bytes32 indexed ilk, uint256 tokenAmt, uint256 getId, uint256 setId);
event LogDepositDai(uint256 tokenAmt, uint256 getId, uint256 setId);
event LogWithdrawDai(uint256 tokenAmt, uint256 getId, uint256 setId);
event LogDepositAndBorrow(
uint256 indexed vault,
bytes32 indexed ilk,
uint256 depositAmt,
uint256 borrowAmt,
uint256 getIdDeposit,
uint256 getIdBorrow,
uint256 setIdDeposit,
uint256 setIdBorrow
);
}

View File

@ -0,0 +1,130 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../../common/math.sol";
import { Basic } from "../../../common/basic.sol";
import { TokenInterface } from "./../../../common/interfaces.sol";
import { BManagerLike, DaiJoinInterface, PotLike, VatLike, JugLike } from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev Manager Interface
*/
BManagerLike internal constant managerContract = BManagerLike(0x3f30c2381CD8B917Dd96EB2f1A4F96D91324BBed);
/**
* @dev DAI Join
*/
DaiJoinInterface internal constant daiJoinContract = DaiJoinInterface(0x9759A6Ac90977b93B58547b4A71c78317f391A28);
/**
* @dev Pot
*/
PotLike internal constant potContract = PotLike(0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7);
/**
* @dev Maker MCD Jug Address.
*/
JugLike internal constant mcdJug = JugLike(0x19c0976f590D67707E62397C87829d896Dc0f1F1);
/**
* @dev Return Close Vault Address.
*/
address internal constant giveAddr = 0x4dD58550eb15190a5B3DfAE28BB14EeC181fC267;
/**
* @dev Get Vault's ilk.
*/
function getVaultData(uint vault) internal view returns (bytes32 ilk, address urn) {
ilk = managerContract.ilks(vault);
urn = managerContract.urns(vault);
}
/**
* @dev Gem Join address is ETH type collateral.
*/
function isEth(address tknAddr) internal pure returns (bool) {
return tknAddr == wethAddr ? true : false;
}
/**
* @dev Get Vault Debt Amount.
*/
function _getVaultDebt(
address vat,
bytes32 ilk,
address urn,
uint vault
) internal view returns (uint wad) {
(, uint rate,,,) = VatLike(vat).ilks(ilk);
(, uint art) = VatLike(vat).urns(ilk, urn);
uint cushion = managerContract.cushion(vault);
art = add(art, cushion);
uint dai = VatLike(vat).dai(urn);
uint rad = sub(mul(art, rate), dai);
wad = rad / RAY;
wad = mul(wad, RAY) < rad ? wad + 1 : wad;
}
/**
* @dev Get Borrow Amount.
*/
function _getBorrowAmt(
address vat,
address urn,
bytes32 ilk,
uint amt
) internal returns (int dart)
{
uint rate = mcdJug.drip(ilk);
uint dai = VatLike(vat).dai(urn);
if (dai < mul(amt, RAY)) {
dart = toInt(sub(mul(amt, RAY), dai) / rate);
dart = mul(uint(dart), rate) < mul(amt, RAY) ? dart + 1 : dart;
}
}
/**
* @dev Get Payback Amount.
*/
function _getWipeAmt(
address vat,
uint amt,
address urn,
bytes32 ilk,
uint vault
) internal view returns (int dart)
{
(, uint rate,,,) = VatLike(vat).ilks(ilk);
(, uint art) = VatLike(vat).urns(ilk, urn);
uint cushion = managerContract.cushion(vault);
art = add(art, cushion);
dart = toInt(amt / rate);
dart = uint(dart) <= art ? - dart : - toInt(art);
}
/**
* @dev Convert String to bytes32.
*/
function stringToBytes32(string memory str) internal pure returns (bytes32 result) {
require(bytes(str).length != 0, "string-empty");
// solium-disable-next-line security/no-inline-assembly
assembly {
result := mload(add(str, 32))
}
}
/**
* @dev Get vault ID. If `vault` is 0, get last opened vault.
*/
function getVault(uint vault) internal view returns (uint _vault) {
if (vault == 0) {
require(managerContract.count(address(this)) > 0, "no-vault-opened");
_vault = managerContract.last(address(this));
} else {
_vault = vault;
}
}
}

View File

@ -0,0 +1,66 @@
pragma solidity ^0.7.0;
import { TokenInterface } from "../../../common/interfaces.sol";
interface ManagerLike {
function cdpCan(address, uint, address) external view returns (uint);
function ilks(uint) external view returns (bytes32);
function last(address) external view returns (uint);
function count(address) external view returns (uint);
function owns(uint) external view returns (address);
function urns(uint) external view returns (address);
function vat() external view returns (address);
function open(bytes32, address) external returns (uint);
function give(uint, address) external;
function frob(uint, int, int) external;
function flux(uint, address, uint) external;
function move(uint, address, uint) external;
}
interface BManagerLike is ManagerLike {
function cushion(uint) external view returns (uint);
function cdpi() external view returns (uint);
}
interface VatLike {
function can(address, address) external view returns (uint);
function ilks(bytes32) external view returns (uint, uint, uint, uint, uint);
function dai(address) external view returns (uint);
function urns(bytes32, address) external view returns (uint, uint);
function frob(
bytes32,
address,
address,
address,
int,
int
) external;
function hope(address) external;
function move(address, address, uint) external;
function gem(bytes32, address) external view returns (uint);
}
interface TokenJoinInterface {
function dec() external returns (uint);
function gem() external returns (TokenInterface);
function join(address, uint) external payable;
function exit(address, uint) external;
}
interface DaiJoinInterface {
function vat() external returns (VatLike);
function dai() external returns (TokenInterface);
function join(address, uint) external payable;
function exit(address, uint) external;
}
interface JugLike {
function drip(bytes32) external returns (uint);
}
interface PotLike {
function pie(address) external view returns (uint);
function drip() external returns (uint);
function join(uint) external;
function exit(uint) external;
}

View File

@ -0,0 +1,521 @@
pragma solidity ^0.7.0;
/**
* @title MakerDAO.
* @dev Collateralized Borrowing.
*/
import { TokenInterface, AccountInterface } from "./../../../common/interfaces.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
import { VatLike, TokenJoinInterface } from "./interface.sol";
abstract contract BMakerResolver is Helpers, Events {
/**
* @dev Open Vault
* @notice Open a MakerDAO Vault
* @param colType Type of Collateral.(eg: 'ETH-A')
*/
function open(string calldata colType) external payable returns (string memory _eventName, bytes memory _eventParam) {
bytes32 ilk = stringToBytes32(colType);
require(instaMapping.gemJoinMapping(ilk) != address(0), "wrong-col-type");
uint256 vault = managerContract.open(ilk, address(this));
_eventName = "LogOpen(uint256,bytes32)";
_eventParam = abi.encode(vault, ilk);
}
/**
* @dev Close Vault
* @notice Close a MakerDAO Vault
* @param vault Vault ID to close.
*/
function close(uint256 vault) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
(uint ink, uint art) = VatLike(managerContract.vat()).urns(ilk, urn);
require(ink == 0 && art == 0, "vault-has-assets");
require(managerContract.owns(_vault) == address(this), "not-owner");
managerContract.give(_vault, giveAddr);
_eventName = "LogClose(uint256,bytes32)";
_eventParam = abi.encode(_vault, ilk);
}
/**
* @dev Transfer Vault
* @notice Transfer a MakerDAO Vault to "nextOwner"
* @param vault Vault ID to close.
* @param nextOwner Address of the next owner of the vault.
*/
function transfer(
uint vault,
address nextOwner
) external payable returns (string memory _eventName, bytes memory _eventParam) {
require(AccountInterface(address(this)).isAuth(nextOwner), "nextOwner-is-not-auth");
uint256 _vault = getVault(vault);
(bytes32 ilk,) = getVaultData(_vault);
require(managerContract.owns(_vault) == address(this), "not-owner");
managerContract.give(_vault, nextOwner);
_eventName = "LogTransfer(uint256,bytes32,address)";
_eventParam = abi.encode(_vault, ilk, nextOwner);
}
/**
* @dev Deposit ETH/ERC20_Token Collateral.
* @notice Deposit collateral to a MakerDAO vault
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt The amount of tokens to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited.
*/
function deposit(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
address colAddr = instaMapping.gemJoinMapping(ilk);
TokenJoinInterface tokenJoinContract = TokenJoinInterface(colAddr);
TokenInterface tokenContract = tokenJoinContract.gem();
if (isEth(address(tokenContract))) {
_amt = _amt == uint(-1) ? address(this).balance : _amt;
tokenContract.deposit{value: _amt}();
} else {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
approve(tokenContract, address(colAddr), _amt);
tokenJoinContract.join(urn, _amt);
managerContract.frob(
_vault,
toInt(convertTo18(tokenJoinContract.dec(), _amt)),
0
);
setUint(setId, _amt);
_eventName = "LogDeposit(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(_vault, ilk, _amt, getId, setId);
}
/**
* @dev Withdraw ETH/ERC20_Token Collateral.
* @notice Withdraw collateral from a MakerDAO vault
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt The amount of tokens to withdraw. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens withdrawn.
*/
function withdraw(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
address colAddr = instaMapping.gemJoinMapping(ilk);
TokenJoinInterface tokenJoinContract = TokenJoinInterface(colAddr);
uint _amt18;
if (_amt == uint(-1)) {
(_amt18,) = VatLike(managerContract.vat()).urns(ilk, urn);
_amt = convert18ToDec(tokenJoinContract.dec(), _amt18);
} else {
_amt18 = convertTo18(tokenJoinContract.dec(), _amt);
}
managerContract.frob(
_vault,
-toInt(_amt18),
0
);
managerContract.flux(
_vault,
address(this),
_amt18
);
TokenInterface tokenContract = tokenJoinContract.gem();
if (isEth(address(tokenContract))) {
tokenJoinContract.exit(address(this), _amt);
tokenContract.withdraw(_amt);
} else {
tokenJoinContract.exit(address(this), _amt);
}
setUint(setId, _amt);
_eventName = "LogWithdraw(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(_vault, ilk, _amt, getId, setId);
}
/**
* @dev Borrow DAI.
* @notice Borrow DAI using a MakerDAO vault
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt The amount of DAI to borrow.
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of DAI borrowed.
*/
function borrow(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
VatLike vatContract = VatLike(managerContract.vat());
managerContract.frob(
_vault,
0,
_getBorrowAmt(
address(vatContract),
urn,
ilk,
_amt
)
);
managerContract.move(
_vault,
address(this),
toRad(_amt)
);
if (vatContract.can(address(this), address(daiJoinContract)) == 0) {
vatContract.hope(address(daiJoinContract));
}
daiJoinContract.exit(address(this), _amt);
setUint(setId, _amt);
_eventName = "LogBorrow(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(_vault, ilk, _amt, getId, setId);
}
/**
* @dev Payback borrowed DAI.
* @notice Payback DAI debt owed by a MakerDAO vault
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt The amount of DAI to payback. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of DAI paid back.
*/
function payback(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
address vat = managerContract.vat();
uint _maxDebt = _getVaultDebt(vat, ilk, urn, vault);
_amt = _amt == uint(-1) ? _maxDebt : _amt;
require(_maxDebt >= _amt, "paying-excess-debt");
approve(daiJoinContract.dai(), address(daiJoinContract), _amt);
daiJoinContract.join(urn, _amt);
managerContract.frob(
_vault,
0,
_getWipeAmt(
vat,
VatLike(vat).dai(urn),
urn,
ilk,
_vault
)
);
setUint(setId, _amt);
_eventName = "LogPayback(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(_vault, ilk, _amt, getId, setId);
}
/**
* @dev Withdraw leftover ETH/ERC20_Token after Liquidation.
* @notice Withdraw leftover collateral after Liquidation.
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt token amount to Withdraw. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of collateral withdrawn.
*/
function withdrawLiquidated(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
(bytes32 ilk, address urn) = getVaultData(vault);
address colAddr = instaMapping.gemJoinMapping(ilk);
TokenJoinInterface tokenJoinContract = TokenJoinInterface(colAddr);
uint _amt18;
if (_amt == uint(-1)) {
_amt18 = VatLike(managerContract.vat()).gem(ilk, urn);
_amt = convert18ToDec(tokenJoinContract.dec(), _amt18);
} else {
_amt18 = convertTo18(tokenJoinContract.dec(), _amt);
}
managerContract.flux(
vault,
address(this),
_amt18
);
TokenInterface tokenContract = tokenJoinContract.gem();
tokenJoinContract.exit(address(this), _amt);
if (isEth(address(tokenContract))) {
tokenContract.withdraw(_amt);
}
setUint(setId, _amt);
_eventName = "LogWithdrawLiquidated(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(vault, ilk, _amt, getId, setId);
}
struct MakerData {
uint _vault;
address colAddr;
TokenJoinInterface tokenJoinContract;
VatLike vatContract;
TokenInterface tokenContract;
}
/**
* @dev Deposit ETH/ERC20_Token Collateral and Borrow DAI.
* @notice Deposit collateral and borrow DAI.
* @param vault Vault ID. (Use 0 for last opened vault)
* @param depositAmt The amount of tokens to deposit. (For max: `uint256(-1)`)
* @param borrowAmt The amount of DAI to borrow.
* @param getIdDeposit ID to retrieve depositAmt.
* @param getIdBorrow ID to retrieve borrowAmt.
* @param setIdDeposit ID stores the amount of tokens deposited.
* @param setIdBorrow ID stores the amount of DAI borrowed.
*/
function depositAndBorrow(
uint256 vault,
uint256 depositAmt,
uint256 borrowAmt,
uint256 getIdDeposit,
uint256 getIdBorrow,
uint256 setIdDeposit,
uint256 setIdBorrow
) external payable returns (string memory _eventName, bytes memory _eventParam) {
MakerData memory makerData;
uint _amtDeposit = getUint(getIdDeposit, depositAmt);
uint _amtBorrow = getUint(getIdBorrow, borrowAmt);
makerData._vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(makerData._vault);
makerData.colAddr = instaMapping.gemJoinMapping(ilk);
makerData.tokenJoinContract = TokenJoinInterface(makerData.colAddr);
makerData.vatContract = VatLike(managerContract.vat());
makerData.tokenContract = makerData.tokenJoinContract.gem();
if (isEth(address(makerData.tokenContract))) {
_amtDeposit = _amtDeposit == uint(-1) ? address(this).balance : _amtDeposit;
makerData.tokenContract.deposit{value: _amtDeposit}();
} else {
_amtDeposit = _amtDeposit == uint(-1) ? makerData.tokenContract.balanceOf(address(this)) : _amtDeposit;
}
approve(makerData.tokenContract, address(makerData.colAddr), _amtDeposit);
makerData.tokenJoinContract.join(urn, _amtDeposit);
managerContract.frob(
makerData._vault,
toInt(convertTo18(makerData.tokenJoinContract.dec(), _amtDeposit)),
_getBorrowAmt(
address(makerData.vatContract),
urn,
ilk,
_amtBorrow
)
);
managerContract.move(
makerData._vault,
address(this),
toRad(_amtBorrow)
);
if (makerData.vatContract.can(address(this), address(daiJoinContract)) == 0) {
makerData.vatContract.hope(address(daiJoinContract));
}
daiJoinContract.exit(address(this), _amtBorrow);
setUint(setIdDeposit, _amtDeposit);
setUint(setIdBorrow, _amtBorrow);
_eventName = "LogDepositAndBorrow(uint256,bytes32,uint256,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(
makerData._vault,
ilk,
_amtDeposit,
_amtBorrow,
getIdDeposit,
getIdBorrow,
setIdDeposit,
setIdBorrow
);
}
/**
* @dev Exit DAI from urn.
* @notice Exit DAI from urn.
* @param vault Vault ID. (Use 0 for last opened vault)
* @param amt The amount of DAI to exit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of DAI exited.
*/
function exitDai(
uint256 vault,
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
uint _vault = getVault(vault);
(bytes32 ilk, address urn) = getVaultData(_vault);
VatLike vatContract = VatLike(managerContract.vat());
if(_amt == uint(-1)) {
_amt = vatContract.dai(urn);
_amt = _amt / 10 ** 27;
}
managerContract.move(
_vault,
address(this),
toRad(_amt)
);
if (vatContract.can(address(this), address(daiJoinContract)) == 0) {
vatContract.hope(address(daiJoinContract));
}
daiJoinContract.exit(address(this), _amt);
setUint(setId, _amt);
_eventName = "LogExitDai(uint256,bytes32,uint256,uint256,uint256)";
_eventParam = abi.encode(_vault, ilk, _amt, getId, setId);
}
/**
* @dev Deposit DAI in DSR.
* @notice Deposit DAI in DSR.
* @param amt The amount of DAI to deposit. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of DAI deposited.
*/
function depositDai(
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
_amt = _amt == uint(-1) ?
daiJoinContract.dai().balanceOf(address(this)) :
_amt;
VatLike vat = daiJoinContract.vat();
uint chi = potContract.drip();
approve(daiJoinContract.dai(), address(daiJoinContract), _amt);
daiJoinContract.join(address(this), _amt);
if (vat.can(address(this), address(potContract)) == 0) {
vat.hope(address(potContract));
}
potContract.join(mul(_amt, RAY) / chi);
setUint(setId, _amt);
_eventName = "LogDepositDai(uint256,uint256,uint256)";
_eventParam = abi.encode(_amt, getId, setId);
}
/**
* @dev Withdraw DAI from DSR.
* @notice Withdraw DAI from DSR.
* @param amt The amount of DAI to withdraw. (For max: `uint256(-1)`)
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of DAI withdrawn.
*/
function withdrawDai(
uint256 amt,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amt = getUint(getId, amt);
VatLike vat = daiJoinContract.vat();
uint chi = potContract.drip();
uint pie;
if (_amt == uint(-1)) {
pie = potContract.pie(address(this));
_amt = mul(chi, pie) / RAY;
} else {
pie = mul(_amt, RAY) / chi;
}
potContract.exit(pie);
uint bal = vat.dai(address(this));
if (vat.can(address(this), address(daiJoinContract)) == 0) {
vat.hope(address(daiJoinContract));
}
daiJoinContract.exit(
address(this),
bal >= mul(_amt, RAY) ? _amt : bal / RAY
);
setUint(setId, _amt);
_eventName = "LogWithdrawDai(uint256,uint256,uint256)";
_eventParam = abi.encode(_amt, getId, setId);
}
}
contract ConnectV2BMakerDAO is BMakerResolver {
string public constant name = "B.MakerDAO-v1.0";
}

View File

@ -0,0 +1,128 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
const buildDSAv2 = require("../../scripts/buildDSAv2")
const encodeSpells = require("../../scripts/encodeSpells.js")
const getMasterSigner = require("../../scripts/getMasterSigner")
const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis");
const constants = require("../../scripts/constant/constant");
const tokens = require("../../scripts/constant/tokens");
const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/b.protocol/compound/main.sol/ConnectV1BCompound.json")
describe("B.Compound", function () {
const connectorName = "B.COMPOUND-TEST-A"
let dsaWallet0
let masterSigner;
let instaConnectorsV2;
let connector;
const wallets = provider.getWallets()
const [wallet0, wallet1, wallet2, wallet3] = wallets
before(async () => {
masterSigner = await getMasterSigner(wallet3)
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: connectV2CompoundArtifacts,
signer: masterSigner,
connectors: instaConnectorsV2
})
console.log("Connector address", connector.address)
})
it("Should have contracts deployed.", async function () {
expect(!!instaConnectorsV2.address).to.be.true;
expect(!!connector.address).to.be.true;
expect(!!masterSigner.address).to.be.true;
expect(await connector.name()).to.be.equal("B.Compound-v1.0");
});
describe("DSA wallet setup", function () {
it("Should build DSA v2", async function () {
dsaWallet0 = await buildDSAv2(wallet0.address)
expect(!!dsaWallet0.address).to.be.true;
});
it("Deposit ETH into DSA wallet", async function () {
await wallet0.sendTransaction({
to: dsaWallet0.address,
value: ethers.utils.parseEther("10")
});
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
});
});
describe("Main", function () {
it("Should deposit ETH in Compound", async function () {
const amount = ethers.utils.parseEther("1") // 1 ETH
const spells = [
{
connector: connectorName,
method: "deposit",
args: ["ETH-A", amount, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9"));
});
it("Should borrow and payback DAI from Compound", async function () {
const amount = ethers.utils.parseEther("100") // 100 DAI
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "borrow",
args: ["DAI-A", amount, 0, setId]
},
{
connector: connectorName,
method: "payback",
args: ["DAI-A", 0, setId, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9"));
});
it("Should deposit all ETH in Compound", async function () {
const spells = [
{
connector: connectorName,
method: "deposit",
args: ["ETH-A", constants.max_value, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("0"));
});
it("Should withdraw all ETH from Compound", async function () {
const spells = [
{
connector: connectorName,
method: "withdraw",
args: ["ETH-A", constants.max_value, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
});
})
})

View File

@ -0,0 +1,181 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
const buildDSAv2 = require("../../scripts/buildDSAv2")
const encodeSpells = require("../../scripts/encodeSpells.js")
const getMasterSigner = require("../../scripts/getMasterSigner")
const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis");
const constants = require("../../scripts/constant/constant");
const tokens = require("../../scripts/constant/tokens");
const connectorLiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/b.protocol/liquity/main.sol/ConnectV2BLiquity.json")
const LUSD_WHALE = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb" // stability pool
const BAMM_ADDRESS = "0x0d3AbAA7E088C2c82f54B2f47613DA438ea8C598"
describe("B.Liquity", function () {
const connectorName = "B.LIQUITY-TEST-A"
let dsaWallet0;
let dsaWallet1;
let masterSigner;
let instaConnectorsV2;
let connector;
let manager;
let vat;
let lusd;
let bammToken;
let stabilityPool;
const wallets = provider.getWallets()
const [wallet0, wallet1, wallet2, wallet3] = wallets
before(async () => {
masterSigner = await getMasterSigner(wallet3)
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: connectorLiquityArtifacts,
signer: masterSigner,
connectors: instaConnectorsV2
})
lusd = await ethers.getContractAt("../artifacts/contracts/mainnet/common/interfaces.sol:TokenInterface", "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0")
bammToken = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/liquity/interface.sol:BAMMLike", BAMM_ADDRESS)
stabilityPool = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/liquity/interface.sol:StabilityPoolLike", "0x66017D22b0f8556afDd19FC67041899Eb65a21bb")
console.log("Connector address", connector.address)
})
it("test veryClose.", async function () {
expect(veryClose(1000001, 1000000)).to.be.true
expect(veryClose(1000000, 1000001)).to.be.true
expect(veryClose(1003000, 1000001)).to.be.false
expect(veryClose(1000001, 1000300)).to.be.false
});
it("Should have contracts deployed.", async function () {
expect(!!instaConnectorsV2.address).to.be.true;
expect(!!connector.address).to.be.true;
expect(!!masterSigner.address).to.be.true;
expect(await connector.name()).to.be.equal("B.Liquity-v1");
});
describe("DSA wallet setup", function () {
it("Should build DSA v2", async function () {
dsaWallet0 = await buildDSAv2(wallet0.address)
expect(!!dsaWallet0.address).to.be.true;
dsaWallet1 = await buildDSAv2(wallet1.address)
expect(!!dsaWallet1.address).to.be.true;
});
it("Deposit LUSD into DSA wallet", async function () {
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [LUSD_WHALE],
});
const signer = await hre.ethers.provider.getSigner(LUSD_WHALE);
await lusd.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("100000"))
expect(await lusd.balanceOf(dsaWallet0.address)).to.equal(ethers.utils.parseEther("100000"));
});
});
describe("Main", function () {
it("should deposit 10k LUSD", async function () {
const totalSupplyBefore = await bammToken.totalSupply();
const lusdBalanceBefore = await stabilityPool.getCompoundedLUSDDeposit(BAMM_ADDRESS);
const amount = ethers.utils.parseEther("10000");
const spells = [
{
connector: connectorName,
method: "deposit",
args: [amount, 0, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
const expectedBalance = totalSupplyBefore.mul(amount).div(lusdBalanceBefore)
expect(veryClose(expectedBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true
});
it("should deposit all LUSD", async function () {
const totalSupplyBefore = await bammToken.totalSupply();
const lusdBalanceBefore = await stabilityPool.getCompoundedLUSDDeposit(BAMM_ADDRESS);
const amount = web3.utils.toBN("2").pow(web3.utils.toBN("256")).sub(web3.utils.toBN("1"));
const balanceBefore = await bammToken.balanceOf(dsaWallet0.address)
const spells = [
{
connector: connectorName,
method: "deposit",
args: [amount, 0, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
const expectedBalance = (totalSupplyBefore.mul(ethers.utils.parseEther("90000")).div(lusdBalanceBefore)).add(balanceBefore)
expect(veryClose(expectedBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true
});
it("should withdraw half of the shares", async function () {
const balanceBefore = await bammToken.balanceOf(dsaWallet0.address)
const halfBalance = balanceBefore.div("2")
const spells = [
{
connector: connectorName,
method: "withdraw",
args: [halfBalance, 0, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(veryClose(halfBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true
expect(veryClose(ethers.utils.parseEther("50000"), await lusd.balanceOf(dsaWallet0.address))).to.be.true
});
it("should withdraw all the shares", async function () {
const amount = web3.utils.toBN("2").pow(web3.utils.toBN("256")).sub(web3.utils.toBN("1"));
const spells = [
{
connector: connectorName,
method: "withdraw",
args: [amount, 0, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(veryClose(ethers.utils.parseEther("100000"), await lusd.balanceOf(dsaWallet0.address))).to.be.true
});
})
})
function veryClose(n1, n2) {
n1 = web3.utils.toBN(n1)
n2 = web3.utils.toBN(n2)
_10000 = web3.utils.toBN(10000)
_9999 = web3.utils.toBN(9999)
if(n1.mul(_10000).lt(n2.mul(_9999))) return false
if(n2.mul(_10000).lt(n1.mul(_9999))) return false
return true
}

View File

@ -0,0 +1,326 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
const buildDSAv2 = require("../../scripts/buildDSAv2")
const encodeSpells = require("../../scripts/encodeSpells.js")
const getMasterSigner = require("../../scripts/getMasterSigner")
const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis");
const constants = require("../../scripts/constant/constant");
const tokens = require("../../scripts/constant/tokens");
const connectorMakerArtifacts = require("../../artifacts/contracts/mainnet/connectors/b.protocol/makerdao/main.sol/ConnectV1BMakerDAO.json")
describe("B.Maker", function () {
const connectorName = "B.MAKER-TEST-A"
let dsaWallet0;
let dsaWallet1;
let masterSigner;
let instaConnectorsV2;
let connector;
let manager;
let vat;
let dai;
const wallets = provider.getWallets()
const [wallet0, wallet1, wallet2, wallet3] = wallets
before(async () => {
masterSigner = await getMasterSigner(wallet3)
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: connectorMakerArtifacts,
signer: masterSigner,
connectors: instaConnectorsV2
})
manager = await ethers.getContractAt("BManagerLike", "0x3f30c2381CD8B917Dd96EB2f1A4F96D91324BBed")
vat = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/makerdao/interface.sol:VatLike", await manager.vat())
dai = await ethers.getContractAt("../artifacts/contracts/mainnet/common/interfaces.sol:TokenInterface", tokens.dai.address)
console.log("Connector address", connector.address)
})
it("test veryClose.", async function () {
expect(veryClose(1000001, 1000000)).to.be.true
expect(veryClose(1000000, 1000001)).to.be.true
expect(veryClose(1003000, 1000001)).to.be.false
expect(veryClose(1000001, 1000300)).to.be.false
});
it("Should have contracts deployed.", async function () {
expect(!!instaConnectorsV2.address).to.be.true;
expect(!!connector.address).to.be.true;
expect(!!masterSigner.address).to.be.true;
expect(await connector.name()).to.be.equal("B.MakerDAO-v1.0");
});
describe("DSA wallet setup", function () {
it("Should build DSA v2", async function () {
dsaWallet0 = await buildDSAv2(wallet0.address)
expect(!!dsaWallet0.address).to.be.true;
dsaWallet1 = await buildDSAv2(wallet1.address)
expect(!!dsaWallet1.address).to.be.true;
});
it("Deposit ETH into DSA wallet", async function () {
await wallet0.sendTransaction({
to: dsaWallet0.address,
value: ethers.utils.parseEther("10")
});
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
await wallet1.sendTransaction({
to: dsaWallet1.address,
value: ethers.utils.parseEther("10")
});
expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10"));
});
});
describe("Main", function () {
let vault
let ilk
let urn
it("Should open ETH-A vault Maker", async function () {
vault = Number(await manager.cdpi()) + 1
const spells = [
{
connector: connectorName,
method: "open",
args: ["ETH-A"]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await manager.owns(vault)).to.be.equal(dsaWallet0.address)
ilk = await manager.ilks(vault)
expect(ilk).to.be.equal("0x4554482d41000000000000000000000000000000000000000000000000000000")
urn = await manager.urns(vault)
});
it("Should deposit", async function () {
const amount = ethers.utils.parseEther("7") // 7 ETH
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "deposit",
args: [vault, amount, 0, setId]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("3"))
const urnData = await vat.urns(ilk, urn)
expect(urnData[0]).to.be.equal(amount) // ink
expect(urnData[1]).to.be.equal("0") // art
});
it("Should withdraw", async function () {
const amount = ethers.utils.parseEther("1") // 1 ETH
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "withdraw",
args: [vault, amount, 0, setId]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("4"))
const urnData = await vat.urns(ilk, urn)
expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink
expect(urnData[1]).to.be.equal("0") // art
});
it("Should borrow", async function () {
const amount = ethers.utils.parseEther("6000") // 6000 dai
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "borrow",
args: [vault, amount, 0, setId]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
const urnData = await vat.urns(ilk, urn)
expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink
expect(urnData[1]).to.be.equal(await daiToArt(vat, ilk, amount)) // art
expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(amount)
});
it("Should repay", async function () {
const amount = ethers.utils.parseEther("500") // 500 dai
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "payback",
args: [vault, amount, 0, setId]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
const urnData = await vat.urns(ilk, urn)
expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink
expect(urnData[1]).to.be.equal(await daiToArt(vat, ilk, ethers.utils.parseEther("5500"))) // art
expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(ethers.utils.parseEther("5500"))
});
it("Should depositAndBorrow", async function () {
const borrowAmount = ethers.utils.parseEther("1000") // 1000 dai
const depositAmount = ethers.utils.parseEther("1") // 1 dai
const setId = "83478237"
const spells = [
{
connector: connectorName,
method: "depositAndBorrow",
args: [vault, depositAmount, borrowAmount, 0, 0, 0, 0]
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
const urnData = await vat.urns(ilk, urn)
expect(urnData[0]).to.be.equal(ethers.utils.parseEther("7")) // ink
expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(ethers.utils.parseEther("6500"))
// calculation is not precise as the jug was dripped
expect(veryClose(urnData[1], await daiToArt(vat, ilk, ethers.utils.parseEther("6500")))).to.be.true
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1"))
});
it("Should close", async function () {
// open a new vault
const newVault = vault + 1
let spells = [
{
connector: connectorName,
method: "open",
args: ["ETH-A"]
}
]
let tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address)
let receipt = await tx.wait()
expect(await manager.owns(newVault)).to.be.equal(dsaWallet1.address)
ilk = await manager.ilks(newVault)
expect(ilk).to.be.equal("0x4554482d41000000000000000000000000000000000000000000000000000000")
urn = await manager.urns(newVault)
// deposit and borrow
const borrowAmount = ethers.utils.parseEther("6000") // 6000 dai
const depositAmount = ethers.utils.parseEther("5") // 5 ETH
spells = [
{
connector: connectorName,
method: "depositAndBorrow",
args: [newVault, depositAmount, borrowAmount, 0, 0, 0, 0]
}
]
tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address)
receipt = await tx.wait()
const setId = 0
// repay borrow
spells = [
{
connector: connectorName,
method: "payback",
args: [newVault, borrowAmount, 0, setId]
}
]
tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address)
receipt = await tx.wait()
// withdraw deposit
spells = [
{
connector: connectorName,
method: "withdraw",
args: [newVault, depositAmount, 0, setId]
}
]
tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address)
receipt = await tx.wait()
// close
spells = [
{
connector: connectorName,
method: "close",
args: [newVault]
}
]
tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address)
receipt = await tx.wait()
expect(await manager.owns(newVault)).not.to.be.equal(dsaWallet1.address)
});
})
})
async function daiToArt(vat, ilk, dai) {
const ilks = await vat.ilks(ilk)
const rate = ilks[1] // second parameter
const _1e27 = ethers.utils.parseEther("1000000000") // 1e9 * 1e18
const art = dai.mul(_1e27).div(rate)
return art.add(1)
}
function veryClose(n1, n2) {
n1 = web3.utils.toBN(n1)
n2 = web3.utils.toBN(n2)
_10000 = web3.utils.toBN(10000)
_9999 = web3.utils.toBN(9999)
if(n1.mul(_10000).lt(n2.mul(_9999))) return false
if(n2.mul(_10000).lt(n1.mul(_9999))) return false
return true
}