config: contract dependencies for testing

This commit is contained in:
gitpusha 2020-11-11 17:06:07 +01:00 committed by Luis Schliesske
parent 5e20f89951
commit 40de5ce04c
13 changed files with 2673 additions and 25 deletions

View File

@ -6,7 +6,7 @@ jobs: # a collection of steps
- image: circleci/node:12.16.2 # ...with this image as the primary container; this is where all `steps` will run
steps: # a collection of executable commands
- checkout # special step to check out source code to working directory
- restore_cache: # special step to restore the dependency cache
- restore_cache: # restore the dependency cache
# Read about caching dependencies: https://circleci.com/docs/2.0/caching/
name: Restore Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
@ -18,30 +18,37 @@ jobs: # a collection of steps
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- restore_cache: # restore hardhat compile cache
name: Restore Hardhat Compilation Cache
key: solidity-files-cache-
- run: # Compile
name: Compile
command: yarn compile
- save_cache: # special step to save the hardhat compile cache
name: Save Hardhat Compilation Cache
key: solidity-files-cache-{{ checksum "./cache/solidity-files-cache.json" }}
paths:
- ./cache/solidity-files-cache.json
- run: # Formatting
name: Prettier Check
command: yarn prettier --check .
- run: # Linting
name: ESLint
command: yarn lint
- restore_cache: # special step to restore the Hardhat Network Fork Cache
# Read about caching dependencies: https://circleci.com/docs/2.0/caching/
- restore_cache: # restore the Hardhat Network Fork Cache
name: Restore Hardhat Network Fork Cache
key: hardhat-network-fork
key: v1-hardhat-network-fork-cache
- run: # Tests
name: Tests using hardhat mainnet fork and gas reporter
command: REPORT_GAS=1 yarn test
- save_cache: # special step to save the Hardhat Network Fork cache
name: Save Hardhat Network Fork Cache
key: v1-hardhat-network-fork-cache
paths:
- ./cache/hardhat-network-fork
- run: # Codechecks
name: Codechecks gas reporting
command: npx codechecks
- save_cache: # special step to save the Hardhat Network Fork cache
name: Save Hardhat Network Fork Cache
key: hardhat-network-fork
paths:
- ./cache/hardhat-network-fork
# - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
# path: coverage
# prefix: coverage

View File

@ -1,4 +1,5 @@
node_modules/
contracts/constants
contracts/dependencies
contracts/functions
contracts/vendor

View File

