Merge branch 'Instadapp:main' into main

This commit is contained in:
yaronvel 2021-08-23 10:57:44 +03:00 committed by GitHub
commit 7ced8a1279
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3999 additions and 104 deletions

View File

@ -19,7 +19,7 @@ abstract contract Helpers is DSMath, Basic {
/** /**
* @dev Compound Mapping * @dev Compound Mapping
*/ */
CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xA8F9D4aA7319C54C04404765117ddBf9448E2082); CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88);
function getMergedCTokens( function getMergedCTokens(
string[] calldata supplyIds, string[] calldata supplyIds,

View File

@ -99,5 +99,5 @@ abstract contract CompResolver is Events, Helpers {
} }
contract ConnectV2COMP is CompResolver { contract ConnectV2COMP is CompResolver {
string public constant name = "COMP-v1"; string public constant name = "COMP-v1.1";
} }

View File

@ -13,7 +13,7 @@ abstract contract Helpers is DSMath, Basic {
/** /**
* @dev Compound Mapping * @dev Compound Mapping
*/ */
CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xA8F9D4aA7319C54C04404765117ddBf9448E2082); CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88);
/** /**
* @dev enter compound market * @dev enter compound market

View File

@ -2,7 +2,7 @@ pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
interface InstaFlashV2Interface { interface InstaFlashV2Interface {
function initiateFlashLoan(address[] calldata tokens, uint256[] calldata amts, uint route, bytes calldata data) external; function initiateFlashLoan(address token, uint256 amt, uint route, bytes calldata data) external;
} }
interface AccountInterface { interface AccountInterface {

View File

@ -0,0 +1,58 @@
pragma solidity ^0.7.6;
contract Events {
/* Trove */
event LogOpen(
address indexed borrower,
uint maxFeePercentage,
uint depositAmount,
uint borrowAmount,
uint256[] getIds,
uint256[] setIds
);
event LogClose(address indexed borrower, uint setId);
event LogDeposit(address indexed borrower, uint amount, uint getId, uint setId);
event LogWithdraw(address indexed borrower, uint amount, uint getId, uint setId);
event LogBorrow(address indexed borrower, uint amount, uint getId, uint setId);
event LogRepay(address indexed borrower, uint amount, uint getId, uint setId);
event LogAdjust(
address indexed borrower,
uint maxFeePercentage,
uint depositAmount,
uint withdrawAmount,
uint borrowAmount,
uint repayAmount,
uint256[] getIds,
uint256[] setIds
);
event LogClaimCollateralFromRedemption(address indexed borrower, uint amount, uint setId);
/* Stability Pool */
event LogStabilityDeposit(
address indexed borrower,
uint amount,
uint ethGain,
uint lqtyGain,
address frontendTag,
uint getDepositId,
uint setDepositId,
uint setEthGainId,
uint setLqtyGainId
);
event LogStabilityWithdraw(address indexed borrower,
uint amount,
uint ethGain,
uint lqtyGain,
uint getWithdrawId,
uint setWithdrawId,
uint setEthGainId,
uint setLqtyGainId
);
event LogStabilityMoveEthGainToTrove(address indexed borrower, uint amount);
/* Staking */
event LogStake(address indexed borrower, uint amount, uint getStakeId, uint setStakeId, uint setEthGainId, uint setLusdGainId);
event LogUnstake(address indexed borrower, uint amount, uint getUnstakeId, uint setUnstakeId, uint setEthGainId, uint setLusdGainId);
event LogClaimStakingGains(address indexed borrower, uint ethGain, uint lusdGain, uint setEthGainId, uint setLusdGainId);
}

View File

@ -0,0 +1,36 @@
pragma solidity ^0.7.6;
import { DSMath } from "../../common/math.sol";
import { Basic } from "../../common/basic.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import {
BorrowerOperationsLike,
TroveManagerLike,
StabilityPoolLike,
StakingLike,
CollateralSurplusLike,
LqtyTokenLike
} from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
BorrowerOperationsLike internal constant borrowerOperations = BorrowerOperationsLike(0x24179CD81c9e782A4096035f7eC97fB8B783e007);
TroveManagerLike internal constant troveManager = TroveManagerLike(0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2);
StabilityPoolLike internal constant stabilityPool = StabilityPoolLike(0x66017D22b0f8556afDd19FC67041899Eb65a21bb);
StakingLike internal constant staking = StakingLike(0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d);
CollateralSurplusLike internal constant collateralSurplus = CollateralSurplusLike(0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521);
LqtyTokenLike internal constant lqtyToken = LqtyTokenLike(0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D);
TokenInterface internal constant lusdToken = TokenInterface(0x5f98805A4E8be255a32880FDeC7F6728C6568bA0);
// Prevents stack-too-deep error
struct AdjustTrove {
uint maxFeePercentage;
uint withdrawAmount;
uint depositAmount;
uint lusdChange;
bool isBorrow;
}
}

View File

@ -0,0 +1,74 @@
pragma solidity ^0.7.6;
interface BorrowerOperationsLike {
function openTrove(
uint256 _maxFee,
uint256 _LUSDAmount,
address _upperHint,
address _lowerHint
) external payable;
function addColl(address _upperHint, address _lowerHint) external payable;
function withdrawColl(
uint256 _amount,
address _upperHint,
address _lowerHint
) external;
function withdrawLUSD(
uint256 _maxFee,
uint256 _amount,
address _upperHint,
address _lowerHint
) external;
function repayLUSD(
uint256 _amount,
address _upperHint,
address _lowerHint
) external;
function closeTrove() external;
function adjustTrove(
uint256 _maxFee,
uint256 _collWithdrawal,
uint256 _debtChange,
bool isDebtIncrease,
address _upperHint,
address _lowerHint
) external payable;
function claimCollateral() external;
}
interface TroveManagerLike {
function getTroveColl(address _borrower) external view returns (uint);
function getTroveDebt(address _borrower) external view returns (uint);
}
interface StabilityPoolLike {
function provideToSP(uint _amount, address _frontEndTag) external;
function withdrawFromSP(uint _amount) external;
function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external;
function getDepositorETHGain(address _depositor) external view returns (uint);
function getDepositorLQTYGain(address _depositor) external view returns (uint);
function getCompoundedLUSDDeposit(address _depositor) external view returns (uint);
}
interface StakingLike {
function stake(uint _LQTYamount) external;
function unstake(uint _LQTYamount) external;
function getPendingETHGain(address _user) external view returns (uint);
function getPendingLUSDGain(address _user) external view returns (uint);
function stakes(address owner) external view returns (uint);
}
interface CollateralSurplusLike {
function getCollateral(address _account) external view returns (uint);
}
interface LqtyTokenLike {
function balanceOf(address account) external view returns (uint256);
}

View File

