mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
Merge branch 'main' into ubiquity
This commit is contained in:
commit
4a7f1cc1d1
27
.github/workflows/stale.yml
vendored
Normal file
27
.github/workflows/stale.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '31 4 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -63,3 +63,4 @@ build/contracts
|
|||
# buidler
|
||||
artifacts
|
||||
cache
|
||||
typechain
|
||||
41
README.md
41
README.md
|
|
@ -8,6 +8,47 @@ You can create a PR to request a support for specific protocol or external contr
|
|||
|
||||
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).
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
10
contracts/arbitrum/connectors/instapool_v4/events.sol
Normal file
10
contracts/arbitrum/connectors/instapool_v4/events.sol
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Events {
|
||||
event LogFlashBorrow(address token, uint256 tokenAmt);
|
||||
event LogFlashPayback(address token, uint256 tokenAmt);
|
||||
|
||||
event LogFlashMultiBorrow(address[] token, uint256[] tokenAmts);
|
||||
event LogFlashMultiPayback(address[] token, uint256[] tokenAmts);
|
||||
}
|
||||
11
contracts/arbitrum/connectors/instapool_v4/interfaces.sol
Normal file
11
contracts/arbitrum/connectors/instapool_v4/interfaces.sol
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface InstaFlashV4Interface {
|
||||
function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;
|
||||
}
|
||||
|
||||
interface AccountInterface {
|
||||
function enable(address) external;
|
||||
function disable(address) external;
|
||||
}
|
||||
138
contracts/arbitrum/connectors/instapool_v4/main.sol
Normal file
138
contracts/arbitrum/connectors/instapool_v4/main.sol
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Instapool.
|
||||
* @dev Inbuilt Flash Loan in DSA
|
||||
*/
|
||||
|
||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { TokenInterface } from "../../common/interfaces.sol";
|
||||
import { AccountInterface } from "./interfaces.sol";
|
||||
import { Stores } from "../../common/stores.sol";
|
||||
import { Variables } from "./variables.sol";
|
||||
import { Events } from "./events.sol";
|
||||
|
||||
contract LiquidityResolver is Stores, Variables, Events {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
/**
|
||||
* @dev Borrow Flashloan and Cast spells.
|
||||
* @notice Borrow Flashloan and Cast spells.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashBorrowAndCast(
|
||||
address token,
|
||||
uint amt,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
address[] memory tokens_ = new address[](1);
|
||||
tokens_[0] = token;
|
||||
uint[] memory amts_ = new uint[](1);
|
||||
amts_[0] = amt;
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
|
||||
_eventName = "LogFlashBorrow(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return token to InstaPool.
|
||||
* @notice Return token to InstaPool.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param getId Get token amount at this ID from `InstaMemory` Contract.
|
||||
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
||||
*/
|
||||
function flashPayback(
|
||||
address token,
|
||||
uint amt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
IERC20 tokenContract = IERC20(token);
|
||||
|
||||
tokenContract.safeTransfer(address(instaPool), _amt);
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogFlashPayback(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @notice Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashMultiBorrowAndCast(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
_eventName = "LogFlashMultiBorrow(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return multi-tokens to InstaPool.
|
||||
* @notice Return multi-tokens to InstaPool.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param getIds Array of getId token amounts.
|
||||
* @param setIds Array of setId token amounts.
|
||||
*/
|
||||
function flashMultiPayback(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint[] memory getIds,
|
||||
uint[] memory setIds
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
for (uint i = 0; i < tokens_.length; i++) {
|
||||
amts_[i] = getUint(getIds[i], amts_[i]);
|
||||
|
||||
IERC20(tokens_[i]).safeTransfer(address(instaPool), amts_[i]);
|
||||
|
||||
setUint(setIds[i], amts_[i]);
|
||||
}
|
||||
|
||||
_eventName = "LogFlashMultiPayback(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract ConnectV2InstaPoolV4 is LiquidityResolver {
|
||||
string public name = "Instapool-v4";
|
||||
}
|
||||
13
contracts/arbitrum/connectors/instapool_v4/variables.sol
Normal file
13
contracts/arbitrum/connectors/instapool_v4/variables.sol
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { InstaFlashV4Interface } from "./interfaces.sol";
|
||||
|
||||
contract Variables {
|
||||
|
||||
/**
|
||||
* @dev Instapool contract proxy
|
||||
*/
|
||||
InstaFlashV4Interface public constant instaPool = InstaFlashV4Interface(address(0)); // TODO: update address
|
||||
|
||||
}
|
||||
10
contracts/arbitrum/connectors/uniswap-sell-beta/events.sol
Normal file
10
contracts/arbitrum/connectors/uniswap-sell-beta/events.sol
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
contract Events {
|
||||
event LogSell(
|
||||
uint24 fee,
|
||||
uint256 amountIn,
|
||||
uint256 amountOut,
|
||||
uint256 amountOutMinimum
|
||||
);
|
||||
}
|
||||
123
contracts/arbitrum/connectors/uniswap-sell-beta/helpers.sol
Normal file
123
contracts/arbitrum/connectors/uniswap-sell-beta/helpers.sol
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
pragma solidity ^0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
import {UniswapV3Pool, ISwapRouter} from "./interface.sol";
|
||||
import {SqrtPriceMath} from "./libraries/SqrtPriceMath.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
|
||||
contract Helpers {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
ISwapRouter internal constant router =
|
||||
ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
|
||||
|
||||
bytes32 internal constant POOL_INIT_CODE_HASH =
|
||||
0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
|
||||
|
||||
struct PoolKey {
|
||||
address token0;
|
||||
address token1;
|
||||
uint24 fee;
|
||||
}
|
||||
|
||||
function getPoolAddress(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint24 fee
|
||||
) internal pure returns (address pool) {
|
||||
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
|
||||
return
|
||||
computeAddress(
|
||||
0x1F98431c8aD98523631AE4a59f267346ea31F984,
|
||||
PoolKey({token0: tokenA, token1: tokenB, fee: fee})
|
||||
);
|
||||
}
|
||||
|
||||
function computeAddress(address factory, PoolKey memory key)
|
||||
internal
|
||||
pure
|
||||
returns (address pool)
|
||||
{
|
||||
require(key.token0 < key.token1);
|
||||
pool = address(
|
||||
uint160(
|
||||
uint256(
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
hex"ff",
|
||||
factory,
|
||||
keccak256(
|
||||
abi.encode(key.token0, key.token1, key.fee)
|
||||
),
|
||||
POOL_INIT_CODE_HASH
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getPriceLimit(
|
||||
uint256 amountIn,
|
||||
bool zeroForOne,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint24 fee
|
||||
) internal view returns (uint160) {
|
||||
UniswapV3Pool state = UniswapV3Pool(
|
||||
getPoolAddress(tokenA, tokenB, fee)
|
||||
);
|
||||
|
||||
return (
|
||||
SqrtPriceMath.getNextSqrtPriceFromInput(
|
||||
state.slot0().sqrtPriceX96,
|
||||
state.liquidity(),
|
||||
amountIn,
|
||||
zeroForOne
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getParams(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
address recipient,
|
||||
uint24 fee,
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMinimum
|
||||
) internal view returns (ISwapRouter.ExactInputSingleParams memory params) {
|
||||
params = ISwapRouter.ExactInputSingleParams({
|
||||
tokenIn: tokenIn,
|
||||
tokenOut: tokenOut,
|
||||
fee: fee,
|
||||
recipient: recipient,
|
||||
deadline: block.timestamp + 1,
|
||||
amountIn: amountIn,
|
||||
amountOutMinimum: amountOutMinimum,
|
||||
sqrtPriceLimitX96: getPriceLimit(
|
||||
amountIn,
|
||||
tokenIn < tokenOut,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
fee
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
function SwapTokens(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
bool zeroForOne
|
||||
) internal pure returns (address, address) {
|
||||
if (!zeroForOne) return (tokenOut, tokenIn);
|
||||
else return (tokenIn, tokenOut);
|
||||
}
|
||||
|
||||
function swapSingleInput(ISwapRouter.ExactInputSingleParams memory params)
|
||||
internal
|
||||
returns (uint256)
|
||||
{
|
||||
return (uint256(router.exactInputSingle(params)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
pragma solidity ^0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
interface UniswapV3Pool {
|
||||
struct Slot0 {
|
||||
uint160 sqrtPriceX96;
|
||||
int24 tick;
|
||||
uint16 observationIndex;
|
||||
uint16 observationCardinality;
|
||||
uint16 observationCardinalityNext;
|
||||
uint8 feeProtocol;
|
||||
bool unlocked;
|
||||
}
|
||||
|
||||
function liquidity() external view returns (uint128);
|
||||
|
||||
function slot0() external view returns (Slot0 memory);
|
||||
}
|
||||
|
||||
interface ISwapRouter {
|
||||
struct ExactInputSingleParams {
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint24 fee;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 amountOutMinimum;
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
function exactInputSingle(ExactInputSingleParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
/// @title FixedPoint96
|
||||
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
|
||||
/// @dev Used in SqrtPriceMath.sol
|
||||
library FixedPoint96 {
|
||||
uint8 internal constant RESOLUTION = 96;
|
||||
uint256 internal constant Q96 = 0x1000000000000000000000000;
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
/// @title Contains 512-bit math functions
|
||||
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
|
||||
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
|
||||
library FullMath {
|
||||
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
||||
/// @param a The multiplicand
|
||||
/// @param b The multiplier
|
||||
/// @param denominator The divisor
|
||||
/// @return result The 256-bit result
|
||||
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
|
||||
function mulDiv(
|
||||
uint256 a,
|
||||
uint256 b,
|
||||
uint256 denominator
|
||||
) internal pure returns (uint256 result) {
|
||||
// 512-bit multiply [prod1 prod0] = a * b
|
||||
// Compute the product mod 2**256 and mod 2**256 - 1
|
||||
// then use the Chinese Remainder Theorem to reconstruct
|
||||
// the 512 bit result. The result is stored in two 256
|
||||
// variables such that product = prod1 * 2**256 + prod0
|
||||
uint256 prod0; // Least significant 256 bits of the product
|
||||
uint256 prod1; // Most significant 256 bits of the product
|
||||
assembly {
|
||||
let mm := mulmod(a, b, not(0))
|
||||
prod0 := mul(a, b)
|
||||
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
|
||||
}
|
||||
|
||||
// Handle non-overflow cases, 256 by 256 division
|
||||
if (prod1 == 0) {
|
||||
require(denominator > 0);
|
||||
assembly {
|
||||
result := div(prod0, denominator)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Make sure the result is less than 2**256.
|
||||
// Also prevents denominator == 0
|
||||
require(denominator > prod1);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// 512 by 256 division.
|
||||
///////////////////////////////////////////////
|
||||
|
||||
// Make division exact by subtracting the remainder from [prod1 prod0]
|
||||
// Compute remainder using mulmod
|
||||
uint256 remainder;
|
||||
assembly {
|
||||
remainder := mulmod(a, b, denominator)
|
||||
}
|
||||
// Subtract 256 bit number from 512 bit number
|
||||
assembly {
|
||||
prod1 := sub(prod1, gt(remainder, prod0))
|
||||
prod0 := sub(prod0, remainder)
|
||||
}
|
||||
|
||||
// Factor powers of two out of denominator
|
||||
// Compute largest power of two divisor of denominator.
|
||||
// Always >= 1.
|
||||
uint256 twos = -denominator & denominator;
|
||||
// Divide denominator by power of two
|
||||
assembly {
|
||||
denominator := div(denominator, twos)
|
||||
}
|
||||
|
||||
// Divide [prod1 prod0] by the factors of two
|
||||
assembly {
|
||||
prod0 := div(prod0, twos)
|
||||
}
|
||||
// Shift in bits from prod1 into prod0. For this we need
|
||||
// to flip `twos` such that it is 2**256 / twos.
|
||||
// If twos is zero, then it becomes one
|
||||
assembly {
|
||||
twos := add(div(sub(0, twos), twos), 1)
|
||||
}
|
||||
prod0 |= prod1 * twos;
|
||||
|
||||
// Invert denominator mod 2**256
|
||||
// Now that denominator is an odd number, it has an inverse
|
||||
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
|
||||
// Compute the inverse by starting with a seed that is correct
|
||||
// correct for four bits. That is, denominator * inv = 1 mod 2**4
|
||||
uint256 inv = (3 * denominator) ^ 2;
|
||||
// Now use Newton-Raphson iteration to improve the precision.
|
||||
// Thanks to Hensel's lifting lemma, this also works in modular
|
||||
// arithmetic, doubling the correct bits in each step.
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**8
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**16
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**32
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**64
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**128
|
||||
inv *= 2 - denominator * inv; // inverse mod 2**256
|
||||
|
||||
// Because the division is now exact we can divide by multiplying
|
||||
// with the modular inverse of denominator. This will give us the
|
||||
// correct result modulo 2**256. Since the precoditions guarantee
|
||||
// that the outcome is less than 2**256, this is the final result.
|
||||
// We don't need to compute the high bits of the result and prod1
|
||||
// is no longer required.
|
||||
result = prod0 * inv;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
||||
/// @param a The multiplicand
|
||||
/// @param b The multiplier
|
||||
/// @param denominator The divisor
|
||||
/// @return result The 256-bit result
|
||||
function mulDivRoundingUp(
|
||||
uint256 a,
|
||||
uint256 b,
|
||||
uint256 denominator
|
||||
) internal pure returns (uint256 result) {
|
||||
result = mulDiv(a, b, denominator);
|
||||
if (mulmod(a, b, denominator) > 0) {
|
||||
require(result < type(uint256).max);
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.7.0;
|
||||
|
||||
/// @title Optimized overflow and underflow safe math operations
|
||||
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
|
||||
library LowGasSafeMath {
|
||||
/// @notice Returns x + y, reverts if sum overflows uint256
|
||||
/// @param x The augend
|
||||
/// @param y The addend
|
||||
/// @return z The sum of x and y
|
||||
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||
require((z = x + y) >= x);
|
||||
}
|
||||
|
||||
/// @notice Returns x - y, reverts if underflows
|
||||
/// @param x The minuend
|
||||
/// @param y The subtrahend
|
||||
/// @return z The difference of x and y
|
||||
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||
require((z = x - y) <= x);
|
||||
}
|
||||
|
||||
/// @notice Returns x * y, reverts if overflows
|
||||
/// @param x The multiplicand
|
||||
/// @param y The multiplier
|
||||
/// @return z The product of x and y
|
||||
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||
require(x == 0 || (z = x * y) / x == y);
|
||||
}
|
||||
|
||||
/// @notice Returns x + y, reverts if overflows or underflows
|
||||
/// @param x The augend
|
||||
/// @param y The addend
|
||||
/// @return z The sum of x and y
|
||||
function add(int256 x, int256 y) internal pure returns (int256 z) {
|
||||
require((z = x + y) >= x == (y >= 0));
|
||||
}
|
||||
|
||||
/// @notice Returns x - y, reverts if overflows or underflows
|
||||
/// @param x The minuend
|
||||
/// @param y The subtrahend
|
||||
/// @return z The difference of x and y
|
||||
function sub(int256 x, int256 y) internal pure returns (int256 z) {
|
||||
require((z = x - y) <= x == (y >= 0));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
/// @title Safe casting methods
|
||||
/// @notice Contains methods for safely casting between types
|
||||
library SafeCast {
|
||||
/// @notice Cast a uint256 to a uint160, revert on overflow
|
||||
/// @param y The uint256 to be downcasted
|
||||
/// @return z The downcasted integer, now type uint160
|
||||
function toUint160(uint256 y) internal pure returns (uint160 z) {
|
||||
require((z = uint160(y)) == y);
|
||||
}
|
||||
|
||||
/// @notice Cast a int256 to a int128, revert on overflow or underflow
|
||||
/// @param y The int256 to be downcasted
|
||||
/// @return z The downcasted integer, now type int128
|
||||
function toInt128(int256 y) internal pure returns (int128 z) {
|
||||
require((z = int128(y)) == y);
|
||||
}
|
||||
|
||||
/// @notice Cast a uint256 to a int256, revert on overflow
|
||||
/// @param y The uint256 to be casted
|
||||
/// @return z The casted integer, now type int256
|
||||
function toInt256(uint256 y) internal pure returns (int256 z) {
|
||||
require(y < 2**255);
|
||||
z = int256(y);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
import './LowGasSafeMath.sol';
|
||||
import './SafeCast.sol';
|
||||
|
||||
import './FullMath.sol';
|
||||
import './UnsafeMath.sol';
|
||||
import './FixedPoint96.sol';
|
||||
|
||||
/// @title Functions based on Q64.96 sqrt price and liquidity
|
||||
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
|
||||
library SqrtPriceMath {
|
||||
using LowGasSafeMath for uint256;
|
||||
using SafeCast for uint256;
|
||||
|
||||
/// @notice Gets the next sqrt price given a delta of token0
|
||||
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
|
||||
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
|
||||
/// price less in order to not send too much output.
|
||||
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
|
||||
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
|
||||
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param amount How much of token0 to add or remove from virtual reserves
|
||||
/// @param add Whether to add or remove the amount of token0
|
||||
/// @return The price after adding or removing amount, depending on add
|
||||
function getNextSqrtPriceFromAmount0RoundingUp(
|
||||
uint160 sqrtPX96,
|
||||
uint128 liquidity,
|
||||
uint256 amount,
|
||||
bool add
|
||||
) internal pure returns (uint160) {
|
||||
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
|
||||
if (amount == 0) return sqrtPX96;
|
||||
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
|
||||
|
||||
if (add) {
|
||||
uint256 product;
|
||||
if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
|
||||
uint256 denominator = numerator1 + product;
|
||||
if (denominator >= numerator1)
|
||||
// always fits in 160 bits
|
||||
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
|
||||
}
|
||||
|
||||
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
|
||||
} else {
|
||||
uint256 product;
|
||||
// if the product overflows, we know the denominator underflows
|
||||
// in addition, we must check that the denominator does not underflow
|
||||
require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
|
||||
uint256 denominator = numerator1 - product;
|
||||
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Gets the next sqrt price given a delta of token1
|
||||
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
|
||||
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
|
||||
/// price less in order to not send too much output.
|
||||
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
|
||||
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param amount How much of token1 to add, or remove, from virtual reserves
|
||||
/// @param add Whether to add, or remove, the amount of token1
|
||||
/// @return The price after adding or removing `amount`
|
||||
function getNextSqrtPriceFromAmount1RoundingDown(
|
||||
uint160 sqrtPX96,
|
||||
uint128 liquidity,
|
||||
uint256 amount,
|
||||
bool add
|
||||
) internal pure returns (uint160) {
|
||||
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
|
||||
// in both cases, avoid a mulDiv for most inputs
|
||||
if (add) {
|
||||
uint256 quotient =
|
||||
(
|
||||
amount <= type(uint160).max
|
||||
? (amount << FixedPoint96.RESOLUTION) / liquidity
|
||||
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
|
||||
);
|
||||
|
||||
return uint256(sqrtPX96).add(quotient).toUint160();
|
||||
} else {
|
||||
uint256 quotient =
|
||||
(
|
||||
amount <= type(uint160).max
|
||||
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
|
||||
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
|
||||
);
|
||||
|
||||
require(sqrtPX96 > quotient);
|
||||
// always fits 160 bits
|
||||
return uint160(sqrtPX96 - quotient);
|
||||
}
|
||||
}
|
||||
|
||||
/// @notice Gets the next sqrt price given an input amount of token0 or token1
|
||||
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
|
||||
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param amountIn How much of token0, or token1, is being swapped in
|
||||
/// @param zeroForOne Whether the amount in is token0 or token1
|
||||
/// @return sqrtQX96 The price after adding the input amount to token0 or token1
|
||||
function getNextSqrtPriceFromInput(
|
||||
uint160 sqrtPX96,
|
||||
uint128 liquidity,
|
||||
uint256 amountIn,
|
||||
bool zeroForOne
|
||||
) internal pure returns (uint160 sqrtQX96) {
|
||||
require(sqrtPX96 > 0);
|
||||
require(liquidity > 0);
|
||||
|
||||
// round to make sure that we don't pass the target price
|
||||
return
|
||||
zeroForOne
|
||||
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
|
||||
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
|
||||
}
|
||||
|
||||
/// @notice Gets the next sqrt price given an output amount of token0 or token1
|
||||
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
|
||||
/// @param sqrtPX96 The starting price before accounting for the output amount
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param amountOut How much of token0, or token1, is being swapped out
|
||||
/// @param zeroForOne Whether the amount out is token0 or token1
|
||||
/// @return sqrtQX96 The price after removing the output amount of token0 or token1
|
||||
function getNextSqrtPriceFromOutput(
|
||||
uint160 sqrtPX96,
|
||||
uint128 liquidity,
|
||||
uint256 amountOut,
|
||||
bool zeroForOne
|
||||
) internal pure returns (uint160 sqrtQX96) {
|
||||
require(sqrtPX96 > 0);
|
||||
require(liquidity > 0);
|
||||
|
||||
// round to make sure that we pass the target price
|
||||
return
|
||||
zeroForOne
|
||||
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
|
||||
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
|
||||
}
|
||||
|
||||
/// @notice Gets the amount0 delta between two prices
|
||||
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
|
||||
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
|
||||
/// @param sqrtRatioAX96 A sqrt price
|
||||
/// @param sqrtRatioBX96 Another sqrt price
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param roundUp Whether to round the amount up or down
|
||||
/// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
|
||||
function getAmount0Delta(
|
||||
uint160 sqrtRatioAX96,
|
||||
uint160 sqrtRatioBX96,
|
||||
uint128 liquidity,
|
||||
bool roundUp
|
||||
) internal pure returns (uint256 amount0) {
|
||||
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
|
||||
|
||||
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
|
||||
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
|
||||
|
||||
require(sqrtRatioAX96 > 0);
|
||||
|
||||
return
|
||||
roundUp
|
||||
? UnsafeMath.divRoundingUp(
|
||||
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
|
||||
sqrtRatioAX96
|
||||
)
|
||||
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
|
||||
}
|
||||
|
||||
/// @notice Gets the amount1 delta between two prices
|
||||
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
|
||||
/// @param sqrtRatioAX96 A sqrt price
|
||||
/// @param sqrtRatioBX96 Another sqrt price
|
||||
/// @param liquidity The amount of usable liquidity
|
||||
/// @param roundUp Whether to round the amount up, or down
|
||||
/// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
|
||||
function getAmount1Delta(
|
||||
uint160 sqrtRatioAX96,
|
||||
uint160 sqrtRatioBX96,
|
||||
uint128 liquidity,
|
||||
bool roundUp
|
||||
) internal pure returns (uint256 amount1) {
|
||||
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
|
||||
|
||||
return
|
||||
roundUp
|
||||
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
|
||||
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
|
||||
}
|
||||
|
||||
/// @notice Helper that gets signed token0 delta
|
||||
/// @param sqrtRatioAX96 A sqrt price
|
||||
/// @param sqrtRatioBX96 Another sqrt price
|
||||
/// @param liquidity The change in liquidity for which to compute the amount0 delta
|
||||
/// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
|
||||
function getAmount0Delta(
|
||||
uint160 sqrtRatioAX96,
|
||||
uint160 sqrtRatioBX96,
|
||||
int128 liquidity
|
||||
) internal pure returns (int256 amount0) {
|
||||
return
|
||||
liquidity < 0
|
||||
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
|
||||
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
|
||||
}
|
||||
|
||||
/// @notice Helper that gets signed token1 delta
|
||||
/// @param sqrtRatioAX96 A sqrt price
|
||||
/// @param sqrtRatioBX96 Another sqrt price
|
||||
/// @param liquidity The change in liquidity for which to compute the amount1 delta
|
||||
/// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
|
||||
function getAmount1Delta(
|
||||
uint160 sqrtRatioAX96,
|
||||
uint160 sqrtRatioBX96,
|
||||
int128 liquidity
|
||||
) internal pure returns (int256 amount1) {
|
||||
return
|
||||
liquidity < 0
|
||||
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
|
||||
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
/// @title Math functions that do not check inputs or outputs
|
||||
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
|
||||
library UnsafeMath {
|
||||
/// @notice Returns ceil(x / y)
|
||||
/// @dev division by 0 has unspecified behavior, and must be checked externally
|
||||
/// @param x The dividend
|
||||
/// @param y The divisor
|
||||
/// @return z The quotient, ceil(x / y)
|
||||
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
|
||||
assembly {
|
||||
z := add(div(x, y), gt(mod(x, y), 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
41
contracts/arbitrum/connectors/uniswap-sell-beta/main.sol
Normal file
41
contracts/arbitrum/connectors/uniswap-sell-beta/main.sol
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
pragma solidity ^0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
import "./helpers.sol";
|
||||
import {Events} from "./events.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
|
||||
contract uniswapSellBeta is Helpers, Events {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
function sell(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint24 fee,
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMinimum
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (string memory _eventName, bytes memory _eventParam)
|
||||
{
|
||||
IERC20(tokenIn).safeApprove(address(router), amountIn);
|
||||
uint256 amountOut = swapSingleInput(
|
||||
getParams(
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
address(this),
|
||||
fee,
|
||||
amountIn,
|
||||
amountOutMinimum
|
||||
)
|
||||
);
|
||||
_eventName = "LogSell(uint24,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(fee, amountIn, amountOut, amountOutMinimum);
|
||||
}
|
||||
}
|
||||
|
||||
contract ConnectV2UniswapSellBeta is uniswapSellBeta {
|
||||
string public constant name = "Uniswap-Sell-Beta";
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
contract Events {
|
||||
event LogClaimedReward(uint256 rewardAmt, uint256 setId);
|
||||
event LogDelegate(address delegatee);
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { DSMath } from "../../../common/math.sol";
|
||||
import { Basic } from "../../../common/basic.sol";
|
||||
import { ComptrollerInterface, QiInterface, BenqiMappingInterface } from "./interface.sol";
|
||||
|
||||
abstract contract Helpers is DSMath, Basic {
|
||||
/**
|
||||
* @dev Benqi Comptroller
|
||||
*/
|
||||
ComptrollerInterface internal constant troller = ComptrollerInterface(0x486Af39519B4Dc9a7fCcd318217352830E8AD9b4);
|
||||
|
||||
/**
|
||||
* @dev Qi Reward Token
|
||||
*/
|
||||
QiInterface internal constant benqiToken = QiInterface(0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5);
|
||||
|
||||
/**
|
||||
* @dev Benqi Mapping
|
||||
*/
|
||||
BenqiMappingInterface internal constant qiMapping = BenqiMappingInterface(0xe19Fba29ac9BAACc1F584aEcD9C98B4F6fC58ba6);
|
||||
|
||||
/**
|
||||
* @dev Benqi reward token type to show BENQI or AVAX
|
||||
*/
|
||||
uint8 internal constant rewardQi = 0;
|
||||
uint8 internal constant rewardAvax = 1;
|
||||
|
||||
function getMergedQiTokens(
|
||||
string[] calldata supplyIds,
|
||||
string[] calldata borrowIds
|
||||
) internal view returns (address[] memory qitokens, bool isBorrow, bool isSupply) {
|
||||
uint _supplyLen = supplyIds.length;
|
||||
uint _borrowLen = borrowIds.length;
|
||||
uint _totalLen = add(_supplyLen, _borrowLen);
|
||||
qitokens = new address[](_totalLen);
|
||||
|
||||
if(_supplyLen > 0) {
|
||||
isSupply = true;
|
||||
for (uint i = 0; i < _supplyLen; i++) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(supplyIds[i]);
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
qitokens[i] = qiToken;
|
||||
}
|
||||
}
|
||||
|
||||
if(_borrowLen > 0) {
|
||||
isBorrow = true;
|
||||
for (uint i = 0; i < _borrowLen; i++) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(borrowIds[i]);
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
qitokens[_supplyLen + i] = qiToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
interface ComptrollerInterface {
|
||||
function claimReward(uint8 rewardType, address holder) external;
|
||||
function claimReward(uint8 rewardType, address holder, address[] calldata) external;
|
||||
function claimReward(uint8 rewardType, address[] calldata holders, address[] calldata qiTokens, bool borrowers, bool suppliers) external;
|
||||
}
|
||||
|
||||
interface QiInterface {
|
||||
function delegate(address delegatee) external;
|
||||
function delegates(address) external view returns(address);
|
||||
}
|
||||
|
||||
interface BenqiMappingInterface {
|
||||
function qiTokenMapping(string calldata tokenId) external view returns (address);
|
||||
function getMapping(string calldata tokenId) external view returns (address, address);
|
||||
}
|
||||
171
contracts/avalanche/connectors/benqi/benqi-rewards/main.sol
Normal file
171
contracts/avalanche/connectors/benqi/benqi-rewards/main.sol
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Reward.
|
||||
* @dev Claim Reward.
|
||||
*/
|
||||
import { TokenInterface } from "../../../common/interfaces.sol";
|
||||
import { Stores } from "../../../common/stores.sol";
|
||||
import { Helpers } from "./helpers.sol";
|
||||
import { Events } from "./events.sol";
|
||||
|
||||
abstract contract IncentivesResolver is Events, Helpers {
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Qi Token.
|
||||
* @notice Claim Accrued Qi Token.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimQiReward(uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
TokenInterface _benqiToken = TokenInterface(address(benqiToken));
|
||||
uint intialBal = _benqiToken.balanceOf(address(this));
|
||||
troller.claimReward(rewardQi, address(this));
|
||||
uint finalBal = _benqiToken.balanceOf(address(this));
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Qi Token.
|
||||
* @notice Claim Accrued Qi Token.
|
||||
* @param tokenIds Array of supplied and borrowed token IDs.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimQiRewardTwo(string[] calldata tokenIds, uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _len = tokenIds.length;
|
||||
address[] memory qitokens = new address[](_len);
|
||||
for (uint i = 0; i < _len; i++) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(tokenIds[i]);
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
qitokens[i] = qiToken;
|
||||
}
|
||||
|
||||
TokenInterface _benqiToken = TokenInterface(address(benqiToken));
|
||||
uint intialBal = _benqiToken.balanceOf(address(this));
|
||||
troller.claimReward(rewardQi, address(this), qitokens);
|
||||
uint finalBal = _benqiToken.balanceOf(address(this));
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Qi Token.
|
||||
* @notice Claim Accrued Qi Token.
|
||||
* @param supplyTokenIds Array of supplied tokenIds.
|
||||
* @param borrowTokenIds Array of borrowed tokenIds.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimQiRewardThree(string[] calldata supplyTokenIds, string[] calldata borrowTokenIds, uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
(address[] memory qitokens, bool isBorrow, bool isSupply) = getMergedQiTokens(supplyTokenIds, borrowTokenIds);
|
||||
|
||||
address[] memory holders = new address[](1);
|
||||
holders[0] = address(this);
|
||||
|
||||
TokenInterface _benqiToken = TokenInterface(address(benqiToken));
|
||||
uint intialBal = _benqiToken.balanceOf(address(this));
|
||||
troller.claimReward(rewardQi, holders, qitokens, isBorrow, isSupply);
|
||||
uint finalBal = _benqiToken.balanceOf(address(this));
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Avax Token.
|
||||
* @notice Claim Accrued Avax Token.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimAvaxReward(uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint intialBal = address(this).balance;
|
||||
troller.claimReward(rewardAvax, address(this));
|
||||
uint finalBal = address(this).balance;
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Avax Token.
|
||||
* @notice Claim Accrued Avax Token.
|
||||
* @param tokenIds Array of supplied and borrowed token IDs.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimAvaxRewardTwo(string[] calldata tokenIds, uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _len = tokenIds.length;
|
||||
address[] memory qitokens = new address[](_len);
|
||||
for (uint i = 0; i < _len; i++) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(tokenIds[i]);
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
qitokens[i] = qiToken;
|
||||
}
|
||||
|
||||
uint intialBal = address(this).balance;
|
||||
troller.claimReward(rewardAvax, address(this), qitokens);
|
||||
uint finalBal = address(this).balance;
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Claim Accrued Avax Token.
|
||||
* @notice Claim Accrued Avax Token.
|
||||
* @param supplyTokenIds Array of supplied tokenIds.
|
||||
* @param borrowTokenIds Array of borrowed tokenIds.
|
||||
* @param setId ID stores the amount of Reward claimed.
|
||||
*/
|
||||
function ClaimAvaxRewardThree(string[] calldata supplyTokenIds, string[] calldata borrowTokenIds, uint256 setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
(address[] memory qitokens, bool isBorrow, bool isSupply) = getMergedQiTokens(supplyTokenIds, borrowTokenIds);
|
||||
|
||||
address[] memory holders = new address[](1);
|
||||
holders[0] = address(this);
|
||||
|
||||
uint intialBal = address(this).balance;
|
||||
troller.claimReward(rewardAvax, holders, qitokens, isBorrow, isSupply);
|
||||
uint finalBal = address(this).balance;
|
||||
uint amt = sub(finalBal, intialBal);
|
||||
|
||||
setUint(setId, amt);
|
||||
|
||||
_eventName = "LogClaimedReward(uint256,uint256)";
|
||||
_eventParam = abi.encode(amt, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Delegate votes.
|
||||
* @notice Delegate votes.
|
||||
* @param delegatee The address to delegate the votes to.
|
||||
*/
|
||||
function delegate(address delegatee) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
require(benqiToken.delegates(address(this)) != delegatee, "Already delegated to same delegatee.");
|
||||
|
||||
benqiToken.delegate(delegatee);
|
||||
|
||||
_eventName = "LogDelegate(address)";
|
||||
_eventParam = abi.encode(delegatee);
|
||||
}
|
||||
}
|
||||
|
||||
contract ConnectV2BenqiIncentivesAvalanche is IncentivesResolver {
|
||||
string public constant name = "Benqi-Incentives-v1";
|
||||
}
|
||||
62
contracts/avalanche/connectors/benqi/v2/events.sol
Normal file
62
contracts/avalanche/connectors/benqi/v2/events.sol
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
contract Events {
|
||||
event LogDeposit(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogWithdraw(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogBorrow(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogPayback(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogDepositQiToken(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 qiTokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogWithdrawQiToken(
|
||||
address indexed token,
|
||||
address qiToken,
|
||||
uint256 tokenAmt,
|
||||
uint256 qiTokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
|
||||
event LogLiquidate(
|
||||
address indexed borrower,
|
||||
address indexed tokenToPay,
|
||||
address indexed tokenInReturn,
|
||||
uint256 tokenAmt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
);
|
||||
}
|
||||
35
contracts/avalanche/connectors/benqi/v2/helpers.sol
Normal file
35
contracts/avalanche/connectors/benqi/v2/helpers.sol
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
import { DSMath } from "../../../common/math.sol";
|
||||
import { Basic } from "../../../common/basic.sol";
|
||||
import { ComptrollerInterface, BenqiMappingInterface } from "./interface.sol";
|
||||
|
||||
abstract contract Helpers is DSMath, Basic {
|
||||
/**
|
||||
* @dev Benqi Comptroller
|
||||
*/
|
||||
ComptrollerInterface internal constant troller = ComptrollerInterface(0x486Af39519B4Dc9a7fCcd318217352830E8AD9b4);
|
||||
|
||||
/**
|
||||
* @dev Benqi Mapping
|
||||
*/
|
||||
BenqiMappingInterface internal constant qiMapping = BenqiMappingInterface(0xe19Fba29ac9BAACc1F584aEcD9C98B4F6fC58ba6);
|
||||
|
||||
/**
|
||||
* @dev enter benqi market
|
||||
*/
|
||||
function enterMarket(address qiToken) internal {
|
||||
address[] memory markets = troller.getAssetsIn(address(this));
|
||||
bool isEntered = false;
|
||||
for (uint i = 0; i < markets.length; i++) {
|
||||
if (markets[i] == qiToken) {
|
||||
isEntered = true;
|
||||
}
|
||||
}
|
||||
if (!isEntered) {
|
||||
address[] memory toEnter = new address[](1);
|
||||
toEnter[0] = qiToken;
|
||||
troller.enterMarkets(toEnter);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
contracts/avalanche/connectors/benqi/v2/interface.sol
Normal file
36
contracts/avalanche/connectors/benqi/v2/interface.sol
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
interface QiTokenInterface {
|
||||
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 ARC20
|
||||
function liquidateBorrow(address borrower, uint repayAmount, address qiTokenCollateral) 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 QiAVAXInterface {
|
||||
function mint() external payable;
|
||||
function repayBorrow() external payable;
|
||||
function repayBorrowBehalf(address borrower) external payable;
|
||||
function liquidateBorrow(address borrower, address qiTokenCollateral) external payable;
|
||||
}
|
||||
|
||||
interface ComptrollerInterface {
|
||||
function enterMarkets(address[] calldata qiTokens) external returns (uint[] memory);
|
||||
function exitMarket(address qiTokenAddress) external returns (uint);
|
||||
function getAssetsIn(address account) external view returns (address[] memory);
|
||||
function getAccountLiquidity(address account) external view returns (uint, uint, uint);
|
||||
function claimReward(uint8 rewardType, address) external;
|
||||
}
|
||||
|
||||
interface BenqiMappingInterface {
|
||||
function qiTokenMapping(string calldata tokenId) external view returns (address);
|
||||
function getMapping(string calldata tokenId) external view returns (address, address);
|
||||
}
|
||||
441
contracts/avalanche/connectors/benqi/v2/main.sol
Normal file
441
contracts/avalanche/connectors/benqi/v2/main.sol
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Benqi.
|
||||
* @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 { QiAVAXInterface, QiTokenInterface } from "./interface.sol";
|
||||
|
||||
abstract contract BenqiResolver is Events, Helpers {
|
||||
/**
|
||||
* @dev Deposit AVAX/ARC20_Token.
|
||||
* @notice Deposit a token to Benqi for lending / collaterization.
|
||||
* @param token The address of the token to deposit. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @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 qiToken,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
enterMarket(qiToken);
|
||||
if (token == avaxAddr) {
|
||||
_amt = _amt == uint(-1) ? address(this).balance : _amt;
|
||||
QiAVAXInterface(qiToken).mint{value: _amt}();
|
||||
} else {
|
||||
TokenInterface tokenContract = TokenInterface(token);
|
||||
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
|
||||
approve(tokenContract, qiToken, _amt);
|
||||
require(QiTokenInterface(qiToken).mint(_amt) == 0, "deposit-failed");
|
||||
}
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogDeposit(address,address,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, _amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deposit AVAX/ARC20_Token using the Mapping.
|
||||
* @notice Deposit a token to Benqi for lending / collaterization.
|
||||
* @param tokenId The token id of the token to deposit.(For eg: AVAX-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 qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = depositRaw(token, qiToken, amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw AVAX/ARC20_Token.
|
||||
* @notice Withdraw deposited token from Benqi
|
||||
* @param token The address of the token to withdraw. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @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 qiToken,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
QiTokenInterface qiTokenContract = QiTokenInterface(qiToken);
|
||||
if (_amt == uint(-1)) {
|
||||
TokenInterface tokenContract = TokenInterface(token);
|
||||
uint initialBal = token == avaxAddr ? address(this).balance : tokenContract.balanceOf(address(this));
|
||||
require(qiTokenContract.redeem(qiTokenContract.balanceOf(address(this))) == 0, "full-withdraw-failed");
|
||||
uint finalBal = token == avaxAddr ? address(this).balance : tokenContract.balanceOf(address(this));
|
||||
_amt = finalBal - initialBal;
|
||||
} else {
|
||||
require(qiTokenContract.redeemUnderlying(_amt) == 0, "withdraw-failed");
|
||||
}
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogWithdraw(address,address,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, _amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw AVAX/ARC20_Token using the Mapping.
|
||||
* @notice Withdraw deposited token from Benqi
|
||||
* @param tokenId The token id of the token to withdraw.(For eg: AVAX-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 qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = withdrawRaw(token, qiToken, amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow AVAX/ARC20_Token.
|
||||
* @notice Borrow a token using Benqi
|
||||
* @param token The address of the token to borrow. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @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 qiToken,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
enterMarket(qiToken);
|
||||
require(QiTokenInterface(qiToken).borrow(_amt) == 0, "borrow-failed");
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogBorrow(address,address,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, _amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow AVAX/ARC20_Token using the Mapping.
|
||||
* @notice Borrow a token using Benqi
|
||||
* @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 qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = borrowRaw(token, qiToken, amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Payback borrowed AVAX/ARC20_Token.
|
||||
* @notice Payback debt owed.
|
||||
* @param token The address of the token to payback. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @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 qiToken,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
QiTokenInterface qiTokenContract = QiTokenInterface(qiToken);
|
||||
_amt = _amt == uint(-1) ? qiTokenContract.borrowBalanceCurrent(address(this)) : _amt;
|
||||
|
||||
if (token == avaxAddr) {
|
||||
require(address(this).balance >= _amt, "not-enough-avax");
|
||||
QiAVAXInterface(qiToken).repayBorrow{value: _amt}();
|
||||
} else {
|
||||
TokenInterface tokenContract = TokenInterface(token);
|
||||
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
|
||||
approve(tokenContract, qiToken, _amt);
|
||||
require(qiTokenContract.repayBorrow(_amt) == 0, "repay-failed.");
|
||||
}
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogPayback(address,address,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, _amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Payback borrowed AVAX/ARC20_Token using the Mapping.
|
||||
* @notice Payback debt owed.
|
||||
* @param tokenId The token id of the token to payback.(For eg: BENQI-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 qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = paybackRaw(token, qiToken, amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deposit AVAX/ARC20_Token.
|
||||
* @notice Same as depositRaw. The only difference is this method stores qiToken amount in set ID.
|
||||
* @param token The address of the token to deposit. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @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 qiTokens received.
|
||||
*/
|
||||
function depositQiTokenRaw(
|
||||
address token,
|
||||
address qiToken,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
enterMarket(qiToken);
|
||||
|
||||
QiTokenInterface qitokenContract = QiTokenInterface(qiToken);
|
||||
uint initialBal = qitokenContract.balanceOf(address(this));
|
||||
|
||||
if (token == avaxAddr) {
|
||||
_amt = _amt == uint(-1) ? address(this).balance : _amt;
|
||||
QiAVAXInterface(qiToken).mint{value: _amt}();
|
||||
} else {
|
||||
TokenInterface tokenContract = TokenInterface(token);
|
||||
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
|
||||
approve(tokenContract, qiToken, _amt);
|
||||
require(qitokenContract.mint(_amt) == 0, "deposit-qitoken-failed.");
|
||||
}
|
||||
|
||||
uint _cAmt;
|
||||
|
||||
{
|
||||
uint finalBal = qitokenContract.balanceOf(address(this));
|
||||
_cAmt = sub(finalBal, initialBal);
|
||||
|
||||
setUint(setId, _cAmt);
|
||||
}
|
||||
|
||||
_eventName = "LogDepositQiToken(address,address,uint256,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, _amt, _cAmt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deposit AVAX/ARC20_Token using the Mapping.
|
||||
* @notice Same as deposit. The only difference is this method stores qiToken amount in set ID.
|
||||
* @param tokenId The token id of the token to depositQiToken.(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 qiTokens received.
|
||||
*/
|
||||
function depositQiToken(
|
||||
string calldata tokenId,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = depositQiTokenRaw(token, qiToken, amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw QiAVAX/QiARC20_Token using qiToken Amt.
|
||||
* @notice Same as withdrawRaw. The only difference is this method fetch qiToken amount in get ID.
|
||||
* @param token The address of the token to withdraw. (For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiToken The address of the corresponding qiToken.
|
||||
* @param qiTokenAmt The amount of qiTokens to withdraw
|
||||
* @param getId ID to retrieve qiTokenAmt
|
||||
* @param setId ID stores the amount of tokens withdrawn.
|
||||
*/
|
||||
function withdrawQiTokenRaw(
|
||||
address token,
|
||||
address qiToken,
|
||||
uint qiTokenAmt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _cAmt = getUint(getId, qiTokenAmt);
|
||||
require(token != address(0) && qiToken != address(0), "invalid token/qitoken address");
|
||||
|
||||
QiTokenInterface qiTokenContract = QiTokenInterface(qiToken);
|
||||
TokenInterface tokenContract = TokenInterface(token);
|
||||
_cAmt = _cAmt == uint(-1) ? qiTokenContract.balanceOf(address(this)) : _cAmt;
|
||||
|
||||
uint withdrawAmt;
|
||||
{
|
||||
uint initialBal = token != avaxAddr ? tokenContract.balanceOf(address(this)) : address(this).balance;
|
||||
require(qiTokenContract.redeem(_cAmt) == 0, "redeem-failed");
|
||||
uint finalBal = token != avaxAddr ? tokenContract.balanceOf(address(this)) : address(this).balance;
|
||||
|
||||
withdrawAmt = sub(finalBal, initialBal);
|
||||
}
|
||||
|
||||
setUint(setId, withdrawAmt);
|
||||
|
||||
_eventName = "LogWithdrawQiToken(address,address,uint256,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(token, qiToken, withdrawAmt, _cAmt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw QiAVAX/QiARC20_Token using qiToken Amt & the Mapping.
|
||||
* @notice Same as withdraw. The only difference is this method fetch qiToken amount in get ID.
|
||||
* @param tokenId The token id of the token to withdraw QiToken.(For eg: AVAX-A)
|
||||
* @param qiTokenAmt The amount of qiTokens to withdraw
|
||||
* @param getId ID to retrieve qiTokenAmt
|
||||
* @param setId ID stores the amount of tokens withdrawn.
|
||||
*/
|
||||
function withdrawQiToken(
|
||||
string calldata tokenId,
|
||||
uint qiTokenAmt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
(address token, address qiToken) = qiMapping.getMapping(tokenId);
|
||||
(_eventName, _eventParam) = withdrawQiTokenRaw(token, qiToken, qiTokenAmt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Liquidate a position.
|
||||
* @notice Liquidate a position.
|
||||
* @param borrower Borrower's Address.
|
||||
* @param tokenToPay The address of the token to pay for liquidation.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||
* @param qiTokenPay Corresponding qiToken address.
|
||||
* @param tokenInReturn The address of the token to return for liquidation.
|
||||
* @param qiTokenColl Corresponding qiToken address.
|
||||
* @param amt The token amount to pay for liquidation.
|
||||
* @param getId ID to retrieve amt.
|
||||
* @param setId ID stores the amount of paid for liquidation.
|
||||
*/
|
||||
function liquidateRaw(
|
||||
address borrower,
|
||||
address tokenToPay,
|
||||
address qiTokenPay,
|
||||
address tokenInReturn,
|
||||
address qiTokenColl,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
require(tokenToPay != address(0) && qiTokenPay != address(0), "invalid token/qitoken address");
|
||||
require(tokenInReturn != address(0) && qiTokenColl != address(0), "invalid token/qitoken address");
|
||||
|
||||
QiTokenInterface qiTokenContract = QiTokenInterface(qiTokenPay);
|
||||
|
||||
{
|
||||
(,, uint shortfal) = troller.getAccountLiquidity(borrower);
|
||||
require(shortfal != 0, "account-cannot-be-liquidated");
|
||||
_amt = _amt == uint(-1) ? qiTokenContract.borrowBalanceCurrent(borrower) : _amt;
|
||||
}
|
||||
|
||||
if (tokenToPay == avaxAddr) {
|
||||
require(address(this).balance >= _amt, "not-enough-avax");
|
||||
QiAVAXInterface(qiTokenPay).liquidateBorrow{value: _amt}(borrower, qiTokenColl);
|
||||
} else {
|
||||
TokenInterface tokenContract = TokenInterface(tokenToPay);
|
||||
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
|
||||
approve(tokenContract, qiTokenPay, _amt);
|
||||
require(qiTokenContract.liquidateBorrow(borrower, _amt, qiTokenColl) == 0, "liquidate-failed");
|
||||
}
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogLiquidate(address,address,address,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(
|
||||
address(this),
|
||||
tokenToPay,
|
||||
tokenInReturn,
|
||||
_amt,
|
||||
getId,
|
||||
setId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Liquidate a position using the mapping.
|
||||
* @notice Liquidate a position using the mapping.
|
||||
* @param borrower Borrower's Address.
|
||||
* @param tokenIdToPay token id of the token to pay for liquidation.(For eg: AVAX-A)
|
||||
* @param tokenIdInReturn token id of the token to return for liquidation.(For eg: USDC-A)
|
||||
* @param amt token amount to pay for liquidation.
|
||||
* @param getId ID to retrieve amt.
|
||||
* @param setId ID stores the amount of paid for liquidation.
|
||||
*/
|
||||
function liquidate(
|
||||
address borrower,
|
||||
string calldata tokenIdToPay,
|
||||
string calldata tokenIdInReturn,
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
(address tokenToPay, address qiTokenToPay) = qiMapping.getMapping(tokenIdToPay);
|
||||
(address tokenInReturn, address qiTokenColl) = qiMapping.getMapping(tokenIdInReturn);
|
||||
|
||||
(_eventName, _eventParam) = liquidateRaw(
|
||||
borrower,
|
||||
tokenToPay,
|
||||
qiTokenToPay,
|
||||
tokenInReturn,
|
||||
qiTokenColl,
|
||||
amt,
|
||||
getId,
|
||||
setId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract ConnectV2BenqiAvalanche is BenqiResolver {
|
||||
string public constant name = "Benqi-v1";
|
||||
}
|
||||
10
contracts/avalanche/connectors/instapool_v4/events.sol
Normal file
10
contracts/avalanche/connectors/instapool_v4/events.sol
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Events {
|
||||
event LogFlashBorrow(address token, uint256 tokenAmt);
|
||||
event LogFlashPayback(address token, uint256 tokenAmt);
|
||||
|
||||
event LogFlashMultiBorrow(address[] token, uint256[] tokenAmts);
|
||||
event LogFlashMultiPayback(address[] token, uint256[] tokenAmts);
|
||||
}
|
||||
11
contracts/avalanche/connectors/instapool_v4/interfaces.sol
Normal file
11
contracts/avalanche/connectors/instapool_v4/interfaces.sol
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface InstaFlashV4Interface {
|
||||
function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;
|
||||
}
|
||||
|
||||
interface AccountInterface {
|
||||
function enable(address) external;
|
||||
function disable(address) external;
|
||||
}
|
||||
138
contracts/avalanche/connectors/instapool_v4/main.sol
Normal file
138
contracts/avalanche/connectors/instapool_v4/main.sol
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Instapool.
|
||||
* @dev Inbuilt Flash Loan in DSA
|
||||
*/
|
||||
|
||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { TokenInterface } from "../../common/interfaces.sol";
|
||||
import { AccountInterface } from "./interfaces.sol";
|
||||
import { Stores } from "../../common/stores.sol";
|
||||
import { Variables } from "./variables.sol";
|
||||
import { Events } from "./events.sol";
|
||||
|
||||
contract LiquidityResolver is Stores, Variables, Events {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
/**
|
||||
* @dev Borrow Flashloan and Cast spells.
|
||||
* @notice Borrow Flashloan and Cast spells.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashBorrowAndCast(
|
||||
address token,
|
||||
uint amt,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
address[] memory tokens_ = new address[](1);
|
||||
tokens_[0] = token;
|
||||
uint[] memory amts_ = new uint[](1);
|
||||
amts_[0] = amt;
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
|
||||
_eventName = "LogFlashBorrow(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return token to InstaPool.
|
||||
* @notice Return token to InstaPool.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param getId Get token amount at this ID from `InstaMemory` Contract.
|
||||
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
||||
*/
|
||||
function flashPayback(
|
||||
address token,
|
||||
uint amt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
IERC20 tokenContract = IERC20(token);
|
||||
|
||||
tokenContract.safeTransfer(address(instaPool), _amt);
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogFlashPayback(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @notice Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashMultiBorrowAndCast(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
_eventName = "LogFlashMultiBorrow(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return multi-tokens to InstaPool.
|
||||
* @notice Return multi-tokens to InstaPool.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param getIds Array of getId token amounts.
|
||||
* @param setIds Array of setId token amounts.
|
||||
*/
|
||||
function flashMultiPayback(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint[] memory getIds,
|
||||
uint[] memory setIds
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
for (uint i = 0; i < tokens_.length; i++) {
|
||||
amts_[i] = getUint(getIds[i], amts_[i]);
|
||||
|
||||
IERC20(tokens_[i]).safeTransfer(address(instaPool), amts_[i]);
|
||||
|
||||
setUint(setIds[i], amts_[i]);
|
||||
}
|
||||
|
||||
_eventName = "LogFlashMultiPayback(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract ConnectV2InstaPoolV4 is LiquidityResolver {
|
||||
string public name = "Instapool-v4";
|
||||
}
|
||||
13
contracts/avalanche/connectors/instapool_v4/variables.sol
Normal file
13
contracts/avalanche/connectors/instapool_v4/variables.sol
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { InstaFlashV4Interface } from "./interfaces.sol";
|
||||
|
||||
contract Variables {
|
||||
|
||||
/**
|
||||
* @dev Instapool contract proxy
|
||||
*/
|
||||
InstaFlashV4Interface public constant instaPool = InstaFlashV4Interface(address(0)); // TODO: update address
|
||||
|
||||
}
|
||||
148
contracts/avalanche/mapping/benqi.sol
Normal file
148
contracts/avalanche/mapping/benqi.sol
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IndexInterface {
|
||||
function master() external view returns (address);
|
||||
}
|
||||
|
||||
interface ConnectorsInterface {
|
||||
function chief(address) external view returns (bool);
|
||||
}
|
||||
|
||||
interface QiTokenInterface {
|
||||
function isQiToken() external view returns (bool);
|
||||
function underlying() external view returns (address);
|
||||
}
|
||||
|
||||
interface MappingControllerInterface {
|
||||
function hasRole(address,address) external view returns (bool);
|
||||
}
|
||||
|
||||
abstract contract Helpers {
|
||||
|
||||
struct TokenMap {
|
||||
address qitoken;
|
||||
address token;
|
||||
}
|
||||
|
||||
event LogQiTokenAdded(string indexed name, address indexed token, address indexed qitoken);
|
||||
event LogQiTokenUpdated(string indexed name, address indexed token, address indexed qitoken);
|
||||
|
||||
// InstaConnectorsV2
|
||||
ConnectorsInterface public constant connectors = ConnectorsInterface(0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14);
|
||||
// InstaIndex Address.
|
||||
IndexInterface public constant instaIndex = IndexInterface(0x6CE3e607C808b4f4C26B7F6aDAeB619e49CAbb25);
|
||||
|
||||
// InstaMappingController Address.
|
||||
MappingControllerInterface public constant mappingController = MappingControllerInterface(0xF2113d0c99f36D7D6F6c6FAf05E0863892255999);
|
||||
|
||||
address public constant avaxAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
mapping (string => TokenMap) public qiTokenMapping;
|
||||
|
||||
modifier isChief {
|
||||
require(msg.sender == instaIndex.master() || connectors.chief(msg.sender), "not-an-chief");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier hasRoleOrIsChief {
|
||||
require(
|
||||
msg.sender == instaIndex.master() ||
|
||||
connectors.chief(msg.sender) ||
|
||||
mappingController.hasRole(address(this), msg.sender),
|
||||
"not-an-chief/controller"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
function _addQitokenMapping(
|
||||
string[] memory _names,
|
||||
address[] memory _tokens,
|
||||
address[] memory _qitokens
|
||||
) internal {
|
||||
require(_names.length == _tokens.length, "addQitokenMapping: not same length");
|
||||
require(_names.length == _qitokens.length, "addQitokenMapping: not same length");
|
||||
|
||||
for (uint i = 0; i < _qitokens.length; i++) {
|
||||
TokenMap memory _data = qiTokenMapping[_names[i]];
|
||||
|
||||
require(_data.qitoken == address(0), "addQitokenMapping: mapping added already");
|
||||
require(_data.token == address(0), "addQitokenMapping: mapping added already");
|
||||
|
||||
require(_tokens[i] != address(0), "addQitokenMapping: _tokens address not vaild");
|
||||
require(_qitokens[i] != address(0), "addQitokenMapping: _qitokens address not vaild");
|
||||
|
||||
QiTokenInterface _qitokenContract = QiTokenInterface(_qitokens[i]);
|
||||
|
||||
require(_qitokenContract.isQiToken(), "addQitokenMapping: not a qiToken");
|
||||
if (_tokens[i] != avaxAddr) {
|
||||
require(_qitokenContract.underlying() == _tokens[i], "addQitokenMapping: mapping mismatch");
|
||||
}
|
||||
|
||||
qiTokenMapping[_names[i]] = TokenMap(
|
||||
_qitokens[i],
|
||||
_tokens[i]
|
||||
);
|
||||
emit LogQiTokenAdded(_names[i], _tokens[i], _qitokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateQitokenMapping(
|
||||
string[] calldata _names,
|
||||
address[] memory _tokens,
|
||||
address[] calldata _qitokens
|
||||
) external isChief {
|
||||
|
||||
require(_names.length == _tokens.length, "updateQitokenMapping: not same length");
|
||||
require(_names.length == _qitokens.length, "updateQitokenMapping: not same length");
|
||||
|
||||
for (uint i = 0; i < _qitokens.length; i++) {
|
||||
TokenMap memory _data = qiTokenMapping[_names[i]];
|
||||
|
||||
require(_data.qitoken != address(0), "updateQitokenMapping: mapping does not exist");
|
||||
require(_data.token != address(0), "updateQitokenMapping: mapping does not exist");
|
||||
|
||||
require(_tokens[i] != address(0), "updateQitokenMapping: _tokens address not vaild");
|
||||
require(_qitokens[i] != address(0), "updateQitokenMapping: _qitokens address not vaild");
|
||||
|
||||
QiTokenInterface _qitokenContract = QiTokenInterface(_qitokens[i]);
|
||||
|
||||
require(_qitokenContract.isQiToken(), "updateQitokenMapping: not a qiToken");
|
||||
if (_tokens[i] != avaxAddr) {
|
||||
require(_qitokenContract.underlying() == _tokens[i], "addQitokenMapping: mapping mismatch");
|
||||
}
|
||||
|
||||
qiTokenMapping[_names[i]] = TokenMap(
|
||||
_qitokens[i],
|
||||
_tokens[i]
|
||||
);
|
||||
emit LogQiTokenUpdated(_names[i], _tokens[i], _qitokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function addQitokenMapping(
|
||||
string[] memory _names,
|
||||
address[] memory _tokens,
|
||||
address[] memory _qitokens
|
||||
) external hasRoleOrIsChief {
|
||||
_addQitokenMapping(_names, _tokens, _qitokens);
|
||||
}
|
||||
|
||||
function getMapping(string memory _tokenId) external view returns (address, address) {
|
||||
TokenMap memory _data = qiTokenMapping[_tokenId];
|
||||
return (_data.token, _data.qitoken);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract InstaBenqiMappingAvalanche is Helpers {
|
||||
string constant public name = "Benqi-Mapping-v1.0";
|
||||
|
||||
constructor(
|
||||
string[] memory _qitokenNames,
|
||||
address[] memory _tokens,
|
||||
address[] memory _qitokens
|
||||
) {
|
||||
_addQitokenMapping(_qitokenNames, _tokens, _qitokens);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,6 @@ contract Variables {
|
|||
/**
|
||||
* @dev Instapool contract proxy
|
||||
*/
|
||||
InstaFlashV2Interface public constant instaPool = InstaFlashV2Interface(0xF77A5935f35aDD4C2f524788805293EF86B87560);
|
||||
InstaFlashV2Interface public constant instaPool = InstaFlashV2Interface(0x276B88D057b368179480CB707366d497DfC79726);
|
||||
|
||||
}
|
||||
10
contracts/mainnet/connectors/instapool_v4/events.sol
Normal file
10
contracts/mainnet/connectors/instapool_v4/events.sol
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Events {
|
||||
event LogFlashBorrow(address token, uint256 tokenAmt);
|
||||
event LogFlashPayback(address token, uint256 tokenAmt);
|
||||
|
||||
event LogFlashMultiBorrow(address[] token, uint256[] tokenAmts);
|
||||
event LogFlashMultiPayback(address[] token, uint256[] tokenAmts);
|
||||
}
|
||||
11
contracts/mainnet/connectors/instapool_v4/interfaces.sol
Normal file
11
contracts/mainnet/connectors/instapool_v4/interfaces.sol
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface InstaFlashV4Interface {
|
||||
function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;
|
||||
}
|
||||
|
||||
interface AccountInterface {
|
||||
function enable(address) external;
|
||||
function disable(address) external;
|
||||
}
|
||||
136
contracts/mainnet/connectors/instapool_v4/main.sol
Normal file
136
contracts/mainnet/connectors/instapool_v4/main.sol
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Instapool.
|
||||
* @dev Inbuilt Flash Loan in DSA
|
||||
*/
|
||||
|
||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { AccountInterface } from "./interfaces.sol";
|
||||
import { Stores } from "../../common/stores.sol";
|
||||
import { Variables } from "./variables.sol";
|
||||
import { Events } from "./events.sol";
|
||||
|
||||
contract LiquidityResolver is Stores, Variables, Events {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
/**
|
||||
* @dev Borrow Flashloan and Cast spells.
|
||||
* @notice Borrow Flashloan and Cast spells.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashBorrowAndCast(
|
||||
address token,
|
||||
uint amt,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
address[] memory tokens_ = new address[](1);
|
||||
tokens_[0] = token;
|
||||
uint[] memory amts_ = new uint[](1);
|
||||
amts_[0] = amt;
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
|
||||
_eventName = "LogFlashBorrow(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return token to InstaPool.
|
||||
* @notice Return token to InstaPool.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param getId Get token amount at this ID from `InstaMemory` Contract.
|
||||
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
||||
*/
|
||||
function flashPayback(
|
||||
address token,
|
||||
uint amt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
IERC20 tokenContract = IERC20(token);
|
||||
|
||||
tokenContract.safeTransfer(address(instaPool), _amt);
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogFlashPayback(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @notice Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashMultiBorrowAndCast(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
_eventName = "LogFlashMultiBorrow(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return multi-tokens to InstaPool.
|
||||
* @notice Return multi-tokens to InstaPool.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param getIds Array of getId token amounts.
|
||||
* @param setIds Array of setId token amounts.
|
||||
*/
|
||||
function flashMultiPayback(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint[] memory getIds,
|
||||
uint[] memory setIds
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
for (uint i = 0; i < tokens_.length; i++) {
|
||||
amts_[i] = getUint(getIds[i], amts_[i]);
|
||||
|
||||
IERC20(tokens_[i]).safeTransfer(address(instaPool), amts_[i]);
|
||||
|
||||
setUint(setIds[i], amts_[i]);
|
||||
}
|
||||
|
||||
_eventName = "LogFlashMultiPayback(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract ConnectV2InstaPoolV4 is LiquidityResolver {
|
||||
string public name = "Instapool-v4";
|
||||
}
|
||||
13
contracts/mainnet/connectors/instapool_v4/variables.sol
Normal file
13
contracts/mainnet/connectors/instapool_v4/variables.sol
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { InstaFlashV4Interface } from "./interfaces.sol";
|
||||
|
||||
contract Variables {
|
||||
|
||||
/**
|
||||
* @dev Instapool contract proxy
|
||||
*/
|
||||
InstaFlashV4Interface public constant instaPool = InstaFlashV4Interface(0x619Ad2D02dBeE6ebA3CDbDA3F98430410e892882);
|
||||
|
||||
}
|
||||
20
contracts/mainnet/connectors/universeFinance/events.sol
Normal file
20
contracts/mainnet/connectors/universeFinance/events.sol
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
contract Events {
|
||||
|
||||
event LogDeposit(
|
||||
address indexed universeVault,
|
||||
uint256 amountA,
|
||||
uint256 amountB,
|
||||
uint256 share0,
|
||||
uint256 share1
|
||||
);
|
||||
|
||||
event LogWithdraw(
|
||||
address indexed universeVault,
|
||||
uint256 amountA,
|
||||
uint256 amountB,
|
||||
uint256 share0,
|
||||
uint256 share1
|
||||
);
|
||||
}
|
||||
43
contracts/mainnet/connectors/universeFinance/helpers.sol
Normal file
43
contracts/mainnet/connectors/universeFinance/helpers.sol
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
pragma solidity ^0.7.6;
|
||||
|
||||
import {TokenInterface} from "../../common/interfaces.sol";
|
||||
import {DSMath} from "../../common/math.sol";
|
||||
import {Basic} from "../../common/basic.sol";
|
||||
|
||||
import "./interface.sol";
|
||||
|
||||
abstract contract Helpers is DSMath, Basic {
|
||||
|
||||
IUniverseAdapter constant universeAdapter = IUniverseAdapter(0x876861Ad49f911442720cF97c9b3fCe4070F07d5);
|
||||
|
||||
function _deposit(
|
||||
address universeVault,
|
||||
uint256 amount0,
|
||||
uint256 amount1
|
||||
) internal returns(uint256, uint256){
|
||||
return universeAdapter.depositProxy(universeVault, amount0, amount1);
|
||||
}
|
||||
|
||||
function _withdraw(
|
||||
address universeVault,
|
||||
uint256 share0,
|
||||
uint256 share1
|
||||
) internal returns(uint256, uint256){
|
||||
require(share0 > 0 || share1 > 0, "ZERO");
|
||||
return IVaultV3(universeVault).withdraw(share0, share1);
|
||||
}
|
||||
|
||||
function _approve(address universeVault, uint256 amount0, uint256 amount1) internal {
|
||||
IVaultV3 universe = IVaultV3(universeVault);
|
||||
TokenInterface token;
|
||||
if (amount0 > 0) {
|
||||
token = universe.token0();
|
||||
token.approve(address(universeAdapter), amount0);
|
||||
}
|
||||
if (amount1 > 0) {
|
||||
token = universe.token1();
|
||||
token.approve(address(universeAdapter), amount1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
contracts/mainnet/connectors/universeFinance/interface.sol
Normal file
24
contracts/mainnet/connectors/universeFinance/interface.sol
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
pragma solidity ^0.7.6;
|
||||
|
||||
import "../../common/interfaces.sol";
|
||||
|
||||
pragma abicoder v2;
|
||||
|
||||
interface IUniverseAdapter {
|
||||
|
||||
function depositProxy(
|
||||
address universeVault,
|
||||
uint256 amount0,
|
||||
uint256 amount1
|
||||
) external returns(uint256, uint256);
|
||||
|
||||
}
|
||||
|
||||
interface IVaultV3 {
|
||||
|
||||
function token0() external returns(TokenInterface);
|
||||
|
||||
function token1() external returns(TokenInterface);
|
||||
|
||||
function withdraw(uint256 share0, uint256 share1) external returns(uint256, uint256);
|
||||
}
|
||||
92
contracts/mainnet/connectors/universeFinance/main.sol
Normal file
92
contracts/mainnet/connectors/universeFinance/main.sol
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
pragma solidity ^0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
/**
|
||||
* @title Universe finance
|
||||
* @dev Maximising uniswap v3 returns
|
||||
*/
|
||||
|
||||
import {TokenInterface} from "../../common/interfaces.sol";
|
||||
import {Helpers} from "./helpers.sol";
|
||||
import {Events} from "./events.sol";
|
||||
|
||||
abstract contract UniverseFinanceConnect is Helpers, Events {
|
||||
/**
|
||||
* @notice Deposit in Universe Vault by Adapter
|
||||
* @dev Deposit in universe vault
|
||||
* @param universeVault Universe Official Vault Address
|
||||
* @param amountA Amount of tokenA
|
||||
* @param amountB Amount of tokenB
|
||||
* @param getIds ID to retrieve amountA and amountB
|
||||
* @param setIds ID to store amountA and amountB
|
||||
*/
|
||||
function deposit(
|
||||
address universeVault,
|
||||
uint256 amountA,
|
||||
uint256 amountB,
|
||||
uint256[] calldata getIds,
|
||||
uint256[] calldata setIds
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (string memory _eventName, bytes memory _eventParam)
|
||||
{
|
||||
amountA = getUint(getIds[0], amountA);
|
||||
amountB = getUint(getIds[1], amountB);
|
||||
_approve(universeVault, amountA, amountB);
|
||||
(uint256 share0, uint256 share1) = _deposit(
|
||||
universeVault,
|
||||
amountA,
|
||||
amountB
|
||||
);
|
||||
setUint(setIds[0], share0);
|
||||
setUint(setIds[1], share1);
|
||||
// EVENT
|
||||
_eventName = "LogDeposit(address,uint256,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(
|
||||
universeVault,
|
||||
amountA,
|
||||
amountB,
|
||||
share0,
|
||||
share1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraw Token0 & Token1 From Universe Vault
|
||||
* @dev Withdraw supplied token0 and token1 from universe vault
|
||||
* @param universeVault Universe Official Vault Address
|
||||
* @param share0 Amount of uToken0.
|
||||
* @param share1 Amount of uToken1.
|
||||
* @param getIds ID to retrieve amount of output token
|
||||
* @param setIds stores the amount of output tokens
|
||||
*/
|
||||
function withdraw(
|
||||
address universeVault,
|
||||
uint256 share0,
|
||||
uint256 share1,
|
||||
uint256[] calldata getIds,
|
||||
uint256[] calldata setIds
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (string memory _eventName, bytes memory _eventParam)
|
||||
{
|
||||
share0 = getUint(getIds[0], share0);
|
||||
share1 = getUint(getIds[1], share1);
|
||||
(uint256 _amtA, uint256 _amtB) = _withdraw(
|
||||
universeVault,
|
||||
share0,
|
||||
share1
|
||||
);
|
||||
setUint(setIds[0], _amtA);
|
||||
setUint(setIds[1], _amtB);
|
||||
// EVENT
|
||||
_eventName = "LogWithdraw(address,uint256,uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(universeVault, _amtA, _amtB, share0, share1);
|
||||
}
|
||||
}
|
||||
|
||||
contract ConnectV2UniverseFinance is UniverseFinanceConnect {
|
||||
string public constant name = "UniverseFinance-v1";
|
||||
}
|
||||
10
contracts/polygon/connectors/instapool_v4/events.sol
Normal file
10
contracts/polygon/connectors/instapool_v4/events.sol
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Events {
|
||||
event LogFlashBorrow(address token, uint256 tokenAmt);
|
||||
event LogFlashPayback(address token, uint256 tokenAmt);
|
||||
|
||||
event LogFlashMultiBorrow(address[] token, uint256[] tokenAmts);
|
||||
event LogFlashMultiPayback(address[] token, uint256[] tokenAmts);
|
||||
}
|
||||
11
contracts/polygon/connectors/instapool_v4/interfaces.sol
Normal file
11
contracts/polygon/connectors/instapool_v4/interfaces.sol
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
pragma solidity >=0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface InstaFlashV4Interface {
|
||||
function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external;
|
||||
}
|
||||
|
||||
interface AccountInterface {
|
||||
function enable(address) external;
|
||||
function disable(address) external;
|
||||
}
|
||||
138
contracts/polygon/connectors/instapool_v4/main.sol
Normal file
138
contracts/polygon/connectors/instapool_v4/main.sol
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/**
|
||||
* @title Instapool.
|
||||
* @dev Inbuilt Flash Loan in DSA
|
||||
*/
|
||||
|
||||
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { TokenInterface } from "../../common/interfaces.sol";
|
||||
import { AccountInterface } from "./interfaces.sol";
|
||||
import { Stores } from "../../common/stores.sol";
|
||||
import { Variables } from "./variables.sol";
|
||||
import { Events } from "./events.sol";
|
||||
|
||||
contract LiquidityResolver is Stores, Variables, Events {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
/**
|
||||
* @dev Borrow Flashloan and Cast spells.
|
||||
* @notice Borrow Flashloan and Cast spells.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashBorrowAndCast(
|
||||
address token,
|
||||
uint amt,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
address[] memory tokens_ = new address[](1);
|
||||
tokens_[0] = token;
|
||||
uint[] memory amts_ = new uint[](1);
|
||||
amts_[0] = amt;
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
|
||||
_eventName = "LogFlashBorrow(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return token to InstaPool.
|
||||
* @notice Return token to InstaPool.
|
||||
* @param token Token Address.
|
||||
* @param amt Token Amount.
|
||||
* @param getId Get token amount at this ID from `InstaMemory` Contract.
|
||||
* @param setId Set token amount at this ID in `InstaMemory` Contract.
|
||||
*/
|
||||
function flashPayback(
|
||||
address token,
|
||||
uint amt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
IERC20 tokenContract = IERC20(token);
|
||||
|
||||
tokenContract.safeTransfer(address(instaPool), _amt);
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogFlashPayback(address,uint256)";
|
||||
_eventParam = abi.encode(token, amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @notice Borrow multi-tokens Flashloan and Cast spells.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param route Flashloan source route.
|
||||
* @param data targets & data for cast.
|
||||
* @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes
|
||||
*/
|
||||
function flashMultiBorrowAndCast(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint route,
|
||||
bytes memory data,
|
||||
bytes memory extraData
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
AccountInterface(address(this)).enable(address(instaPool));
|
||||
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
|
||||
|
||||
bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
|
||||
|
||||
instaPool.flashLoan(tokens_, amts_, route, callData_, extraData);
|
||||
|
||||
AccountInterface(address(this)).disable(address(instaPool));
|
||||
_eventName = "LogFlashMultiBorrow(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return multi-tokens to InstaPool.
|
||||
* @notice Return multi-tokens to InstaPool.
|
||||
* @param tokens_ Array of Token Addresses.
|
||||
* @param amts_ Array of Token Amounts.
|
||||
* @param getIds Array of getId token amounts.
|
||||
* @param setIds Array of setId token amounts.
|
||||
*/
|
||||
function flashMultiPayback(
|
||||
address[] memory tokens_,
|
||||
uint[] memory amts_,
|
||||
uint[] memory getIds,
|
||||
uint[] memory setIds
|
||||
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
for (uint i = 0; i < tokens_.length; i++) {
|
||||
amts_[i] = getUint(getIds[i], amts_[i]);
|
||||
|
||||
IERC20(tokens_[i]).safeTransfer(address(instaPool), amts_[i]);
|
||||
|
||||
setUint(setIds[i], amts_[i]);
|
||||
}
|
||||
|
||||
_eventName = "LogFlashMultiPayback(address[],uint256[])";
|
||||
_eventParam = abi.encode(tokens_, amts_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract ConnectV2InstaPoolV4 is LiquidityResolver {
|
||||
string public name = "Instapool-v4";
|
||||
}
|
||||
13
contracts/polygon/connectors/instapool_v4/variables.sol
Normal file
13
contracts/polygon/connectors/instapool_v4/variables.sol
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.7.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { InstaFlashV4Interface } from "./interfaces.sol";
|
||||
|
||||
contract Variables {
|
||||
|
||||
/**
|
||||
* @dev Instapool contract proxy
|
||||
*/
|
||||
InstaFlashV4Interface public constant instaPool = InstaFlashV4Interface(address(0)); // TODO: update address
|
||||
|
||||
}
|
||||
6
contracts/polygon/connectors/wmatic/events.sol
Normal file
6
contracts/polygon/connectors/wmatic/events.sol
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
contract Events {
|
||||
event LogDeposit(uint256 tokenAmt, uint256 getId, uint256 setId);
|
||||
event LogWithdraw(uint256 tokenAmt, uint256 getId, uint256 setId);
|
||||
}
|
||||
8
contracts/polygon/connectors/wmatic/helpers.sol
Normal file
8
contracts/polygon/connectors/wmatic/helpers.sol
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
import { TokenInterface } from "../../common/interfaces.sol";
|
||||
|
||||
|
||||
abstract contract Helpers {
|
||||
TokenInterface constant internal wmaticContract = TokenInterface(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);
|
||||
}
|
||||
65
contracts/polygon/connectors/wmatic/main.sol
Normal file
65
contracts/polygon/connectors/wmatic/main.sol
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
pragma solidity ^0.7.0;
|
||||
|
||||
/**
|
||||
* @title WMATIC.
|
||||
* @dev Wrap and Unwrap WMATIC.
|
||||
*/
|
||||
|
||||
import { DSMath } from "../../common/math.sol";
|
||||
import { Basic } from "../../common/basic.sol";
|
||||
import { Events } from "./events.sol";
|
||||
import { Helpers } from "./helpers.sol";
|
||||
|
||||
abstract contract Resolver is Events, DSMath, Basic, Helpers {
|
||||
|
||||
/**
|
||||
* @dev Deposit MATIC into WMATIC.
|
||||
* @notice Wrap MATIC into WMATIC
|
||||
* @param amt The amount of MATIC to deposit. (For max: `uint256(-1)`)
|
||||
* @param getId ID to retrieve amt.
|
||||
* @param setId ID stores the amount of MATIC deposited.
|
||||
*/
|
||||
function deposit(
|
||||
uint256 amt,
|
||||
uint256 getId,
|
||||
uint256 setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
_amt = _amt == uint(-1) ? address(this).balance : _amt;
|
||||
wmaticContract.deposit{value: _amt}();
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogDeposit(uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(_amt, getId, setId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw MATIC from WMATIC from Smart Account
|
||||
* @notice Unwrap MATIC from WMATIC
|
||||
* @param amt The amount of wmatic to withdraw. (For max: `uint256(-1)`)
|
||||
* @param getId ID to retrieve amt.
|
||||
* @param setId ID stores the amount of MATIC withdrawn.
|
||||
*/
|
||||
function withdraw(
|
||||
uint amt,
|
||||
uint getId,
|
||||
uint setId
|
||||
) public payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||
uint _amt = getUint(getId, amt);
|
||||
|
||||
_amt = _amt == uint(-1) ? wmaticContract.balanceOf(address(this)) : _amt;
|
||||
approve(wmaticContract, wmaticAddr, _amt);
|
||||
wmaticContract.withdraw(_amt);
|
||||
|
||||
setUint(setId, _amt);
|
||||
|
||||
_eventName = "LogWithdraw(uint256,uint256,uint256)";
|
||||
_eventParam = abi.encode(_amt, getId, setId);
|
||||
}
|
||||
}
|
||||
|
||||
contract ConnectV2WMATICPolygon is Resolver {
|
||||
string constant public name = "WMATIC-v1.0";
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
require("@nomiclabs/hardhat-waffle");
|
||||
require("@nomiclabs/hardhat-ethers");
|
||||
require("@tenderly/hardhat-tenderly");
|
||||
require("@nomiclabs/hardhat-etherscan");
|
||||
require("@nomiclabs/hardhat-web3");
|
||||
require("hardhat-deploy");
|
||||
require("hardhat-deploy-ethers");
|
||||
require("dotenv").config();
|
||||
|
||||
const { utils } = require("ethers");
|
||||
|
||||
const PRIVATE_KEY = process.env.PRIVATE_KEY;
|
||||
const ALCHEMY_ID = process.env.ALCHEMY_ID;
|
||||
const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
|
||||
|
||||
if (!process.env.ALCHEMY_ID) {
|
||||
throw new Error("ENV Variable ALCHEMY_ID not set!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
module.exports = {
|
||||
solidity: {
|
||||
compilers: [
|
||||
{
|
||||
version: "0.7.6",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
version: "0.6.0"
|
||||
},
|
||||
{
|
||||
version: "0.6.2"
|
||||
},
|
||||
{
|
||||
version: "0.6.5"
|
||||
}
|
||||
]
|
||||
},
|
||||
networks: {
|
||||
kovan: {
|
||||
url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`,
|
||||
accounts: [`0x${PRIVATE_KEY}`]
|
||||
},
|
||||
mainnet: {
|
||||
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000,
|
||||
gasPrice: parseInt(utils.parseUnits("30", "gwei"))
|
||||
},
|
||||
rinkeby: {
|
||||
url: `https://eth-rinkeby.alchemyapi.io/v2/${ALCHEMY_ID}`,
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000
|
||||
},
|
||||
hardhat: {
|
||||
forking: {
|
||||
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
|
||||
blockNumber: 13097100
|
||||
},
|
||||
blockGasLimit: 12000000,
|
||||
gasPrice: parseInt(utils.parseUnits("300", "gwei"))
|
||||
},
|
||||
local: {
|
||||
url: "http://127.0.0.1:8545"
|
||||
},
|
||||
matic: {
|
||||
url: "https://rpc-mainnet.maticvigil.com/",
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000,
|
||||
gasPrice: parseInt(utils.parseUnits("1", "gwei"))
|
||||
},
|
||||
arbitrum: {
|
||||
chainId: 42161,
|
||||
url: `https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_ID}`,
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000,
|
||||
gasPrice: parseInt(utils.parseUnits("2", "gwei"))
|
||||
},
|
||||
avax: {
|
||||
url: "https://api.avax.network/ext/bc/C/rpc",
|
||||
chainId: 43114,
|
||||
accounts: [`0x${PRIVATE_KEY}`],
|
||||
timeout: 150000,
|
||||
gasPrice: parseInt(utils.parseUnits("225", "gwei"))
|
||||
}
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: ETHERSCAN_API_KEY
|
||||
},
|
||||
tenderly: {
|
||||
project: process.env.TENDERLY_PROJECT,
|
||||
username: process.env.TENDERLY_USERNAME
|
||||
},
|
||||
mocha: {
|
||||
timeout: 100 * 1000,
|
||||
bail: true
|
||||
}
|
||||
};
|
||||
130
hardhat.config.ts
Normal file
130
hardhat.config.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import "@nomiclabs/hardhat-waffle";
|
||||
import "@nomiclabs/hardhat-ethers";
|
||||
import "@tenderly/hardhat-tenderly";
|
||||
import "@nomiclabs/hardhat-etherscan";
|
||||
import "@nomiclabs/hardhat-web3";
|
||||
import "hardhat-deploy";
|
||||
import "hardhat-deploy-ethers";
|
||||
import "@typechain/hardhat";
|
||||
|
||||
import { resolve } from "path";
|
||||
import { config as dotenvConfig } from "dotenv";
|
||||
import { HardhatUserConfig } from "hardhat/config";
|
||||
import { NetworkUserConfig } from "hardhat/types";
|
||||
import { utils } from "ethers";
|
||||
import Web3 from "web3";
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "./.env") });
|
||||
|
||||
const chainIds = {
|
||||
ganache: 1337,
|
||||
hardhat: 31337,
|
||||
mainnet: 1,
|
||||
avalanche: 43114,
|
||||
polygon: 137,
|
||||
arbitrum: 42161,
|
||||
};
|
||||
|
||||
const alchemyApiKey = process.env.ALCHEMY_API_KEY;
|
||||
if (!alchemyApiKey) {
|
||||
throw new Error("Please set your ALCHEMY_API_KEY in a .env file");
|
||||
}
|
||||
|
||||
const PRIVATE_KEY = process.env.PRIVATE_KEY;
|
||||
const ETHERSCAN_API = process.env.ETHERSCAN_API_KEY;
|
||||
const POLYGONSCAN_API = process.env.POLYGON_API_KEY;
|
||||
const ARBISCAN_API = process.env.ARBISCAN_API_KEY;
|
||||
const SNOWTRACE_API = process.env.SNOWTRACE_API_KEY;
|
||||
const mnemonic =
|
||||
process.env.MNEMONIC ??
|
||||
"test test test test test test test test test test test junk";
|
||||
|
||||
function createConfig(network: string) {
|
||||
return {
|
||||
url: getNetworkUrl(network),
|
||||
accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic },
|
||||
};
|
||||
}
|
||||
|
||||
function getNetworkUrl(networkType: string) {
|
||||
//console.log(process.env);
|
||||
if (networkType === "avalanche")
|
||||
return "https://api.avax.network/ext/bc/C/rpc";
|
||||
else if (networkType === "polygon")
|
||||
return `https://polygon-mainnet.g.alchemy.com/v2/${alchemyApiKey}`;
|
||||
else if (networkType === "arbitrum")
|
||||
return `https://arb-mainnet.g.alchemy.com/v2/${alchemyApiKey}`;
|
||||
else return `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`;
|
||||
}
|
||||
|
||||
function getScanApiKey(networkType: string) {
|
||||
if (networkType === "avalanche") return SNOWTRACE_API;
|
||||
else if (networkType === "polygon") return POLYGONSCAN_API;
|
||||
else if (networkType === "arbitrum") return ARBISCAN_API;
|
||||
else return ETHERSCAN_API;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
const config: HardhatUserConfig = {
|
||||
solidity: {
|
||||
compilers: [
|
||||
{
|
||||
version: "0.7.6",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
version: "0.6.0",
|
||||
},
|
||||
{
|
||||
version: "0.6.2",
|
||||
},
|
||||
{
|
||||
version: "0.6.5",
|
||||
},
|
||||
],
|
||||
},
|
||||
networks: {
|
||||
hardhat: {
|
||||
accounts: {
|
||||
mnemonic,
|
||||
},
|
||||
chainId: chainIds.hardhat,
|
||||
forking: {
|
||||
url: String(getNetworkUrl(String(process.env.networkType))),
|
||||
},
|
||||
},
|
||||
mainnet: createConfig("mainnet"),
|
||||
polygon: createConfig("polygon"),
|
||||
avalanche: createConfig("avalanche"),
|
||||
arbitrum: createConfig("arbitrum"),
|
||||
},
|
||||
paths: {
|
||||
artifacts: "./artifacts",
|
||||
cache: "./cache",
|
||||
sources: "./contracts",
|
||||
tests: "./test",
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: getScanApiKey(getNetworkUrl(String(process.env.networkType))),
|
||||
},
|
||||
typechain: {
|
||||
outDir: "typechain",
|
||||
target: "ethers-v5",
|
||||
},
|
||||
mocha: {
|
||||
timeout: 10000 * 1000, // 10,000 seconds
|
||||
},
|
||||
// tenderly: {
|
||||
// project: process.env.TENDERLY_PROJECT,
|
||||
// username: process.env.TENDERLY_USERNAME,
|
||||
// },
|
||||
};
|
||||
|
||||
export default config;
|
||||
24532
package-lock.json
generated
24532
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
|
|
@ -4,11 +4,14 @@
|
|||
"description": "",
|
||||
"directories": {},
|
||||
"scripts": {
|
||||
"test": "hardhat test",
|
||||
"test": "hardhat run scripts/tests/global-test.ts",
|
||||
"coverage": "./node_modules/.bin/solidity-coverage",
|
||||
"check": "node status-checks/huskyCheck.js",
|
||||
"check-husky": "node status-checks/huskyCheck.js",
|
||||
"deploy": "node scripts/deployConnectorsFromCmd.js"
|
||||
"deploy": "node scripts/deployConnectorsFromCmd.js",
|
||||
"test:runner": "hardhat run scripts/tests/run-tests.ts",
|
||||
"typechain": "hardhat typechain",
|
||||
"compile": "hardhat compile"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -22,33 +25,45 @@
|
|||
"homepage": "https://github.com/InstaDApp/dsa-connectors-new#readme",
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^3.4.0-solc-0.7",
|
||||
"@typechain/ethers-v5": "^8.0.5",
|
||||
"@typechain/hardhat": "^3.0.0",
|
||||
"@uniswap/v3-core": "^1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.2.1",
|
||||
"chalk": "^4.1.2",
|
||||
"@uniswap/v3-periphery": "^1.3.0",
|
||||
"chalk": "^5.0.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"hardhat-docgen": "^1.1.2",
|
||||
"hardhat-docgen": "^1.2.0",
|
||||
"inquirer": "^8.2.0",
|
||||
"minimist": "^1.2.5",
|
||||
"solc": "^0.7.6"
|
||||
"solc": "^0.8.10",
|
||||
"typechain": "^6.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@nomiclabs/hardhat-etherscan": "^2.1.7",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.3",
|
||||
"@nomiclabs/hardhat-etherscan": "^2.1.8",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"@studydefi/money-legos": "^2.4.2",
|
||||
"@tenderly/hardhat-tenderly": "^1.0.12",
|
||||
"@tenderly/hardhat-tenderly": "^1.0.13",
|
||||
"@types/chai": "^4.2.22",
|
||||
"@types/chai-as-promised": "^7.1.4",
|
||||
"@types/inquirer": "^8.1.3",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.11",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"ethereum-waffle": "^3.4.0",
|
||||
"ethers": "^5.5.1",
|
||||
"hardhat": "^2.6.7",
|
||||
"hardhat-deploy": "^0.9.4",
|
||||
"ethers": "^5.5.2",
|
||||
"hardhat": "^2.7.0",
|
||||
"hardhat-deploy": "^0.9.14",
|
||||
"hardhat-deploy-ethers": "^0.3.0-beta.11",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.18",
|
||||
"solhint": "^3.3.6",
|
||||
"husky": "^7.0.4",
|
||||
"solidity-coverage": "0.5.11",
|
||||
"web3": "^1.6.0"
|
||||
"solidity-coverage": "0.7.17",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.2",
|
||||
"web3": "^1.6.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
const addresses = require("./constant/addresses");
|
||||
const abis = require("./constant/abis");
|
||||
|
||||
const instaImplementations_m1 = require("../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json")
|
||||
|
||||
module.exports = async function (owner) {
|
||||
const instaIndex = await ethers.getContractAt(abis.core.instaIndex, addresses.core.instaIndex)
|
||||
|
||||
const tx = await instaIndex.build(owner, 2, owner);
|
||||
const receipt = await tx.wait()
|
||||
const event = receipt.events.find(a => a.event === "LogAccountCreated")
|
||||
return await ethers.getContractAt(instaImplementations_m1.abi, event.args.account)
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export const abis: Record<string, any> = {
|
||||
core: {
|
||||
connectorsV2: require("./abi/core/connectorsV2.json"),
|
||||
instaIndex: require("./abi/core/instaIndex.json"),
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
module.exports = {
|
||||
connectors: {
|
||||
basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6",
|
||||
auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9",
|
||||
"INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2",
|
||||
},
|
||||
core: {
|
||||
connectorsV2: "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11",
|
||||
instaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723",
|
||||
},
|
||||
};
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
address_zero: "0x0000000000000000000000000000000000000000",
|
||||
eth_addr: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
max_value: "115792089237316195423570985008687907853269984665640564039457584007913129639935"
|
||||
};
|
||||
|
||||
6
scripts/constant/constant.ts
Normal file
6
scripts/constant/constant.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export const constants = {
|
||||
address_zero: "0x0000000000000000000000000000000000000000",
|
||||
native_address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
max_value:
|
||||
"115792089237316195423570985008687907853269984665640564039457584007913129639935",
|
||||
};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
module.exports = {
|
||||
"eth": {
|
||||
"type": "token",
|
||||
"symbol": "ETH",
|
||||
"name": "Ethereum",
|
||||
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"decimals": 18
|
||||
},
|
||||
"dai": {
|
||||
"type": "token",
|
||||
"symbol": "DAI",
|
||||
"name": "DAI Stable",
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"decimals": 18
|
||||
},
|
||||
"usdc": {
|
||||
"type": "token",
|
||||
"symbol": "USDC",
|
||||
"name": "USD Coin",
|
||||
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
|
||||
const deployConnector = require("./deployConnector");
|
||||
|
||||
async function main() {
|
||||
const accounts = await hre.ethers.getSigners()
|
||||
const wallet = accounts[0]
|
||||
|
||||
const connectMapping = {
|
||||
'1INCH-A': 'ConnectV2OneInch',
|
||||
'1INCH-B': 'ConnectV2OneProto',
|
||||
'AAVE-V1-A': 'ConnectV2AaveV1',
|
||||
'AAVE-V2-A': 'ConnectV2AaveV2',
|
||||
'AUTHORITY-A': 'ConnectV2Auth',
|
||||
'BASIC-A': 'ConnectV2Basic',
|
||||
'COMP-A': 'ConnectV2COMP',
|
||||
'COMPOUND-A': 'ConnectV2Compound',
|
||||
'DYDX-A': 'ConnectV2Dydx',
|
||||
'FEE-A': 'ConnectV2Fee',
|
||||
'GELATO-A': 'ConnectV2Gelato',
|
||||
'MAKERDAO-A': 'ConnectV2Maker',
|
||||
'UNISWAP-A': 'ConnectV2UniswapV2'
|
||||
}
|
||||
|
||||
const addressMapping = {}
|
||||
|
||||
for (const key in connectMapping) {
|
||||
addressMapping[key] = await deployConnector(connectMapping[key])
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
const abis = require("./constant/abis");
|
||||
const addresses = require("./constant/addresses");
|
||||
|
||||
const hre = require("hardhat");
|
||||
const { ethers, waffle } = hre;
|
||||
const { deployContract } = waffle;
|
||||
const fs = require("fs")
|
||||
|
||||
|
||||
module.exports = async function ({connectorName, contractArtifact, signer, connectors}) {
|
||||
const connectorInstanace = await deployContract(signer, contractArtifact, []);
|
||||
|
||||
await connectors.connect(signer).addConnectors([connectorName], [connectorInstanace.address])
|
||||
|
||||
addresses.connectors[connectorName] = connectorInstanace.address
|
||||
abis.connectors[connectorName] = contractArtifact.abi;
|
||||
|
||||
return connectorInstanace;
|
||||
};
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers, deployments, getUnnamedAccounts } = hre;
|
||||
const { deploy } = deployments;
|
||||
|
||||
|
||||
async function main() {
|
||||
|
||||
const deployer = (await getUnnamedAccounts())[0]
|
||||
|
||||
const connector = "ConnectV2InstaPoolV3Avalanche"
|
||||
|
||||
const connectorInstance = await deploy(connector, {
|
||||
from: deployer,
|
||||
})
|
||||
console.log(`${connector} deployed: `, connectorInstance.address);
|
||||
|
||||
await hre.run("sourcify", {
|
||||
address: connectorInstance.address,
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
|
||||
module.exports = async (connectorName) => {
|
||||
const Connector = await ethers.getContractFactory(connectorName);
|
||||
const connector = await Connector.deploy();
|
||||
await connector.deployed();
|
||||
|
||||
console.log(`${connectorName} Deployed: ${connector.address}`);
|
||||
|
||||
try {
|
||||
await hre.run("verify:verify", {
|
||||
address: connector.address,
|
||||
constructorArguments: []
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(`Failed to verify: ${connectorName}@${connector.address}`)
|
||||
console.log(error)
|
||||
console.log()
|
||||
}
|
||||
|
||||
return connector.address
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
const fs = require("fs");
|
||||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
|
||||
let args = process.argv;
|
||||
args = args.splice(2, args.length);
|
||||
let params = {};
|
||||
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
if (args[i][0] !== "-" || args[i][1] !== "-") {
|
||||
console.log("Please add '--' for the key");
|
||||
process.exit(-1);
|
||||
}
|
||||
let key = args[i].slice(2, args[i].length);
|
||||
params[key] = args[i + 1];
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty('connector')) {
|
||||
console.error("Should include connector params")
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty('network')) {
|
||||
console.error("Should include network params")
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty('gasPrice')) {
|
||||
console.error("Should include gas params")
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
let privateKey = process.env.PRIVATE_KEY;
|
||||
let provider = new ethers.providers.JsonRpcProvider(hre.config.networks[params['network']].url);
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
hre.network.name = params['networkName'];
|
||||
hre.network.config = hre.config.networks[params['networkName']];
|
||||
hre.network.provider = provider;
|
||||
let contracts = [];
|
||||
|
||||
const parseFile = async (filePath) => {
|
||||
const data = fs.readFileSync(filePath, "utf-8");
|
||||
let parsedData = data.split("contract ");
|
||||
parsedData = parsedData[parsedData.length - 1].split(" ");
|
||||
parsedData = parsedData[0];
|
||||
return parsedData;
|
||||
}
|
||||
|
||||
const parseDir = async (root, basePath, addPath) => {
|
||||
for(let i = 0; i < root.length; i++) {
|
||||
addPath = "/" + root[i];
|
||||
const dir = fs.readdirSync(basePath + addPath);
|
||||
if(dir.indexOf("main.sol") !== -1) {
|
||||
const fileData = await parseFile(basePath + addPath + "/main.sol");
|
||||
contracts.push(fileData)
|
||||
} else {
|
||||
await parseDir(dir, basePath + addPath, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
const mainnet = fs.readdirSync("./contracts/mainnet/connectors/");
|
||||
const polygon = fs.readdirSync("./contracts/polygon/connectors/");
|
||||
let basePathMainnet = "./contracts/mainnet/connectors/";
|
||||
let basePathPolygon = "./contracts/polygon/connectors/";
|
||||
|
||||
const connectorName = params['connector'];
|
||||
|
||||
await parseDir(mainnet, basePathMainnet, "");
|
||||
await parseDir(polygon, basePathPolygon, "");
|
||||
|
||||
if(contracts.indexOf(connectorName) === -1) {
|
||||
throw new Error("can not find the connector!\n" + "supported connector names are:\n" + contracts.join("\n"));
|
||||
}
|
||||
|
||||
const Connector = await ethers.getContractFactory(connectorName);
|
||||
const connector = await Connector.connect(wallet).deploy({ gasPrice: ethers.utils.parseUnits(params['gasPrice'], "gwei") });
|
||||
await connector.deployed();
|
||||
|
||||
console.log(`${connectorName} Deployed: ${connector.address}`);
|
||||
try {
|
||||
await hre.run("verify:verify", {
|
||||
address: connector.address,
|
||||
constructorArguments: []
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(`Failed to verify: ${connectorName}@${connector.address}`)
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
return connector.address
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
console.log("Done successfully");
|
||||
process.exit(0)
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("error:", err);
|
||||
process.exit(1);
|
||||
})
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
const hre = require('hardhat')
|
||||
const { ethers } = hre
|
||||
|
||||
async function main () {
|
||||
if (hre.network.name === 'mainnet') {
|
||||
console.log(
|
||||
'\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort'
|
||||
)
|
||||
} else if (hre.network.name === 'hardhat') {
|
||||
console.log(
|
||||
'\n\n Deploying Contracts to hardhat.'
|
||||
)
|
||||
}
|
||||
|
||||
const InstaMappingController = await ethers.getContractFactory('InstaMappingController')
|
||||
const instaMappingController = await InstaMappingController.deploy()
|
||||
await instaMappingController.deployed()
|
||||
|
||||
console.log('InstaMappingController deployed: ', instaMappingController.address)
|
||||
|
||||
if (hre.network.name === 'mainnet') {
|
||||
await hre.run('verify:verify', {
|
||||
address: instaMappingController.address,
|
||||
constructorArguments: []
|
||||
})
|
||||
} else if (hre.network.name === 'hardhat') {
|
||||
console.log("Contracts deployed.")
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
const hre = require('hardhat')
|
||||
const { ethers } = hre
|
||||
|
||||
async function main () {
|
||||
if (hre.network.name === 'mainnet') {
|
||||
console.log(
|
||||
'\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort'
|
||||
)
|
||||
} else if (hre.network.name === 'hardhat') {
|
||||
console.log(
|
||||
'\n\n Deploying Contracts to hardhat.'
|
||||
)
|
||||
}
|
||||
|
||||
const mappingContract = "CONTRACT_NAME"
|
||||
|
||||
const InstaProtocolMapping = await ethers.getContractFactory(mappingContract)
|
||||
const instaProtocolMapping = await InstaProtocolMapping.deploy()
|
||||
await instaProtocolMapping.deployed()
|
||||
|
||||
console.log(`${mappingContract} deployed: `, instaProtocolMapping.address)
|
||||
|
||||
if (hre.network.name === 'mainnet') {
|
||||
await hre.run('verify:verify', {
|
||||
address: instaProtocolMapping.address,
|
||||
constructorArguments: []
|
||||
})
|
||||
} else if (hre.network.name === 'hardhat') {
|
||||
console.log("Contracts deployed.")
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
|
||||
const deployConnector = require("./deployConnector");
|
||||
|
||||
async function main() {
|
||||
const address = await deployConnector("ConnectOne") // Example
|
||||
|
||||
const connectorsAbi = [
|
||||
"function addConnectors(string[] _connectorNames, address[] _connectors)"
|
||||
]
|
||||
|
||||
const connectorsContract = new ethers.Contract("0x84b457c6D31025d56449D5A01F0c34bF78636f67", connectorsAbi, ethers.provider);
|
||||
|
||||
await connectorsContract.addConnectors(['1inch'], [address])
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
35
scripts/deployment/deploy.ts
Normal file
35
scripts/deployment/deploy.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { ethers } from "hardhat";
|
||||
import { deployConnector } from "./deployConnector";
|
||||
|
||||
async function main() {
|
||||
const accounts = await ethers.getSigners();
|
||||
|
||||
const connectMapping: Record<string, string> = {
|
||||
"1INCH-A": "ConnectV2OneInch",
|
||||
"1INCH-B": "ConnectV2OneProto",
|
||||
"AAVE-V1-A": "ConnectV2AaveV1",
|
||||
"AAVE-V2-A": "ConnectV2AaveV2",
|
||||
"AUTHORITY-A": "ConnectV2Auth",
|
||||
"BASIC-A": "ConnectV2Basic",
|
||||
"COMP-A": "ConnectV2COMP",
|
||||
"COMPOUND-A": "ConnectV2Compound",
|
||||
"DYDX-A": "ConnectV2Dydx",
|
||||
"FEE-A": "ConnectV2Fee",
|
||||
"GELATO-A": "ConnectV2Gelato",
|
||||
"MAKERDAO-A": "ConnectV2Maker",
|
||||
"UNISWAP-A": "ConnectV2UniswapV2",
|
||||
};
|
||||
|
||||
const addressMapping: Record<string, string> = {};
|
||||
|
||||
for (const key in connectMapping) {
|
||||
addressMapping[key] = await deployConnector(connectMapping[key]);
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
24
scripts/deployment/deployAndVerifyOnSourcify.ts
Normal file
24
scripts/deployment/deployAndVerifyOnSourcify.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import hre from "hardhat";
|
||||
const { ethers, deployments, getUnnamedAccounts } = hre;
|
||||
const { deploy } = deployments;
|
||||
|
||||
async function main() {
|
||||
const deployer = (await getUnnamedAccounts())[0];
|
||||
const connector = "// Add connector name over here Eg: ConnectV2InstaPoolV3Avalanche"
|
||||
|
||||
const connectorInstance = await deploy(connector, {
|
||||
from: deployer,
|
||||
});
|
||||
console.log(`${connector} deployed: `, connectorInstance.address);
|
||||
|
||||
await hre.run("sourcify", {
|
||||
address: connectorInstance.address,
|
||||
});
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
const hre = require("hardhat");
|
||||
import hre from "hardhat";
|
||||
const { ethers } = hre;
|
||||
|
||||
async function main() {
|
||||
|
||||
const CONNECTORS_V2 = "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11";
|
||||
|
||||
const ctokenMapping = {
|
||||
|
|
@ -23,7 +21,7 @@ async function main() {
|
|||
"AAVE-A": "0xe65cdb6479bac1e22340e4e755fae7e509ecd06c",
|
||||
"TUSD-A": "0x12392f67bdf24fae0af363c24ac620a2f67dad86",
|
||||
"LINK-A": "0xface851a4921ce59e912d19329929ce6da6eb0c7",
|
||||
}
|
||||
};
|
||||
|
||||
const tokenMapping = {
|
||||
"ETH-A": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
|
|
@ -43,7 +41,7 @@ async function main() {
|
|||
"AAVE-A": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
|
||||
"TUSD-A": "0x0000000000085d4780B73119b644AE5ecd22b376",
|
||||
"LINK-A": "0x514910771af9ca656af840dff83e8264ecf986ca",
|
||||
}
|
||||
};
|
||||
|
||||
const Mapping = await ethers.getContractFactory("InstaCompoundMapping");
|
||||
const mapping = await Mapping.deploy(
|
||||
|
|
@ -58,25 +56,24 @@ async function main() {
|
|||
|
||||
try {
|
||||
await hre.run("verify:verify", {
|
||||
address: mapping.address,
|
||||
constructorArguments: [
|
||||
CONNECTORS_V2,
|
||||
Object.keys(ctokenMapping),
|
||||
Object.values(tokenMapping),
|
||||
Object.values(ctokenMapping)
|
||||
]
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(`Failed to verify: InstaCompoundMapping@${mapping.address}`)
|
||||
console.log(error)
|
||||
console.log()
|
||||
}
|
||||
address: mapping.address,
|
||||
constructorArguments: [
|
||||
CONNECTORS_V2,
|
||||
Object.keys(ctokenMapping),
|
||||
Object.values(tokenMapping),
|
||||
Object.values(ctokenMapping),
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Failed to verify: InstaCompoundMapping@${mapping.address}`);
|
||||
console.log(error);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
10
scripts/deployment/deployConnector.ts
Normal file
10
scripts/deployment/deployConnector.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { ethers } from "hardhat";
|
||||
|
||||
export const deployConnector = async (connectorName: string) => {
|
||||
const Connector = await ethers.getContractFactory(connectorName);
|
||||
const connector = await Connector.deploy();
|
||||
await connector.deployed();
|
||||
|
||||
console.log(`${connectorName} Deployed: ${connector.address}`);
|
||||
return connector.address;
|
||||
};
|
||||
111
scripts/deployment/deployConnectorsFromCmd.ts
Normal file
111
scripts/deployment/deployConnectorsFromCmd.ts
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import fs from "fs";
|
||||
import hre from "hardhat"
|
||||
const { ethers, network, config } = hre;
|
||||
|
||||
let args = process.argv;
|
||||
args = args.splice(2, args.length);
|
||||
let params: Record<string, string> = {};
|
||||
|
||||
for (let i = 0; i < args.length; i += 2) {
|
||||
if (args[i][0] !== "-" || args[i][1] !== "-") {
|
||||
console.log("Please add '--' for the key");
|
||||
process.exit(-1);
|
||||
}
|
||||
let key = args[i].slice(2, args[i].length);
|
||||
params[key] = args[i + 1];
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty("connector")) {
|
||||
console.error("Should include connector params");
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty("network")) {
|
||||
console.error("Should include network params");
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (!params.hasOwnProperty("gasPrice")) {
|
||||
console.error("Should include gas params");
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
let privateKey = String(process.env.PRIVATE_KEY);
|
||||
let provider = new ethers.providers.JsonRpcProvider(
|
||||
config.networks[params["network"]].url
|
||||
);
|
||||
let wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
network.name = params["networkName"];
|
||||
network.config = config.networks[params["networkName"]];
|
||||
network.provider = provider;
|
||||
let contracts: (string | string[])[] = [];
|
||||
|
||||
const parseFile = async (filePath: fs.PathOrFileDescriptor) => {
|
||||
const data = fs.readFileSync(filePath, "utf-8");
|
||||
let parsedData = data.split("contract ");
|
||||
parsedData = parsedData[parsedData.length - 1].split(" ");
|
||||
return parsedData[0];
|
||||
};
|
||||
|
||||
const parseDir = async (root: string | any[], basePath: string, addPath: string) => {
|
||||
for (let i = 0; i < root.length; i++) {
|
||||
addPath = "/" + root[i];
|
||||
const dir = fs.readdirSync(basePath + addPath);
|
||||
if (dir.indexOf("main.sol") !== -1) {
|
||||
const fileData = await parseFile(basePath + addPath + "/main.sol");
|
||||
contracts.push(fileData);
|
||||
} else {
|
||||
await parseDir(dir, basePath + addPath, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const mainnet = fs.readdirSync("./contracts/mainnet/connectors/");
|
||||
const polygon = fs.readdirSync("./contracts/polygon/connectors/");
|
||||
let basePathMainnet = "./contracts/mainnet/connectors/";
|
||||
let basePathPolygon = "./contracts/polygon/connectors/";
|
||||
|
||||
const connectorName = params["connector"];
|
||||
|
||||
await parseDir(mainnet, basePathMainnet, "");
|
||||
await parseDir(polygon, basePathPolygon, "");
|
||||
|
||||
if (contracts.indexOf(connectorName) === -1) {
|
||||
throw new Error(
|
||||
"can not find the connector!\n" +
|
||||
"supported connector names are:\n" +
|
||||
contracts.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
const Connector = await ethers.getContractFactory(connectorName);
|
||||
const connector = await Connector.connect(wallet).deploy({
|
||||
gasPrice: ethers.utils.parseUnits(params["gasPrice"], "gwei"),
|
||||
});
|
||||
await connector.deployed();
|
||||
|
||||
console.log(`${connectorName} Deployed: ${connector.address}`);
|
||||
try {
|
||||
await hre.run("verify:verify", {
|
||||
address: connector.address,
|
||||
constructorArguments: [],
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Failed to verify: ${connectorName}@${connector.address}`);
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return connector.address;
|
||||
};
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
console.log("Done successfully");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("error:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
37
scripts/deployment/deployInstaMappingController.ts
Normal file
37
scripts/deployment/deployInstaMappingController.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import hre from "hardhat";
|
||||
const { ethers } = hre;
|
||||
|
||||
async function main() {
|
||||
if (hre.network.name === "mainnet") {
|
||||
console.log("\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort");
|
||||
} else if (hre.network.name === "hardhat") {
|
||||
console.log("\n\n Deploying Contracts to hardhat.");
|
||||
}
|
||||
|
||||
const InstaMappingController = await ethers.getContractFactory(
|
||||
"InstaMappingController"
|
||||
);
|
||||
const instaMappingController = await InstaMappingController.deploy();
|
||||
await instaMappingController.deployed();
|
||||
|
||||
console.log(
|
||||
"InstaMappingController deployed: ",
|
||||
instaMappingController.address
|
||||
);
|
||||
|
||||
if (hre.network.name === "mainnet") {
|
||||
await hre.run("verify:verify", {
|
||||
address: instaMappingController.address,
|
||||
constructorArguments: [],
|
||||
});
|
||||
} else if (hre.network.name === "hardhat") {
|
||||
console.log("Contracts deployed.");
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
34
scripts/deployment/deployMappingContract.ts
Normal file
34
scripts/deployment/deployMappingContract.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import hre from "hardhat";
|
||||
const { ethers } = hre;
|
||||
|
||||
async function main() {
|
||||
if (hre.network.name === "mainnet") {
|
||||
console.log("\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort");
|
||||
} else if (hre.network.name === "hardhat") {
|
||||
console.log("\n\n Deploying Contracts to hardhat.");
|
||||
}
|
||||
|
||||
const mappingContract = "CONTRACT_NAME";
|
||||
|
||||
const InstaProtocolMapping = await ethers.getContractFactory(mappingContract);
|
||||
const instaProtocolMapping = await InstaProtocolMapping.deploy();
|
||||
await instaProtocolMapping.deployed();
|
||||
|
||||
console.log(`${mappingContract} deployed: `, instaProtocolMapping.address);
|
||||
|
||||
if (hre.network.name === "mainnet") {
|
||||
await hre.run("verify:verify", {
|
||||
address: instaProtocolMapping.address,
|
||||
constructorArguments: [],
|
||||
});
|
||||
} else if (hre.network.name === "hardhat") {
|
||||
console.log("Contracts deployed.");
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
const abis = require("./constant/abis");
|
||||
const addresses = require("./constant/addresses");
|
||||
const { web3 } = hre;
|
||||
|
||||
const encodeSpells = require("./encodeSpells.js")
|
||||
|
||||
|
||||
module.exports = function (spells) {
|
||||
const encodeSpellsData = encodeSpells(spells);
|
||||
const targetType = "string[]";
|
||||
let argTypes = [targetType, "bytes[]"];
|
||||
return web3.eth.abi.encodeParameters(argTypes, [
|
||||
encodeSpellsData[0],
|
||||
encodeSpellsData[1],
|
||||
]);
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
const abis = require("./constant/abis");
|
||||
const addresses = require("./constant/addresses");
|
||||
const { web3 } = hre;
|
||||
|
||||
module.exports = function (spells) {
|
||||
const targets = spells.map(a => a.connector)
|
||||
const calldatas = spells.map(a => {
|
||||
const functionName = a.method;
|
||||
// console.log(functionName)
|
||||
const abi = abis.connectors[a.connector].find(b => {
|
||||
return b.name === functionName
|
||||
});
|
||||
// console.log(functionName)
|
||||
if (!abi) throw new Error("Couldn't find function")
|
||||
return web3.eth.abi.encodeFunctionCall(abi, a.args)
|
||||
})
|
||||
return [targets, calldatas]
|
||||
};
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
const addresses = require("./constant/addresses");
|
||||
const abis = require("./constant/abis");
|
||||
|
||||
module.exports = async function() {
|
||||
const [_, __, ___, wallet3] = await ethers.getSigners();
|
||||
const instaIndex = new ethers.Contract(
|
||||
addresses.core.instaIndex,
|
||||
abis.core.instaIndex,
|
||||
wallet3
|
||||
);
|
||||
|
||||
const masterAddress = await instaIndex.master(); // TODO: make it constant?
|
||||
await hre.network.provider.request({
|
||||
method: "hardhat_impersonateAccount",
|
||||
params: [masterAddress],
|
||||
});
|
||||
await wallet3.sendTransaction({
|
||||
to: masterAddress,
|
||||
value: ethers.utils.parseEther("10"),
|
||||
});
|
||||
|
||||
return await ethers.getSigner(masterAddress);
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
const addresses = require("./constant/addresses");
|
||||
const abis = require("../constant/abis");
|
||||
|
||||
const instaImplementations_m1 = require("../../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json")
|
||||
|
||||
module.exports = async function (owner) {
|
||||
const instaIndex = await ethers.getContractAt(abis.core.instaIndex, addresses.core.instaIndex)
|
||||
|
||||
const tx = await instaIndex.build(owner, 2, owner);
|
||||
const receipt = await tx.wait()
|
||||
const event = receipt.events.find(a => a.event === "LogAccountCreated")
|
||||
return await ethers.getContractAt(instaImplementations_m1.abi, event.args.account)
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = {
|
||||
address_zero: "0x0000000000000000000000000000000000000000",
|
||||
eth_addr: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
matic_addr: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
||||
max_value: "115792089237316195423570985008687907853269984665640564039457584007913129639935"
|
||||
};
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
module.exports = {
|
||||
"matic": {
|
||||
"type": "token",
|
||||
"symbol": "MATIC",
|
||||
"name": "Matic",
|
||||
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"decimals": 18
|
||||
},
|
||||
"eth": {
|
||||
"type": "token",
|
||||
"symbol": "ETH",
|
||||
"name": "Ethereum",
|
||||
"address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"decimals": 18
|
||||
},
|
||||
"dai": {
|
||||
"type": "token",
|
||||
"symbol": "DAI",
|
||||
"name": "DAI Stable",
|
||||
"address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
|
||||
"decimals": 18
|
||||
},
|
||||
"usdc": {
|
||||
"type": "token",
|
||||
"symbol": "USDC",
|
||||
"name": "USD Coin",
|
||||
"address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
const abis = require("../constant/abis");
|
||||
const addresses = require("./constant/addresses");
|
||||
|
||||
const hre = require("hardhat");
|
||||
const { ethers, waffle } = hre;
|
||||
const { deployContract } = waffle;
|
||||
const fs = require("fs")
|
||||
|
||||
|
||||
module.exports = async function ({connectorName, contractArtifact, signer, connectors}) {
|
||||
const connectorInstanace = await deployContract(signer, contractArtifact, []);
|
||||
|
||||
await connectors.connect(signer).addConnectors([connectorName], [connectorInstanace.address])
|
||||
|
||||
addresses.connectors[connectorName] = connectorInstanace.address
|
||||
abis.connectors[connectorName] = contractArtifact.abi;
|
||||
|
||||
return connectorInstanace;
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
const abis = require("../constant/abis");
|
||||
const addresses = require("./constant/addresses");
|
||||
const { web3 } = hre;
|
||||
|
||||
module.exports = function (spells) {
|
||||
const targets = spells.map(a => a.connector)
|
||||
const calldatas = spells.map(a => {
|
||||
const functionName = a.method;
|
||||
// console.log(functionName)
|
||||
const abi = abis.connectors[a.connector].find(b => {
|
||||
return b.name === functionName
|
||||
});
|
||||
// console.log(functionName)
|
||||
if (!abi) throw new Error("Couldn't find function")
|
||||
return web3.eth.abi.encodeFunctionCall(abi, a.args)
|
||||
})
|
||||
return [targets, calldatas]
|
||||
};
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
const hre = require("hardhat");
|
||||
const { ethers } = hre;
|
||||
const addresses = require("./constant/addresses");
|
||||
const abis = require("../constant/abis");
|
||||
|
||||
module.exports = async function() {
|
||||
const [_, __, ___, wallet3] = await ethers.getSigners();
|
||||
const instaIndex = new ethers.Contract(
|
||||
addresses.core.instaIndex,
|
||||
abis.core.instaIndex,
|
||||
wallet3
|
||||
);
|
||||
|
||||
const masterAddress = await instaIndex.master(); // TODO: make it constant?
|
||||
await hre.network.provider.request({
|
||||
method: "hardhat_impersonateAccount",
|
||||
params: [masterAddress],
|
||||
});
|
||||
await wallet3.sendTransaction({
|
||||
to: masterAddress,
|
||||
value: ethers.utils.parseEther("10"),
|
||||
});
|
||||
|
||||
return await ethers.getSigner(masterAddress);
|
||||
};
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
const { ethers } = require("hardhat");
|
||||
const impersonateAccounts = require("./impersonate");
|
||||
import { Provider } from "@ethersproject/abstract-provider";
|
||||
import { Signer } from "@ethersproject/abstract-signer";
|
||||
import { ethers } from "hardhat";
|
||||
import { impersonateAccounts } from "./impersonate";
|
||||
|
||||
const mineTx = async (tx) => {
|
||||
const mineTx = async (tx: any) => {
|
||||
await (await tx).wait();
|
||||
};
|
||||
|
||||
const tokenMapping = {
|
||||
const tokenMapping: Record<string, any> = {
|
||||
usdc: {
|
||||
impersonateSigner: "0xfcb19e6a322b27c06842a71e8c725399f049ae3a",
|
||||
address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||
abi: [
|
||||
"function mint(address _to, uint256 _amount) external returns (bool);",
|
||||
],
|
||||
process: async function(owner, to, amt) {
|
||||
process: async function(owner: Signer | Provider, to: any, amt: any) {
|
||||
const contract = new ethers.Contract(this.address, this.abi, owner);
|
||||
|
||||
await mineTx(contract.mint(to, amt));
|
||||
|
|
@ -22,7 +24,7 @@ const tokenMapping = {
|
|||
impersonateSigner: "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503",
|
||||
abi: ["function transfer(address to, uint value)"],
|
||||
address: "0x6b175474e89094c44da98b954eedeac495271d0f",
|
||||
process: async function(owner, to, amt) {
|
||||
process: async function(owner: Signer | Provider, to: any, amt: any) {
|
||||
const contract = new ethers.Contract(this.address, this.abi, owner);
|
||||
await mineTx(contract.transfer(to, amt));
|
||||
},
|
||||
|
|
@ -34,7 +36,7 @@ const tokenMapping = {
|
|||
"function issue(uint amount)",
|
||||
"function transfer(address to, uint value)",
|
||||
],
|
||||
process: async function(owner, address, amt) {
|
||||
process: async function(owner: Signer | Provider, address: any, amt: any) {
|
||||
const contract = new ethers.Contract(this.address, this.abi, owner);
|
||||
|
||||
await mineTx(contract.issue(amt));
|
||||
|
|
@ -45,7 +47,7 @@ const tokenMapping = {
|
|||
impersonateSigner: "0xCA06411bd7a7296d7dbdd0050DFc846E95fEBEB7",
|
||||
address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
||||
abi: ["function mint(address _to, uint256 _amount) public returns (bool)"],
|
||||
process: async function(owner, address, amt) {
|
||||
process: async function(owner: Signer | Provider, address: any, amt: any) {
|
||||
const contract = new ethers.Contract(this.address, this.abi, owner);
|
||||
await mineTx(contract.mint(address, amt));
|
||||
},
|
||||
|
|
@ -54,20 +56,19 @@ const tokenMapping = {
|
|||
impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88",
|
||||
address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb",
|
||||
abi: ["function transfer(address to, uint value)"],
|
||||
process: async function(owner, address, amt) {
|
||||
process: async function(owner: Signer | Provider, address: any, amt: any) {
|
||||
const contract = new ethers.Contract(this.address, this.abi, owner);
|
||||
await mineTx(contract.transfer(address, amt));
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = async (tokenName, address, amt) => {
|
||||
export async function addLiquidity(tokenName: string, address: any, amt: any) {
|
||||
const [signer] = await ethers.getSigners();
|
||||
tokenName = tokenName.toLowerCase();
|
||||
if (!tokenMapping[tokenName]) {
|
||||
throw new Error(
|
||||
"Add liquidity doesn't support the following token: ",
|
||||
tokenName
|
||||
`Add liquidity doesn't support the following token: ${tokenName}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -84,4 +85,4 @@ module.exports = async (tokenName, address, amt) => {
|
|||
});
|
||||
|
||||
await token.process(impersonatedSigner, address, amt);
|
||||
};
|
||||
}
|
||||
11
scripts/tests/arbitrum/addresses.ts
Normal file
11
scripts/tests/arbitrum/addresses.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const addresses: Record<string, any> = {
|
||||
connectors: {
|
||||
// basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7",
|
||||
// auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D",
|
||||
// "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b",
|
||||
},
|
||||
core: {
|
||||
connectorsV2: "0x67fCE99Dd6d8d659eea2a1ac1b8881c57eb6592B",
|
||||
instaIndex: "0x1eE00C305C51Ff3bE60162456A9B533C07cD9288",
|
||||
},
|
||||
};
|
||||
23
scripts/tests/arbitrum/tokens.ts
Normal file
23
scripts/tests/arbitrum/tokens.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export const tokens = {
|
||||
eth: {
|
||||
type: "token",
|
||||
symbol: "ETH",
|
||||
name: "Ethereum",
|
||||
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
decimals: 18,
|
||||
},
|
||||
dai: {
|
||||
type: "token",
|
||||
symbol: "DAI",
|
||||
name: "DAI Stable",
|
||||
address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70",
|
||||
decimals: 18,
|
||||
},
|
||||
usdc: {
|
||||
type: "token",
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664",
|
||||
decimals: 6,
|
||||
},
|
||||
};
|
||||
11
scripts/tests/avalanche/addresses.ts
Normal file
11
scripts/tests/avalanche/addresses.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const addresses: Record<string, any> = {
|
||||
connectors: {
|
||||
// basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7",
|
||||
// auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D",
|
||||
// "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b",
|
||||
},
|
||||
core: {
|
||||
connectorsV2: "0x67fCE99Dd6d8d659eea2a1ac1b8881c57eb6592B",
|
||||
instaIndex: "0x1eE00C305C51Ff3bE60162456A9B533C07cD9288",
|
||||
},
|
||||
};
|
||||
23
scripts/tests/avalanche/tokens.ts
Normal file
23
scripts/tests/avalanche/tokens.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export const tokens = {
|
||||
eth: {
|
||||
type: "token",
|
||||
symbol: "ETH",
|
||||
name: "Ethereum",
|
||||
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
decimals: 18,
|
||||
},
|
||||
dai: {
|
||||
type: "token",
|
||||
symbol: "DAI",
|
||||
name: "DAI Stable",
|
||||
address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70",
|
||||
decimals: 18,
|
||||
},
|
||||
usdc: {
|
||||
type: "token",
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664",
|
||||
decimals: 6,
|
||||
},
|
||||
};
|
||||
29
scripts/tests/buildDSAv2.ts
Normal file
29
scripts/tests/buildDSAv2.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { ethers } from "hardhat";
|
||||
|
||||
import { addresses as addressesPolygon } from "./polygon/addresses";
|
||||
import { addresses as addressesArbitrum } from "./arbitrum/addresses";
|
||||
import { addresses as addressesAvalanche } from "./avalanche/addresses";
|
||||
import { addresses } from "./mainnet/addresses";
|
||||
import { abis } from "../constant/abis";
|
||||
import { abi } from "../../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json";
|
||||
|
||||
function getAddress(network: string | undefined) {
|
||||
if (network === "polygon") return addressesPolygon.core.instaIndex;
|
||||
else if (network === "arbitrum") return addressesArbitrum.core.instaIndex;
|
||||
else if (network === "avalanche") return addressesAvalanche.core.instaIndex;
|
||||
else return addresses.core.instaIndex;
|
||||
}
|
||||
|
||||
export async function buildDSAv2(owner: any) {
|
||||
const instaIndex = await ethers.getContractAt(
|
||||
abis.core.instaIndex,
|
||||
getAddress(String(process.env.networkType))
|
||||
);
|
||||
|
||||
const tx = await instaIndex.build(owner, 2, owner);
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(
|
||||
(a: { event: string }) => a.event === "LogAccountCreated"
|
||||
);
|
||||
return await ethers.getContractAt(abi, event.args.account);
|
||||
}
|
||||
33
scripts/tests/command.ts
Normal file
33
scripts/tests/command.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { execFile, spawn } from "child_process";
|
||||
|
||||
interface ICommand {
|
||||
readonly cmd: string;
|
||||
readonly args: string[];
|
||||
readonly env: {
|
||||
[param: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function execScript(input: ICommand): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let cmdEnv = Object.create(process.env);
|
||||
for (let param in input.env) {
|
||||
cmdEnv[param] = input.env[param];
|
||||
}
|
||||
|
||||
const proc = spawn(input.cmd, [...input.args], {
|
||||
env: cmdEnv,
|
||||
shell: true,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
proc.on("exit", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(code);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(code);
|
||||
});
|
||||
});
|
||||
}
|
||||
47
scripts/tests/deployAndEnableConnector.ts
Normal file
47
scripts/tests/deployAndEnableConnector.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { addresses as addressesPolygon } from "./polygon/addresses";
|
||||
import { addresses } from "./mainnet/addresses";
|
||||
import { abis } from "../constant/abis";
|
||||
import { addresses as addressesArbitrum } from "./arbitrum/addresses";
|
||||
import { addresses as addressesAvalanche } from "./avalanche/addresses";
|
||||
|
||||
import hre from "hardhat";
|
||||
import type { Signer, Contract } from "ethers";
|
||||
import type { ContractJSON } from "ethereum-waffle/dist/esm/ContractJSON";
|
||||
|
||||
const { ethers, waffle } = hre;
|
||||
const { deployContract } = waffle;
|
||||
|
||||
interface DeployInterface {
|
||||
connectorName: string;
|
||||
contractArtifact: ContractJSON;
|
||||
signer: Signer;
|
||||
connectors: Contract;
|
||||
}
|
||||
|
||||
function getAddress(network: string | undefined) {
|
||||
if (network === "polygon") return addressesPolygon;
|
||||
else if (network === "arbitrum") return addressesArbitrum;
|
||||
else if (network === "avalanche") return addressesAvalanche;
|
||||
else return addresses;
|
||||
}
|
||||
|
||||
export async function deployAndEnableConnector(
|
||||
{
|
||||
connectorName,
|
||||
contractArtifact,
|
||||
signer,
|
||||
connectors
|
||||
} : DeployInterface
|
||||
) {
|
||||
const connectorInstanace: Contract = await deployContract(signer, contractArtifact);
|
||||
|
||||
await connectors
|
||||
.connect(signer)
|
||||
.addConnectors([connectorName], [connectorInstanace.address]);
|
||||
|
||||
getAddress(String(process.env.networkType)).connectors[connectorName] =
|
||||
connectorInstanace.address;
|
||||
abis.connectors[connectorName] = contractArtifact.abi;
|
||||
|
||||
return connectorInstanace;
|
||||
}
|
||||
13
scripts/tests/encodeFlashcastData.ts
Normal file
13
scripts/tests/encodeFlashcastData.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import hre from "hardhat";
|
||||
const { web3 } = hre;
|
||||
|
||||
import { encodeSpells } from "./encodeSpells";
|
||||
|
||||
export default function encodeFlashcastData(spells: any) {
|
||||
const encodeSpellsData = encodeSpells(spells);
|
||||
let argTypes = ["string[]", "bytes[]"];
|
||||
return web3.eth.abi.encodeParameters(argTypes, [
|
||||
encodeSpellsData[0],
|
||||
encodeSpellsData[1],
|
||||
]);
|
||||
};
|
||||
17
scripts/tests/encodeSpells.ts
Normal file
17
scripts/tests/encodeSpells.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { web3 } from "hardhat";
|
||||
import { abis } from "../constant/abis";
|
||||
|
||||
export function encodeSpells(spells: any[]) {
|
||||
const targets = spells.map((a) => a.connector);
|
||||
const calldatas = spells.map((a) => {
|
||||
const functionName = a.method;
|
||||
// console.log(functionName)
|
||||
const abi = abis.connectors[a.connector].find((b: { name: any }) => {
|
||||
return b.name === functionName;
|
||||
});
|
||||
// console.log(functionName)
|
||||
if (!abi) throw new Error("Couldn't find function");
|
||||
return web3.eth.abi.encodeFunctionCall(abi, a.args);
|
||||
});
|
||||
return [targets, calldatas];
|
||||
}
|
||||
35
scripts/tests/getMasterSigner.ts
Normal file
35
scripts/tests/getMasterSigner.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { ethers, network } from "hardhat";
|
||||
import { addresses } from "./mainnet/addresses";
|
||||
import { addresses as addressesPolygon } from "./polygon/addresses";
|
||||
import { addresses as addressesArbitrum } from "./arbitrum/addresses";
|
||||
import { addresses as addressesAvalanche } from "./avalanche/addresses";
|
||||
import { abis } from "../constant/abis";
|
||||
|
||||
function getAddress(network: string | undefined) {
|
||||
if (network === "polygon") return addressesPolygon.core.instaIndex;
|
||||
else if (network === "arbitrum") return addressesArbitrum.core.instaIndex;
|
||||
else if (network === "avalanche") return addressesAvalanche.core.instaIndex;
|
||||
else return addresses.core.instaIndex;
|
||||
}
|
||||
|
||||
export async function getMasterSigner() {
|
||||
const [_, __, ___, wallet3] = await ethers.getSigners();
|
||||
const instaIndex = new ethers.Contract(
|
||||
getAddress(String(process.env.networkType)),
|
||||
abis.core.instaIndex,
|
||||
wallet3
|
||||
);
|
||||
|
||||
const masterAddress = await instaIndex.master();
|
||||
await network.provider.request({
|
||||
method: "hardhat_impersonateAccount",
|
||||
params: [masterAddress],
|
||||
});
|
||||
|
||||
await network.provider.send("hardhat_setBalance", [
|
||||
masterAddress,
|
||||
"0x8ac7230489e80000", // 1e19 wei
|
||||
]);
|
||||
|
||||
return await ethers.getSigner(masterAddress);
|
||||
}
|
||||
44
scripts/tests/global-test.ts
Normal file
44
scripts/tests/global-test.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { promises as fs } from "fs";
|
||||
|
||||
import { join } from "path";
|
||||
import { execScript } from "./command";
|
||||
|
||||
let start: number, end: number;
|
||||
|
||||
async function testRunner() {
|
||||
const chain = ["avalanche", "mainnet", "polygon"];
|
||||
start = Date.now();
|
||||
|
||||
for (let ch of chain) {
|
||||
console.log(`📗Running test for %c${ch}: `, "blue");
|
||||
let path: string;
|
||||
const testsPath = join(__dirname, "../../test", ch);
|
||||
await fs.access(testsPath);
|
||||
const availableTests = await fs.readdir(testsPath);
|
||||
|
||||
if (availableTests.length !== 0) {
|
||||
for (let test of availableTests) {
|
||||
path = join(testsPath, test);
|
||||
path += "/*";
|
||||
await execScript({
|
||||
cmd: "npx",
|
||||
args: ["hardhat", "test", path],
|
||||
env: {
|
||||
networkType: ch,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end = Date.now();
|
||||
}
|
||||
|
||||
testRunner()
|
||||
.then(() =>
|
||||
console.log(
|
||||
`🙌 finished running the test, total time taken ${(end - start) /
|
||||
1000} sec`
|
||||
)
|
||||
)
|
||||
.catch((err) => console.error("❌ failed due to error: ", err));
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
const { ethers, network } = require("hardhat");
|
||||
import { ethers, network } from "hardhat";
|
||||
|
||||
module.exports = async (accounts) => {
|
||||
export const impersonateAccounts = async (accounts: any) => {
|
||||
const signers = [];
|
||||
for (const account of accounts) {
|
||||
await network.provider.request({
|
||||
|
|
@ -10,6 +10,5 @@ module.exports = async (accounts) => {
|
|||
|
||||
signers.push(await ethers.getSigner(account));
|
||||
}
|
||||
|
||||
return signers;
|
||||
};
|
||||
11
scripts/tests/mainnet/addresses.ts
Normal file
11
scripts/tests/mainnet/addresses.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const addresses: Record<string, any> = {
|
||||
connectors: {
|
||||
"basic": "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6",
|
||||
"auth": "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9",
|
||||
"INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2",
|
||||
},
|
||||
core: {
|
||||
"connectorsV2": "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11",
|
||||
"instaIndex": "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723",
|
||||
},
|
||||
};
|
||||
23
scripts/tests/mainnet/tokens.ts
Normal file
23
scripts/tests/mainnet/tokens.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export const tokens = {
|
||||
eth: {
|
||||
type: "token",
|
||||
symbol: "ETH",
|
||||
name: "Ethereum",
|
||||
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
decimals: 18,
|
||||
},
|
||||
dai: {
|
||||
type: "token",
|
||||
symbol: "DAI",
|
||||
name: "DAI Stable",
|
||||
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
decimals: 18,
|
||||
},
|
||||
usdc: {
|
||||
type: "token",
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
decimals: 6,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export const addresses: Record<string, any> = {
|
||||
connectors: {
|
||||
basic: "0x1cAF5EC802ca602E98139AD96A8f2B7BC524264E",
|
||||
auth: "0xf6474aD0dA75A0dE15D2c915e601D9f754B9e6fe",
|
||||
|
|
@ -7,4 +7,4 @@ module.exports = {
|
|||
connectorsV2: "0x2A00684bFAb9717C21271E0751BCcb7d2D763c88",
|
||||
instaIndex: "0xA9B99766E6C676Cf1975c0D3166F96C0848fF5ad",
|
||||
},
|
||||
};
|
||||
};
|
||||
30
scripts/tests/polygon/tokens.ts
Normal file
30
scripts/tests/polygon/tokens.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export const tokens = {
|
||||
matic: {
|
||||
type: "token",
|
||||
symbol: "MATIC",
|
||||
name: "Matic",
|
||||
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
decimals: 18,
|
||||
},
|
||||
eth: {
|
||||
type: "token",
|
||||
symbol: "ETH",
|
||||
name: "Ethereum",
|
||||
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
decimals: 18,
|
||||
},
|
||||
dai: {
|
||||
type: "token",
|
||||
symbol: "DAI",
|
||||
name: "DAI Stable",
|
||||
address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
|
||||
decimals: 18,
|
||||
},
|
||||
usdc: {
|
||||
type: "token",
|
||||
symbol: "USDC",
|
||||
name: "USD Coin",
|
||||
address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
|
||||
decimals: 6,
|
||||
},
|
||||
};
|
||||
68
scripts/tests/run-tests.ts
Normal file
68
scripts/tests/run-tests.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import inquirer from "inquirer";
|
||||
import { promises as fs } from "fs";
|
||||
|
||||
import { join } from "path";
|
||||
import { execScript } from "./command";
|
||||
|
||||
let start: number, end: number;
|
||||
|
||||
async function testRunner() {
|
||||
const { chain } = await inquirer.prompt([
|
||||
{
|
||||
name: "chain",
|
||||
message: "What chain do you want to run tests on?",
|
||||
type: "list",
|
||||
choices: ["mainnet", "polygon", "avalanche", "arbitrum"],
|
||||
},
|
||||
]);
|
||||
const testsPath = join(__dirname, "../../test", chain);
|
||||
await fs.access(testsPath);
|
||||
const availableTests = await fs.readdir(testsPath);
|
||||
if (availableTests.length === 0) {
|
||||
throw new Error(`No tests available for ${chain}`);
|
||||
}
|
||||
|
||||
const { testName } = await inquirer.prompt([
|
||||
{
|
||||
name: "testName",
|
||||
message: "For which resolver you want to run the tests?",
|
||||
type: "list",
|
||||
choices: ["all", ...availableTests],
|
||||
},
|
||||
]);
|
||||
start = Date.now();
|
||||
let path: string;
|
||||
if (testName === "all") {
|
||||
for (let test of availableTests) {
|
||||
path = join(testsPath, test);
|
||||
path += "/*";
|
||||
await execScript({
|
||||
cmd: "npx",
|
||||
args: ["hardhat", "test", path],
|
||||
env: {
|
||||
networkType: chain,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
path = join(testsPath, testName);
|
||||
path += "/*";
|
||||
|
||||
await execScript({
|
||||
cmd: "npx",
|
||||
args: ["hardhat", "test", path],
|
||||
env: {
|
||||
networkType: chain,
|
||||
},
|
||||
});
|
||||
}
|
||||
end = Date.now();
|
||||
}
|
||||
|
||||
testRunner()
|
||||
.then(() =>
|
||||
console.log(
|
||||
`🙌 finished the test runner, time taken ${(end - start) / 1000} sec`
|
||||
)
|
||||
)
|
||||
.catch((err) => console.error("❌ failed due to error: ", err));
|
||||
|
|
@ -1,370 +0,0 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const forbiddenStrings = ['selfdestruct']
|
||||
|
||||
const getConnectorsList = async (connectorsRootsDirs) => {
|
||||
try {
|
||||
const connectors = []
|
||||
for (let index = 0; index < connectorsRootsDirs.length; index++) {
|
||||
const dirs = [connectorsRootsDirs[index]]
|
||||
while (dirs.length) {
|
||||
const currentDir = dirs.pop()
|
||||
const subs = fs.readdirSync(currentDir, { withFileTypes: true })
|
||||
for (let index = 0; index < subs.length; index++) {
|
||||
const sub = subs[index]
|
||||
if (sub.isFile() && sub.name === 'main.sol') {
|
||||
connectors.push(currentDir)
|
||||
} else if (sub.isDirectory()) {
|
||||
dirs.push(`${currentDir}/${sub.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return connectors.map(dir => ({ path: dir }))
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkCodeForbidden = async (code, codePath) => {
|
||||
try {
|
||||
const forbidden = []
|
||||
for (let i1 = 0; i1 < forbiddenStrings.length; i1++) {
|
||||
const forbiddenStr = forbiddenStrings[i1]
|
||||
const strs = code.split('\n')
|
||||
for (let i2 = 0; i2 < strs.length; i2++) {
|
||||
if (strs[i2].includes(forbiddenStr)) {
|
||||
forbidden.push(`found '${forbiddenStr}' in ${codePath}:${i2 + 1}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return forbidden
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkForbidden = async (parentPath, codePath = './main.sol') => {
|
||||
try {
|
||||
if (codePath.startsWith('@')) {
|
||||
codePath = path.resolve('node_modules', `./${codePath}`)
|
||||
} else {
|
||||
codePath = path.resolve(parentPath, codePath)
|
||||
}
|
||||
const code = fs.readFileSync(codePath, { encoding: 'utf8' })
|
||||
const forbidden = await checkCodeForbidden(code, codePath)
|
||||
if (code.includes('import')) {
|
||||
const importsPathes = code
|
||||
.split('\n')
|
||||
.filter(str => str.includes('import') && str.includes('from') && str.includes('.sol'))
|
||||
.map(str => str.split('from')[1].replace(/["; ]/gi, ''))
|
||||
for (let index = 0; index < importsPathes.length; index++) {
|
||||
const forbiddenErrors = await checkForbidden(
|
||||
path.parse(codePath).dir,
|
||||
importsPathes[index]
|
||||
)
|
||||
forbidden.push(...forbiddenErrors)
|
||||
}
|
||||
}
|
||||
return codePath.endsWith('main.sol') ? { forbiddenErrors: forbidden, code } : forbidden
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkEvents = async (connector) => {
|
||||
try {
|
||||
const errors = []
|
||||
const warnings = []
|
||||
const eventsPath = `${connector.path}/events.sol`
|
||||
const mainPath = `${connector.path}/main.sol`
|
||||
if (connector.events.length) {
|
||||
const eventNames = []
|
||||
for (let i1 = 0; i1 < connector.mainEvents.length; i1++) {
|
||||
const mainEvent = connector.mainEvents[i1]
|
||||
const name = mainEvent.split('(')[0]
|
||||
eventNames.push(name)
|
||||
const event = connector.events.find(e => e.split('(')[0].split(' ')[1] === name)
|
||||
if (event) {
|
||||
const mainEventArgs = mainEvent.split('(')[1].split(')')[0].split(',').map(a => a.trim())
|
||||
const eventArgs = event.split('(')[1].split(')')[0].split(',').map(a => a.trim())
|
||||
if (mainEventArgs.length !== eventArgs.length) {
|
||||
errors.push(`arguments amount don't match for ${name} at ${mainPath}:${connector.mainEventsLines[i1]}`)
|
||||
continue
|
||||
}
|
||||
for (let i2 = 0; i2 < mainEventArgs.length; i2++) {
|
||||
if (!mainEventArgs[i2].startsWith(eventArgs[i2].split(' ')[0])) {
|
||||
errors.push(`invalid argument #${i2 + 1} for ${name} at ${mainPath}:${connector.mainEventsLines[i1]}`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.push(`event ${name} missing at ${eventsPath}`)
|
||||
}
|
||||
}
|
||||
if (connector.mainEvents.length < connector.events.length) {
|
||||
const deprecatedEvents = connector.events.filter(e => {
|
||||
let used = false
|
||||
for (let index = 0; index < eventNames.length; index++) {
|
||||
if (e.split('(')[0].split(' ')[1] === eventNames[index]) used = true
|
||||
}
|
||||
return !used
|
||||
})
|
||||
warnings.push(`${deprecatedEvents.map(e => e.split('(')[0].split(' ')[1]).join(', ')} event(s) not used at ${connector.path}/main.sol`)
|
||||
}
|
||||
} else {
|
||||
warnings.push(`missing events file for ${connector.path}/main.sol`)
|
||||
}
|
||||
return { eventsErrors: errors, eventsWarnings: warnings }
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getCommments = async (strs) => {
|
||||
try {
|
||||
const comments = []
|
||||
let type
|
||||
for (let index = strs.length - 1; index >= 0; index--) {
|
||||
const str = strs[index]
|
||||
if (!type) {
|
||||
if (str.trim().startsWith('//')) {
|
||||
type = 'single'
|
||||
} else if (str.trim().startsWith('*/')) {
|
||||
type = 'multiple'
|
||||
}
|
||||
}
|
||||
if (type === 'single' && str.trim().startsWith('//')) {
|
||||
comments.push(str.replace(/[/]/gi, '').trim())
|
||||
} else if (type === 'multiple' && !str.trim().startsWith('/**') && !str.trim().startsWith('*/')) {
|
||||
comments.push(str.replace(/[*]/gi, '').trim())
|
||||
} else if (type === 'single' && !str.trim().startsWith('//')) {
|
||||
break
|
||||
} else if (type === 'multiple' && str.trim().startsWith('/**')) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return comments
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const parseCode = async (connector) => {
|
||||
try {
|
||||
const strs = connector.code.split('\n')
|
||||
const events = []
|
||||
const eventsFirstLines = []
|
||||
let func = []
|
||||
let funcs = []
|
||||
let event = []
|
||||
let mainEvents = []
|
||||
let firstLine
|
||||
let mainEventsLines = []
|
||||
for (let index = 0; index < strs.length; index++) {
|
||||
const str = strs[index]
|
||||
if (str.includes('function') && !str.trim().startsWith('//')) {
|
||||
func = [str]
|
||||
firstLine = index + 1
|
||||
} else if (func.length && !str.trim().startsWith('//')) {
|
||||
func.push(str)
|
||||
}
|
||||
if (func.length && str.startsWith(`${func[0].split('function')[0]}}`)) {
|
||||
funcs.push({
|
||||
raw: func.map(str => str.trim()).join(' '),
|
||||
comments: await getCommments(strs.slice(0, firstLine)),
|
||||
firstLine
|
||||
})
|
||||
func = []
|
||||
}
|
||||
}
|
||||
const allPublicFuncs = funcs
|
||||
.filter(({ raw }) => {
|
||||
return raw.includes('external') || raw.includes('public')
|
||||
})
|
||||
.map(f => {
|
||||
const name = f.raw.split('(')[0].split('function')[1].trim()
|
||||
return {
|
||||
...f,
|
||||
name
|
||||
}
|
||||
})
|
||||
funcs = allPublicFuncs
|
||||
.filter(({ raw }) => {
|
||||
if (raw.includes('returns')) {
|
||||
const returns = raw.split('returns')[1].split('(')[1].split(')')[0]
|
||||
return returns.includes('string') && returns.includes('bytes')
|
||||
}
|
||||
return false
|
||||
})
|
||||
.map(f => {
|
||||
const args = f.raw.split('(')[1].split(')')[0].split(',')
|
||||
.map(arg => arg.trim())
|
||||
.filter(arg => arg !== '')
|
||||
return {
|
||||
...f,
|
||||
args
|
||||
}
|
||||
})
|
||||
const eventsPath = `${connector.path}/events.sol`
|
||||
if (fs.existsSync(eventsPath)) {
|
||||
mainEvents = funcs
|
||||
.map(({ raw }) => raw.split('_eventName')[2].trim().split('"')[1])
|
||||
.filter(raw => !!raw)
|
||||
mainEventsLines = mainEvents.map(me => strs.findIndex(str => str.includes(me)) + 1)
|
||||
const eventsCode = fs.readFileSync(eventsPath, { encoding: 'utf8' })
|
||||
const eventsStrs = eventsCode.split('\n')
|
||||
for (let index = 0; index < eventsStrs.length; index++) {
|
||||
const str = eventsStrs[index]
|
||||
if (str.includes('event')) {
|
||||
event = [str]
|
||||
firstLine = index + 1
|
||||
} else if (event.length && !str.trim().startsWith('//')) {
|
||||
event.push(str)
|
||||
}
|
||||
if (event.length && str.includes(')')) {
|
||||
events.push(event.map(str => str.trim()).join(' '))
|
||||
eventsFirstLines.push(firstLine)
|
||||
event = []
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
...connector,
|
||||
events,
|
||||
eventsFirstLines,
|
||||
mainEvents,
|
||||
mainEventsLines,
|
||||
funcs,
|
||||
allPublicFuncs
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkComments = async (connector) => {
|
||||
try {
|
||||
const errors = []
|
||||
for (let i1 = 0; i1 < connector.funcs.length; i1++) {
|
||||
const func = connector.funcs[i1]
|
||||
for (let i2 = 0; i2 < func.args.length; i2++) {
|
||||
const argName = func.args[i2].split(' ').pop()
|
||||
if (!func.comments.some(
|
||||
comment => comment.startsWith('@param') && comment.split(' ')[1] === argName
|
||||
)) {
|
||||
errors.push(`argument ${argName} has no @param for function ${func.name} at ${connector.path}/main.sol:${func.firstLine}`)
|
||||
}
|
||||
}
|
||||
const reqs = ['@dev', '@notice']
|
||||
for (let i3 = 0; i3 < reqs.length; i3++) {
|
||||
if (!func.comments.some(comment => comment.startsWith(reqs[i3]))) {
|
||||
errors.push(`no ${reqs[i3]} for function ${func.name} at ${connector.path}/main.sol:${func.firstLine}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkPublicFuncs = async (connector) => {
|
||||
try {
|
||||
const errors = []
|
||||
for (let i1 = 0; i1 < connector.allPublicFuncs.length; i1++) {
|
||||
const { raw, firstLine, name } = connector.allPublicFuncs[i1]
|
||||
if (!raw.includes('payable')) {
|
||||
errors.push(`public function ${name} is not payable at ${connector.path}/main.sol:${firstLine}`)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkName = async (connector) => {
|
||||
try {
|
||||
const strs = connector.code.split('\n')
|
||||
let haveName = false
|
||||
for (let index = strs.length - 1; index > 0; index--) {
|
||||
const str = strs[index]
|
||||
if (str.includes('string') && str.includes('public') && str.includes('name = ')) {
|
||||
haveName = true
|
||||
}
|
||||
}
|
||||
return haveName ? [] : [`name variable missing in ${connector.path}/main.sol`]
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const checkHeadComments = async (connector) => {
|
||||
try {
|
||||
const errors = []
|
||||
const strs = connector.code.split('\n')
|
||||
let haveTitle = false
|
||||
let haveDev = false
|
||||
for (let index = 0; index < strs.length; index++) {
|
||||
if (!strs[index].includes('{')) {
|
||||
if (strs[index].includes('@title')) haveTitle = true
|
||||
if (strs[index].includes('@dev')) haveDev = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!haveTitle) errors.push(`@title missing in ${connector.path}/main.sol`)
|
||||
if (!haveDev) errors.push(`@dev missing in ${connector.path}/main.sol`)
|
||||
return errors
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkMain () {
|
||||
try {
|
||||
const connectorsRootsDirsDefault = ['mainnet', 'polygon'].map(v=> `contracts/${v}/connectors`)
|
||||
const customPathArg = process.argv.find(a => a.startsWith('connector='))
|
||||
const connectorsRootsDirs = customPathArg
|
||||
? [customPathArg.slice(10)]
|
||||
: connectorsRootsDirsDefault
|
||||
const errors = []
|
||||
const warnings = []
|
||||
const connectors = await getConnectorsList(connectorsRootsDirs)
|
||||
for (let index = 0; index < connectors.length; index++) {
|
||||
const { forbiddenErrors, code } = await checkForbidden(connectors[index].path)
|
||||
connectors[index].code = code
|
||||
connectors[index] = await parseCode(connectors[index])
|
||||
const { eventsErrors, eventsWarnings } = await checkEvents(connectors[index])
|
||||
const commentsErrors = await checkComments(connectors[index])
|
||||
const nameErrors = await checkName(connectors[index])
|
||||
const headCommentsErrors = await checkHeadComments(connectors[index])
|
||||
const publicFuncsErrors = await checkPublicFuncs(connectors[index])
|
||||
|
||||
errors.push(...forbiddenErrors)
|
||||
errors.push(...eventsErrors)
|
||||
errors.push(...commentsErrors)
|
||||
errors.push(...nameErrors)
|
||||
errors.push(...headCommentsErrors)
|
||||
errors.push(...publicFuncsErrors)
|
||||
warnings.push(...eventsWarnings)
|
||||
}
|
||||
if (errors.length) {
|
||||
console.log('\x1b[31m%s\x1b[0m', `Total errors: ${errors.length}`)
|
||||
errors.forEach(error => console.log('\x1b[31m%s\x1b[0m', error))
|
||||
} else {
|
||||
console.log('\x1b[32m%s\x1b[0m', 'No Errors Found')
|
||||
}
|
||||
if (warnings.length) {
|
||||
console.log('\x1b[33m%s\x1b[0m', `Total warnings: ${warnings.length}`)
|
||||
warnings.forEach(warning => console.log('\x1b[33m%s\x1b[0m', warning))
|
||||
} else {
|
||||
console.log('\x1b[32m%s\x1b[0m', 'No Warnings Found')
|
||||
}
|
||||
if (errors.length) return Promise.reject(errors.join('\n'))
|
||||
} catch (error) {
|
||||
console.error('check execution error:', error)
|
||||
}
|
||||
}
|
||||
module.exports = checkMain
|
||||
448
status-checks/check.ts
Normal file
448
status-checks/check.ts
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
const forbiddenStrings: any = ["selfdestruct"];
|
||||
|
||||
const getConnectorsList= async (connectorsRootsDirs: string | any[]): Promise<Record<string, any>> => {
|
||||
try {
|
||||
const connectors = [];
|
||||
for (let index = 0; index < connectorsRootsDirs.length; index++) {
|
||||
const dirs = [connectorsRootsDirs[index]];
|
||||
while (dirs.length) {
|
||||
const currentDir = dirs.pop();
|
||||
const subs = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||
for (let index = 0; index < subs.length; index++) {
|
||||
const sub = subs[index];
|
||||
if (sub.isFile() && sub.name === "main.sol") {
|
||||
connectors.push(currentDir);
|
||||
} else if (sub.isDirectory()) {
|
||||
dirs.push(`${currentDir}/${sub.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return connectors.map((dir) => ({ path: dir }));
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkCodeForbidden = async (code: string, codePath: string) => {
|
||||
try {
|
||||
const forbidden = [];
|
||||
for (let i1 = 0; i1 < forbiddenStrings.length; i1++) {
|
||||
const forbiddenStr = forbiddenStrings[i1];
|
||||
const strs = code.split("\n");
|
||||
for (let i2 = 0; i2 < strs.length; i2++) {
|
||||
if (strs[i2].includes(forbiddenStr)) {
|
||||
forbidden.push(`found '${forbiddenStr}' in ${codePath}:${i2 + 1}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return forbidden;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkForbidden = async (parentPath: string, codePath = "./main.sol") => {
|
||||
try {
|
||||
if (codePath.startsWith("@")) {
|
||||
codePath = path.resolve("node_modules", `./${codePath}`);
|
||||
} else {
|
||||
codePath = path.resolve(parentPath, codePath);
|
||||
}
|
||||
const code = fs.readFileSync(codePath, { encoding: "utf8" });
|
||||
const forbidden: any = await checkCodeForbidden(code, codePath);
|
||||
if (code.includes("import")) {
|
||||
const importsPathes = code
|
||||
.split("\n")
|
||||
.filter(
|
||||
(str) =>
|
||||
str.includes("import") &&
|
||||
str.includes("from") &&
|
||||
str.includes(".sol")
|
||||
)
|
||||
.map((str) => str.split("from")[1].replace(/["; ]/gi, ""));
|
||||
for (let index = 0; index < importsPathes.length; index++) {
|
||||
const forbiddenErrors = await checkForbidden(
|
||||
path.parse(codePath).dir,
|
||||
importsPathes[index]
|
||||
);
|
||||
forbidden.push(...forbiddenErrors);
|
||||
}
|
||||
}
|
||||
return codePath.endsWith("main.sol")
|
||||
? { forbiddenErrors: forbidden, code }
|
||||
: forbidden;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkEvents = async (connector: {
|
||||
path: any;
|
||||
events?: any;
|
||||
mainEvents?: any;
|
||||
mainEventsLines?: any;
|
||||
}) => {
|
||||
try {
|
||||
const errors = [];
|
||||
const warnings = [];
|
||||
const eventsPath = `${connector.path}/events.sol`;
|
||||
const mainPath = `${connector.path}/main.sol`;
|
||||
if (connector.events.length) {
|
||||
const eventNames = [];
|
||||
for (let i1 = 0; i1 < connector.mainEvents.length; i1++) {
|
||||
const mainEvent = connector.mainEvents[i1];
|
||||
const name = mainEvent.split("(")[0];
|
||||
eventNames.push(name);
|
||||
const event = connector.events.find(
|
||||
(e: string) => e.split("(")[0].split(" ")[1] === name
|
||||
);
|
||||
if (event) {
|
||||
const mainEventArgs = mainEvent
|
||||
.split("(")[1]
|
||||
.split(")")[0]
|
||||
.split(",")
|
||||
.map((a: string) => a.trim());
|
||||
const eventArgs = event
|
||||
.split("(")[1]
|
||||
.split(")")[0]
|
||||
.split(",")
|
||||
.map((a: string) => a.trim());
|
||||
if (mainEventArgs.length !== eventArgs.length) {
|
||||
errors.push(
|
||||
`arguments amount don't match for ${name} at ${mainPath}:${connector.mainEventsLines[i1]}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
for (let i2 = 0; i2 < mainEventArgs.length; i2++) {
|
||||
if (!mainEventArgs[i2].startsWith(eventArgs[i2].split(" ")[0])) {
|
||||
errors.push(
|
||||
`invalid argument #${i2 + 1} for ${name} at ${mainPath}:${
|
||||
connector.mainEventsLines[i1]
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.push(`event ${name} missing at ${eventsPath}`);
|
||||
}
|
||||
}
|
||||
if (connector.mainEvents.length < connector.events.length) {
|
||||
const deprecatedEvents = connector.events.filter((e) => {
|
||||
let used = false;
|
||||
for (let index = 0; index < eventNames.length; index++) {
|
||||
if (e.split("(")[0].split(" ")[1] === eventNames[index])
|
||||
used = true;
|
||||
}
|
||||
return !used;
|
||||
});
|
||||
warnings.push(
|
||||
`${deprecatedEvents
|
||||
.map((e: string) => e.split("(")[0].split(" ")[1])
|
||||
.join(", ")} event(s) not used at ${connector.path}/main.sol`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
warnings.push(`missing events file for ${connector.path}/main.sol`);
|
||||
}
|
||||
return { eventsErrors: errors, eventsWarnings: warnings };
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const getCommments = async (strs: string | any[]) => {
|
||||
try {
|
||||
const comments = [];
|
||||
let type: string;
|
||||
for (let index = strs.length - 1; index >= 0; index--) {
|
||||
const str = strs[index];
|
||||
if (!type) {
|
||||
if (str.trim().startsWith("//")) {
|
||||
type = "single";
|
||||
} else if (str.trim().startsWith("*/")) {
|
||||
type = "multiple";
|
||||
}
|
||||
}
|
||||
if (type === "single" && str.trim().startsWith("//")) {
|
||||
comments.push(str.replace(/[/]/gi, "").trim());
|
||||
} else if (
|
||||
type === "multiple" &&
|
||||
!str.trim().startsWith("/**") &&
|
||||
!str.trim().startsWith("*/")
|
||||
) {
|
||||
comments.push(str.replace(/[*]/gi, "").trim());
|
||||
} else if (type === "single" && !str.trim().startsWith("//")) {
|
||||
break;
|
||||
} else if (type === "multiple" && str.trim().startsWith("/**")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return comments;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const parseCode = async (connector: { path: any; code?: any }) => {
|
||||
try {
|
||||
const strs = connector.code.split("\n");
|
||||
const events = [];
|
||||
const eventsFirstLines = [];
|
||||
let func = [];
|
||||
let funcs = [];
|
||||
let event = [];
|
||||
let mainEvents = [];
|
||||
let firstLine: number;
|
||||
let mainEventsLines = [];
|
||||
for (let index = 0; index < strs.length; index++) {
|
||||
const str = strs[index];
|
||||
if (str.includes("function") && !str.trim().startsWith("//")) {
|
||||
func = [str];
|
||||
firstLine = index + 1;
|
||||
} else if (func.length && !str.trim().startsWith("//")) {
|
||||
func.push(str);
|
||||
}
|
||||
if (func.length && str.startsWith(`${func[0].split("function")[0]}}`)) {
|
||||
funcs.push({
|
||||
raw: func.map((str) => str.trim()).join(" "),
|
||||
comments: await getCommments(strs.slice(0, firstLine)),
|
||||
firstLine,
|
||||
});
|
||||
func = [];
|
||||
}
|
||||
}
|
||||
const allPublicFuncs = funcs
|
||||
.filter(({ raw }) => {
|
||||
return raw.includes("external") || raw.includes("public");
|
||||
})
|
||||
.map((f) => {
|
||||
const name = f.raw
|
||||
.split("(")[0]
|
||||
.split("function")[1]
|
||||
.trim();
|
||||
return {
|
||||
...f,
|
||||
name,
|
||||
};
|
||||
});
|
||||
funcs = allPublicFuncs
|
||||
.filter(({ raw }) => {
|
||||
if (raw.includes("returns")) {
|
||||
const returns = raw
|
||||
.split("returns")[1]
|
||||
.split("(")[1]
|
||||
.split(")")[0];
|
||||
return returns.includes("string") && returns.includes("bytes");
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map((f) => {
|
||||
const args = f.raw
|
||||
.split("(")[1]
|
||||
.split(")")[0]
|
||||
.split(",")
|
||||
.map((arg) => arg.trim())
|
||||
.filter((arg) => arg !== "");
|
||||
return {
|
||||
...f,
|
||||
args,
|
||||
};
|
||||
});
|
||||
const eventsPath = `${connector.path}/events.sol`;
|
||||
if (fs.existsSync(eventsPath)) {
|
||||
mainEvents = funcs
|
||||
.map(
|
||||
({ raw }) =>
|
||||
raw
|
||||
.split("_eventName")[2]
|
||||
.trim()
|
||||
.split('"')[1]
|
||||
)
|
||||
.filter((raw) => !!raw);
|
||||
mainEventsLines = mainEvents.map(
|
||||
(me) => strs.findIndex((str: string | any[]) => str.includes(me)) + 1
|
||||
);
|
||||
const eventsCode = fs.readFileSync(eventsPath, { encoding: "utf8" });
|
||||
const eventsStrs = eventsCode.split("\n");
|
||||
for (let index = 0; index < eventsStrs.length; index++) {
|
||||
const str = eventsStrs[index];
|
||||
if (str.includes("event")) {
|
||||
event = [str];
|
||||
firstLine = index + 1;
|
||||
} else if (event.length && !str.trim().startsWith("//")) {
|
||||
event.push(str);
|
||||
}
|
||||
if (event.length && str.includes(")")) {
|
||||
events.push(event.map((str) => str.trim()).join(" "));
|
||||
eventsFirstLines.push(firstLine);
|
||||
event = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
...connector,
|
||||
events,
|
||||
eventsFirstLines,
|
||||
mainEvents,
|
||||
mainEventsLines,
|
||||
funcs,
|
||||
allPublicFuncs,
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkComments = async (connector) => {
|
||||
try {
|
||||
const errors = [];
|
||||
for (let i1 = 0; i1 < connector.funcs.length; i1++) {
|
||||
const func = connector.funcs[i1];
|
||||
for (let i2 = 0; i2 < func.args.length; i2++) {
|
||||
const argName = func.args[i2].split(" ").pop();
|
||||
if (
|
||||
!func.comments.some(
|
||||
(comment: string) =>
|
||||
comment.startsWith("@param") && comment.split(" ")[1] === argName
|
||||
)
|
||||
) {
|
||||
errors.push(
|
||||
`argument ${argName} has no @param for function ${func.name} at ${connector.path}/main.sol:${func.firstLine}`
|
||||
);
|
||||
}
|
||||
}
|
||||
const reqs = ["@dev", "@notice"];
|
||||
for (let i3 = 0; i3 < reqs.length; i3++) {
|
||||
if (!func.comments.some((comment) => comment.startsWith(reqs[i3]))) {
|
||||
errors.push(
|
||||
`no ${reqs[i3]} for function ${func.name} at ${connector.path}/main.sol:${func.firstLine}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkPublicFuncs = async (connector: {
|
||||
path: any;
|
||||
allPublicFuncs?: any;
|
||||
}) => {
|
||||
try {
|
||||
const errors = [];
|
||||
for (let i1 = 0; i1 < connector.allPublicFuncs.length; i1++) {
|
||||
const { raw, firstLine, name } = connector.allPublicFuncs[i1];
|
||||
if (!raw.includes("payable")) {
|
||||
errors.push(
|
||||
`public function ${name} is not payable at ${connector.path}/main.sol:${firstLine}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkName = async (connector: { path: any; code?: any }) => {
|
||||
try {
|
||||
const strs = connector.code.split("\n");
|
||||
let haveName = false;
|
||||
for (let index = strs.length - 1; index > 0; index--) {
|
||||
const str = strs[index];
|
||||
if (
|
||||
str.includes("string") &&
|
||||
str.includes("public") &&
|
||||
str.includes("name = ")
|
||||
) {
|
||||
haveName = true;
|
||||
}
|
||||
}
|
||||
return haveName
|
||||
? []
|
||||
: [`name variable missing in ${connector.path}/main.sol`];
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkHeadComments = async (connector: { path: any; code?: any }) => {
|
||||
try {
|
||||
const errors = [];
|
||||
const strs = connector.code.split("\n");
|
||||
let haveTitle = false;
|
||||
let haveDev = false;
|
||||
for (let index = 0; index < strs.length; index++) {
|
||||
if (!strs[index].includes("{")) {
|
||||
if (strs[index].includes("@title")) haveTitle = true;
|
||||
if (strs[index].includes("@dev")) haveDev = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!haveTitle) errors.push(`@title missing in ${connector.path}/main.sol`);
|
||||
if (!haveDev) errors.push(`@dev missing in ${connector.path}/main.sol`);
|
||||
return errors;
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
async function checkMain() {
|
||||
try {
|
||||
const connectorsRootsDirsDefault = ["mainnet", "polygon"].map(
|
||||
(v) => `contracts/${v}/connectors`
|
||||
);
|
||||
const customPathArg = process.argv.find((a) => a.startsWith("connector="));
|
||||
const connectorsRootsDirs = customPathArg
|
||||
? [customPathArg.slice(10)]
|
||||
: connectorsRootsDirsDefault;
|
||||
const errors = [];
|
||||
const warnings = [];
|
||||
const connectors = await getConnectorsList(connectorsRootsDirs);
|
||||
for (let index = 0; index < connectors.length; index++) {
|
||||
const { forbiddenErrors, code } = await checkForbidden(
|
||||
connectors[index].path
|
||||
);
|
||||
connectors[index].code = code;
|
||||
connectors[index] = await parseCode(connectors[index]);
|
||||
const { eventsErrors, eventsWarnings } = await checkEvents(
|
||||
connectors[index]
|
||||
);
|
||||
const commentsErrors = await checkComments(connectors[index]);
|
||||
const nameErrors = await checkName(connectors[index]);
|
||||
const headCommentsErrors = await checkHeadComments(connectors[index]);
|
||||
const publicFuncsErrors = await checkPublicFuncs(connectors[index]);
|
||||
|
||||
errors.push(...forbiddenErrors);
|
||||
errors.push(...eventsErrors);
|
||||
errors.push(...commentsErrors);
|
||||
errors.push(...nameErrors);
|
||||
errors.push(...headCommentsErrors);
|
||||
errors.push(...publicFuncsErrors);
|
||||
warnings.push(...eventsWarnings);
|
||||
}
|
||||
if (errors.length) {
|
||||
console.log("\x1b[31m%s\x1b[0m", `Total errors: ${errors.length}`);
|
||||
errors.forEach((error) => console.log("\x1b[31m%s\x1b[0m", error));
|
||||
} else {
|
||||
console.log("\x1b[32m%s\x1b[0m", "No Errors Found");
|
||||
}
|
||||
if (warnings.length) {
|
||||
console.log("\x1b[33m%s\x1b[0m", `Total warnings: ${warnings.length}`);
|
||||
warnings.forEach((warning) => console.log("\x1b[33m%s\x1b[0m", warning));
|
||||
} else {
|
||||
console.log("\x1b[32m%s\x1b[0m", "No Warnings Found");
|
||||
}
|
||||
if (errors.length) return Promise.reject(errors.join("\n"));
|
||||
} catch (error) {
|
||||
console.error("check execution error:", error);
|
||||
}
|
||||
}
|
||||
export default checkMain;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user