@ -4,7 +4,7 @@ pragma solidity 0.7.4;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {SafeMath} from "@gelatonetwork/core/contracts/external/SafeMath.sol";
import {SafeMath} from "../../../vendor/SafeMath.sol";
import {
IGelatoCore
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.6.10;
import "@gelatonetwork/core/contracts/gelato_core/GelatoCore.sol";
// solhint-disable-next-line no-empty-blocks
contract GelatoTestDependencies {
}

View File

@ -0,0 +1,685 @@
pragma solidity ^0.6.0;
interface CTokenInterface {
function mint(uint256 mintAmount) external returns (uint256);
function redeem(uint256 redeemTokens) external returns (uint256);
function borrow(uint256 borrowAmount) external returns (uint256);
function repayBorrow(uint256 repayAmount) external returns (uint256);
function repayBorrowBehalf(address borrower, uint256 repayAmount)
external
returns (uint256); // For ERC20
function liquidateBorrow(
address borrower,
uint256 repayAmount,
address cTokenCollateral
) external returns (uint256);
function borrowBalanceCurrent(address account) external returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
function exchangeRateCurrent() external returns (uint256);
function balanceOf(address owner) external view returns (uint256 balance);
}
interface CETHInterface {
function mint() external payable;
function repayBorrow() external payable;
function repayBorrowBehalf(address borrower) external payable;
function liquidateBorrow(address borrower, address cTokenCollateral)
external
payable;
}
interface TokenInterface {
function allowance(address, address) external view returns (uint256);
function balanceOf(address) external view returns (uint256);
function approve(address, uint256) external;
function transfer(address, uint256) external returns (bool);
function transferFrom(
address,
address,
uint256
) external returns (bool);
}
interface ComptrollerInterface {
function enterMarkets(address[] calldata cTokens)
external
returns (uint256[] memory);
function exitMarket(address cTokenAddress) external returns (uint256);
function getAssetsIn(address account)
external
view
returns (address[] memory);
function getAccountLiquidity(address account)
external
view
returns (
uint256,
uint256,
uint256
);
function claimComp(address) external;
}
interface InstaMapping {
function cTokenMapping(address) external view returns (address);
}
interface MemoryInterface {
function getUint(uint256 _id) external returns (uint256 _num);
function setUint(uint256 _id, uint256 _val) external;
}
interface EventInterface {
function emitEvent(
uint256 _connectorType,
uint256 _connectorID,
bytes32 _eventCode,
bytes calldata _eventData
) external;
}
contract DSMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "math-not-safe");
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "math-not-safe");
}
uint256 constant WAD = 10**18;
function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = add(mul(x, WAD), y / 2) / y;
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
}
contract Helpers is DSMath {
/**
* @dev Return ethereum address
*/
function getAddressETH() internal pure returns (address) {
return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH Address
}
/**
* @dev Return Memory Variable Address
*/
function getMemoryAddr() internal pure returns (address) {
return 0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F; // InstaMemory Address
}
/**
* @dev Return InstaEvent Address.
*/
function getEventAddr() internal pure returns (address) {
return 0x2af7ea6Cb911035f3eb1ED895Cb6692C39ecbA97; // InstaEvent Address
}
/**
* @dev Get Uint value from InstaMemory Contract.
*/
function getUint(uint256 getId, uint256 val)
internal
returns (uint256 returnVal)
{
returnVal = getId == 0
? val
: MemoryInterface(getMemoryAddr()).getUint(getId);
}
/**
* @dev Set Uint value in InstaMemory Contract.
*/
function setUint(uint256 setId, uint256 val) internal {
if (setId != 0) MemoryInterface(getMemoryAddr()).setUint(setId, val);
}
/**
* @dev Connector Details
*/
function connectorID() public pure returns (uint256 _type, uint256 _id) {
(_type, _id) = (1, 24);
}
}
contract CompoundHelpers is Helpers {
/**
* @dev Return Compound Comptroller Address
*/
function getComptrollerAddress() internal pure returns (address) {
return 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;
}
/**
* @dev Return COMP Token Address.
*/
function getCompTokenAddress() internal pure returns (address) {
return 0xc00e94Cb662C3520282E6f5717214004A7f26888;
}
/**
* @dev Return InstaDApp Mapping Addresses
*/
function getMappingAddr() internal pure returns (address) {
return 0xe81F70Cc7C0D46e12d70efc60607F16bbD617E88; // InstaMapping Address
}
/**
* @dev enter compound market
*/
function enterMarket(address cToken) internal {
ComptrollerInterface troller = ComptrollerInterface(
getComptrollerAddress()
);
address[] memory markets = troller.getAssetsIn(address(this));
bool isEntered = false;
for (uint256 i = 0; i < markets.length; i++) {
if (markets[i] == cToken) {
isEntered = true;
}
}
if (!isEntered) {
address[] memory toEnter = new address[](1);
toEnter[0] = cToken;
troller.enterMarkets(toEnter);
}
}
}
contract BasicResolver is CompoundHelpers {
event LogDeposit(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogWithdraw(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogBorrow(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
event LogPayback(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
/**
* @dev Deposit ETH/ERC20_Token.
* @param token token address to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to deposit.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function deposit(
address token,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
enterMarket(cToken);
if (token == getAddressETH()) {
_amt = _amt == uint256(-1) ? address(this).balance : _amt;
CETHInterface(cToken).mint.value(_amt)();
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint256(-1)
? tokenContract.balanceOf(address(this))
: _amt;
tokenContract.approve(cToken, _amt);
require(CTokenInterface(cToken).mint(_amt) == 0, "borrow-failed");
}
setUint(setId, _amt);
emit LogDeposit(token, cToken, _amt, getId, setId);
bytes32 _eventCode = keccak256(
"LogDeposit(address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Withdraw ETH/ERC20_Token.
* @param token token address to withdraw.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to withdraw.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function withdraw(
address token,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
CTokenInterface cTokenContract = CTokenInterface(cToken);
if (_amt == uint256(-1)) {
TokenInterface tokenContract = TokenInterface(token);
uint256 initialBal = token == getAddressETH()
? address(this).balance
: tokenContract.balanceOf(address(this));
require(
cTokenContract.redeem(
cTokenContract.balanceOf(address(this))
) == 0,
"full-withdraw-failed"
);
uint256 finalBal = token == getAddressETH()
? address(this).balance
: tokenContract.balanceOf(address(this));
_amt = finalBal - initialBal;
} else {
require(
cTokenContract.redeemUnderlying(_amt) == 0,
"withdraw-failed"
);
}
setUint(setId, _amt);
emit LogWithdraw(token, cToken, _amt, getId, setId);
bytes32 _eventCode = keccak256(
"LogWithdraw(address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Borrow ETH/ERC20_Token.
* @param token token address to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to borrow.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function borrow(
address token,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
enterMarket(cToken);
require(CTokenInterface(cToken).borrow(_amt) == 0, "borrow-failed");
setUint(setId, _amt);
emit LogBorrow(token, cToken, _amt, getId, setId);
bytes32 _eventCode = keccak256(
"LogBorrow(address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Payback borrowed ETH/ERC20_Token.
* @param token token address to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to payback.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function payback(
address token,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
CTokenInterface cTokenContract = CTokenInterface(cToken);
_amt = _amt == uint256(-1)
? cTokenContract.borrowBalanceCurrent(address(this))
: _amt;
if (token == getAddressETH()) {
require(address(this).balance >= _amt, "not-enough-eth");
CETHInterface(cToken).repayBorrow.value(_amt)();
} else {
TokenInterface tokenContract = TokenInterface(token);
require(
tokenContract.balanceOf(address(this)) >= _amt,
"not-enough-token"
);
tokenContract.approve(cToken, _amt);
require(cTokenContract.repayBorrow(_amt) == 0, "repay-failed.");
}
setUint(setId, _amt);
emit LogPayback(token, cToken, _amt, getId, setId);
bytes32 _eventCode = keccak256(
"LogPayback(address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
}
contract ExtraResolver is BasicResolver {
event LogClaimedComp(uint256 compAmt, uint256 setId);
event LogDepositCToken(
address indexed token,
address cToken,
uint256 tokenAmt,
uint256 cTokenAmt,
uint256 getId,
uint256 setId
);
event LogWithdrawCToken(
address indexed token,
address cToken,
uint256 cTokenAmt,
uint256 getId,
uint256 setId
);
event LogLiquidate(
address indexed borrower,
address indexed tokenToPay,
address indexed tokenInReturn,
uint256 tokenAmt,
uint256 getId,
uint256 setId
);
/**
* @dev Claim Accrued COMP Token.
* @param setId Set ctoken amount at this ID in `InstaMemory` Contract.
*/
function ClaimComp(uint256 setId) external payable {
TokenInterface compToken = TokenInterface(getCompTokenAddress());
uint256 intialBal = compToken.balanceOf(address(this));
ComptrollerInterface(getComptrollerAddress()).claimComp(address(this));
uint256 finalBal = compToken.balanceOf(address(this));
uint256 amt = sub(finalBal, intialBal);
setUint(setId, amt);
emit LogClaimedComp(amt, setId);
bytes32 _eventCode = keccak256("LogClaimedComp(uint256,uint256)");
bytes memory _eventParam = abi.encode(amt, setId);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Deposit ETH/ERC20_Token.
* @param token token address to depositCToken.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to depositCToken.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set ctoken amount at this ID in `InstaMemory` Contract.
*/
function depositCToken(
address token,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
enterMarket(cToken);
CTokenInterface ctokenContract = CTokenInterface(cToken);
uint256 initialBal = ctokenContract.balanceOf(address(this));
if (token == getAddressETH()) {
_amt = _amt == uint256(-1) ? address(this).balance : _amt;
CETHInterface(cToken).mint.value(_amt)();
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint256(-1)
? tokenContract.balanceOf(address(this))
: _amt;
tokenContract.approve(cToken, _amt);
require(ctokenContract.mint(_amt) == 0, "deposit-ctoken-failed.");
}
uint256 finalBal = ctokenContract.balanceOf(address(this));
uint256 _cAmt = finalBal - initialBal;
setUint(setId, _cAmt);
emit LogDepositCToken(token, cToken, _amt, _cAmt, getId, setId);
bytes32 _eventCode = keccak256(
"LogDepositCToken(address,address,uint256,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
_cAmt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Withdraw CETH/CERC20_Token using cToken Amt.
* @param token token address to withdraw CToken.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param cTokenAmt ctoken amount to withdrawCToken.
* @param getId Get ctoken amount at this ID from `InstaMemory` Contract.
* @param setId Set ctoken amount at this ID in `InstaMemory` Contract.
*/
function withdrawCToken(
address token,
uint256 cTokenAmt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, cTokenAmt);
address cToken = InstaMapping(getMappingAddr()).cTokenMapping(token);
CTokenInterface cTokenContract = CTokenInterface(cToken);
_amt = _amt == uint256(-1)
? cTokenContract.balanceOf(address(this))
: _amt;
require(cTokenContract.redeem(_amt) == 0, "redeem-failed");
setUint(setId, _amt);
emit LogWithdrawCToken(token, cToken, _amt, getId, setId);
bytes32 _eventCode = keccak256(
"LogWithdrawCToken(address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
token,
cToken,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
/**
* @dev Liquidate a position.
* @param borrower Borrower's Address.
* @param tokenToPay token address to pay for liquidation.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param tokenInReturn token address to return for liquidation.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param amt token amount to pay for liquidation.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function liquidate(
address borrower,
address tokenToPay,
address tokenInReturn,
uint256 amt,
uint256 getId,
uint256 setId
) external payable {
uint256 _amt = getUint(getId, amt);
address cTokenPay = InstaMapping(getMappingAddr()).cTokenMapping(
tokenToPay
);
address cTokenColl = InstaMapping(getMappingAddr()).cTokenMapping(
tokenInReturn
);
CTokenInterface cTokenContract = CTokenInterface(cTokenPay);
(, , uint256 shortfal) = ComptrollerInterface(getComptrollerAddress())
.getAccountLiquidity(borrower);
require(shortfal != 0, "account-cannot-be-liquidated");
_amt = _amt == uint256(-1)
? cTokenContract.borrowBalanceCurrent(borrower)
: _amt;
if (tokenToPay == getAddressETH()) {
require(address(this).balance >= _amt, "not-enought-eth");
CETHInterface(cTokenPay).liquidateBorrow.value(_amt)(
borrower,
cTokenColl
);
} else {
TokenInterface tokenContract = TokenInterface(tokenToPay);
require(
tokenContract.balanceOf(address(this)) >= _amt,
"not-enough-token"
);
tokenContract.approve(cTokenPay, _amt);
require(
cTokenContract.liquidateBorrow(borrower, _amt, cTokenColl) == 0,
"liquidate-failed"
);
}
setUint(setId, _amt);
emit LogLiquidate(
address(this),
tokenToPay,
tokenInReturn,
_amt,
getId,
setId
);
bytes32 _eventCode = keccak256(
"LogLiquidate(address,address,address,uint256,uint256,uint256)"
);
bytes memory _eventParam = abi.encode(
address(this),
tokenToPay,
tokenInReturn,
_amt,
getId,
setId
);
(uint256 _type, uint256 _id) = connectorID();
EventInterface(getEventAddr()).emitEvent(
_type,
_id,
_eventCode,
_eventParam
);
}
}
contract ConnectCompound is ExtraResolver {
string public name = "Compound-v1.2";
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,177 @@
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
/**
* @title InstaAccount.
* @dev DeFi Smart Account Wallet.
*/
interface IndexInterface {
function connectors(uint256 version) external view returns (address);
function check(uint256 version) external view returns (address);
function list() external view returns (address);
}
interface ConnectorsInterface {
function isConnector(address[] calldata logicAddr)
external
view
returns (bool);
function isStaticConnector(address[] calldata logicAddr)
external
view
returns (bool);
}
interface CheckInterface {
function isOk() external view returns (bool);
}
interface ListInterface {
function addAuth(address user) external;
function removeAuth(address user) external;
}
contract Record {
event LogEnable(address indexed user);
event LogDisable(address indexed user);
event LogSwitchShield(bool _shield);
// InstaIndex Address.
address
public constant instaIndex = 0x0000000000000000000000000000000000000000;
// The Account Module Version.
uint256 public constant version = 1;
// Auth Module(Address of Auth => bool).
mapping(address => bool) private auth;
// Is shield true/false.
bool public shield;
/**
* @dev Check for Auth if enabled.
* @param user address/user/owner.
*/
function isAuth(address user) public view returns (bool) {
return auth[user];
}
/**
* @dev Change Shield State.
*/
function switchShield(bool _shield) external {
require(auth[msg.sender], "not-self");
require(shield != _shield, "shield is set");
shield = _shield;
emit LogSwitchShield(shield);
}
/**
* @dev Enable New User.
* @param user Owner of the Smart Account.
*/
function enable(address user) public {
require(
msg.sender == address(this) || msg.sender == instaIndex,
"not-self-index"
);
require(user != address(0), "not-valid");
require(!auth[user], "already-enabled");
auth[user] = true;
ListInterface(IndexInterface(instaIndex).list()).addAuth(user);
emit LogEnable(user);
}
/**
* @dev Disable User.
* @param user Owner of the Smart Account.
*/
function disable(address user) public {
require(msg.sender == address(this), "not-self");
require(user != address(0), "not-valid");
require(auth[user], "already-disabled");
delete auth[user];
ListInterface(IndexInterface(instaIndex).list()).removeAuth(user);
emit LogDisable(user);
}
}
contract InstaAccount is Record {
event LogCast(
address indexed origin,
address indexed sender,
uint256 value
);
receive() external payable {}
/**
* @dev Delegate the calls to Connector And this function is ran by cast().
* @param _target Target to of Connector.
* @param _data CallData of function in Connector.
*/
function spell(address _target, bytes memory _data) internal {
require(_target != address(0), "target-invalid");
assembly {
let succeeded := delegatecall(
gas(),
_target,
add(_data, 0x20),
mload(_data),
0,
0
)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
let size := returndatasize()
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
/**
* @dev This is the main function, Where all the different functions are called
* from Smart Account.
* @param _targets Array of Target(s) to of Connector.
* @param _datas Array of Calldata(S) of function.
*/
function cast(
address[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable {
require(
isAuth(msg.sender) || msg.sender == instaIndex,
"permission-denied"
);
require(_targets.length == _datas.length, "array-length-invalid");
IndexInterface indexContract = IndexInterface(instaIndex);
bool isShield = shield;
if (!isShield) {
require(
ConnectorsInterface(indexContract.connectors(version))
.isConnector(_targets),
"not-connector"
);
} else {
require(
ConnectorsInterface(indexContract.connectors(version))
.isStaticConnector(_targets),
"not-static-connector"
);
}
for (uint256 i = 0; i < _targets.length; i++) {
spell(_targets[i], _datas[i]);
}
address _check = indexContract.check(version);
if (_check != address(0) && !isShield)
require(CheckInterface(_check).isOk(), "not-ok");
emit LogCast(_origin, msg.sender, msg.value);
}
}

View File

@ -0,0 +1,200 @@
pragma solidity ^0.6.0;
/**
* @title InstaConnectors
* @dev Registry for Connectors.
*/
interface IndexInterface {
function master() external view returns (address);
}
interface ConnectorInterface {
function connectorID() external view returns (uint256 _type, uint256 _id);
function name() external view returns (string memory);
}
contract DSMath {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
}
contract Controllers is DSMath {
event LogAddController(address indexed addr);
event LogRemoveController(address indexed addr);
// InstaIndex Address.
address
public constant instaIndex = 0x0000000000000000000000000000000000000000;
// Enabled Chief(Address of Chief => bool).
mapping(address => bool) public chief;
// Enabled Connectors(Connector Address => bool).
mapping(address => bool) public connectors;
// Enabled Static Connectors(Connector Address => bool).
mapping(address => bool) public staticConnectors;
/**
* @dev Throws if the sender not is Master Address from InstaIndex
* or Enabled Chief.
*/
modifier isChief {
require(
chief[msg.sender] ||
msg.sender == IndexInterface(instaIndex).master(),
"not-an-chief"
);
_;
}
/**
* @dev Enable a Chief.
* @param _userAddress Chief Address.
*/
function enableChief(address _userAddress) external isChief {
chief[_userAddress] = true;
emit LogAddController(_userAddress);
}
/**
* @dev Disables a Chief.
* @param _userAddress Chief Address.
*/
function disableChief(address _userAddress) external isChief {
delete chief[_userAddress];
emit LogRemoveController(_userAddress);
}
}
contract Listings is Controllers {
// Connectors Array.
address[] public connectorArray;
// Count of Connector's Enabled.
uint256 public connectorCount;
/**
* @dev Add Connector to Connector's array.
* @param _connector Connector Address.
**/
function addToArr(address _connector) internal {
require(_connector != address(0), "Not-valid-connector");
(, uint256 _id) = ConnectorInterface(_connector).connectorID();
require(_id == (connectorArray.length + 1), "ConnectorID-doesnt-match");
ConnectorInterface(_connector).name(); // Checking if connector has function name()
connectorArray.push(_connector);
}
// Static Connectors Array.
address[] public staticConnectorArray;
/**
* @dev Add Connector to Static Connector's array.
* @param _connector Static Connector Address.
**/
function addToArrStatic(address _connector) internal {
require(_connector != address(0), "Not-valid-connector");
(, uint256 _id) = ConnectorInterface(_connector).connectorID();
require(
_id == (staticConnectorArray.length + 1),
"ConnectorID-doesnt-match"
);
ConnectorInterface(_connector).name(); // Checking if connector has function name()
staticConnectorArray.push(_connector);
}
}
contract InstaConnectors is Listings {
event LogEnable(address indexed connector);
event LogDisable(address indexed connector);
event LogEnableStatic(address indexed connector);
/**
* @dev Enable Connector.
* @param _connector Connector Address.
*/
function enable(address _connector) external isChief {
require(!connectors[_connector], "already-enabled");
addToArr(_connector);
connectors[_connector] = true;
connectorCount++;
emit LogEnable(_connector);
}
/**
* @dev Disable Connector.
* @param _connector Connector Address.
*/
function disable(address _connector) external isChief {
require(connectors[_connector], "already-disabled");
delete connectors[_connector];
connectorCount--;
emit LogDisable(_connector);
}
/**
* @dev Enable Static Connector.
* @param _connector Static Connector Address.
*/
function enableStatic(address _connector) external isChief {
require(!staticConnectors[_connector], "already-enabled");
addToArrStatic(_connector);
staticConnectors[_connector] = true;
emit LogEnableStatic(_connector);
}
/**
* @dev Check if Connector addresses are enabled.
* @param _connectors Array of Connector Addresses.
*/
function isConnector(address[] calldata _connectors)
external
view
returns (bool isOk)
{
isOk = true;
for (uint256 i = 0; i < _connectors.length; i++) {
if (!connectors[_connectors[i]]) {
isOk = false;
break;
}
}
}
/**
* @dev Check if Connector addresses are static enabled.
* @param _connectors Array of Connector Addresses.
*/
function isStaticConnector(address[] calldata _connectors)
external
view
returns (bool isOk)
{
isOk = true;
for (uint256 i = 0; i < _connectors.length; i++) {
if (!staticConnectors[_connectors[i]]) {
isOk = false;
break;
}
}
}
/**
* @dev get Connector's Array length.
*/
function connectorLength() external view returns (uint256) {
return connectorArray.length;
}
/**
* @dev get Static Connector's Array length.
*/
function staticConnectorLength() external view returns (uint256) {
return staticConnectorArray.length;
}
}

View File

@ -0,0 +1,255 @@
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
/**
* @title InstaIndex
* @dev Main Contract For DeFi Smart Accounts. This is also a factory contract, Which deploys new Smart Account.
* Also Registry for DeFi Smart Accounts.
*/
interface AccountInterface {
function version() external view returns (uint256);
function enable(address authority) external;
function cast(
address[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32[] memory responses);
}
interface ListInterface {
function init(address _account) external;
}
contract AddressIndex {
event LogNewMaster(address indexed master);
event LogUpdateMaster(address indexed master);
event LogNewCheck(uint256 indexed accountVersion, address indexed check);
event LogNewAccount(
address indexed _newAccount,
address indexed _connectors,
address indexed _check
);
// New Master Address.
address private newMaster;
// Master Address.
address public master;
// List Registry Address.
address public list;
// Connectors Modules(Account Module Version => Connectors Registry Module Address).
mapping(uint256 => address) public connectors;
// Check Modules(Account Module Version => Check Module Address).
mapping(uint256 => address) public check;
// Account Modules(Account Module Version => Account Module Address).
mapping(uint256 => address) public account;
// Version Count of Account Modules.
uint256 public versionCount;
/**
* @dev Throws if the sender not is Master Address.
*/
modifier isMaster() {
require(msg.sender == master, "not-master");
_;
}
/**
* @dev Change the Master Address.
* @param _newMaster New Master Address.
*/
function changeMaster(address _newMaster) external isMaster {
require(_newMaster != master, "already-a-master");
require(_newMaster != address(0), "not-valid-address");
require(newMaster != _newMaster, "already-a-new-master");
newMaster = _newMaster;
emit LogNewMaster(_newMaster);
}
function updateMaster() external {
require(newMaster != address(0), "not-valid-address");
require(msg.sender == newMaster, "not-master");
master = newMaster;
newMaster = address(0);
emit LogUpdateMaster(master);
}
/**
* @dev Change the Check Address of a specific Account Module version.
* @param accountVersion Account Module version.
* @param _newCheck The New Check Address.
*/
function changeCheck(uint256 accountVersion, address _newCheck)
external
isMaster
{
require(_newCheck != check[accountVersion], "already-a-check");
check[accountVersion] = _newCheck;
emit LogNewCheck(accountVersion, _newCheck);
}
/**
* @dev Add New Account Module.
* @param _newAccount The New Account Module Address.
* @param _connectors Connectors Registry Module Address.
* @param _check Check Module Address.
*/
function addNewAccount(
address _newAccount,
address _connectors,
address _check
) external isMaster {
require(_newAccount != address(0), "not-valid-address");
versionCount++;
require(
AccountInterface(_newAccount).version() == versionCount,
"not-valid-version"
);
account[versionCount] = _newAccount;
if (_connectors != address(0)) connectors[versionCount] = _connectors;
if (_check != address(0)) check[versionCount] = _check;
emit LogNewAccount(_newAccount, _connectors, _check);
}
}
contract CloneFactory is AddressIndex {
/**
* @dev Clone a new Account Module.
* @param version Account Module version to clone.
*/
function createClone(uint256 version) internal returns (address result) {
bytes20 targetBytes = bytes20(account[version]);
// solium-disable-next-line security/no-inline-assembly
assembly {
let clone := mload(0x40)
mstore(
clone,
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
)
mstore(add(clone, 0x14), targetBytes)
mstore(
add(clone, 0x28),
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
)
result := create(0, clone, 0x37)
}
}
/**
* @dev Check if Account Module is a clone.
* @param version Account Module version.
* @param query Account Module Address.
*/
function isClone(uint256 version, address query)
external
view
returns (bool result)
{
bytes20 targetBytes = bytes20(account[version]);
// solium-disable-next-line security/no-inline-assembly
assembly {
let clone := mload(0x40)
mstore(
clone,
0x363d3d373d3d3d363d7300000000000000000000000000000000000000000000
)
mstore(add(clone, 0xa), targetBytes)
mstore(
add(clone, 0x1e),
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
)
let other := add(clone, 0x40)
extcodecopy(query, other, 0, 0x2d)
result := and(
eq(mload(clone), mload(other)),
eq(mload(add(clone, 0xd)), mload(add(other, 0xd)))
)
}
}
}
contract InstaIndex is CloneFactory {
event LogAccountCreated(
address sender,
address indexed owner,
address indexed account,
address indexed origin
);
/**
* @dev Create a new DeFi Smart Account for a user and run cast function in the new Smart Account.
* @param _owner Owner of the Smart Account.
* @param accountVersion Account Module version.
* @param _targets Array of Target to run cast function.
* @param _datas Array of Data(callData) to run cast function.
* @param _origin Where Smart Account is created.
*/
function buildWithCast(
address _owner,
uint256 accountVersion,
address[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (address _account) {
_account = build(_owner, accountVersion, _origin);
if (_targets.length > 0)
AccountInterface(_account).cast.value(msg.value)(
_targets,
_datas,
_origin
);
}
/**
* @dev Create a new DeFi Smart Account for a user.
* @param _owner Owner of the Smart Account.
* @param accountVersion Account Module version.
* @param _origin Where Smart Account is created.
*/
function build(
address _owner,
uint256 accountVersion,
address _origin
) public returns (address _account) {
require(
accountVersion != 0 && accountVersion <= versionCount,
"not-valid-account"
);
_account = createClone(accountVersion);
ListInterface(list).init(_account);
AccountInterface(_account).enable(_owner);
emit LogAccountCreated(msg.sender, _owner, _account, _origin);
}
/**
* @dev Setup Initial things for InstaIndex, after its been deployed and can be only run once.
* @param _master The Master Address.
* @param _list The List Address.
* @param _account The Account Module Address.
* @param _connectors The Connectors Registry Module Address.
*/
function setBasics(
address _master,
address _list,
address _account,
address _connectors
) external {
require(
master == address(0) &&
list == address(0) &&
account[1] == address(0) &&
connectors[1] == address(0) &&
versionCount == 0,
"already-defined"
);
master = _master;
list = _list;
versionCount++;
account[versionCount] = _account;
connectors[versionCount] = _connectors;
}
}

View File

@ -0,0 +1,163 @@
pragma solidity ^0.6.0;
/**
* @title InstaList
* @dev Registry For DeFi Smart Account Authorised user.
*/
interface AccountInterface {
function isAuth(address _user) external view returns (bool);
}
contract DSMath {
function add(uint64 x, uint64 y) internal pure returns (uint64 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint64 x, uint64 y) internal pure returns (uint64 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
}
contract Variables is DSMath {
// InstaIndex Address.
address
public constant instaIndex = 0x0000000000000000000000000000000000000000;
// Smart Account Count.
uint64 public accounts;
// Smart Account ID (Smart Account Address => Account ID).
mapping(address => uint64) public accountID;
// Smart Account Address (Smart Account ID => Smart Account Address).
mapping(uint64 => address) public accountAddr;
// User Link (User Address => UserLink(Account ID of First and Last And Count of Smart Accounts)).
mapping(address => UserLink) public userLink;
// Linked List of Users (User Address => Smart Account ID => UserList(Previous and next Account ID)).
mapping(address => mapping(uint64 => UserList)) public userList;
struct UserLink {
uint64 first;
uint64 last;
uint64 count;
}
struct UserList {
uint64 prev;
uint64 next;
}
// Account Link (Smart Account ID => AccountLink).
mapping(uint64 => AccountLink) public accountLink; // account => account linked list connection
// Linked List of Accounts (Smart Account ID => Account Address => AccountList).
mapping(uint64 => mapping(address => AccountList)) public accountList; // account => user address => list
struct AccountLink {
address first;
address last;
uint64 count;
}
struct AccountList {
address prev;
address next;
}
}
contract Configure is Variables {
/**
* @dev Add Account to User Linked List.
* @param _owner Account Owner.
* @param _account Smart Account Address.
*/
function addAccount(address _owner, uint64 _account) internal {
if (userLink[_owner].last != 0) {
userList[_owner][_account].prev = userLink[_owner].last;
userList[_owner][userLink[_owner].last].next = _account;
}
if (userLink[_owner].first == 0) userLink[_owner].first = _account;
userLink[_owner].last = _account;
userLink[_owner].count = add(userLink[_owner].count, 1);
}
/**
* @dev Remove Account from User Linked List.
* @param _owner Account Owner/User.
* @param _account Smart Account Address.
*/
function removeAccount(address _owner, uint64 _account) internal {
uint64 _prev = userList[_owner][_account].prev;
uint64 _next = userList[_owner][_account].next;
if (_prev != 0) userList[_owner][_prev].next = _next;
if (_next != 0) userList[_owner][_next].prev = _prev;
if (_prev == 0) userLink[_owner].first = _next;
if (_next == 0) userLink[_owner].last = _prev;
userLink[_owner].count = sub(userLink[_owner].count, 1);
delete userList[_owner][_account];
}
/**
* @dev Add Owner to Account Linked List.
* @param _owner Account Owner.
* @param _account Smart Account Address.
*/
function addUser(address _owner, uint64 _account) internal {
if (accountLink[_account].last != address(0)) {
accountList[_account][_owner].prev = accountLink[_account].last;
accountList[_account][accountLink[_account].last].next = _owner;
}
if (accountLink[_account].first == address(0))
accountLink[_account].first = _owner;
accountLink[_account].last = _owner;
accountLink[_account].count = add(accountLink[_account].count, 1);
}
/**
* @dev Remove Owner from Account Linked List.
* @param _owner Account Owner.
* @param _account Smart Account Address.
*/
function removeUser(address _owner, uint64 _account) internal {
address _prev = accountList[_account][_owner].prev;
address _next = accountList[_account][_owner].next;
if (_prev != address(0)) accountList[_account][_prev].next = _next;
if (_next != address(0)) accountList[_account][_next].prev = _prev;
if (_prev == address(0)) accountLink[_account].first = _next;
if (_next == address(0)) accountLink[_account].last = _prev;
accountLink[_account].count = sub(accountLink[_account].count, 1);
delete accountList[_account][_owner];
}
}
contract InstaList is Configure {
/**
* @dev Enable Auth for Smart Account.
* @param _owner Owner Address.
*/
function addAuth(address _owner) external {
require(accountID[msg.sender] != 0, "not-account");
require(AccountInterface(msg.sender).isAuth(_owner), "not-owner");
addAccount(_owner, accountID[msg.sender]);
addUser(_owner, accountID[msg.sender]);
}
/**
* @dev Disable Auth for Smart Account.
* @param _owner Owner Address.
*/
function removeAuth(address _owner) external {
require(accountID[msg.sender] != 0, "not-account");
require(!AccountInterface(msg.sender).isAuth(_owner), "already-owner");
removeAccount(_owner, accountID[msg.sender]);
removeUser(_owner, accountID[msg.sender]);
}
/**
* @dev Setup Initial configuration of Smart Account.
* @param _account Smart Account Address.
*/
function init(address _account) external {
require(msg.sender == instaIndex, "not-index");
accounts++;
accountID[_account] = accounts;
accountAddr[accounts] = _account;
}
}

View File

@ -91,10 +91,40 @@ module.exports = {
},
},
solidity: {
version: "0.7.4",
settings: {
optimizer: {enabled: process.env.DEBUG ? false : true},
},
compilers: [
{
version: "0.6.10",
settings: {
optimizer: {enabled: process.env.DEBUG ? false : true},
},
},
{
version: "0.7.4",
settings: {
optimizer: {enabled: process.env.DEBUG ? false : true},
},
},
],
// overrides: {
// "contracts/vendor/DependenciesSix.sol": {
// version: "0.6.10",
// settings: {
// optimizer: {enabled: process.env.DEBUG ? false : true},
// },
// },
// "@gelatonetwork/core/contracts/gelato_core/GelatoCore.sol": {
// version: "0.6.10",
// settings: {
// optimizer: {enabled: process.env.DEBUG ? false : true},
// },
// },
// "@gelatonetwork/core/contracts/external/Ownable.sol": {
// version: "0.6.10",
// settings: {
// optimizer: {enabled: process.env.DEBUG ? false : true},
// },
// },
// },
},
};

View File

@ -16,8 +16,8 @@
"debug": "DEBUG=true yarn compile && npx hardhat test"
},
"devDependencies": {
"@codechecks/client": "^0.1.10",
"@gelatonetwork/core": "1.0.0",
"@codechecks/client": "0.1.10",
"@gelatonetwork/core": "1.3.1",
"@nomiclabs/hardhat-ethers": "2.0.0",
"@nomiclabs/hardhat-waffle": "2.0.0",
"chai": "4.2.0",
@ -28,8 +28,8 @@
"ethers": "5.0.19",
"hardhat": "2.0.2",
"hardhat-deploy": "0.7.0-beta.28",
"hardhat-deploy-ethers": "^0.3.0-beta.5",
"hardhat-gas-reporter": "^1.0.1",
"hardhat-deploy-ethers": "0.3.0-beta.5",
"hardhat-gas-reporter": "1.0.1",
"husky": ">=4",
"lint-staged": "10.5.1",
"prettier": "2.1.2",

View File

@ -23,7 +23,7 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@codechecks/client@^0.1.10":
"@codechecks/client@0.1.10":
version "0.1.10"
resolved "https://registry.yarnpkg.com/@codechecks/client/-/client-0.1.10.tgz#41fe736c424976d9feb8116b131fb9c1f099d105"
integrity sha512-rvX+LknmMohsLTU8mHJqIcNTo8fKfw6A5i7JvT6JJWqwCLi+TujHpRO8BLf48iF96+gU5viVvKfRaUyhc3wloA==
@ -550,10 +550,10 @@
"@ethersproject/properties" "^5.0.3"
"@ethersproject/strings" "^5.0.4"
"@gelatonetwork/core@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@gelatonetwork/core/-/core-1.0.0.tgz#57caedca0a19c945f509fc714358ab695aec7909"
integrity sha512-VuN+bVD8w+X/zwfFDOZHivuNOnzIHhHU0nYj8KJ01toFRnX6ABX428xUOO73mRQsiPVhmRxTu3eBBn9roWaNJw==
"@gelatonetwork/core@1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@gelatonetwork/core/-/core-1.3.1.tgz#cff0cb4d06a2bbb7859f3918995f44efc692bb11"
integrity sha512-xnl9jskZfAwwaJlIxmiXI+Cm1eKnPoDDBbMPlkNJ2NCDxoDOq/6tjHm9VXIVPDTjpqmNSPzQJjDxFWDrKUrosQ==
"@nomiclabs/ethereumjs-vm@^4.1.1":
version "4.2.0"
@ -4448,7 +4448,7 @@ har-validator@~5.1.3:
ajv "^6.12.3"
har-schema "^2.0.0"
hardhat-deploy-ethers@^0.3.0-beta.5:
hardhat-deploy-ethers@0.3.0-beta.5:
version "0.3.0-beta.5"
resolved "https://registry.yarnpkg.com/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.3.0-beta.5.tgz#c365ebb5c29e7474b2b35912f27dc9699a0e63b3"
integrity sha512-KoUswkCPSuARGjZQIf8kItL5rPFt6srgVj+q0B9YTQ7Vyw/k2N0R8u7aYWq4Piy9mjDKcEGQTPBXhaX02BMkVw==
@ -4478,7 +4478,7 @@ hardhat-deploy@0.7.0-beta.28:
murmur-128 "^0.2.1"
qs "^6.9.4"
hardhat-gas-reporter@^1.0.1:
hardhat-gas-reporter@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.1.tgz#37f96da5f11e5ae34b28a68c5d972d3168165c95"
integrity sha512-YC+SCYIkBdRtISNbisU2BwDSelUdCrIKRsJXt3M9Jw1VF5CmtSZb8VuuOc2Zl4AMcEV2jEy6ZuAksYomPiApYQ==