@ -0,0 +1,458 @@
pragma solidity ^0.7.6;
/**
* @title Liquity.
* @dev Lending & Borrowing.
*/
import {
BorrowerOperationsLike,
TroveManagerLike,
StabilityPoolLike,
StakingLike,
CollateralSurplusLike,
LqtyTokenLike
} from "./interface.sol";
import { Stores } from "../../common/stores.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
abstract contract LiquityResolver is Events, Helpers {
/* Begin: Trove */
/**
* @dev Deposit native ETH and borrow LUSD
* @notice Opens a Trove by depositing ETH and borrowing LUSD
* @param depositAmount The amount of ETH to deposit
* @param maxFeePercentage The maximum borrow fee that this transaction should permit
* @param borrowAmount The amount of LUSD to borrow
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getIds Optional (default: 0) Optional storage slot to get deposit & borrow amounts stored using other spells
* @param setIds Optional (default: 0) Optional storage slot to set deposit & borrow amounts to be used in future spells
*/
function open(
uint depositAmount,
uint maxFeePercentage,
uint borrowAmount,
address upperHint,
address lowerHint,
uint[] memory getIds,
uint[] memory setIds
) external payable returns (string memory _eventName, bytes memory _eventParam) {
depositAmount = getUint(getIds[0], depositAmount);
borrowAmount = getUint(getIds[1], borrowAmount);
depositAmount = depositAmount == uint(-1) ? address(this).balance : depositAmount;
borrowerOperations.openTrove{value: depositAmount}(
maxFeePercentage,
borrowAmount,
upperHint,
lowerHint
);
setUint(setIds[0], depositAmount);
setUint(setIds[1], borrowAmount);
_eventName = "LogOpen(address,uint256,uint256,uint256,uint256[],uint256[])";
_eventParam = abi.encode(address(this), maxFeePercentage, depositAmount, borrowAmount, getIds, setIds);
}
/**
* @dev Repay LUSD debt from the DSA account's LUSD balance, and withdraw ETH to DSA
* @notice Closes a Trove by repaying LUSD debt
* @param setId Optional storage slot to store the ETH withdrawn from the Trove
*/
function close(uint setId) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint collateral = troveManager.getTroveColl(address(this));
borrowerOperations.closeTrove();
// Allow other spells to use the collateral released from the Trove
setUint(setId, collateral);
_eventName = "LogClose(address,uint256)";
_eventParam = abi.encode(address(this), setId);
}
/**
* @dev Deposit ETH to Trove
* @notice Increase Trove collateral (collateral Top up)
* @param amount Amount of ETH to deposit into Trove
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getId Optional storage slot to retrieve the ETH from
* @param setId Optional storage slot to set the ETH deposited
*/
function deposit(
uint amount,
address upperHint,
address lowerHint,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
_amount = _amount == uint(-1) ? address(this).balance : _amount;
borrowerOperations.addColl{value: _amount}(upperHint, lowerHint);
setUint(setId, _amount);
_eventName = "LogDeposit(address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), _amount, getId, setId);
}
/**
* @dev Withdraw ETH from Trove
* @notice Move Trove collateral from Trove to DSA
* @param amount Amount of ETH to move from Trove to DSA
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getId Optional storage slot to get the amount of ETH to withdraw
* @param setId Optional storage slot to store the withdrawn ETH in
*/
function withdraw(
uint amount,
address upperHint,
address lowerHint,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
_amount = _amount == uint(-1) ? troveManager.getTroveColl(address(this)) : _amount;
borrowerOperations.withdrawColl(_amount, upperHint, lowerHint);
setUint(setId, _amount);
_eventName = "LogWithdraw(address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), _amount, getId, setId);
}
/**
* @dev Mints LUSD tokens
* @notice Borrow LUSD via an existing Trove
* @param maxFeePercentage The maximum borrow fee that this transaction should permit
* @param amount Amount of LUSD to borrow
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getId Optional storage slot to retrieve the amount of LUSD to borrow
* @param setId Optional storage slot to store the final amount of LUSD borrowed
*/
function borrow(
uint maxFeePercentage,
uint amount,
address upperHint,
address lowerHint,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
borrowerOperations.withdrawLUSD(maxFeePercentage, _amount, upperHint, lowerHint);
setUint(setId, _amount);
_eventName = "LogBorrow(address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), _amount, getId, setId);
}
/**
* @dev Send LUSD to repay debt
* @notice Repay LUSD Trove debt
* @param amount Amount of LUSD to repay
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getId Optional storage slot to retrieve the amount of LUSD from
* @param setId Optional storage slot to store the final amount of LUSD repaid
*/
function repay(
uint amount,
address upperHint,
address lowerHint,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
if (_amount == uint(-1)) {
uint _lusdBal = lusdToken.balanceOf(address(this));
uint _totalDebt = troveManager.getTroveDebt(address(this));
_amount = _lusdBal > _totalDebt ? _totalDebt : _lusdBal;
}
borrowerOperations.repayLUSD(_amount, upperHint, lowerHint);
setUint(setId, _amount);
_eventName = "LogRepay(address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), _amount, getId, setId);
}
/**
* @dev Increase or decrease Trove ETH collateral and LUSD debt in one transaction
* @notice Adjust Trove debt and/or collateral
* @param maxFeePercentage The maximum borrow fee that this transaction should permit
* @param withdrawAmount Amount of ETH to withdraw
* @param depositAmount Amount of ETH to deposit
* @param borrowAmount Amount of LUSD to borrow
* @param repayAmount Amount of LUSD to repay
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
* @param getIds Optional Get Ids for deposit, withdraw, borrow & repay
* @param setIds Optional Set Ids for deposit, withdraw, borrow & repay
*/
function adjust(
uint maxFeePercentage,
uint depositAmount,
uint withdrawAmount,
uint borrowAmount,
uint repayAmount,
address upperHint,
address lowerHint,
uint[] memory getIds,
uint[] memory setIds
) external payable returns (string memory _eventName, bytes memory _eventParam) {
AdjustTrove memory adjustTrove;
adjustTrove.maxFeePercentage = maxFeePercentage;
depositAmount = getUint(getIds[0], depositAmount);
adjustTrove.depositAmount = depositAmount == uint(-1) ? address(this).balance : depositAmount;
withdrawAmount = getUint(getIds[1], withdrawAmount);
adjustTrove.withdrawAmount = withdrawAmount == uint(-1) ? troveManager.getTroveColl(address(this)) : withdrawAmount;
borrowAmount = getUint(getIds[2], borrowAmount);
repayAmount = getUint(getIds[3], repayAmount);
if (repayAmount == uint(-1)) {
uint _lusdBal = lusdToken.balanceOf(address(this));
uint _totalDebt = troveManager.getTroveDebt(address(this));
repayAmount = _lusdBal > _totalDebt ? _totalDebt : _lusdBal;
}
adjustTrove.isBorrow = borrowAmount > 0;
adjustTrove.lusdChange = adjustTrove.isBorrow ? borrowAmount : repayAmount;
borrowerOperations.adjustTrove{value: adjustTrove.depositAmount}(
adjustTrove.maxFeePercentage,
adjustTrove.withdrawAmount,
adjustTrove.lusdChange,
adjustTrove.isBorrow,
upperHint,
lowerHint
);
setUint(setIds[0], adjustTrove.depositAmount);
setUint(setIds[1], adjustTrove.withdrawAmount);
setUint(setIds[2], borrowAmount);
setUint(setIds[3], repayAmount);
_eventName = "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256[],uint256[])";
_eventParam = abi.encode(address(this), maxFeePercentage, adjustTrove.depositAmount, adjustTrove.withdrawAmount, borrowAmount, repayAmount, getIds, setIds);
}
/**
* @dev Withdraw remaining ETH balance from user's redeemed Trove to their DSA
* @param setId Optional storage slot to store the ETH claimed
* @notice Claim remaining collateral from Trove
*/
function claimCollateralFromRedemption(uint setId) external payable returns(string memory _eventName, bytes memory _eventParam) {
uint amount = collateralSurplus.getCollateral(address(this));
borrowerOperations.claimCollateral();
setUint(setId, amount);
_eventName = "LogClaimCollateralFromRedemption(address,uint256,uint256)";
_eventParam = abi.encode(address(this), amount, setId);
}
/* End: Trove */
/* Begin: Stability Pool */
/**
* @dev Deposit LUSD into Stability Pool
* @notice Deposit LUSD into Stability Pool
* @param amount Amount of LUSD to deposit into Stability Pool
* @param frontendTag Address of the frontend to make this deposit against (determines the kickback rate of rewards)
* @param getDepositId Optional storage slot to retrieve the amount of LUSD from
* @param setDepositId Optional storage slot to store the final amount of LUSD deposited
* @param setEthGainId Optional storage slot to store any ETH gains in
* @param setLqtyGainId Optional storage slot to store any LQTY gains in
*/
function stabilityDeposit(
uint amount,
address frontendTag,
uint getDepositId,
uint setDepositId,
uint setEthGainId,
uint setLqtyGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
amount = getUint(getDepositId, amount);
amount = amount == uint(-1) ? lusdToken.balanceOf(address(this)) : amount;
uint ethGain = stabilityPool.getDepositorETHGain(address(this));
uint lqtyBalanceBefore = lqtyToken.balanceOf(address(this));
stabilityPool.provideToSP(amount, frontendTag);
uint lqtyBalanceAfter = lqtyToken.balanceOf(address(this));
uint lqtyGain = sub(lqtyBalanceAfter, lqtyBalanceBefore);
setUint(setDepositId, amount);
setUint(setEthGainId, ethGain);
setUint(setLqtyGainId, lqtyGain);
_eventName = "LogStabilityDeposit(address,uint256,uint256,uint256,address,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), amount, ethGain, lqtyGain, frontendTag, getDepositId, setDepositId, setEthGainId, setLqtyGainId);
}
/**
* @dev Withdraw user deposited LUSD from Stability Pool
* @notice Withdraw LUSD from Stability Pool
* @param amount Amount of LUSD to withdraw from Stability Pool
* @param getWithdrawId Optional storage slot to retrieve the amount of LUSD to withdraw from
* @param setWithdrawId Optional storage slot to store the withdrawn LUSD
* @param setEthGainId Optional storage slot to store any ETH gains in
* @param setLqtyGainId Optional storage slot to store any LQTY gains in
*/
function stabilityWithdraw(
uint amount,
uint getWithdrawId,
uint setWithdrawId,
uint setEthGainId,
uint setLqtyGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
amount = getUint(getWithdrawId, amount);
amount = amount == uint(-1) ? stabilityPool.getCompoundedLUSDDeposit(address(this)) : amount;
uint ethGain = stabilityPool.getDepositorETHGain(address(this));
uint lqtyBalanceBefore = lqtyToken.balanceOf(address(this));
stabilityPool.withdrawFromSP(amount);
uint lqtyBalanceAfter = lqtyToken.balanceOf(address(this));
uint lqtyGain = sub(lqtyBalanceAfter, lqtyBalanceBefore);
setUint(setWithdrawId, amount);
setUint(setEthGainId, ethGain);
setUint(setLqtyGainId, lqtyGain);
_eventName = "LogStabilityWithdraw(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), amount, ethGain, lqtyGain, getWithdrawId, setWithdrawId, setEthGainId, setLqtyGainId);
}
/**
* @dev Increase Trove collateral by sending Stability Pool ETH gain to user's Trove
* @notice Moves user's ETH gain from the Stability Pool into their Trove
* @param upperHint Address of the Trove near the upper bound of where the user's Trove should now sit in the ordered Trove list
* @param lowerHint Address of the Trove near the lower bound of where the user's Trove should now sit in the ordered Trove list
*/
function stabilityMoveEthGainToTrove(
address upperHint,
address lowerHint
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint amount = stabilityPool.getDepositorETHGain(address(this));
stabilityPool.withdrawETHGainToTrove(upperHint, lowerHint);
_eventName = "LogStabilityMoveEthGainToTrove(address,uint256)";
_eventParam = abi.encode(address(this), amount);
}
/* End: Stability Pool */
/* Begin: Staking */
/**
* @dev Sends LQTY tokens from user to Staking Pool
* @notice Stake LQTY in Staking Pool
* @param amount Amount of LQTY to stake
* @param getStakeId Optional storage slot to retrieve the amount of LQTY to stake
* @param setStakeId Optional storage slot to store the final staked amount (can differ if requested with max balance: uint(-1))
* @param setEthGainId Optional storage slot to store any ETH gains
* @param setLusdGainId Optional storage slot to store any LUSD gains
*/
function stake(
uint amount,
uint getStakeId,
uint setStakeId,
uint setEthGainId,
uint setLusdGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
amount = getUint(getStakeId, amount);
amount = amount == uint(-1) ? lqtyToken.balanceOf(address(this)) : amount;
uint ethGain = staking.getPendingETHGain(address(this));
uint lusdGain = staking.getPendingLUSDGain(address(this));
staking.stake(amount);
setUint(setStakeId, amount);
setUint(setEthGainId, ethGain);
setUint(setLusdGainId, lusdGain);
_eventName = "LogStake(address,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), amount, getStakeId, setStakeId, setEthGainId, setLusdGainId);
}
/**
* @dev Sends LQTY tokens from Staking Pool to user
* @notice Unstake LQTY in Staking Pool
* @param amount Amount of LQTY to unstake
* @param getUnstakeId Optional storage slot to retrieve the amount of LQTY to unstake
* @param setUnstakeId Optional storage slot to store the unstaked LQTY
* @param setEthGainId Optional storage slot to store any ETH gains
* @param setLusdGainId Optional storage slot to store any LUSD gains
*/
function unstake(
uint amount,
uint getUnstakeId,
uint setUnstakeId,
uint setEthGainId,
uint setLusdGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
amount = getUint(getUnstakeId, amount);
amount = amount == uint(-1) ? staking.stakes(address(this)) : amount;
uint ethGain = staking.getPendingETHGain(address(this));
uint lusdGain = staking.getPendingLUSDGain(address(this));
staking.unstake(amount);
setUint(setUnstakeId, amount);
setUint(setEthGainId, ethGain);
setUint(setLusdGainId, lusdGain);
_eventName = "LogUnstake(address,uint256,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), amount, getUnstakeId, setUnstakeId, setEthGainId, setLusdGainId);
}
/**
* @dev Sends ETH and LUSD gains from Staking to user
* @notice Claim ETH and LUSD gains from Staking
* @param setEthGainId Optional storage slot to store the claimed ETH
* @param setLusdGainId Optional storage slot to store the claimed LUSD
*/
function claimStakingGains(
uint setEthGainId,
uint setLusdGainId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint ethGain = staking.getPendingETHGain(address(this));
uint lusdGain = staking.getPendingLUSDGain(address(this));
// Gains are claimed when a user's stake is adjusted, so we unstake 0 to trigger the claim
staking.unstake(0);
setUint(setEthGainId, ethGain);
setUint(setLusdGainId, lusdGain);
_eventName = "LogClaimStakingGains(address,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(address(this), ethGain, lusdGain, setEthGainId, setLusdGainId);
}
/* End: Staking */
}
contract ConnectV2Liquity is LiquityResolver {
string public name = "Liquity-v1";
}

