feat: add README and common files

This commit is contained in:
Shriya Tyagi 2023-12-16 15:55:57 +05:30
parent ea17c4f9d8
commit 1c81ddc6c3
5 changed files with 325 additions and 0 deletions

114
README.md
View File

@ -1,2 +1,116 @@
# dsa-connectors-2.0
DSA Connectors 2.0
Connectors are standard proxy logics contract that let DeFi Smart Account (DSA) interact with various smart contracts, and make the important actions accessible like cross protocol interoperability.
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 below.
List of all the mainnet connector for referrence is [here](https://github.com/Instadapp/dsa-connectors/tree/main/contracts/mainnet/connectors)
## Usage
### Pre Requisites
Before running any command, make sure to install dependencies:
```sh
$ npm install
```
### Compile
Compile the smart contracts with Hardhat:
```sh
$ npm run compile
```
### TypeChain
Compile the smart contracts and generate TypeChain artifacts:
```sh
$ npm run typechain
```
### Test
Run tests using interactive CLI
```sh
$ npm run test:runner
```
Run all the tests:
```sh
$ npm run test
```
(Striclty use this envirnment to test, or otherwise make suitable changes in config file before testing).
### Deploy
To deploy a connector using interactive CLI
```sh
$ npm run deploy:runner
```
(To deploy script manually use `scripts/deployment/deployManually.ts` script)
### checks
To check that code is compatible with github actions
```sh
$ npm run check
```
## How to add a new connector
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.
### New connector should follow the current directory structure
Common files for all connectors are in `contracts/common` directory.
- `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
Connectors are under `contracts/connectors` directory, and should be formatted as follows:
- 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`
Few things to consider while writing the connector:
- 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
### Support
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).

View File

@ -0,0 +1,59 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import { TokenInterface } from "./interfaces.sol";
import { Stores } from "./stores.sol";
import { DSMath } from "./math.sol";
abstract contract Basic is DSMath, Stores {
function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
amt = (_amt / 10 ** (18 - _dec));
}
function convertTo18(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
amt = mul(_amt, 10 ** (18 - _dec));
}
function getTokenBal(TokenInterface token) internal view returns(uint _amt) {
_amt = address(token) == ethAddr ? address(this).balance : token.balanceOf(address(this));
}
function getTokensDec(TokenInterface buyAddr, TokenInterface sellAddr) internal view returns(uint buyDec, uint sellDec) {
buyDec = address(buyAddr) == ethAddr ? 18 : buyAddr.decimals();
sellDec = address(sellAddr) == ethAddr ? 18 : sellAddr.decimals();
}
function encodeEvent(string memory eventName, bytes memory eventParam) internal pure returns (bytes memory) {
return abi.encode(eventName, eventParam);
}
function approve(TokenInterface token, address spender, uint256 amount) internal {
try token.approve(spender, amount) {
} catch {
token.approve(spender, 0);
token.approve(spender, amount);
}
}
function changeEthAddress(address buy, address sell) internal pure returns(TokenInterface _buy, TokenInterface _sell){
_buy = buy == ethAddr ? TokenInterface(wethAddr) : TokenInterface(buy);
_sell = sell == ethAddr ? TokenInterface(wethAddr) : TokenInterface(sell);
}
function changeEthAddrToWethAddr(address token) internal pure returns(address tokenAddr){
tokenAddr = token == ethAddr ? wethAddr : token;
}
function convertEthToWeth(bool isEth, TokenInterface token, uint amount) internal {
if(isEth) token.deposit{value: amount}();
}
function convertWethToEth(bool isEth, TokenInterface token, uint amount) internal {
if(isEth) {
approve(token, address(token), amount);
token.withdraw(amount);
}
}
}

View File

@ -0,0 +1,43 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
interface TokenInterface {
function approve(address, uint256) external;
function transfer(address, uint) external;
function transferFrom(address, address, uint) external;
function deposit() external payable;
function withdraw(uint) external;
function balanceOf(address) external view returns (uint);
function decimals() external view returns (uint);
function totalSupply() external view returns (uint);
function allowance(address owner, address spender) external view returns (uint256);
}
interface MemoryInterface {
function getUint(uint id) external returns (uint num);
function setUint(uint id, uint val) external;
}
interface InstaMapping {
function cTokenMapping(address) external view returns (address);
function gemJoinMapping(bytes32) external view returns (address);
}
interface AccountInterface {
function enable(address) external;
function disable(address) external;
function isAuth(address) external view returns (bool);
function cast(
string[] calldata _targetNames,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32[] memory responses);
}
interface ListInterface {
function accountID(address) external returns (uint64);
}
interface InstaConnectors {
function isConnectors(string[] calldata) external returns (bool, address[] memory);
}

56
contracts/common/math.sol Normal file
View File

@ -0,0 +1,56 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract DSMath {
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function add(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(x, y);
}
function sub(uint x, uint y) internal virtual pure returns (uint z) {
z = SafeMath.sub(x, y);
}
function mul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.mul(x, y);
}
function div(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.div(x, y);
}
function wmul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, y), WAD / 2) / WAD;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, RAY), y / 2) / y;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, y), RAY / 2) / RAY;
}
function toInt(uint x) internal pure returns (int y) {
y = int(x);
require(y >= 0, "int-overflow");
}
function toUint(int256 x) internal pure returns (uint256) {
require(x >= 0, "int-overflow");
return uint256(x);
}
function toRad(uint wad) internal pure returns (uint rad) {
rad = mul(wad, 10 ** 27);
}
}

View File

@ -0,0 +1,53 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import { MemoryInterface, InstaMapping, ListInterface, InstaConnectors } from "./interfaces.sol";
abstract contract Stores {
/**
* @dev Return ethereum address
*/
address constant internal ethAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/**
* @dev Return Wrapped ETH address
*/
address constant internal wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/**
* @dev Return memory variable address
*/
MemoryInterface constant internal instaMemory = MemoryInterface(0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F);
/**
* @dev Return InstaDApp Mapping Addresses
*/
InstaMapping constant internal instaMapping = InstaMapping(0xe81F70Cc7C0D46e12d70efc60607F16bbD617E88);
/**
* @dev Return InstaList Address
*/
ListInterface internal constant instaList = ListInterface(0x4c8a1BEb8a87765788946D6B19C6C6355194AbEb);
/**
* @dev Return connectors registry address
*/
InstaConnectors internal constant instaConnectors = InstaConnectors(0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11);
/**
* @dev Get Uint value from InstaMemory Contract.
*/
function getUint(uint getId, uint val) internal returns (uint returnVal) {
returnVal = getId == 0 ? val : instaMemory.getUint(getId);
}
/**
* @dev Set Uint value in InstaMemory Contract.
*/
function setUint(uint setId, uint val) virtual internal {
if (setId != 0) instaMemory.setUint(setId, val);
}
}