Merge branch 'main' into feature/yearn-v2-connector

This commit is contained in:
Thomas Bouder 2021-08-29 19:01:03 +02:00 committed by GitHub
commit f323508f7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 14858 additions and 18749 deletions

412
README.md
View File

@ -4,391 +4,53 @@ Connectors are standard proxy logics contract that let DeFi Smart Account (DSA)
DSAs are powerful because they can easily be extended with connectors. Every new connector that is added is immediately usable by any developer building on top of DSAs. Connectors can either be base connectors to protocols, auth connectors, higher level connectors with more specific use cases like optimized lending, or connectors to native liquidity pools.
You can create a PR to request a support for specific protocol or external contracts. The process to add a new connector is explained [here](docs/how-to-add-new-connector.md). Following is the list of all the supported connectors. Following is the list of all the primary connectors used to cast spells:
You can create a PR to request a support for specific protocol or external contracts. The process to add a new connector is explained below.
[Read this post to learn about getId and setId used in the connectors](https://discuss.instadapp.io/t/how-to-use-getid-setid/104)
List of all the mainnet connector for referrence is [here](https://github.com/Instadapp/dsa-connectors/tree/main/contracts/mainnet/connectors)
## Authority
## How to add a new connector
[Code](contracts/mainnet/connectors/authority/main.sol)
You can create a new PR to add a new connector. To get the PR merged, certain requirements needs to be met which will be explained here.
### `add(authority)`
### New connector should follow the current directory structure
**Add an address authority**
Common files for all connectors are in `contracts/common` directory.
`authority` - Address of the authority to add
* `math.sol` has methods for mathematical operations (`DSMath`)
* `interfaces.sol` contains the common interfaces
* `TokenInterface` for ERC-20 interface including WETH
* `stores.sol` contains the global constants as well as methods `getId` & `setId` (`Stores`)
* `basic.sol` inherits `DSMath` & `Stores` contracts. This contains few details explained below
* Wrapping & unwrapping ETH (`convertEthToWeth` & `convertWethToEth`)
* Getting token & ETH balance of DSA
### `remove(authority)`
Connectors are under `contracts/connectors` directory, and should be formatted as follows:
**Remove an address authority**
* Connector events should be in a separate contract: `events.sol`
* Interfaces should be defined in a seperate file: `interface.sol`
* If the connector has helper methods & constants (including interface instances), this should be defined in a separate file: `helpers.sol`
* `Helpers` contract should inherit `Basic` contract from common directory
* If the connector doesn't have any helper methods, the main contract should inherit `Basic` contract
* The main logic of the contract should be under `main.sol`, and the contract should inherit `Helpers` (if exists, otherwise `Basic`) & `Events`
`authority` - Address of the authority to remove
Few things to consider while writing the connector:
## Basic
* Connector should have a public constant string declared `name`, which will be the name of the connector. This will be versioned. Ex: `Compound-v1`
* Contract name should start with `ConnectV2` appended with protocol name. Eg: `ConnectV2Compound`
* User interacting methods (`external` methods) will not be emitting events, rather the methods will be returning 2 variables:
* `_eventName` of `string` type: This will be the event signture defined in the `Events` contract. Ex: `LogDeposit(address,address,uint256,uint256,uint256)`
* `_eventParam` of `bytes` type: This will be the abi encoded event parameters
* The contracts should not have `selfdestruct()`
* The contracts should not have `delegatecall()`
* Use `uint(-1)` of `type(uint256).max` for maximum amount everywhere
* Use `ethAddr` (declared in `Stores`) to denote Ethereum (non-ERC20)
* Use `address(this)` instead of `msg.sender` for fetching balance on-chain, etc
* Only `approve()` (declared in `Basic`) limited amount while giving ERC20 allowance, which strictly needs to be 0 by the end of the spell.
* User interacting functions should have natspec comments(@dev, @notice, @param).
* Use `getUint()` (declared in `Stores`) for getting value that saved from previous spell
* Use `setUint()` (declared in `Stores`) for setting value to save for the future spell
[Code](contracts/mainnet/connectors/basic/main.sol)
### Support
### `deposit(erc20, amt, getId, setId)`
If you can't find something you're looking for or have any questions, ask them at our developers community on [Discord](https://discord.gg/83vvrnY) or simply send an [Email](mailto:info@instadapp.io).
**Deposit a token or ETH to DSA.**
`erc20` - Address of the token to deposit. ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
`amt` - Amount of token to deposit
In case of an ERC20 Token, allowance must be given to DSA before depositing
### `withdraw(erc20, amt, getId, setId)`
**Withdraw a token or ETH from DSA.**
`erc20` - Address of the token to withdraw. ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
`amt` - Amount of token to withdraw
`to` - Address to which token will be withdrawn
## MakerDAO
[Code](contracts/mainnet/connectors/makerdao/main.sol)
### `open(collateralType)`
**Open a Maker vault** of the `collateralType`. E.g. "ETH-A", "USDC-B", etc...
### `close(vault)`
**Close a Maker vault**
`vault` - Vault ID (Use 0 for last opened vault)
### `deposit(vault, amt, getId, setId)`
**Deposit collateral to a Maker vault.**
`vault` - Vault ID (Use 0 for last opened vault)
`amt` - Amount of collteral to deposit
### `withdraw(vault, amt, getId, setId)`
**Withdraw collateral from a Maker vault.**
`vault` - Vault ID (Use 0 for last opened vault)
`amt` - Amount of collteral to withdraw
### `borrow(vault, amt, getId, setId)`
**Borrow DAI from a Maker vault.**
`vault` - Vault ID (Use 0 for last opened vault)
`amt` - Amount of DAI to borrow
### `payback(vault, amt, getId, setId)`
**Payback DAI to a Maker vault.**
`vault` - Vault ID (Use 0 for last opened vault)
`amt` - Amount of DAI to payback
### `withdrawLiquidated(vault, amt, getId, setId)`
**Withdraw leftover collateral after liquidation.**
`vault` - Vault ID (Use 0 for last opened vault)
`amt` - Amount of collateral to withdraw
### `depositAndBorrow(vault, depositAmt, borrowAmt, getIdDeposit, getIdBorrow, setIdDeposit, setIdBorrow)`
**Deposit collateral & borrow DAI from a vault.**
`vault` - Vault ID (Use 0 for last opened vault)
`depositAmt` - Amount of collateral to deposit
`borrowAmt` - Amount of DAI to borrow
## Compound
[Code](contracts/mainnet/connectors/compound/main.sol)
### `deposit(token, amt, getId, setId)`
**Deposit token to Compound.**
`token` - Address of the token to deposit
`amt` - Amount of token to deposit
### `withdraw(token, amt, getId, setId)`
**Withdraw token from Compound.**
`token` - Address of the token to withdraw
`amt` - Amount of token to withdraw
### `borrow(token, amt, getId, setId)`
**Borrow token from Compound.**
`token` - Address of the token to borrow
`amt` - Amount of token to borrow
### `payback(token, amt, getId, setId)`
**Payback debt to Compound.**
`token` - Address of the token to payback
`amt` - Amount of token to payback
## COMP
[Code](contracts/mainnet/connectors/COMP/main.sol)
### `ClaimComp(setId)`
**Claim unclaimed COMP**
### `ClaimCompTwo(tokens, setId)`
**Claim unclaimed COMP**
`tokens` - List of tokens supplied or borrowed
### `ClaimCompThree(supplyTokens, borrowTokens, setId)`
**Claim unclaimed COMP**
`supplyTokens` - List of tokens supplied
`borrowTokens` - List of tokens borrowed
### `delegate(delegatee)`
**Delegate COMP votes**
`delegatee` - Address of the delegatee
## Aave v1
[Code](contracts/mainnet/connectors/aave/main.sol)
### `deposit(token, amt, getId, setId)`
**Deposit token to Aave.**
`token` - Address of the token to deposit
`amt` - Amount of token to deposit
### `withdraw(token, amt, getId, setId)`
**Withdraw token from Aave.**
`token` - Address of the token to withdraw
`amt` - Amount of token to withdraw
### `borrow(token, amt, getId, setId)`
**Borrow token from Aave.**
`token` - Address of the token to borrow
`amt` - Amount of token to borrow
### `payback(token, amt, getId, setId)`
**Payback debt to Aave.**
`token` - Address of the token to payback
`amt` - Amount of token to payback
## Aave v2
[Code](contracts/mainnet/connectors/aave_v2/main.sol)
### `deposit(token, amt, getId, setId)`
**Deposit token to Aave.**
`token` - Address of the token to deposit
`amt` - Amount of token to deposit
### `withdraw(token, amt, getId, setId)`
**Withdraw token from Aave.**
`token` - Address of the token to withdraw
`amt` - Amount of token to withdraw
### `borrow(token, amt, rateMode, getId, setId)`
**Borrow token from Aave.**
`token` - Address of the token to borrow
`amt` - Amount of token to borrow
`rateMode` - Borrow interest rate mode (1 = Stable & 2 = Variable)
### `payback(token, amt, rateMode, getId, setId)`
**Payback debt to Aave.**
`token` - Address of the token to payback
`amt` - Amount of token to payback
`rateMode` - Borrow interest rate mode (1 = Stable & 2 = Variable)
## dYdX
[Code](contracts/mainnet/connectors/dydx/main.sol)
### `deposit(token, amt, getId, setId)`
**Deposit token to dYdX.**
`token` - Address of the token to deposit
`amt` - Amount of token to deposit
### `withdraw(token, amt, getId, setId)`
**Withdraw token from dYdX.**
`token` - Address of the token to withdraw
`amt` - Amount of token to withdraw
### `borrow(token, amt, getId, setId)`
**Borrow token from dYdX.**
`token` - Address of the token to borrow
`amt` - Amount of token to borrow
### `payback(token, amt, getId, setId)`
**Payback debt to dYdX.**
`token` - Address of the token to payback
`amt` - Amount of token to payback
## Uniswap
[Code](contracts/mainnet/connectors/uniswap/main.sol)
### `deposit(tokenA, tokenB, amtA, unitAmt, slippage, getId, setId)`
**Deposit liquidity to tokenA/tokenB pool**
`tokenA` - Address of token A
`tokenB` - Address of token B
`amtA` - Amount of token A to deposit
`unitAmt` - Unit amount of amtB/amtA with slippage.
`slippage` - Slippage amount in wei
### `withdraw(tokenA, tokenB, uniAmt, unitAmtA, unitAmtB, getId, setId)`
**Withdraw liquidity from tokenA/tokenB pool**
`tokenA` - Address of token A
`tokenB` - Address of token B
`uniAmt` - Amount of LP tokens to withdraw
`unitAmtA` - Unit amount of amtA/uniAmt with slippage.
`unitAmtB` - Unit amount of amtB/uniAmt with slippage.
### `buy(buyAddr, sellAddr, buyAmt, unitAmt, getId, setId)`
**Buy a token/ETH**
`buyAddr` - Address of the buying token
`sellAddr` - Address of the selling token
`buyAmt` - Amount of tokens to buy
`unitAmt` - Unit amount of sellAmt/buyAmt with slippage
### `sell(buyAddr, sellAddr, sellAmt, unitAmt, getId, setId)`
**Sell a token/ETH**
`buyAddr` - Address of the buying token
`sellAddr` - Address of the selling token
`sellAmt` - Amount of tokens to sell
`unitAmt` - Unit amount of buyAmt/sellAmt with slippage
## 1Inch
[Code](contracts/mainnet/connectors/1inch/main.sol)
### `sell(buyAddr, sellAddr, sellAmt, unitAmt, getId, setId)`
**Sell ETH/ERC20 using 1proto**
`buyAddr` - Address of the buying token
`sellAddr` - Address of the selling token
`sellAmt` - Amount of tokens to sell
`unitAmt` - Unit amount of buyAmt/sellAmt with slippage
### `sellTwo(buyAddr, sellAddr, sellAmt, unitAmt, getId, setId)`
**Sell ETH/ERC20 using 1proto**
`buyAddr` - Address of the buying token
`sellAddr` - Address of the selling token
`sellAmt` - Amount of tokens to sell
`unitAmt` - Unit amount of buyAmt/sellAmt with slippage
`[]distribution` - Distribution of swap across different dex.
`disableDexes` - Disable a dex. (To disable none: 0)
### `sellTwo(buyAddr, sellAddr, sellAmt, unitAmt, getId, setId)`
**Sell ETH/ERC20 using 1inch**
Use [1Inch API](https://docs.1inch.exchange/api/) for calldata
`buyAddr` - Address of the buying token
`sellAddr` - Address of the selling token
`sellAmt` - Amount of tokens to sell
`unitAmt` - Unit amount of buyAmt/sellAmt with slippage
`callData` - Data from 1inch API

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

@ -17,8 +17,8 @@ abstract contract BasicResolver is Events, DSMath, Basic {
/**
* @dev Deposit Assets To Smart Account.
* @notice Deposit a token to DSA
* @param token The address of the token to deposit. (For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @notice Deposit a token to DSA.
* @param token The address of the token to deposit.<br>(For <b>ETH</b>: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE and need to pass `value` parameter equal to `amt` in cast function ```dsa.cast({..., value: amt})```.<br>For <b>ERC20</b>: Need to give allowance prior casting spells.)
* @param amt The amount of tokens to deposit. (For max: `uint256(-1)` (Not valid for ETH))
* @param getId ID to retrieve amt.
* @param setId ID stores the amount of tokens deposited.

View File

@ -23,6 +23,7 @@ contract LiquidityResolver is DSMath, Stores, Variables, Events {
* @dev Borrow Flashloan and Cast spells.
* @param token Token Address.
* @param amt Token Amount.
* @param route Flashloan source route. (0: dYdX(ETH,DAI,USDC only), 1: MakerDAO(DAI only), 2: Compound(All borrowable tokens in Compound), 3: AaveV2(All borrowable tokens in AaveV2))
* @param data targets & data for cast.
*/
function flashBorrowAndCast(
@ -74,4 +75,4 @@ contract LiquidityResolver is DSMath, Stores, Variables, Events {
contract ConnectV2InstaPool is LiquidityResolver {
string public name = "Instapool-v1.1";
}
}

View File

@ -0,0 +1,34 @@
pragma solidity ^0.7.0;
contract Events {
event LogMint(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amtA,
uint256 amtB,
int24 tickLower,
int24 tickUpper
);
event LogDeposit(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
);
event LogWithdraw(
uint256 indexed tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
);
event LogCollect(
uint256 tokenId,
uint256 amountA,
uint256 amountB
);
event LogBurnPosition(uint256 tokenId);
}

View File

@ -0,0 +1,265 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import {TokenInterface} from "../../common/interfaces.sol";
import {DSMath} from "../../common/math.sol";
import {Basic} from "../../common/basic.sol";
import "./interface.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev uniswap v3 NFT Position Manager & Swap Router
*/
INonfungiblePositionManager constant nftManager =
INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
ISwapRouter constant swapRouter =
ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
struct MintParams {
address tokenA;
address tokenB;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amtA;
uint256 amtB;
uint256 slippage;
}
/**
* @dev Get Last NFT Index
* @param user: User address
*/
function _getLastNftId(address user)
internal
view
returns (uint256 tokenId)
{
uint256 len = nftManager.balanceOf(user);
tokenId = nftManager.tokenOfOwnerByIndex(user, len - 1);
}
function getMinAmount(
TokenInterface token,
uint256 amt,
uint256 slippage
) internal view returns (uint256 minAmt) {
uint256 _amt18 = convertTo18(token.decimals(), amt);
minAmt = wmul(_amt18, sub(WAD, slippage));
minAmt = convert18ToDec(token.decimals(), minAmt);
}
/**
* @dev Mint function which interact with Uniswap v3
*/
function _mint(MintParams memory params)
internal
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amountA,
uint256 amountB
)
{
(TokenInterface _token0, TokenInterface _token1) = changeEthAddress(
params.tokenA,
params.tokenB
);
uint256 _amount0 = params.amtA == uint256(-1)
? getTokenBal(TokenInterface(params.tokenA))
: params.amtA;
uint256 _amount1 = params.amtB == uint256(-1)
? getTokenBal(TokenInterface(params.tokenB))
: params.amtB;
convertEthToWeth(address(_token0) == wethAddr, _token0, _amount0);
convertEthToWeth(address(_token1) == wethAddr, _token1, _amount1);
approve(_token0, address(nftManager), _amount0);
approve(_token1, address(nftManager), _amount1);
uint256 _minAmt0 = getMinAmount(_token0, _amount0, params.slippage);
uint256 _minAmt1 = getMinAmount(_token1, _amount1, params.slippage);
INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams(
address(_token0),
address(_token1),
params.fee,
params.tickLower,
params.tickUpper,
_amount0,
_amount1,
_minAmt0,
_minAmt1,
address(this),
block.timestamp
);
(tokenId, liquidity, amountA, amountB) = nftManager.mint(params);
}
function getNftTokenPairAddresses(uint256 _tokenId)
internal
view
returns (address token0, address token1)
{
(bool success, bytes memory data) = address(nftManager).staticcall(
abi.encodeWithSelector(nftManager.positions.selector, _tokenId)
);
require(success, "fetching positions failed");
{
(, , token0, token1, , , , ) = abi.decode(
data,
(
uint96,
address,
address,
address,
uint24,
int24,
int24,
uint128
)
);
}
}
/**
* @dev Check if token address is etherAddr and convert it to weth
*/
function _checkETH(
address _token0,
address _token1,
uint256 _amount0,
uint256 _amount1
) internal {
bool isEth0 = _token0 == wethAddr;
bool isEth1 = _token1 == wethAddr;
convertEthToWeth(isEth0, TokenInterface(_token0), _amount0);
convertEthToWeth(isEth1, TokenInterface(_token1), _amount1);
approve(TokenInterface(_token0), address(nftManager), _amount0);
approve(TokenInterface(_token1), address(nftManager), _amount1);
}
/**
* @dev addLiquidityWrapper function wrapper of _addLiquidity
*/
function _addLiquidityWrapper(
uint256 tokenId,
uint256 amountA,
uint256 amountB,
uint256 slippage
)
internal
returns (
uint256 liquidity,
uint256 amtA,
uint256 amtB
)
{
(address token0, address token1) = getNftTokenPairAddresses(tokenId);
(liquidity, amtA, amtB) = _addLiquidity(
tokenId,
token0,
token1,
amountA,
amountB,
slippage
);
}
/**
* @dev addLiquidity function which interact with Uniswap v3
*/
function _addLiquidity(
uint256 _tokenId,
address _token0,
address _token1,
uint256 _amount0,
uint256 _amount1,
uint256 _slippage
)
internal
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
)
{
_checkETH(_token0, _token1, _amount0, _amount1);
uint256 _amount0Min = getMinAmount(
TokenInterface(_token0),
_amount0,
_slippage
);
uint256 _amount1Min = getMinAmount(
TokenInterface(_token1),
_amount1,
_slippage
);
INonfungiblePositionManager.IncreaseLiquidityParams
memory params = INonfungiblePositionManager.IncreaseLiquidityParams(
_tokenId,
_amount0,
_amount1,
_amount0Min,
_amount1Min,
block.timestamp
);
(liquidity, amount0, amount1) = nftManager.increaseLiquidity(params);
}
/**
* @dev decreaseLiquidity function which interact with Uniswap v3
*/
function _decreaseLiquidity(
uint256 _tokenId,
uint128 _liquidity,
uint256 _amount0Min,
uint256 _amount1Min
) internal returns (uint256 amount0, uint256 amount1) {
INonfungiblePositionManager.DecreaseLiquidityParams
memory params = INonfungiblePositionManager.DecreaseLiquidityParams(
_tokenId,
_liquidity,
_amount0Min,
_amount1Min,
block.timestamp
);
(amount0, amount1) = nftManager.decreaseLiquidity(params);
}
/**
* @dev collect function which interact with Uniswap v3
*/
function _collect(
uint256 _tokenId,
uint128 _amount0Max,
uint128 _amount1Max
) internal returns (uint256 amount0, uint256 amount1) {
INonfungiblePositionManager.CollectParams
memory params = INonfungiblePositionManager.CollectParams(
_tokenId,
address(this),
_amount0Max,
_amount1Max
);
(amount0, amount1) = nftManager.collect(params);
}
/**
* @dev Burn Function
*/
function _burn(uint256 _tokenId) internal {
nftManager.burn(_tokenId);
}
}

View File

@ -0,0 +1,368 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params)
external
payable
returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params)
external
payable
returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params)
external
payable
returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params)
external
payable
returns (uint256 amountIn);
}
/// @title Creates and initializes V3 Pools
/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
/// require the pool to exist.
interface IPoolInitializer {
/// @notice Creates a new pool if it does not exist, then initializes if not initialized
/// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
/// @param token0 The contract address of token0 of the pool
/// @param token1 The contract address of token1 of the pool
/// @param fee The fee amount of the v3 pool for the specified token pair
/// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
/// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}
/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
/// @return Returns the address of the Uniswap V3 factory
function factory() external view returns (address);
/// @return Returns the address of WETH9
function WETH9() external view returns (address);
}
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
/// @param recipient The address receiving ETH
function unwrapWETH9(uint256 amountMinimum, address recipient)
external
payable;
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`
/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
/// that use ether for the input amount
function refundETH() external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to `recipient`
/// @param amountMinimum The minimum amount of token required for a transfer
/// @param recipient The destination address of the token
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}
/// @title ERC721 with permit
/// @notice Extension to ERC721 that includes a permit function for signature based approvals
interface IERC721Permit is IERC721 {
/// @notice The permit typehash used in the permit signature
/// @return The typehash for the permit
function PERMIT_TYPEHASH() external pure returns (bytes32);
/// @notice The domain separator used in the permit signature
/// @return The domain seperator used in encoding of permit signature
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Approve of a specific token ID for spending by spender via signature
/// @param spender The account that is being approved
/// @param tokenId The ID of the token that is being approved for spending
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is
IPoolInitializer,
IPeripheryPayments,
IPeripheryImmutableState,
IERC721Metadata,
IERC721Enumerable,
IERC721Permit
{
/// @notice Emitted when liquidity is increased for a position NFT
/// @dev Also emitted when a token is minted
/// @param tokenId The ID of the token for which liquidity was increased
/// @param liquidity The amount by which liquidity for the NFT position was increased
/// @param amount0 The amount of token0 that was paid for the increase in liquidity
/// @param amount1 The amount of token1 that was paid for the increase in liquidity
event IncreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when liquidity is decreased for a position NFT
/// @param tokenId The ID of the token for which liquidity was decreased
/// @param liquidity The amount by which liquidity for the NFT position was decreased
/// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
/// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
event DecreaseLiquidity(
uint256 indexed tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when tokens are collected for a position NFT
/// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
/// @param tokenId The ID of the token for which underlying tokens were collected
/// @param recipient The address of the account that received the collected tokens
/// @param amount0 The amount of token0 owed to the position that was collected
/// @param amount1 The amount of token1 owed to the position that was collected
event Collect(
uint256 indexed tokenId,
address recipient,
uint256 amount0,
uint256 amount1
);
/// @notice Returns the position information associated with a given token ID.
/// @dev Throws if the token ID is not valid.
/// @param tokenId The ID of the token that represents the position
/// @return nonce The nonce for permits
/// @return operator The address that is approved for spending
/// @return token0 The address of the token0 for a specific pool
/// @return token1 The address of the token1 for a specific pool
/// @return fee The fee associated with the pool
/// @return tickLower The lower end of the tick range for the position
/// @return tickUpper The higher end of the tick range for the position
/// @return liquidity The liquidity of the position
/// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
/// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
/// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
/// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
/// @param params tokenId The ID of the token for which liquidity is being increased,
/// amount0Desired The desired amount of token0 to be spent,
/// amount1Desired The desired amount of token1 to be spent,
/// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 to acheive resulting liquidity
/// @return amount1 The amount of token1 to acheive resulting liquidity
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position
/// @param params tokenId The ID of the token for which liquidity is being decreased,
/// amount The amount by which liquidity will be decreased,
/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 accounted to the position's tokens owed
/// @return amount1 The amount of token1 accounted to the position's tokens owed
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
/// @param params tokenId The ID of the NFT for which tokens are being collected,
/// recipient The account that should receive the tokens,
/// amount0Max The maximum amount of token0 to collect,
/// amount1Max The maximum amount of token1 to collect
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(CollectParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
/// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
/// must be collected first.
/// @param tokenId The ID of the token that is being burned
function burn(uint256 tokenId) external payable;
}

View File

@ -0,0 +1,210 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
/**
* @title Uniswap v3.
* @dev Decentralized Exchange.
*/
import {TokenInterface} from "../../common/interfaces.sol";
import {Helpers} from "./helpers.sol";
import {Events} from "./events.sol";
abstract contract UniswapResolver is Helpers, Events {
/**
* @dev Mint New Position
* @notice Mint New NFT LP Position
* @param tokenA tokenA addreess
* @param tokenB tokenB addreess
* @param fee fee percentage
* @param tickLower Lower tick
* @param tickUpper Upper tick
* @param amtA amount of tokenA
* @param amtB amount of tokenB
* @param slippage slippage percentage
* @param getIds ID to retrieve amtA
* @param setId ID stores the amount of LP token
*/
function mint(
address tokenA,
address tokenB,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint256 amtA,
uint256 amtB,
uint256 slippage,
uint256[] calldata getIds,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
MintParams memory params;
{
params = MintParams(
tokenA,
tokenB,
fee,
tickLower,
tickUpper,
amtA,
amtB,
slippage
);
}
params.amtA = getUint(getIds[0], params.amtA);
params.amtB = getUint(getIds[1], params.amtB);
(
uint256 _tokenId,
uint256 liquidity,
uint256 amountA,
uint256 amountB
) = _mint(params);
setUint(setId, liquidity);
_eventName = "LogMint(uint256,uint256,uint256,uint256,int24,int24)";
_eventParam = abi.encode(
_tokenId,
liquidity,
amountA,
amountB,
params.tickLower,
params.tickUpper
);
}
/**
* @dev Increase Liquidity
* @notice Increase Liquidity of NFT Position
* @param tokenId NFT LP Token ID.
* @param amountA tokenA amounts.
* @param amountB tokenB amounts.
* @param slippage slippage.
* @param getIds IDs to retrieve token amounts
* @param setId stores the liquidity amount
*/
function deposit(
uint256 tokenId,
uint256 amountA,
uint256 amountB,
uint256 slippage,
uint256[] calldata getIds,
uint256 setId
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
amountA = getUint(getIds[0], amountA);
amountB = getUint(getIds[1], amountB);
(
uint256 _liquidity,
uint256 _amtA,
uint256 _amtB
) = _addLiquidityWrapper(tokenId, amountA, amountB, slippage);
setUint(setId, _liquidity);
_eventName = "LogDeposit(uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
}
/**
* @dev Decrease Liquidity
* @notice Decrease Liquidity of NFT Position
* @param tokenId NFT LP Token ID.
* @param liquidity LP Token amount.
* @param amountAMin Min amount of tokenA.
* @param amountBMin Min amount of tokenB.
* @param getId ID to retrieve LP token amounts
* @param setIds stores the amount of output tokens
*/
function withdraw(
uint256 tokenId,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
uint256 getId,
uint256[] calldata setIds
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
uint128 _liquidity = uint128(getUint(getId, liquidity));
(uint256 _amtA, uint256 _amtB) = _decreaseLiquidity(
tokenId,
_liquidity,
amountAMin,
amountBMin
);
setUint(setIds[0], _amtA);
setUint(setIds[1], _amtB);
_eventName = "LogWithdraw(uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, _liquidity, _amtA, _amtB);
}
/**
* @dev Collect function
* @notice Collect from NFT Position
* @param tokenId NFT LP Token ID.
* @param amount0Max Max amount of token0.
* @param amount1Max Max amount of token1.
* @param getIds IDs to retrieve amounts
* @param setIds stores the amount of output tokens
*/
function collect(
uint256 tokenId,
uint256 amount0Max,
uint256 amount1Max,
uint256[] calldata getIds,
uint256[] calldata setIds
)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
uint128 _amount0Max = uint128(getUint(getIds[0], amount0Max));
uint128 _amount1Max = uint128(getUint(getIds[1], amount1Max));
(uint256 amount0, uint256 amount1) = _collect(
tokenId,
_amount0Max,
_amount1Max
);
setUint(setIds[0], amount0);
setUint(setIds[1], amount1);
_eventName = "LogCollect(uint256,uint256,uint256)";
_eventParam = abi.encode(tokenId, amount0, amount1);
}
/**
* @dev Burn Function
* @notice Burn NFT LP Position
* @param tokenId NFT LP Token ID
*/
function burn(uint256 tokenId)
external
payable
returns (string memory _eventName, bytes memory _eventParam)
{
if (tokenId == 0) tokenId = _getLastNftId(address(this));
_burn(tokenId);
_eventName = "LogBurnPosition(uint256)";
_eventParam = abi.encode(tokenId);
}
}
contract ConnectV2UniswapV3 is UniswapResolver {
string public constant name = "UniswapV3-v1";
}

View File

@ -31,7 +31,7 @@ Few things to consider while writing the connector:
* `_eventParam` of `bytes` type: This will be the abi encoded event parameters
* The contracts should not have `selfdestruct()`
* The contracts should not have `delegatecall()`
* Use `uint(-1)` for maximum amount everywhere
* Use `uint(-1)` of `type(uint256).max` for maximum amount everywhere
* Use `ethAddr` (declared in `Stores`) to denote Ethereum (non-ERC20)
* Use `address(this)` instead of `msg.sender` for fetching balance on-chain, etc
* Only `approve()` limited amount while giving ERC20 allowance, which strictly needs to be 0 by the end of the spell.

View File

@ -60,6 +60,7 @@ module.exports = {
blockNumber: 12796965,
},
blockGasLimit: 12000000,
gasPrice: parseInt(utils.parseUnits("300", "gwei"))
},
matic: {
url: "https://rpc-mainnet.maticvigil.com/",

20335
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,7 @@
"test": "hardhat test",
"coverage": "./node_modules/.bin/solidity-coverage",
"check-husky": "node status-checks/huskyCheck.js",
"build-contracts": "sol-merger \"./contracts/connectors/mock.sol\" ./contracts/build",
"prepare": "husky install"
"build-contracts": "sol-merger \"./contracts/connectors/mock.sol\" ./contracts/build"
},
"repository": {
"type": "git",
@ -22,6 +21,8 @@
"homepage": "https://github.com/InstaDApp/dsa-connectors-new#readme",
"dependencies": {
"@openzeppelin/contracts": "^3.4.0-solc-0.7",
"@uniswap/v3-core": "^1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"chalk": "^4.0.0",
"commander": "^7.1.0",
"dotenv": "^7.0.0",
@ -31,21 +32,21 @@
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.3",
"@nomiclabs/hardhat-etherscan": "^2.1.4",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/test-helpers": "^0.5.6",
"@openzeppelin/test-helpers": "^0.5.12",
"@studydefi/money-legos": "^2.3.7",
"@tenderly/hardhat-tenderly": "^1.0.6",
"@tenderly/hardhat-tenderly": "^1.0.12",
"chai-as-promised": "^7.1.1",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.1.4",
"hardhat": "^2.3.0",
"hardhat-deploy": "^0.7.5",
"hardhat-deploy-ethers": "^0.3.0-beta.7",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.4",
"hardhat": "^2.6.0",
"hardhat-deploy": "^0.8.11",
"hardhat-deploy-ethers": "^0.3.0-beta.10",
"husky": "^6.0.0",
"sol-merger": "^2.0.1",
"solidity-coverage": "0.5.11",
"web3": "^1.3.0"
"web3": "^1.3.6"
}
}

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
}

View File

@ -0,0 +1,324 @@
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 encodeFlashcastData = require("../../scripts/encodeFlashcastData.js")
const getMasterSigner = require("../../scripts/getMasterSigner")
const addLiquidity = require("../../scripts/addLiquidity");
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 { abi: nftManagerAbi } = require("@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json")
const connectV2UniswapV3Artifacts = require("../../artifacts/contracts/mainnet/connectors/uniswapV3/main.sol/ConnectV2UniswapV3.json");
const { eth } = require("../../scripts/constant/tokens");
const { BigNumber } = require("ethers");
const FeeAmount = {
LOW: 500,
MEDIUM: 3000,
HIGH: 10000,
}
const TICK_SPACINGS = {
500: 10,
3000: 60,
10000: 200
}
const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"
const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"
let tokenIds = []
let liquidities = []
const abiCoder = ethers.utils.defaultAbiCoder
describe("UniswapV3", function () {
const connectorName = "UniswapV3-v1"
let dsaWallet0
let masterSigner;
let instaConnectorsV2;
let connector;
let nftManager;
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);
nftManager = await ethers.getContractAt(nftManagerAbi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88");
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: connectV2UniswapV3Artifacts,
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;
});
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 & DAI 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 addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000"));
});
it("Deposit ETH & USDT 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 addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000"));
});
});
describe("Main", function () {
it("Should mint successfully", async function () {
const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH
const daiAmount = ethers.utils.parseEther("400") // 1 ETH
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12) // 1 ETH
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
const getIds = ["0", "0"]
const setId = "0"
const spells = [
{
connector: connectorName,
method: "mint",
args: [
DAI_ADDR,
ethAddress,
FeeAmount.MEDIUM,
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
daiAmount,
ethAmount,
"500000000000000000",
getIds,
setId
],
},
{
connector: connectorName,
method: "mint",
args: [
DAI_ADDR,
USDT_ADDR,
FeeAmount.MEDIUM,
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
daiAmount,
usdtAmount,
"300000000000000000",
getIds,
setId
],
},
{
connector: connectorName,
method: "mint",
args: [
ethAddress,
USDT_ADDR,
FeeAmount.MEDIUM,
getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
ethAmount,
usdtAmount,
"300000000000000000",
getIds,
setId
],
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
let receipt = await tx.wait()
let castEvent = new Promise((resolve, reject) => {
dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => {
const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[0]);
const params1 = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[2]);
tokenIds.push(params[0]);
tokenIds.push(params1[0]);
liquidities.push(params[1]);
event.removeListener();
resolve({
eventNames,
});
});
setTimeout(() => {
reject(new Error('timeout'));
}, 60000)
});
let event = await castEvent
const data = await nftManager.positions(tokenIds[0])
expect(data.liquidity).to.be.equals(liquidities[0]);
})
it("Should deposit successfully", async function () {
const daiAmount = ethers.utils.parseEther("400") // 1 ETH
const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH
const usdtAmount = ethers.utils.parseEther("400") / Math.pow(10, 12) // 1 ETH
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
const getIds = ["0", "0"]
const setId = "0"
const spells = [
{
connector: connectorName,
method: "deposit",
args: [
tokenIds[0],
daiAmount,
ethAmount,
"500000000000000000",
getIds,
setId
],
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
let castEvent = new Promise((resolve, reject) => {
dsaWallet0.on('LogCast', (origin, sender, value, targetNames, targets, eventNames, eventParams, event) => {
const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256"], eventParams[0]);
liquidities[0] = liquidities[0].add(params[1]);
event.removeListener();
resolve({
eventNames,
});
});
setTimeout(() => {
reject(new Error('timeout'));
}, 60000)
});
let event = await castEvent
const data = await nftManager.positions(tokenIds[0])
expect(data.liquidity).to.be.equals(liquidities[0]);
})
it("Should withdraw successfully", async function () {
const getId = "0"
const setIds = ["0", "0"]
const data = await nftManager.positions(tokenIds[0])
let data1 = await nftManager.positions(tokenIds[1])
const spells = [
{
connector: connectorName,
method: "withdraw",
args: [
tokenIds[0],
data.liquidity,
0,
0,
getId,
setIds
],
},
{
connector: connectorName,
method: "withdraw",
args: [
0,
data1.liquidity,
0,
0,
getId,
setIds
],
},
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
data1 = await nftManager.positions(tokenIds[1])
expect(data1.liquidity.toNumber()).to.be.equals(0);
})
it("Should collect successfully", async function () {
const ethAmount = ethers.utils.parseEther("0.2") // 1 ETH
const daiAmount = ethers.utils.parseEther("800") // 1 ETH
const getIds = ["0", "0"]
const setIds = ["0", "0"]
const spells = [
{
connector: connectorName,
method: "collect",
args: [
tokenIds[0],
daiAmount,
ethAmount,
getIds,
setIds
],
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
})
it("Should burn successfully", async function () {
const spells = [
{
connector: connectorName,
method: "burn",
args: [
tokenIds[0]
],
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
})
})
})
const getMinTick = (tickSpacing) => Math.ceil(-887272 / tickSpacing) * tickSpacing
const getMaxTick = (tickSpacing) => Math.floor(887272 / tickSpacing) * tickSpacing

9630
yarn.lock Normal file

File diff suppressed because it is too large Load Diff