View File

@ -35,7 +35,7 @@ contract Helpers is Basic {
/** /**
* @dev Return InstaDApp Mapping Address * @dev Return InstaDApp Mapping Address
*/ */
address constant internal getMappingAddr = 0xA8F9D4aA7319C54C04404765117ddBf9448E2082; // CompoundMapping Address address constant internal getMappingAddr = 0xe7a85d0adDB972A4f0A4e57B698B37f171519e88; // CompoundMapping Address
/** /**
* @dev Return Compound Comptroller Address * @dev Return Compound Comptroller Address

View File

@ -335,5 +335,5 @@ contract RefinanceResolver is CompoundHelpers, AaveV1Helpers, AaveV2Helpers {
} }
contract ConnectV2Refinance is RefinanceResolver { contract ConnectV2Refinance is RefinanceResolver {
string public name = "Refinance-v1.0"; string public name = "Refinance-v1.1";
} }

View File

@ -37,8 +37,7 @@ abstract contract Helpers is DSMath, Basic {
* @dev Return Reflexer mapping Address. * @dev Return Reflexer mapping Address.
*/ */
function getGebMappingAddress() internal pure returns (address) { function getGebMappingAddress() internal pure returns (address) {
// TODO: Set the real deployed Reflexer mapping address return 0x573e5132693C046D1A9F75Bac683889164bA41b4;
return 0x0000000000000000000000000000000000000000;
} }
function getCollateralJoinAddress(bytes32 collateralType) internal view returns (address) { function getCollateralJoinAddress(bytes32 collateralType) internal view returns (address) {

View File

@ -1,4 +1,4 @@
pragma solidity ^0.6.0; pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
interface CollateralJoinInterface { interface CollateralJoinInterface {
@ -17,11 +17,10 @@ interface MappingControllerInterface {
function hasRole(address,address) external view returns (bool); function hasRole(address,address) external view returns (bool);
} }
contract Helpers { contract Helpers {
// TODO: thrilok, verify this address ConnectorsInterface public constant connectors = ConnectorsInterface(0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11); // InstaConnectorsV2
ConnectorsInterface public constant connectors = ConnectorsInterface(0xFE2390DAD597594439f218190fC2De40f9Cf1179);
IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723);
// TODO: add address for MappingController
MappingControllerInterface public constant mappingController = MappingControllerInterface(address(0)); MappingControllerInterface public constant mappingController = MappingControllerInterface(0xDdd075D5e1024901E4038461e1e4BbC3A48a08d4);
uint public version = 1; uint public version = 1;
mapping (bytes32 => address) public collateralJoinMapping; mapping (bytes32 => address) public collateralJoinMapping;
@ -55,8 +54,8 @@ contract Helpers {
} }
contract GebMapping is Helpers { contract InstaReflexerGebMapping is Helpers {
string constant public name = "Reflexer-Mapping-v1"; string constant public name = "Reflexer-Geb-Mapping-v1";
constructor() public { constructor() public {
address[] memory collateralJoins = new address[](1); address[] memory collateralJoins = new address[](1);

View File

@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma solidity >=0.6.0 <0.8.0;
import "@openzeppelin/contracts/utils/EnumerableSet.sol"; import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/Address.sol";
@ -10,6 +9,10 @@ interface IndexInterface {
function master() external view returns (address); function master() external view returns (address);
} }
interface ConnectorsInterface {
function chief(address) external view returns (bool);
}
contract InstaMappingController is Context { contract InstaMappingController is Context {
using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.AddressSet;
using Address for address; using Address for address;
@ -18,6 +21,9 @@ contract InstaMappingController is Context {
IndexInterface public constant instaIndex = IndexInterface public constant instaIndex =
IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723);
ConnectorsInterface public constant connectors =
ConnectorsInterface(0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11); // InstaConnectorsV2
/** /**
* @dev Emitted when `account` is granted `role`. * @dev Emitted when `account` is granted `role`.
@ -39,8 +45,8 @@ contract InstaMappingController is Context {
modifier onlyMaster { modifier onlyMaster {
require( require(
instaIndex.master() == _msgSender(), instaIndex.master() == _msgSender() || connectors.chief(_msgSender()),
"MappingController: sender must be master" "MappingController: sender must be master or chief"
); );
_; _;
} }

View File

@ -1,35 +1,43 @@
{ {
"1" : { "connectors": {
"AUTHORITY-A": "0x351Bb32e90C35647Df7a584f3c1a3A0c38F31c68", "1" : {
"BASIC-A": "0x9926955e0Dd681Dc303370C52f4Ad0a4dd061687", "AUTHORITY-A": "0x351Bb32e90C35647Df7a584f3c1a3A0c38F31c68",
"1INCH-A": "0x235fca310ac7be45c7ad45f111203468743e4b7c", "BASIC-A": "0x9926955e0Dd681Dc303370C52f4Ad0a4dd061687",
"1INCH-B": "0xaBac3dCf164eD827EAfda8e05eCc8208D6bc5E04", "1INCH-A": "0x235fca310ac7be45c7ad45f111203468743e4b7c",
"COMPOUND-A": "0xbb153cf09a123746e0eb3b3a436c544a7eeb24b6", "1INCH-B": "0xaBac3dCf164eD827EAfda8e05eCc8208D6bc5E04",
"AAVE-V1-A": "0x612c5CA43230D9F97a0ac87E4420F66b8DF97e9D", "COMPOUND-A": "0x1B1EACaa31abbE544117073f6F8F658a56A3aE25",
"AAVE-V2-A": "0x68b27A84101ac5120bBAb7Ce8d6b096C961df52C", "AAVE-V1-A": "0x612c5CA43230D9F97a0ac87E4420F66b8DF97e9D",
"MAKERDAO-A": "0x4049db23c605b197f764072569b8db2464653ef6", "AAVE-V2-A": "0x68b27A84101ac5120bBAb7Ce8d6b096C961df52C",
"UNISWAP-V2-A": "0x1E5CE41BdB653734445FeC3553b61FebDdaFC43c", "MAKERDAO-A": "0x4049db23c605b197f764072569b8db2464653ef6",
"COMP-A": "0xB446e325D44C52b93eC122Bf76301f235f90B9c9", "UNISWAP-V2-A": "0x1E5CE41BdB653734445FeC3553b61FebDdaFC43c",
"UNISWAP-A": "0xA4BF319968986D2352FA1c550D781bBFCCE3FcaB", "COMP-A": "0x907F0C8c99B08606eE0A51ec5Bc3dFdbFC2d92f3",
"POLYGON-BRIDGE-A": "0x1b79B302132370B434fb7807b36CB72FB0510aD5", "UNISWAP-A": "0xA4BF319968986D2352FA1c550D781bBFCCE3FcaB",
"AAVE-CLAIM-A": "0x611C1FA59Aa1d6352c4C8bD44882063c6aEE85E0", "POLYGON-BRIDGE-A": "0x1b79B302132370B434fb7807b36CB72FB0510aD5",
"AAVE-STAKE-A": "0xf73c94402bc24148b744083ed02654eec2c37d5b", "AAVE-CLAIM-A": "0x611C1FA59Aa1d6352c4C8bD44882063c6aEE85E0",
"G-UNISWAP-A": "0x2fca923c7535083f25f761dcf289d7d81f024dda", "AAVE-STAKE-A": "0xf73c94402bc24148b744083ed02654eec2c37d5b",
"INST-STAKING-A": "0x37a63939e128d284e0eae5d3e517aad44f5204d4", "G-UNISWAP-A": "0x2fca923c7535083f25f761dcf289d7d81f024dda",
"AAVE-V2-IMPORT-B": "0x6fe05374924830B6aC98849f75A3D5766E51Ef10", "INST-STAKING-A": "0x37a63939e128d284e0eae5d3e517aad44f5204d4",
"COMPOUND-IMPORT-B": "0xdA101870ca6136539628F28041E1B55baf4EB6C0", "AAVE-V2-IMPORT-B": "0x6fe05374924830B6aC98849f75A3D5766E51Ef10",
"INSTAPOOL-A": "0x5806Af7AB22E2916fA582Ff05731Bf7C682387B2", "COMPOUND-IMPORT-B": "0xdA101870ca6136539628F28041E1B55baf4EB6C0",
"MAKERDAO-CLAIM-A": "0x2f8cBE650af98602a215b6482F2aD60893C5A4E8", "INSTAPOOL-A": "0x5806Af7AB22E2916fA582Ff05731Bf7C682387B2",
"WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3", "MAKERDAO-CLAIM-A": "0x2f8cBE650af98602a215b6482F2aD60893C5A4E8",
"REFINANCE-A": "0x9eA34bE6dA51aa9F6408FeA79c946FDCFA424442", "WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3",
"INST-A": "0x52C2C4a0db049255fF345EB9D3Fb1f555b7a924A" "REFINANCE-A": "0x6f22931423e8ffC8d51f6E5aF73118fC64b27856",
"INST-A": "0x52C2C4a0db049255fF345EB9D3Fb1f555b7a924A",
"REFLEXER-A": "0xaC6dc28a6251F49Bbe5755E630107Dccde9ae2C8"
},
"137" : {
"1INCH-A": "0xC0d9210496afE9763F5d8cEb8deFfBa817232A9e",
"AAVE-V2-A": "0xE84d8010Afc3663919F44685cB53ED88866da3eE",
"AUTHORITY-A": "0xf73C94402BC24148b744083eD02654EEc2C37D5B",
"BASIC-A": "0x1cAF5EC802ca602E98139AD96A8f2B7BC524264E",
"AAVE-CLAIM-A": "0xC7Cb1dE2721BFC0E0DA1b9D526bCdC54eF1C0eFC",
"PARASWAP-A": "0xFb3a1D56eD56F046721B9aCa749895100754578b"
}
}, },
"137" : { "mappings": {
"1INCH-A": "0xC0d9210496afE9763F5d8cEb8deFfBa817232A9e", "InstaMappingController": "0xDdd075D5e1024901E4038461e1e4BbC3A48a08d4",
"AAVE-V2-A": "0xE84d8010Afc3663919F44685cB53ED88866da3eE", "InstaCompoundMapping": "0xe7a85d0adDB972A4f0A4e57B698B37f171519e88",
"AUTHORITY-A": "0xf73C94402BC24148b744083eD02654EEc2C37D5B", "InstaReflexerGebMapping": "0x573e5132693C046D1A9F75Bac683889164bA41b4"
"BASIC-A": "0x1cAF5EC802ca602E98139AD96A8f2B7BC524264E",
"AAVE-CLAIM-A": "0xC7Cb1dE2721BFC0E0DA1b9D526bCdC54eF1C0eFC",
"PARASWAP-A": "0xFb3a1D56eD56F046721B9aCa749895100754578b"
} }
} }

View File

@ -1,12 +1,11 @@
require("@nomiclabs/hardhat-waffle"); require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers"); require("@nomiclabs/hardhat-ethers");
require("@tenderly/hardhat-tenderly"); require("@tenderly/hardhat-tenderly");
require("@nomiclabs/hardhat-etherscan"); require("@nomiclabs/hardhat-etherscan");
require("@nomiclabs/hardhat-web3") require("@nomiclabs/hardhat-web3");
require("hardhat-deploy"); require("hardhat-deploy");
require("hardhat-deploy-ethers"); require("hardhat-deploy-ethers");
require('dotenv').config(); require("dotenv").config();
const { utils } = require("ethers"); const { utils } = require("ethers");
@ -28,32 +27,32 @@ module.exports = {
settings: { settings: {
optimizer: { optimizer: {
enabled: false, enabled: false,
runs: 200 runs: 200,
} },
} },
}, },
{ {
version: "0.6.0" version: "0.6.0",
}, },
{ {
version: "0.6.2" version: "0.6.2",
}, },
{ {
version: "0.6.5" version: "0.6.5",
} },
] ],
}, },
networks: { networks: {
// defaultNetwork: "hardhat", // defaultNetwork: "hardhat",
kovan: { kovan: {
url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`] accounts: [`0x${PRIVATE_KEY}`],
}, },
mainnet: { mainnet: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
gasPrice: parseInt(utils.parseUnits("30", "gwei")) gasPrice: parseInt(utils.parseUnits("30", "gwei")),
}, },
hardhat: { hardhat: {
forking: { forking: {
@ -66,17 +65,17 @@ module.exports = {
url: "https://rpc-mainnet.maticvigil.com/", url: "https://rpc-mainnet.maticvigil.com/",
accounts: [`0x${PRIVATE_KEY}`], accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000, timeout: 150000,
gasPrice: parseInt(utils.parseUnits("1", "gwei")) gasPrice: parseInt(utils.parseUnits("1", "gwei")),
} },
}, },
etherscan: { etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY apiKey: process.env.ETHERSCAN_API_KEY,
}, },
tenderly: { tenderly: {
project: process.env.TENDERLY_PROJECT, project: process.env.TENDERLY_PROJECT,
username: process.env.TENDERLY_USERNAME, username: process.env.TENDERLY_USERNAME,
}, },
mocha: { mocha: {
timeout: 100 * 1000 timeout: 100 * 1000,
} },
}; };

View File

@ -1,15 +1,15 @@
module.exports = { module.exports = {
core: { core: {
connectorsV2: require("./abi/core/connectorsV2.json"), connectorsV2: require("./abi/core/connectorsV2.json"),
instaIndex: require("./abi/core/instaIndex.json"), instaIndex: require("./abi/core/instaIndex.json"),
}, },
connectors: { connectors: {
basic: require("./abi/connectors/basic.json"), "Basic-v1": require("./abi/connectors/basic.json"),
auth: require("./abi/connectors/auth.json"), basic: require("./abi/connectors/basic.json"),
"INSTAPOOL-A": require("./abi/connectors/instapool.json"), auth: require("./abi/connectors/auth.json"),
}, "INSTAPOOL-A": require("./abi/connectors/instapool.json"),
basic: { },
erc20: require("./abi/basics/erc20.json"), basic: {
}, erc20: require("./abi/basics/erc20.json"),
}; },
};

View File

@ -1,12 +1,11 @@
module.exports = { module.exports = {
connectors: { connectors: {
basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6", basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6",
auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9", auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9",
"INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2" "INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2",
}, },
core: { core: {
connectorsV2: "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11", connectorsV2: "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11",
instaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723", instaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723",
} },
}; };

View File

@ -3,7 +3,7 @@ const { ethers } = hre;
async function main() { async function main() {
const CONNECTORS_V2 = "0xFE2390DAD597594439f218190fC2De40f9Cf1179"; const CONNECTORS_V2 = "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11";
const ctokenMapping = { const ctokenMapping = {
"ETH-A": "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5", "ETH-A": "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
@ -16,7 +16,13 @@ async function main() {
"USDT-A": "0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9", "USDT-A": "0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9",
"WBTC-A": "0xc11b1268c1a384e55c48c2391d8d480264a3a7f4", "WBTC-A": "0xc11b1268c1a384e55c48c2391d8d480264a3a7f4",
"WBTC-B": "0xccF4429DB6322D5C611ee964527D42E5d685DD6a", "WBTC-B": "0xccF4429DB6322D5C611ee964527D42E5d685DD6a",
"ZRX-A": "0xb3319f5d18bc0d84dd1b4825dcde5d5f7266d407" "ZRX-A": "0xb3319f5d18bc0d84dd1b4825dcde5d5f7266d407",
"YFI-A": "0x80a2ae356fc9ef4305676f7a3e2ed04e12c33946",
"SUSHI-A": "0x4b0181102a0112a2ef11abee5563bb4a3176c9d7",
"MKR-A": "0x95b4ef2869ebd94beb4eee400a99824bf5dc325b",
"AAVE-A": "0xe65cdb6479bac1e22340e4e755fae7e509ecd06c",
"TUSD-A": "0x12392f67bdf24fae0af363c24ac620a2f67dad86",
"LINK-A": "0xface851a4921ce59e912d19329929ce6da6eb0c7",
} }
const tokenMapping = { const tokenMapping = {
@ -30,7 +36,13 @@ async function main() {
"USDT-A": "0xdac17f958d2ee523a2206206994597c13d831ec7", "USDT-A": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"WBTC-A": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", "WBTC-A": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"WBTC-B": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", "WBTC-B": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"ZRX-A": "0xe41d2489571d322189246dafa5ebde1f4699f498" "ZRX-A": "0xe41d2489571d322189246dafa5ebde1f4699f498",
"YFI-A": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
"SUSHI-A": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2",
"MKR-A": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
"AAVE-A": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
"TUSD-A": "0x0000000000085d4780B73119b644AE5ecd22b376",
"LINK-A": "0x514910771af9ca656af840dff83e8264ecf986ca",
} }
const Mapping = await ethers.getContractFactory("InstaCompoundMapping"); const Mapping = await ethers.getContractFactory("InstaCompoundMapping");

View File

@ -0,0 +1,36 @@
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)
})

View File

@ -0,0 +1,38 @@
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)
})

View File

@ -180,10 +180,20 @@ const parseCode = async (connector) => {
func = [] func = []
} }
} }
funcs = funcs const allPublicFuncs = funcs
.filter(({ raw }) => { .filter(({ raw }) => {
if ((raw.includes('external') || raw.includes('public')) && return raw.includes('external') || raw.includes('public')
raw.includes('returns')) { })
.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] const returns = raw.split('returns')[1].split('(')[1].split(')')[0]
return returns.includes('string') && returns.includes('bytes') return returns.includes('string') && returns.includes('bytes')
} }
@ -193,11 +203,9 @@ const parseCode = async (connector) => {
const args = f.raw.split('(')[1].split(')')[0].split(',') const args = f.raw.split('(')[1].split(')')[0].split(',')
.map(arg => arg.trim()) .map(arg => arg.trim())
.filter(arg => arg !== '') .filter(arg => arg !== '')
const name = f.raw.split('(')[0].split('function')[1].trim()
return { return {
...f, ...f,
args, args
name
} }
}) })
const eventsPath = `${connector.path}/events.sol` const eventsPath = `${connector.path}/events.sol`
@ -229,7 +237,8 @@ const parseCode = async (connector) => {
eventsFirstLines, eventsFirstLines,
mainEvents, mainEvents,
mainEventsLines, mainEventsLines,
funcs funcs,
allPublicFuncs
} }
} catch (error) { } catch (error) {
return Promise.reject(error) return Promise.reject(error)
@ -262,6 +271,21 @@ const checkComments = async (connector) => {
} }
} }
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) => { const checkName = async (connector) => {
try { try {
const strs = connector.code.split('\n') const strs = connector.code.split('\n')
@ -313,12 +337,14 @@ async function checkMain () {
const commentsErrors = await checkComments(connectors[index]) const commentsErrors = await checkComments(connectors[index])
const nameErrors = await checkName(connectors[index]) const nameErrors = await checkName(connectors[index])
const headCommentsErrors = await checkHeadComments(connectors[index]) const headCommentsErrors = await checkHeadComments(connectors[index])
const publicFuncsErrors = await checkPublicFuncs(connectors[index])
errors.push(...forbiddenErrors) errors.push(...forbiddenErrors)
errors.push(...eventsErrors) errors.push(...eventsErrors)
errors.push(...commentsErrors) errors.push(...commentsErrors)
errors.push(...nameErrors) errors.push(...nameErrors)
errors.push(...headCommentsErrors) errors.push(...headCommentsErrors)
errors.push(...publicFuncsErrors)
warnings.push(...eventsWarnings) warnings.push(...eventsWarnings)
} }
if (errors.length) { if (errors.length) {

View File

@ -0,0 +1,95 @@
const TROVE_MANAGER_ADDRESS = "0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2";
const TROVE_MANAGER_ABI = [
"function getTroveColl(address _borrower) external view returns (uint)",
"function getTroveDebt(address _borrower) external view returns (uint)",
"function getTroveStatus(address _borrower) external view returns (uint)",
"function redeemCollateral(uint _LUSDAmount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFee) external returns (uint)",
"function getNominalICR(address _borrower) external view returns (uint)",
"function liquidate(address _borrower) external",
"function liquidateTroves(uint _n) external",
];
const BORROWER_OPERATIONS_ADDRESS =
"0x24179CD81c9e782A4096035f7eC97fB8B783e007";
const BORROWER_OPERATIONS_ABI = [
"function openTrove(uint256 _maxFee, uint256 _LUSDAmount, address _upperHint, address _lowerHint) external payable",
"function closeTrove() external",
];
const LUSD_TOKEN_ADDRESS = "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0";
const LUSD_TOKEN_ABI = [
"function transfer(address _to, uint256 _value) public returns (bool success)",
"function balanceOf(address account) external view returns (uint256)",
"function approve(address spender, uint256 amount) external returns (bool)",
];
const ACTIVE_POOL_ADDRESS = "0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F";
const ACTIVE_POOL_ABI = ["function getLUSDDebt() external view returns (uint)"];
const PRICE_FEED_ADDRESS = "0x4c517D4e2C851CA76d7eC94B805269Df0f2201De";
const PRICE_FEED_ABI = ["function fetchPrice() external returns (uint)"];
const HINT_HELPERS_ADDRESS = "0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0";
const HINT_HELPERS_ABI = [
"function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns (address firstRedemptionHint, uint partialRedemptionHintNICR, uint truncatedLUSDamount)",
"function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) view returns (address hintAddress, uint diff, uint latestRandomSeed)",
"function computeNominalCR(uint _coll, uint _debt) external pure returns (uint)",
];
const SORTED_TROVES_ADDRESS = "0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6";
const SORTED_TROVES_ABI = [
"function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address)",
"function getLast() external view returns (address)",
];
const STABILITY_POOL_ADDRESS = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb";
const STABILITY_POOL_ABI = [
"function getCompoundedLUSDDeposit(address _depositor) external view returns (uint)",
"function getDepositorETHGain(address _depositor) external view returns (uint)",
"function getDepositorLQTYGain(address _depositor) external view returns (uint)",
];
const STAKING_ADDRESS = "0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d";
const STAKING_ABI = [
"function stake(uint _LQTYamount) external",
"function unstake(uint _LQTYamount) external",
"function getPendingETHGain(address _user) external view returns (uint)",
"function getPendingLUSDGain(address _user) external view returns (uint)",
];
const LQTY_TOKEN_ADDRESS = "0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D";
const LQTY_TOKEN_ABI = [
"function balanceOf(address account) external view returns (uint256)",
"function transfer(address _to, uint256 _value) public returns (bool success)",
"function approve(address spender, uint256 amount) external returns (bool)",
];
const COLL_SURPLUS_ADDRESS = "0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521";
const COLL_SURPLUS_ABI = [
"function getCollateral(address _account) external view returns (uint)",
];
module.exports = {
TROVE_MANAGER_ADDRESS,
TROVE_MANAGER_ABI,
BORROWER_OPERATIONS_ADDRESS,
BORROWER_OPERATIONS_ABI,
LUSD_TOKEN_ADDRESS,
LUSD_TOKEN_ABI,
STABILITY_POOL_ADDRESS,
STABILITY_POOL_ABI,
ACTIVE_POOL_ADDRESS,
ACTIVE_POOL_ABI,
PRICE_FEED_ADDRESS,
PRICE_FEED_ABI,
HINT_HELPERS_ADDRESS,
HINT_HELPERS_ABI,
SORTED_TROVES_ADDRESS,
SORTED_TROVES_ABI,
STAKING_ADDRESS,
STAKING_ABI,
LQTY_TOKEN_ADDRESS,
LQTY_TOKEN_ABI,
COLL_SURPLUS_ADDRESS,
COLL_SURPLUS_ABI,
};

View File

@ -0,0 +1,344 @@
const hre = require("hardhat");
const hardhatConfig = require("../../hardhat.config");
// Instadapp deployment and testing helpers
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
const encodeSpells = require("../../scripts/encodeSpells.js");
const getMasterSigner = require("../../scripts/getMasterSigner");
const buildDSAv2 = require("../../scripts/buildDSAv2");
// Instadapp instadappAddresses/ABIs
const instadappAddresses = require("../../scripts/constant/addresses");
const instadappAbi = require("../../scripts/constant/abis");
// Instadapp Liquity Connector artifacts
const connectV2LiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/liquity/main.sol/ConnectV2Liquity.json");
const connectV2BasicV1Artifacts = require("../../artifacts/contracts/mainnet/connectors/basic/main.sol/ConnectV2Basic.json");
const { ethers } = require("hardhat");
// Instadapp uses a fake address to represent native ETH
const { eth_addr: ETH_ADDRESS } = require("../../scripts/constant/constant");
const LIQUITY_CONNECTOR = "LIQUITY-v1-TEST";
const LUSD_GAS_COMPENSATION = hre.ethers.utils.parseUnits("200", 18); // 200 LUSD gas compensation repaid after loan repayment
const LIQUIDATABLE_TROVES_BLOCK_NUMBER = 12478159; // Deterministic block number for tests to run against, if you change this, tests will break.
const JUSTIN_SUN_ADDRESS = "0x903d12bf2c57a29f32365917c706ce0e1a84cce3"; // LQTY whale address
const LIQUIDATABLE_TROVE_ADDRESS = "0xafbeb4cb97f3b08ec2fe07ef0dac15d37013a347"; // Trove which is liquidatable at blockNumber: LIQUIDATABLE_TROVES_BLOCK_NUMBER
const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit; // Maximum gas limit (12000000)
const INSTADAPP_BASIC_V1_CONNECTOR = "Basic-v1";
const openTroveSpell = async (
dsa,
signer,
depositAmount,
borrowAmount,
upperHint,
lowerHint,
maxFeePercentage
) => {
let address = signer.address;
if (signer.address === undefined) {
address = await signer.getAddress();
}
const openTroveSpell = {
connector: LIQUITY_CONNECTOR,
method: "open",
args: [
depositAmount,
maxFeePercentage,
borrowAmount,
upperHint,
lowerHint,
[0, 0],
[0, 0],
],
};
return await dsa
.connect(signer)
.cast(...encodeSpells([openTroveSpell]), address, {
value: depositAmount,
});
};
const createDsaTrove = async (
dsa,
signer,
liquity,
depositAmount = hre.ethers.utils.parseEther("5"),
borrowAmount = hre.ethers.utils.parseUnits("2000", 18)
) => {
const maxFeePercentage = hre.ethers.utils.parseUnits("0.5", 18); // 0.5% max fee
const { upperHint, lowerHint } = await getTroveInsertionHints(
depositAmount,
borrowAmount,
liquity
);
return await openTroveSpell(
dsa,
signer,
depositAmount,
borrowAmount,
upperHint,
lowerHint,
maxFeePercentage
);
};
const sendToken = async (token, amount, from, to) => {
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [from],
});
const signer = await hre.ethers.provider.getSigner(from);
return await token.connect(signer).transfer(to, amount, {
gasPrice: 0,
});
};
const resetInitialState = async (walletAddress, contracts, isDebug = false) => {
const liquity = await deployAndConnect(contracts, isDebug);
const dsa = await buildDSAv2(walletAddress);
return [liquity, dsa];
};
const resetHardhatBlockNumber = async (blockNumber) => {
return await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: hardhatConfig.networks.hardhat.forking.url,
blockNumber,
},
},
],
});
};
const deployAndConnect = async (contracts, isDebug = false) => {
// Pin Liquity tests to a particular block number to create deterministic state (Ether price etc.)
await resetHardhatBlockNumber(LIQUIDATABLE_TROVES_BLOCK_NUMBER);
const liquity = {
troveManager: null,
borrowerOperations: null,
stabilityPool: null,
lusdToken: null,
lqtyToken: null,
activePool: null,
priceFeed: null,
hintHelpers: null,
sortedTroves: null,
staking: null,
collSurplus: null,
};
const masterSigner = await getMasterSigner();
const instaConnectorsV2 = await ethers.getContractAt(
instadappAbi.core.connectorsV2,
instadappAddresses.core.connectorsV2
);
const connector = await deployAndEnableConnector({
connectorName: LIQUITY_CONNECTOR,
contractArtifact: connectV2LiquityArtifacts,
signer: masterSigner,
connectors: instaConnectorsV2,
});
isDebug &&
console.log(`${LIQUITY_CONNECTOR} Connector address`, connector.address);
const basicConnector = await deployAndEnableConnector({
connectorName: "Basic-v1",
contractArtifact: connectV2BasicV1Artifacts,
signer: masterSigner,
connectors: instaConnectorsV2,
});
isDebug && console.log("Basic-v1 Connector address", basicConnector.address);
liquity.troveManager = new ethers.Contract(
contracts.TROVE_MANAGER_ADDRESS,
contracts.TROVE_MANAGER_ABI,
ethers.provider
);
liquity.borrowerOperations = new ethers.Contract(
contracts.BORROWER_OPERATIONS_ADDRESS,
contracts.BORROWER_OPERATIONS_ABI,
ethers.provider
);
liquity.stabilityPool = new ethers.Contract(
contracts.STABILITY_POOL_ADDRESS,
contracts.STABILITY_POOL_ABI,
ethers.provider
);
liquity.lusdToken = new ethers.Contract(
contracts.LUSD_TOKEN_ADDRESS,
contracts.LUSD_TOKEN_ABI,
ethers.provider
);
liquity.lqtyToken = new ethers.Contract(
contracts.LQTY_TOKEN_ADDRESS,
contracts.LQTY_TOKEN_ABI,
ethers.provider
);
liquity.activePool = new ethers.Contract(
contracts.ACTIVE_POOL_ADDRESS,
contracts.ACTIVE_POOL_ABI,
ethers.provider
);
liquity.priceFeed = new ethers.Contract(
contracts.PRICE_FEED_ADDRESS,
contracts.PRICE_FEED_ABI,
ethers.provider
);
liquity.hintHelpers = new ethers.Contract(
contracts.HINT_HELPERS_ADDRESS,
contracts.HINT_HELPERS_ABI,
ethers.provider
);
liquity.sortedTroves = new ethers.Contract(
contracts.SORTED_TROVES_ADDRESS,
contracts.SORTED_TROVES_ABI,
ethers.provider
);
liquity.staking = new ethers.Contract(
contracts.STAKING_ADDRESS,
contracts.STAKING_ABI,
ethers.provider
);
liquity.collSurplus = new ethers.Contract(
contracts.COLL_SURPLUS_ADDRESS,
contracts.COLL_SURPLUS_ABI,
ethers.provider
);
return liquity;
};
const getTroveInsertionHints = async (depositAmount, borrowAmount, liquity) => {
const nominalCR = await liquity.hintHelpers.computeNominalCR(
depositAmount,
borrowAmount
);
const {
hintAddress,
latestRandomSeed,
} = await liquity.hintHelpers.getApproxHint(nominalCR, 50, 1298379, {
gasLimit: MAX_GAS,
});
randomSeed = latestRandomSeed;
const {
0: upperHint,
1: lowerHint,
} = await liquity.sortedTroves.findInsertPosition(
nominalCR,
hintAddress,
hintAddress,
{
gasLimit: MAX_GAS,
}
);
return {
upperHint,
lowerHint,
};
};
let randomSeed = 4223;
const getRedemptionHints = async (amount, liquity) => {
const ethPrice = await liquity.priceFeed.callStatic.fetchPrice();
const [
firstRedemptionHint,
partialRedemptionHintNicr,
] = await liquity.hintHelpers.getRedemptionHints(amount, ethPrice, 0);
const {
hintAddress,
latestRandomSeed,
} = await liquity.hintHelpers.getApproxHint(
partialRedemptionHintNicr,
50,
randomSeed,
{
gasLimit: MAX_GAS,
}
);
randomSeed = latestRandomSeed;
const {
0: upperHint,
1: lowerHint,
} = await liquity.sortedTroves.findInsertPosition(
partialRedemptionHintNicr,
hintAddress,
hintAddress,
{
gasLimit: MAX_GAS,
}
);
return {
partialRedemptionHintNicr,
firstRedemptionHint,
upperHint,
lowerHint,
};
};
const redeem = async (amount, from, wallet, liquity) => {
await sendToken(liquity.lusdToken, amount, from, wallet.address);
const {
partialRedemptionHintNicr,
firstRedemptionHint,
upperHint,
lowerHint,
} = await getRedemptionHints(amount, liquity);
const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee
return await liquity.troveManager
.connect(wallet)
.redeemCollateral(
amount,
firstRedemptionHint,
upperHint,
lowerHint,
partialRedemptionHintNicr,
0,
maxFeePercentage,
{
gasLimit: MAX_GAS, // permit max gas
}
);
};
module.exports = {
deployAndConnect,
resetInitialState,
createDsaTrove,
sendToken,
getTroveInsertionHints,
getRedemptionHints,
redeem,
LIQUITY_CONNECTOR,
LUSD_GAS_COMPENSATION,
JUSTIN_SUN_ADDRESS,
LIQUIDATABLE_TROVE_ADDRESS,
MAX_GAS,
INSTADAPP_BASIC_V1_CONNECTOR,
ETH_ADDRESS,
};

2708
test/liquity/liquity.test.js Normal file

File diff suppressed because it is too large Load Diff