Merge branch 'main' into pooltogether-connector

This commit is contained in:
eccheung4 2021-08-13 14:38:55 -07:00
commit 6ab5a1ef3e
61 changed files with 5602 additions and 251 deletions

View File

@ -27,6 +27,15 @@ abstract contract Basic is DSMath, Stores {
return abi.encode(eventName, eventParam);
}
function approve(TokenInterface token, address spender, uint256 amount) internal {
try token.approve(spender, amount) {
} catch {
token.approve(spender, 0);
token.approve(spender, amount);
}
}
function changeEthAddress(address buy, address sell) internal pure returns(TokenInterface _buy, TokenInterface _sell){
_buy = buy == ethAddr ? TokenInterface(wethAddr) : TokenInterface(buy);
_sell = sell == ethAddr ? TokenInterface(wethAddr) : TokenInterface(sell);
@ -38,7 +47,7 @@ abstract contract Basic is DSMath, Stores {
function convertWethToEth(bool isEth, TokenInterface token, uint amount) internal {
if(isEth) {
token.approve(address(token), amount);
approve(token, address(token), amount);
token.withdraw(amount);
}
}

View File

@ -9,10 +9,15 @@ abstract contract Helpers is DSMath, Basic {
/**
* @dev 1Inch Address
*/
address internal constant oneInchAddr = 0x111111125434b319222CdBf8C261674aDB56F3ae;
address internal constant oneInchAddr = 0x11111112542D85B3EF69AE05771c2dCCff4fAa26;
/**
* @dev 1inch swap function sig
*/
bytes4 internal constant oneInchSig = 0x90411a32;
bytes4 internal constant oneInchSwapSig = 0x7c025200;
/**
* @dev 1inch swap function sig
*/
bytes4 internal constant oneInchUnoswapSig = 0x2e95b6c8;
}

View File

@ -25,7 +25,7 @@ abstract contract OneInchResolver is Helpers, Events {
assembly {
sig := mload(add(_data, 32))
}
isOk = sig == oneInchSig;
isOk = sig == oneInchSwapSig || sig == oneInchUnoswapSig;
}
/**
@ -74,7 +74,7 @@ abstract contract OneInchResolverHelpers is OneInchResolver {
if (address(_sellAddr) == ethAddr) {
ethAmt = oneInchData._sellAmt;
} else {
TokenInterface(_sellAddr).approve(oneInchAddr, oneInchData._sellAmt);
approve(TokenInterface(_sellAddr), oneInchAddr, oneInchData._sellAmt);
}
require(checkOneInchSig(oneInchData.callData), "Not-swap-function");
@ -124,5 +124,5 @@ abstract contract OneInch is OneInchResolverHelpers {
}
contract ConnectV2OneInch is OneInch {
string public name = "1Inch-v1";
string public name = "1Inch-v1.2";
}

View File

@ -2,7 +2,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title 1Proto.
* @title 1Inch(On-chain).
* @dev On-chain and off-chian DEX Aggregator.
*/
@ -30,7 +30,7 @@ abstract contract OneProtoResolver is Helpers, Events {
if (address(_sellAddr) == ethAddr) {
ethAmt = _sellAmt;
} else {
_sellAddr.approve(address(oneProto), _sellAmt);
approve(_sellAddr, address(oneProto), _sellAmt);
}
@ -64,7 +64,7 @@ abstract contract OneProtoResolver is Helpers, Events {
if (address(_sellAddr) == ethAddr) {
ethAmt = _sellAmt;
} else {
_sellAddr.approve(address(oneProto), _sellAmt);
approve(_sellAddr, address(oneProto), _sellAmt);
}
uint initalBal = getTokenBal(_buyAddr);
@ -221,5 +221,5 @@ abstract contract OneProto is OneProtoResolverHelpers {
}
contract ConnectV2OneProto is OneProto {
string public name = "1proto-v1";
string public name = "1Proto-v1.1";
}

View File

@ -37,7 +37,7 @@ abstract contract AaveResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(aaveProvider.getLendingPoolCore(), _amt);
approve(tokenContract, aaveProvider.getLendingPoolCore(), _amt);
}
aave.deposit{value: ethAmt}(token, _amt, referralCode);
@ -129,7 +129,7 @@ abstract contract AaveResolver is Events, Helpers {
if (token == ethAddr) {
ethAmt = _amt;
} else {
TokenInterface(token).approve(aaveProvider.getLendingPoolCore(), _amt);
approve(TokenInterface(token), aaveProvider.getLendingPoolCore(), _amt);
}
aave.repay{value: ethAmt}(token, _amt, payable(address(this)));
@ -166,5 +166,5 @@ abstract contract AaveResolver is Events, Helpers {
}
contract ConnectV2AaveV1 is AaveResolver {
string constant public name = "AaveV1-v1";
string constant public name = "AaveV1-v1.1";
}

View File

@ -42,7 +42,7 @@ abstract contract AaveResolver is Events, Helpers {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.deposit(_token, _amt, address(this), referralCode);
@ -153,7 +153,7 @@ abstract contract AaveResolver is Events, Helpers {
if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.repay(_token, _amt, rateMode, address(this));

View File

@ -42,7 +42,7 @@ abstract contract AaveResolver is Events, Helpers {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.deposit(_token, _amt, address(this), referralCode);
@ -153,7 +153,7 @@ abstract contract AaveResolver is Events, Helpers {
if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.repay(_token, _amt, rateMode, address(this));
@ -211,5 +211,5 @@ abstract contract AaveResolver is Events, Helpers {
}
contract ConnectV2AaveV2 is AaveResolver {
string constant public name = "AaveV2-v1";
string constant public name = "AaveV2-v1.1";
}

View File

@ -40,7 +40,7 @@ abstract contract CompoundResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(CTokenInterface(cToken).mint(_amt) == 0, "deposit-failed");
}
setUint(setId, _amt);
@ -196,7 +196,7 @@ abstract contract CompoundResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(cTokenContract.repayBorrow(_amt) == 0, "repay-failed.");
}
setUint(setId, _amt);
@ -254,7 +254,7 @@ abstract contract CompoundResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(ctokenContract.mint(_amt) == 0, "deposit-ctoken-failed.");
}
@ -385,7 +385,7 @@ abstract contract CompoundResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(tokenToPay);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
tokenContract.approve(cTokenPay, _amt);
approve(tokenContract, cTokenPay, _amt);
require(cTokenContract.liquidateBorrow(borrower, _amt, cTokenColl) == 0, "liquidate-failed");
}
@ -437,5 +437,5 @@ abstract contract CompoundResolver is Events, Helpers {
}
contract ConnectV2Compound is CompoundResolver {
string public name = "Compound-v1";
string public name = "Compound-v1.1";
}

View File

@ -40,7 +40,7 @@ abstract contract CreamResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(CTokenInterface(cToken).mint(_amt) == 0, "deposit-failed");
}
setUint(setId, _amt);
@ -196,7 +196,7 @@ abstract contract CreamResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(cTokenContract.repayBorrow(_amt) == 0, "repay-failed.");
}
setUint(setId, _amt);
@ -254,7 +254,7 @@ abstract contract CreamResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(cToken, _amt);
approve(tokenContract, cToken, _amt);
require(ctokenContract.mint(_amt) == 0, "deposit-ctoken-failed.");
}
@ -384,7 +384,7 @@ abstract contract CreamResolver is Events, Helpers {
} else {
TokenInterface tokenContract = TokenInterface(tokenToPay);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
tokenContract.approve(cTokenPay, _amt);
approve(tokenContract, cTokenPay, _amt);
require(cTokenContract.liquidateBorrow(borrower, _amt, cTokenColl) == 0, "liquidate-failed");
}

View File

@ -36,11 +36,11 @@ abstract contract DyDxResolver is Events, Helpers {
TokenInterface tokenContract = TokenInterface(wethAddr);
_amt = _amt == uint(-1) ? address(this).balance : _amt;
tokenContract.deposit{value: _amt}();
tokenContract.approve(address(solo), _amt);
approve(tokenContract, address(solo), _amt);
} else {
TokenInterface tokenContract = TokenInterface(token);
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
tokenContract.approve(address(solo), _amt);
approve(tokenContract, address(solo), _amt);
}
solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, true));
@ -77,7 +77,7 @@ abstract contract DyDxResolver is Events, Helpers {
if (token == ethAddr) {
TokenInterface tokenContract = TokenInterface(wethAddr);
tokenContract.approve(address(tokenContract), _amt);
approve(tokenContract, address(tokenContract), _amt);
tokenContract.withdraw(_amt);
}
@ -111,7 +111,7 @@ abstract contract DyDxResolver is Events, Helpers {
if (token == ethAddr) {
TokenInterface tokenContract = TokenInterface(wethAddr);
tokenContract.approve(address(tokenContract), _amt);
approve(tokenContract, address(tokenContract), _amt);
tokenContract.withdraw(_amt);
}
@ -148,11 +148,11 @@ abstract contract DyDxResolver is Events, Helpers {
TokenInterface tokenContract = TokenInterface(wethAddr);
require(address(this).balance >= _amt, "not-enough-eth");
tokenContract.deposit{value: _amt}();
tokenContract.approve(address(solo), _amt);
approve(tokenContract, address(solo), _amt);
} else {
TokenInterface tokenContract = TokenInterface(token);
require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token");
tokenContract.approve(address(solo), _amt);
approve(tokenContract, address(solo), _amt);
}
solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, true));

View File

@ -38,7 +38,7 @@ contract Main is Helpers, Events {
_amt = _amt == uint(-1) ? stakingToken.balanceOf(address(this)) : _amt;
stakingToken.approve(address(stakingContract), _amt);
approve(stakingToken, address(stakingContract), _amt);
stakingContract.stake(_amt);
setUint(setId, _amt);

View File

@ -54,12 +54,12 @@ abstract contract UniswapV3Resolver is Events, Helpers {
if (depositData.amount0In > 0) {
IERC20 _token0 = depositData.poolContract.token0();
convertEthToWeth(address(_token0) == wethAddr, TokenInterface(address(_token0)), depositData.amount0In);
_token0.safeApprove(address(pool), depositData.amount0In);
approve(TokenInterface(address(_token0)), address(pool), depositData.amount0In);
}
if (depositData.amount1In > 0) {
IERC20 _token1 = depositData.poolContract.token1();
convertEthToWeth(address(_token1) == wethAddr, TokenInterface(address(_token1)), depositData.amount1In);
_token1.safeApprove(address(pool), depositData.amount1In);
approve(TokenInterface(address(_token1)), address(pool), depositData.amount1In);
}
(uint amount0, uint amount1,) = depositData.poolContract.mint(depositData.mintAmount, address(this));
@ -151,7 +151,7 @@ abstract contract UniswapV3Resolver is Events, Helpers {
depositAndSwap.mintAmount;
if (address(depositAndSwap._token0) == wethAddr) {
depositAndSwap._token1.approve(address(gUniRouter), amount1In);
approve(TokenInterface(address(depositAndSwap._token1)), address(gUniRouter), amount1In);
(depositAndSwap.amount0, depositAndSwap.amount1, depositAndSwap.mintAmount) =
gUniRouter.rebalanceAndAddLiquidityETH{value: amount0In}(
@ -166,7 +166,7 @@ abstract contract UniswapV3Resolver is Events, Helpers {
address(this)
);
} else if (address(depositAndSwap._token1) == wethAddr) {
depositAndSwap._token0.approve(address(gUniRouter), amount0In);
approve(TokenInterface(address(depositAndSwap._token0)), address(gUniRouter), amount0In);
(depositAndSwap.amount0, depositAndSwap.amount1,depositAndSwap. mintAmount) =
gUniRouter.rebalanceAndAddLiquidityETH{value: amount1In}(
@ -181,8 +181,8 @@ abstract contract UniswapV3Resolver is Events, Helpers {
address(this)
);
} else {
depositAndSwap._token0.approve(address(gUniRouter), amount0In);
depositAndSwap._token1.approve(address(gUniRouter), amount1In);
approve(TokenInterface(address(depositAndSwap._token0)), address(gUniRouter), amount0In);
approve(TokenInterface(address(depositAndSwap._token1)), address(gUniRouter), amount1In);
(depositAndSwap.amount0, depositAndSwap.amount1, depositAndSwap.mintAmount) =
gUniRouter.rebalanceAndAddLiquidity(
depositAndSwap.poolContract,

View File

@ -35,7 +35,7 @@ contract Main is Helpers, Events {
_amt = _amt == uint(-1) ? stakingTokenContract.balanceOf(address(this)) : _amt;
stakingTokenContract.approve(address(stakingContract), _amt);
approve(stakingTokenContract, address(stakingContract), _amt);
stakingContract.stake(_amt);
setUint(setId, _amt);

View File

@ -2,10 +2,10 @@ pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
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 {
function enable(address) external;
function disable(address) external;
}
}

View File

@ -3,10 +3,11 @@ pragma experimental ABIEncoderV2;
/**
* @title Instapool.
* @dev Flash Loan in DSA.
* @dev Inbuilt Flash Loan in DSA
*/
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { AccountInterface } from "./interfaces.sol";
@ -20,10 +21,8 @@ contract LiquidityResolver is DSMath, Stores, Variables, Events {
/**
* @dev Borrow Flashloan and Cast spells.
* @notice Borrows flashloan and cast the spells.
* @param token Token Address.
* @param amt Token Amount.
* @param route Route to borrow.
* @param data targets & data for cast.
*/
function flashBorrowAndCast(
@ -31,25 +30,20 @@ contract LiquidityResolver is DSMath, Stores, Variables, Events {
uint amt,
uint route,
bytes memory data
) external payable returns (string memory _eventName, bytes memory _eventParam) {
) external payable {
AccountInterface(address(this)).enable(address(instaPool));
(string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[]));
address[] memory tokens = new address[](1);
uint[] memory amts = new uint[](1);
tokens[0] = token;
amts[0] = amt;
bytes memory callData = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool));
instaPool.initiateFlashLoan(tokens, amts, route, data);
instaPool.initiateFlashLoan(token, amt, route, callData);
emit LogFlashBorrow(token, amt);
AccountInterface(address(this)).disable(address(instaPool));
_eventName = "LogFlashBorrow(address,uint256)";
_eventParam = abi.encode(token, amt);
}
/**
* @dev Return token to InstaPool.
* @notice Payback borrowed flashloan.
* @param token Token Address.
* @param amt Token Amount.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
@ -60,78 +54,24 @@ contract LiquidityResolver is DSMath, Stores, Variables, Events {
uint amt,
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
) external payable {
uint _amt = getUint(getId, amt);
IERC20 tokenContract = IERC20(token);
tokenContract.safeTransfer(address(instaPool), _amt);
if (token == ethAddr) {
Address.sendValue(payable(address(instaPool)), _amt);
} else {
tokenContract.safeTransfer(address(instaPool), _amt);
}
setUint(setId, _amt);
_eventName = "LogFlashPayback(address,uint256)";
_eventParam = abi.encode(token, _amt);
}
/**
* @dev Borrow Flashloan and Cast spells.
* @notice Borrows multiple flashloan tokens and cast the spells.
* @param tokens Array of token Addresses.
* @param amts Array of token Amounts.
* @param route Route to borrow.
* @param data targets & data for cast.
*/
function flashMultiBorrowAndCast(
address[] calldata tokens,
uint[] calldata amts,
uint route,
bytes calldata data
) external payable returns (string memory _eventName, bytes memory _eventParam) {
AccountInterface(address(this)).enable(address(instaPool));
instaPool.initiateFlashLoan(tokens, amts, route, data);
AccountInterface(address(this)).disable(address(instaPool));
_eventName = "LogFlashMultiBorrow(address[],uint256[])";
_eventParam = abi.encode(tokens, amts);
}
/**
* @dev Return Multiple token liquidity to InstaPool.
* @notice Payback borrowed multiple flashloan tokens.
* @param tokens Array of token addresses.
* @param amts Array of token amounts.
* @param getId get token amounts at this IDs from `InstaMemory` Contract.
* @param setId set token amounts at this IDs in `InstaMemory` Contract.
*/
function flashMultiPayback(
address[] calldata tokens,
uint[] calldata amts,
uint[] calldata getId,
uint[] calldata setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _length = tokens.length;
uint[] memory _amts = new uint[](_length);
for (uint i = 0; i < _length; i++) {
uint _amt = getUint(getId[i], amts[i]);
_amts[i] = _amt;
IERC20 tokenContract = IERC20(tokens[i]);
tokenContract.safeTransfer(address(instaPool), _amt);
setUint(setId[i], _amt);
}
_eventName = "LogFlashMultiPayback(address[],uint256[])";
_eventParam = abi.encode(tokens, _amts);
emit LogFlashPayback(token, _amt);
}
}
contract ConnectV2InstaPool is LiquidityResolver {
string public name = "Instapool-v1";
string public name = "Instapool-v1.1";
}

View File

@ -6,7 +6,7 @@ import { InstaFlashV2Interface } from "./interfaces.sol";
contract Variables {
/**
* @dev Instapool / Receiver contract proxy
* @dev Instapool contract proxy
*/
InstaFlashV2Interface public constant instaPool = InstaFlashV2Interface(0x691d4172331a11912c6D0e6D1A002E3d7CED6a66);
InstaFlashV2Interface public constant instaPool = InstaFlashV2Interface(0x2a1739D7F07d40e76852Ca8f0D82275Aa087992F);
}

View File

@ -37,7 +37,7 @@ abstract contract KyberResolver is Helpers, Events {
} else {
TokenInterface sellContract = TokenInterface(sellAddr);
_sellAmt = _sellAmt == uint(-1) ? sellContract.balanceOf(address(this)) : _sellAmt;
sellContract.approve(address(kyber), _sellAmt);
approve(sellContract, address(kyber), _sellAmt);
}
uint _buyAmt = kyber.trade{value: ethAmt}(

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,37 @@
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 borrowAmount;
uint repayAmount;
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;
adjustTrove.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.repayAmount = repayAmount;
adjustTrove.isBorrow = borrowAmount > 0;
borrowerOperations.adjustTrove{value: adjustTrove.depositAmount}(
adjustTrove.maxFeePercentage,
adjustTrove.withdrawAmount,
adjustTrove.borrowAmount,
adjustTrove.isBorrow,
upperHint,
lowerHint
);
setUint(setIds[0], adjustTrove.depositAmount);
setUint(setIds[1], adjustTrove.withdrawAmount);
setUint(setIds[2], adjustTrove.borrowAmount);
setUint(setIds[3], adjustTrove.repayAmount);
_eventName = "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256[],uint256[])";
_eventParam = abi.encode(address(this), maxFeePercentage, adjustTrove.depositAmount, adjustTrove.withdrawAmount, adjustTrove.borrowAmount, adjustTrove.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

@ -96,7 +96,7 @@ abstract contract MakerResolver is Helpers, Events {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
tokenContract.approve(address(colAddr), _amt);
approve(tokenContract, address(colAddr), _amt);
tokenJoinContract.join(address(this), _amt);
VatLike(managerContract.vat()).frob(
@ -245,7 +245,7 @@ abstract contract MakerResolver is Helpers, Events {
require(_maxDebt >= _amt, "paying-excess-debt");
daiJoinContract.dai().approve(address(daiJoinContract), _amt);
approve(daiJoinContract.dai(), address(daiJoinContract), _amt);
daiJoinContract.join(urn, _amt);
managerContract.frob(
@ -357,7 +357,7 @@ abstract contract MakerResolver is Helpers, Events {
_amtDeposit = _amtDeposit == uint(-1) ? makerData.tokenContract.balanceOf(address(this)) : _amtDeposit;
}
makerData.tokenContract.approve(address(makerData.colAddr), _amtDeposit);
approve(makerData.tokenContract, address(makerData.colAddr), _amtDeposit);
makerData.tokenJoinContract.join(urn, _amtDeposit);
managerContract.frob(
@ -462,7 +462,7 @@ abstract contract MakerResolver is Helpers, Events {
VatLike vat = daiJoinContract.vat();
uint chi = potContract.drip();
daiJoinContract.dai().approve(address(daiJoinContract), _amt);
approve(daiJoinContract.dai(), address(daiJoinContract), _amt);
daiJoinContract.join(address(this), _amt);
if (vat.can(address(this), address(potContract)) == 0) {
vat.hope(address(potContract));
@ -519,5 +519,5 @@ abstract contract MakerResolver is Helpers, Events {
}
contract ConnectV2MakerDAO is MakerResolver {
string public constant name = "MakerDAO-v1.1";
string public constant name = "MakerDAO-v1.2";
}

View File

@ -49,7 +49,7 @@ contract OasisResolver is DSMath, Basic, Events {
bool isEth = address(_sellAddr) == wethAddr;
convertEthToWeth(isEth, _sellAddr, _expectedAmt);
_sellAddr.approve(address(oasis), _expectedAmt);
approve(_sellAddr, address(oasis), _expectedAmt);
uint _sellAmt = oasis.buyAllAmount(
address(_buyAddr),
@ -104,7 +104,7 @@ contract OasisResolver is DSMath, Basic, Events {
bool isEth = address(_sellAddr) == wethAddr;
convertEthToWeth(isEth, _sellAddr, _sellAmt);
_sellAddr.approve(address(oasis), _sellAmt);
approve(_sellAddr, address(oasis), _sellAmt);
uint _buyAmt = oasis.sellAllAmount(
address(_sellAddr),

View File

@ -36,10 +36,10 @@ abstract contract PolygonBridgeResolver is Events, Helpers {
TokenInterface _token = TokenInterface(token);
_amt = _amt == uint(-1) ? _token.balanceOf(address(this)) : _amt;
if (migrator.rootToChildToken(token) != address(0)) {
_token.approve(erc20Predicate, _amt);
approve(_token, erc20Predicate, _amt);
migrator.depositFor(targetDsa, token, abi.encode(_amt));
} else {
_token.approve(address(migratorPlasma), _amt);
approve(_token, address(migratorPlasma), _amt);
migratorPlasma.depositERC20ForUser(token, targetDsa, _amt);
}
}

View File

@ -0,0 +1,155 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { Basic } from "../../common/basic.sol";
import {
AaveV1ProviderInterface,
AaveV1Interface,
AaveV2LendingPoolProviderInterface,
AaveV2DataProviderInterface,
AaveV2Interface,
ComptrollerInterface,
CTokenInterface,
CompoundMappingInterface
} from "./interfaces.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Helpers is Basic {
using SafeERC20 for IERC20;
enum Protocol {
Aave,
AaveV2,
Compound
}
address payable constant feeCollector = 0xb1DC62EC38E6E3857a887210C38418E4A17Da5B2;
/**
* @dev Return InstaDApp Mapping Address
*/
address constant internal getMappingAddr = 0xA8F9D4aA7319C54C04404765117ddBf9448E2082; // CompoundMapping Address
/**
* @dev Return Compound Comptroller Address
*/
address constant internal getComptrollerAddress = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B; // CompoundMapping Address
/**
* @dev get Aave Provider
*/
AaveV1ProviderInterface constant internal getAaveProvider =
AaveV1ProviderInterface(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8);
/**
* @dev get Aave Lending Pool Provider
*/
AaveV2LendingPoolProviderInterface constant internal getAaveV2Provider =
AaveV2LendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
/**
* @dev get Aave Protocol Data Provider
*/
AaveV2DataProviderInterface constant internal getAaveV2DataProvider =
AaveV2DataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
/**
* @dev get Referral Code
*/
uint16 constant internal getReferralCode = 3228;
}
contract protocolHelpers is Helpers {
using SafeERC20 for IERC20;
function getWithdrawBalance(AaveV1Interface aave, address token) internal view returns (uint bal) {
(bal, , , , , , , , , ) = aave.getUserReserveData(token, address(this));
}
function getPaybackBalance(AaveV1Interface aave, address token) internal view returns (uint bal, uint fee) {
(, bal, , , , , fee, , , ) = aave.getUserReserveData(token, address(this));
}
function getTotalBorrowBalance(AaveV1Interface aave, address token) internal view returns (uint amt) {
(, uint bal, , , , , uint fee, , , ) = aave.getUserReserveData(token, address(this));
amt = add(bal, fee);
}
function getWithdrawBalanceV2(AaveV2DataProviderInterface aaveData, address token) internal view returns (uint bal) {
(bal, , , , , , , , ) = aaveData.getUserReserveData(token, address(this));
}
function getPaybackBalanceV2(AaveV2DataProviderInterface aaveData, address token, uint rateMode) internal view returns (uint bal) {
if (rateMode == 1) {
(, bal, , , , , , , ) = aaveData.getUserReserveData(token, address(this));
} else {
(, , bal, , , , , , ) = aaveData.getUserReserveData(token, address(this));
}
}
function getIsColl(AaveV1Interface aave, address token) internal view returns (bool isCol) {
(, , , , , , , , , isCol) = aave.getUserReserveData(token, address(this));
}
function getIsCollV2(AaveV2DataProviderInterface aaveData, address token) internal view returns (bool isCol) {
(, , , , , , , , isCol) = aaveData.getUserReserveData(token, address(this));
}
function getMaxBorrow(Protocol target, address token, CTokenInterface ctoken, uint rateMode) internal returns (uint amt) {
AaveV1Interface aaveV1 = AaveV1Interface(getAaveProvider.getLendingPool());
AaveV2DataProviderInterface aaveData = getAaveV2DataProvider;
if (target == Protocol.Aave) {
(uint _amt, uint _fee) = getPaybackBalance(aaveV1, token);
amt = _amt + _fee;
} else if (target == Protocol.AaveV2) {
amt = getPaybackBalanceV2(aaveData, token, rateMode);
} else if (target == Protocol.Compound) {
amt = ctoken.borrowBalanceCurrent(address(this));
}
}
function transferFees(address token, uint feeAmt) internal {
if (feeAmt > 0) {
if (token == ethAddr) {
feeCollector.transfer(feeAmt);
} else {
IERC20(token).safeTransfer(feeCollector, feeAmt);
}
}
}
function calculateFee(uint256 amount, uint256 fee, bool toAdd) internal pure returns(uint feeAmount, uint _amount){
feeAmount = wmul(amount, fee);
_amount = toAdd ? add(amount, feeAmount) : sub(amount, feeAmount);
}
function getTokenInterfaces(uint length, address[] memory tokens) internal pure returns (TokenInterface[] memory) {
TokenInterface[] memory _tokens = new TokenInterface[](length);
for (uint i = 0; i < length; i++) {
if (tokens[i] == ethAddr) {
_tokens[i] = TokenInterface(wethAddr);
} else {
_tokens[i] = TokenInterface(tokens[i]);
}
}
return _tokens;
}
function getCtokenInterfaces(uint length, string[] memory tokenIds) internal view returns (CTokenInterface[] memory) {
CTokenInterface[] memory _ctokens = new CTokenInterface[](length);
for (uint i = 0; i < length; i++) {
(address token, address cToken) = CompoundMappingInterface(getMappingAddr).getMapping(tokenIds[i]);
require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
_ctokens[i] = CTokenInterface(cToken);
}
return _ctokens;
}
}

View File

@ -0,0 +1,202 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { protocolHelpers } from "../helpers.sol";
import {
AaveV1ProviderInterface,
AaveV1Interface,
AaveV1CoreInterface,
ATokenV1Interface,
CTokenInterface
// AaveV2LendingPoolProviderInterface,
// AaveV2DataProviderInterface,
// AaveV2Interface,
} from "../interfaces.sol";
import { TokenInterface } from "../../../common/interfaces.sol";
contract AaveV1Helpers is protocolHelpers {
struct AaveV1BorrowData {
AaveV1Interface aave;
uint length;
uint fee;
Protocol target;
TokenInterface[] tokens;
CTokenInterface[] ctokens;
uint[] amts;
uint[] borrowRateModes;
uint[] paybackRateModes;
}
struct AaveV1DepositData {
AaveV1Interface aave;
AaveV1CoreInterface aaveCore;
uint length;
uint fee;
TokenInterface[] tokens;
uint[] amts;
}
function _aaveV1BorrowOne(
AaveV1Interface aave,
uint fee,
Protocol target,
TokenInterface token,
CTokenInterface ctoken,
uint amt,
uint borrowRateMode,
uint paybackRateMode
) internal returns (uint) {
if (amt > 0) {
address _token = address(token) == wethAddr ? ethAddr : address(token);
if (amt == uint(-1)) {
amt = getMaxBorrow(target, address(token), ctoken, paybackRateMode);
}
(uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
aave.borrow(_token, _amt, borrowRateMode, getReferralCode);
transferFees(_token, feeAmt);
}
return amt;
}
function _aaveV1Borrow(
AaveV1BorrowData memory data
) internal returns (uint[] memory) {
uint[] memory finalAmts = new uint[](data.length);
for (uint i = 0; i < data.length; i++) {
finalAmts[i] = _aaveV1BorrowOne(
data.aave,
data.fee,
data.target,
data.tokens[i],
data.ctokens[i],
data.amts[i],
data.borrowRateModes[i],
data.paybackRateModes[i]
);
}
return finalAmts;
}
function _aaveV1DepositOne(
AaveV1Interface aave,
AaveV1CoreInterface aaveCore,
uint fee,
TokenInterface token,
uint amt
) internal {
if (amt > 0) {
uint ethAmt;
(uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
bool isEth = address(token) == wethAddr;
address _token = isEth ? ethAddr : address(token);
if (isEth) {
ethAmt = _amt;
} else {
approve(token, address(aaveCore), _amt);
}
transferFees(_token, feeAmt);
aave.deposit{value:ethAmt}(_token, _amt, getReferralCode);
if (!getIsColl(aave, _token))
aave.setUserUseReserveAsCollateral(_token, true);
}
}
function _aaveV1Deposit(
AaveV1DepositData memory data
) internal {
for (uint i = 0; i < data.length; i++) {
_aaveV1DepositOne(
data.aave,
data.aaveCore,
data.fee,
data.tokens[i],
data.amts[i]
);
}
}
function _aaveV1WithdrawOne(
AaveV1Interface aave,
AaveV1CoreInterface aaveCore,
TokenInterface token,
uint amt
) internal returns (uint) {
if (amt > 0) {
address _token = address(token) == wethAddr ? ethAddr : address(token);
ATokenV1Interface atoken = ATokenV1Interface(aaveCore.getReserveATokenAddress(_token));
if (amt == uint(-1)) {
amt = getWithdrawBalance(aave, _token);
}
atoken.redeem(amt);
}
return amt;
}
function _aaveV1Withdraw(
AaveV1Interface aave,
AaveV1CoreInterface aaveCore,
uint length,
TokenInterface[] memory tokens,
uint[] memory amts
) internal returns (uint[] memory) {
uint[] memory finalAmts = new uint[](length);
for (uint i = 0; i < length; i++) {
finalAmts[i] = _aaveV1WithdrawOne(aave, aaveCore, tokens[i], amts[i]);
}
return finalAmts;
}
function _aaveV1PaybackOne(
AaveV1Interface aave,
AaveV1CoreInterface aaveCore,
TokenInterface token,
uint amt
) internal returns (uint) {
if (amt > 0) {
uint ethAmt;
bool isEth = address(token) == wethAddr;
address _token = isEth ? ethAddr : address(token);
if (amt == uint(-1)) {
(uint _amt, uint _fee) = getPaybackBalance(aave, _token);
amt = _amt + _fee;
}
if (isEth) {
ethAmt = amt;
} else {
approve(token, address(aaveCore), amt);
}
aave.repay{value:ethAmt}(_token, amt, payable(address(this)));
}
return amt;
}
function _aaveV1Payback(
AaveV1Interface aave,
AaveV1CoreInterface aaveCore,
uint length,
TokenInterface[] memory tokens,
uint[] memory amts
) internal {
for (uint i = 0; i < length; i++) {
_aaveV1PaybackOne(aave, aaveCore, tokens[i], amts[i]);
}
}
}

View File

@ -0,0 +1,200 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { protocolHelpers } from "../helpers.sol";
import {
// AaveV1ProviderInterface,
// AaveV1Interface,
// AaveV1CoreInterface,
AaveV2LendingPoolProviderInterface,
AaveV2DataProviderInterface,
AaveV2Interface,
ATokenV1Interface,
CTokenInterface
} from "../interfaces.sol";
import { TokenInterface } from "../../../common/interfaces.sol";
contract AaveV2Helpers is protocolHelpers {
struct AaveV2BorrowData {
AaveV2Interface aave;
uint length;
uint fee;
Protocol target;
TokenInterface[] tokens;
CTokenInterface[] ctokens;
uint[] amts;
uint[] rateModes;
}
struct AaveV2PaybackData {
AaveV2Interface aave;
AaveV2DataProviderInterface aaveData;
uint length;
TokenInterface[] tokens;
uint[] amts;
uint[] rateModes;
}
struct AaveV2WithdrawData {
AaveV2Interface aave;
AaveV2DataProviderInterface aaveData;
uint length;
TokenInterface[] tokens;
uint[] amts;
}
function _aaveV2BorrowOne(
AaveV2Interface aave,
uint fee,
Protocol target,
TokenInterface token,
CTokenInterface ctoken,
uint amt,
uint rateMode
) internal returns (uint) {
if (amt > 0) {
bool isEth = address(token) == wethAddr;
address _token = isEth ? ethAddr : address(token);
if (amt == uint(-1)) {
amt = getMaxBorrow(target, _token, ctoken, rateMode);
}
(uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
aave.borrow(address(token), _amt, rateMode, getReferralCode, address(this));
convertWethToEth(isEth, token, amt);
transferFees(_token, feeAmt);
}
return amt;
}
function _aaveV2Borrow(
AaveV2BorrowData memory data
) internal returns (uint[] memory) {
uint[] memory finalAmts = new uint[](data.length);
for (uint i = 0; i < data.length; i++) {
finalAmts[i] = _aaveV2BorrowOne(
data.aave,
data.fee,
data.target,
data.tokens[i],
data.ctokens[i],
data.amts[i],
data.rateModes[i]
);
}
return finalAmts;
}
function _aaveV2DepositOne(
AaveV2Interface aave,
AaveV2DataProviderInterface aaveData,
uint fee,
TokenInterface token,
uint amt
) internal {
if (amt > 0) {
(uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
bool isEth = address(token) == wethAddr;
address _token = isEth ? ethAddr : address(token);
transferFees(_token, feeAmt);
convertEthToWeth(isEth, token, _amt);
approve(token, address(aave), _amt);
aave.deposit(address(token), _amt, address(this), getReferralCode);
if (!getIsCollV2(aaveData, address(token))) {
aave.setUserUseReserveAsCollateral(address(token), true);
}
}
}
function _aaveV2Deposit(
AaveV2Interface aave,
AaveV2DataProviderInterface aaveData,
uint length,
uint fee,
TokenInterface[] memory tokens,
uint[] memory amts
) internal {
for (uint i = 0; i < length; i++) {
_aaveV2DepositOne(aave, aaveData, fee, tokens[i], amts[i]);
}
}
function _aaveV2WithdrawOne(
AaveV2Interface aave,
AaveV2DataProviderInterface aaveData,
TokenInterface token,
uint amt
) internal returns (uint _amt) {
if (amt > 0) {
bool isEth = address(token) == wethAddr;
aave.withdraw(address(token), amt, address(this));
_amt = amt == uint(-1) ? getWithdrawBalanceV2(aaveData, address(token)) : amt;
convertWethToEth(isEth, token, _amt);
}
}
function _aaveV2Withdraw(
AaveV2WithdrawData memory data
) internal returns (uint[] memory) {
uint[] memory finalAmts = new uint[](data.length);
for (uint i = 0; i < data.length; i++) {
finalAmts[i] = _aaveV2WithdrawOne(
data.aave,
data.aaveData,
data.tokens[i],
data.amts[i]
);
}
return finalAmts;
}
function _aaveV2PaybackOne(
AaveV2Interface aave,
AaveV2DataProviderInterface aaveData,
TokenInterface token,
uint amt,
uint rateMode
) internal returns (uint _amt) {
if (amt > 0) {
bool isEth = address(token) == wethAddr;
_amt = amt == uint(-1) ? getPaybackBalanceV2(aaveData, address(token), rateMode) : amt;
convertEthToWeth(isEth, token, _amt);
approve(token, address(aave), _amt);
aave.repay(address(token), _amt, rateMode, address(this));
}
}
function _aaveV2Payback(
AaveV2PaybackData memory data
) internal {
for (uint i = 0; i < data.length; i++) {
_aaveV2PaybackOne(
data.aave,
data.aaveData,
data.tokens[i],
data.amts[i],
data.rateModes[i]
);
}
}
}

View File

@ -0,0 +1,160 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { protocolHelpers } from "../helpers.sol";
import {
ComptrollerInterface,
CTokenInterface,
CompoundMappingInterface,
CETHInterface
} from "../interfaces.sol";
import { TokenInterface } from "../../../common/interfaces.sol";
contract CompoundHelpers is protocolHelpers {
struct CompoundBorrowData {
uint length;
uint fee;
Protocol target;
CTokenInterface[] ctokens;
TokenInterface[] tokens;
uint[] amts;
uint[] rateModes;
}
function _compEnterMarkets(uint length, CTokenInterface[] memory ctokens) internal {
ComptrollerInterface troller = ComptrollerInterface(getComptrollerAddress);
address[] memory _cTokens = new address[](length);
for (uint i = 0; i < length; i++) {
_cTokens[i] = address(ctokens[i]);
}
troller.enterMarkets(_cTokens);
}
function _compBorrowOne(
uint fee,
CTokenInterface ctoken,
TokenInterface token,
uint amt,
Protocol target,
uint rateMode
) internal returns (uint) {
if (amt > 0) {
address _token = address(token) == wethAddr ? ethAddr : address(token);
if (amt == uint(-1)) {
amt = getMaxBorrow(target, address(token), ctoken, rateMode);
}
(uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
require(ctoken.borrow(_amt) == 0, "borrow-failed-collateral?");
transferFees(_token, feeAmt);
}
return amt;
}
function _compBorrow(
CompoundBorrowData memory data
) internal returns (uint[] memory) {
uint[] memory finalAmts = new uint[](data.length);
for (uint i = 0; i < data.length; i++) {
finalAmts[i] = _compBorrowOne(
data.fee,
data.ctokens[i],
data.tokens[i],
data.amts[i],
data.target,
data.rateModes[i]
);
}
return finalAmts;
}
function _compDepositOne(uint fee, CTokenInterface ctoken, TokenInterface token, uint amt) internal {
if (amt > 0) {
address _token = address(token) == wethAddr ? ethAddr : address(token);
(uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
if (_token != ethAddr) {
approve(token, address(ctoken), _amt);
require(ctoken.mint(_amt) == 0, "deposit-failed");
} else {
CETHInterface(address(ctoken)).mint{value:_amt}();
}
transferFees(_token, feeAmt);
}
}
function _compDeposit(
uint length,
uint fee,
CTokenInterface[] memory ctokens,
TokenInterface[] memory tokens,
uint[] memory amts
) internal {
for (uint i = 0; i < length; i++) {
_compDepositOne(fee, ctokens[i], tokens[i], amts[i]);
}
}
function _compWithdrawOne(CTokenInterface ctoken, TokenInterface token, uint amt) internal returns (uint) {
if (amt > 0) {
if (amt == uint(-1)) {
bool isEth = address(token) == wethAddr;
uint initalBal = isEth ? address(this).balance : token.balanceOf(address(this));
require(ctoken.redeem(ctoken.balanceOf(address(this))) == 0, "withdraw-failed");
uint finalBal = isEth ? address(this).balance : token.balanceOf(address(this));
amt = sub(finalBal, initalBal);
} else {
require(ctoken.redeemUnderlying(amt) == 0, "withdraw-failed");
}
}
return amt;
}
function _compWithdraw(
uint length,
CTokenInterface[] memory ctokens,
TokenInterface[] memory tokens,
uint[] memory amts
) internal returns(uint[] memory) {
uint[] memory finalAmts = new uint[](length);
for (uint i = 0; i < length; i++) {
finalAmts[i] = _compWithdrawOne(ctokens[i], tokens[i], amts[i]);
}
return finalAmts;
}
function _compPaybackOne(CTokenInterface ctoken, TokenInterface token, uint amt) internal returns (uint) {
if (amt > 0) {
if (amt == uint(-1)) {
amt = ctoken.borrowBalanceCurrent(address(this));
}
if (address(token) != wethAddr) {
approve(token, address(ctoken), amt);
require(ctoken.repayBorrow(amt) == 0, "repay-failed.");
} else {
CETHInterface(address(ctoken)).repayBorrow{value:amt}();
}
}
return amt;
}
function _compPayback(
uint length,
CTokenInterface[] memory ctokens,
TokenInterface[] memory tokens,
uint[] memory amts
) internal {
for (uint i = 0; i < length; i++) {
_compPaybackOne(ctokens[i], tokens[i], amts[i]);
}
}
}

View File

@ -0,0 +1,113 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
// Compound Helpers
interface CTokenInterface {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function borrow(uint borrowAmount) external returns (uint);
function repayBorrow(uint repayAmount) external returns (uint);
function borrowBalanceCurrent(address account) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function balanceOf(address owner) external view returns (uint256 balance);
}
interface CETHInterface {
function mint() external payable;
function repayBorrow() external payable;
}
interface CompoundMappingInterface {
function cTokenMapping(string calldata tokenId) external view returns (address);
function getMapping(string calldata tokenId) external view returns (address, address);
}
interface ComptrollerInterface {
function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
}
// End Compound Helpers
// Aave v1 Helpers
interface AaveV1Interface {
function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external payable;
function redeemUnderlying(
address _reserve,
address payable _user,
uint256 _amount,
uint256 _aTokenBalanceAfterRedeem
) external;
function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral) external;
function getUserReserveData(address _reserve, address _user) external view returns (
uint256 currentATokenBalance,
uint256 currentBorrowBalance,
uint256 principalBorrowBalance,
uint256 borrowRateMode,
uint256 borrowRate,
uint256 liquidityRate,
uint256 originationFee,
uint256 variableBorrowIndex,
uint256 lastUpdateTimestamp,
bool usageAsCollateralEnabled
);
function borrow(address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode) external;
function repay(address _reserve, uint256 _amount, address payable _onBehalfOf) external payable;
}
interface AaveV1ProviderInterface {
function getLendingPool() external view returns (address);
function getLendingPoolCore() external view returns (address);
}
interface AaveV1CoreInterface {
function getReserveATokenAddress(address _reserve) external view returns (address);
}
interface ATokenV1Interface {
function redeem(uint256 _amount) external;
function balanceOf(address _user) external view returns(uint256);
function principalBalanceOf(address _user) external view returns(uint256);
function allowance(address, address) external view returns (uint);
function approve(address, uint) external;
function transfer(address, uint) external returns (bool);
function transferFrom(address, address, uint) external returns (bool);
}
// End Aave v1 Helpers
// Aave v2 Helpers
interface AaveV2Interface {
function deposit(address _asset, uint256 _amount, address _onBehalfOf, uint16 _referralCode) external;
function withdraw(address _asset, uint256 _amount, address _to) external;
function borrow(
address _asset,
uint256 _amount,
uint256 _interestRateMode,
uint16 _referralCode,
address _onBehalfOf
) external;
function repay(address _asset, uint256 _amount, uint256 _rateMode, address _onBehalfOf) external;
function setUserUseReserveAsCollateral(address _asset, bool _useAsCollateral) external;
}
interface AaveV2LendingPoolProviderInterface {
function getLendingPool() external view returns (address);
}
// Aave Protocol Data Provider
interface AaveV2DataProviderInterface {
function getUserReserveData(address _asset, address _user) external view returns (
uint256 currentATokenBalance,
uint256 currentStableDebt,
uint256 currentVariableDebt,
uint256 principalStableDebt,
uint256 scaledVariableDebt,
uint256 stableBorrowRate,
uint256 liquidityRate,
uint40 stableRateLastUpdated,
bool usageAsCollateralEnabled
);
}
// End Aave v2 Helpers

View File

@ -0,0 +1,339 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title Refinance.
* @dev Refinancing.
*/
import { TokenInterface } from "../../common/interfaces.sol";
import {
AaveV1ProviderInterface,
AaveV1Interface,
AaveV1CoreInterface,
AaveV2LendingPoolProviderInterface,
AaveV2DataProviderInterface,
AaveV2Interface,
ComptrollerInterface,
CTokenInterface,
CompoundMappingInterface
} from "./interfaces.sol";
import { AaveV1Helpers } from "./helpers/aaveV1.sol";
import { AaveV2Helpers } from "./helpers/aaveV2.sol";
import { CompoundHelpers } from "./helpers/compound.sol";
contract RefinanceResolver is CompoundHelpers, AaveV1Helpers, AaveV2Helpers {
struct RefinanceData {
Protocol source;
Protocol target;
uint collateralFee;
uint debtFee;
address[] tokens;
string[] ctokenIds;
uint[] borrowAmts;
uint[] withdrawAmts;
uint[] borrowRateModes;
uint[] paybackRateModes;
}
struct RefinanceInternalData {
AaveV2Interface aaveV2;
AaveV1Interface aaveV1;
AaveV1CoreInterface aaveCore;
AaveV2DataProviderInterface aaveData;
uint[] depositAmts;
uint[] paybackAmts;
TokenInterface[] tokens;
CTokenInterface[] _ctokens;
}
function _refinance(RefinanceData calldata data)
internal returns (string memory _eventName, bytes memory _eventParam)
{
require(data.source != data.target, "source-and-target-unequal");
uint length = data.tokens.length;
require(data.borrowAmts.length == length, "length-mismatch");
require(data.withdrawAmts.length == length, "length-mismatch");
require(data.borrowRateModes.length == length, "length-mismatch");
require(data.paybackRateModes.length == length, "length-mismatch");
require(data.ctokenIds.length == length, "length-mismatch");
RefinanceInternalData memory refinanceInternalData;
refinanceInternalData.aaveV2 = AaveV2Interface(getAaveV2Provider.getLendingPool());
refinanceInternalData.aaveV1 = AaveV1Interface(getAaveProvider.getLendingPool());
refinanceInternalData.aaveCore = AaveV1CoreInterface(getAaveProvider.getLendingPoolCore());
refinanceInternalData.aaveData = getAaveV2DataProvider;
refinanceInternalData.depositAmts;
refinanceInternalData.paybackAmts;
refinanceInternalData.tokens = getTokenInterfaces(length, data.tokens);
refinanceInternalData._ctokens = getCtokenInterfaces(length, data.ctokenIds);
if (data.source == Protocol.Aave && data.target == Protocol.AaveV2) {
AaveV2BorrowData memory _aaveV2BorrowData;
_aaveV2BorrowData.aave = refinanceInternalData.aaveV2;
_aaveV2BorrowData.length = length;
_aaveV2BorrowData.fee = data.debtFee;
_aaveV2BorrowData.target = data.source;
_aaveV2BorrowData.tokens = refinanceInternalData.tokens;
_aaveV2BorrowData.ctokens = refinanceInternalData._ctokens;
_aaveV2BorrowData.amts = data.borrowAmts;
_aaveV2BorrowData.rateModes = data.borrowRateModes;
{
refinanceInternalData.paybackAmts = _aaveV2Borrow(_aaveV2BorrowData);
_aaveV1Payback(
refinanceInternalData.aaveV1,
refinanceInternalData.aaveCore,
length,
refinanceInternalData.tokens,
refinanceInternalData.paybackAmts
);
refinanceInternalData.depositAmts = _aaveV1Withdraw(
refinanceInternalData.aaveV1,
refinanceInternalData.aaveCore,
length,
refinanceInternalData.tokens,
data.withdrawAmts
);
_aaveV2Deposit(
refinanceInternalData.aaveV2,
refinanceInternalData.aaveData,
length,
data.collateralFee,
refinanceInternalData.tokens,
refinanceInternalData.depositAmts
);
}
} else if (data.source == Protocol.Aave && data.target == Protocol.Compound) {
_compEnterMarkets(length, refinanceInternalData._ctokens);
CompoundBorrowData memory _compoundBorrowData;
_compoundBorrowData.length = length;
_compoundBorrowData.fee = data.debtFee;
_compoundBorrowData.target = data.source;
_compoundBorrowData.ctokens = refinanceInternalData._ctokens;
_compoundBorrowData.tokens = refinanceInternalData.tokens;
_compoundBorrowData.amts = data.borrowAmts;
_compoundBorrowData.rateModes = data.borrowRateModes;
refinanceInternalData.paybackAmts = _compBorrow(_compoundBorrowData);
_aaveV1Payback(
refinanceInternalData.aaveV1,
refinanceInternalData.aaveCore,
length,
refinanceInternalData.tokens,
refinanceInternalData.paybackAmts
);
refinanceInternalData.depositAmts = _aaveV1Withdraw(
refinanceInternalData.aaveV1,
refinanceInternalData.aaveCore,
length,
refinanceInternalData.tokens,
data.withdrawAmts
);
_compDeposit(
length,
data.collateralFee,
refinanceInternalData._ctokens,
refinanceInternalData.tokens,
refinanceInternalData.depositAmts
);
} else if (data.source == Protocol.AaveV2 && data.target == Protocol.Aave) {
AaveV1BorrowData memory _aaveV1BorrowData;
AaveV2PaybackData memory _aaveV2PaybackData;
AaveV2WithdrawData memory _aaveV2WithdrawData;
{
_aaveV1BorrowData.aave = refinanceInternalData.aaveV1;
_aaveV1BorrowData.length = length;
_aaveV1BorrowData.fee = data.debtFee;
_aaveV1BorrowData.target = data.source;
_aaveV1BorrowData.tokens = refinanceInternalData.tokens;
_aaveV1BorrowData.ctokens = refinanceInternalData._ctokens;
_aaveV1BorrowData.amts = data.borrowAmts;
_aaveV1BorrowData.borrowRateModes = data.borrowRateModes;
_aaveV1BorrowData.paybackRateModes = data.paybackRateModes;
refinanceInternalData.paybackAmts = _aaveV1Borrow(_aaveV1BorrowData);
}
{
_aaveV2PaybackData.aave = refinanceInternalData.aaveV2;
_aaveV2PaybackData.aaveData = refinanceInternalData.aaveData;
_aaveV2PaybackData.length = length;
_aaveV2PaybackData.tokens = refinanceInternalData.tokens;
_aaveV2PaybackData.amts = refinanceInternalData.paybackAmts;
_aaveV2PaybackData.rateModes = data.paybackRateModes;
_aaveV2Payback(_aaveV2PaybackData);
}
{
_aaveV2WithdrawData.aave = refinanceInternalData.aaveV2;
_aaveV2WithdrawData.aaveData = refinanceInternalData.aaveData;
_aaveV2WithdrawData.length = length;
_aaveV2WithdrawData.tokens = refinanceInternalData.tokens;
_aaveV2WithdrawData.amts = data.withdrawAmts;
refinanceInternalData.depositAmts = _aaveV2Withdraw(_aaveV2WithdrawData);
}
{
AaveV1DepositData memory _aaveV1DepositData;
_aaveV1DepositData.aave = refinanceInternalData.aaveV1;
_aaveV1DepositData.aaveCore = refinanceInternalData.aaveCore;
_aaveV1DepositData.length = length;
_aaveV1DepositData.fee = data.collateralFee;
_aaveV1DepositData.tokens = refinanceInternalData.tokens;
_aaveV1DepositData.amts = refinanceInternalData.depositAmts;
_aaveV1Deposit(_aaveV1DepositData);
}
} else if (data.source == Protocol.AaveV2 && data.target == Protocol.Compound) {
_compEnterMarkets(length, refinanceInternalData._ctokens);
{
CompoundBorrowData memory _compoundBorrowData;
_compoundBorrowData.length = length;
_compoundBorrowData.fee = data.debtFee;
_compoundBorrowData.target = data.source;
_compoundBorrowData.ctokens = refinanceInternalData._ctokens;
_compoundBorrowData.tokens = refinanceInternalData.tokens;
_compoundBorrowData.amts = data.borrowAmts;
_compoundBorrowData.rateModes = data.borrowRateModes;
refinanceInternalData.paybackAmts = _compBorrow(_compoundBorrowData);
}
AaveV2PaybackData memory _aaveV2PaybackData;
_aaveV2PaybackData.aave = refinanceInternalData.aaveV2;
_aaveV2PaybackData.aaveData = refinanceInternalData.aaveData;
_aaveV2PaybackData.length = length;
_aaveV2PaybackData.tokens = refinanceInternalData.tokens;
_aaveV2PaybackData.amts = refinanceInternalData.paybackAmts;
_aaveV2PaybackData.rateModes = data.paybackRateModes;
_aaveV2Payback(_aaveV2PaybackData);
{
AaveV2WithdrawData memory _aaveV2WithdrawData;
_aaveV2WithdrawData.aave = refinanceInternalData.aaveV2;
_aaveV2WithdrawData.aaveData = refinanceInternalData.aaveData;
_aaveV2WithdrawData.length = length;
_aaveV2WithdrawData.tokens = refinanceInternalData.tokens;
_aaveV2WithdrawData.amts = data.withdrawAmts;
refinanceInternalData.depositAmts = _aaveV2Withdraw(_aaveV2WithdrawData);
}
_compDeposit(
length,
data.collateralFee,
refinanceInternalData._ctokens,
refinanceInternalData.tokens,
refinanceInternalData.depositAmts
);
} else if (data.source == Protocol.Compound && data.target == Protocol.Aave) {
AaveV1BorrowData memory _aaveV1BorrowData;
_aaveV1BorrowData.aave = refinanceInternalData.aaveV1;
_aaveV1BorrowData.length = length;
_aaveV1BorrowData.fee = data.debtFee;
_aaveV1BorrowData.target = data.source;
_aaveV1BorrowData.tokens = refinanceInternalData.tokens;
_aaveV1BorrowData.ctokens = refinanceInternalData._ctokens;
_aaveV1BorrowData.amts = data.borrowAmts;
_aaveV1BorrowData.borrowRateModes = data.borrowRateModes;
_aaveV1BorrowData.paybackRateModes = data.paybackRateModes;
refinanceInternalData.paybackAmts = _aaveV1Borrow(_aaveV1BorrowData);
{
_compPayback(
length,
refinanceInternalData._ctokens,
refinanceInternalData.tokens,
refinanceInternalData.paybackAmts
);
refinanceInternalData.depositAmts = _compWithdraw(
length,
refinanceInternalData._ctokens,
refinanceInternalData.tokens,
data.withdrawAmts
);
}
{
AaveV1DepositData memory _aaveV1DepositData;
_aaveV1DepositData.aave = refinanceInternalData.aaveV1;
_aaveV1DepositData.aaveCore = refinanceInternalData.aaveCore;
_aaveV1DepositData.length = length;
_aaveV1DepositData.fee = data.collateralFee;
_aaveV1DepositData.tokens = refinanceInternalData.tokens;
_aaveV1DepositData.amts = refinanceInternalData.depositAmts;
_aaveV1Deposit(_aaveV1DepositData);
}
} else if (data.source == Protocol.Compound && data.target == Protocol.AaveV2) {
AaveV2BorrowData memory _aaveV2BorrowData;
_aaveV2BorrowData.aave = refinanceInternalData.aaveV2;
_aaveV2BorrowData.length = length;
_aaveV2BorrowData.fee = data.debtFee;
_aaveV2BorrowData.target = data.source;
_aaveV2BorrowData.tokens = refinanceInternalData.tokens;
_aaveV2BorrowData.ctokens = refinanceInternalData._ctokens;
_aaveV2BorrowData.amts = data.borrowAmts;
_aaveV2BorrowData.rateModes = data.borrowRateModes;
refinanceInternalData.paybackAmts = _aaveV2Borrow(_aaveV2BorrowData);
_compPayback(length, refinanceInternalData._ctokens, refinanceInternalData.tokens, refinanceInternalData.paybackAmts);
refinanceInternalData.depositAmts = _compWithdraw(
length,
refinanceInternalData._ctokens,
refinanceInternalData.tokens,
data.withdrawAmts
);
_aaveV2Deposit(
refinanceInternalData.aaveV2,
refinanceInternalData.aaveData,
length,
data.collateralFee,
refinanceInternalData.tokens,
refinanceInternalData.depositAmts
);
} else {
revert("invalid-options");
}
}
/**
* @dev Refinance
* @notice Refinancing between AaveV1, AaveV2 and Compound
* @param data refinance data.
*/
function refinance(RefinanceData calldata data)
external payable returns (string memory _eventName, bytes memory _eventParam) {
(_eventName, _eventParam) = _refinance(data);
}
}
contract ConnectV2Refinance is RefinanceResolver {
string public name = "Refinance-v1.0";
}

View File

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

View File

@ -73,7 +73,7 @@ abstract contract GebResolver is Helpers, Events {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
tokenContract.approve(address(colAddr), _amt);
approve(tokenContract, address(colAddr), _amt);
tokenJoinContract.join(address(this), _amt);
SafeEngineLike(managerContract.safeEngine()).modifySAFECollateralization(
@ -222,7 +222,7 @@ abstract contract GebResolver is Helpers, Events {
require(_maxDebt >= _amt, "paying-excess-debt");
coinJoinContract.systemCoin().approve(address(coinJoinContract), _amt);
approve(coinJoinContract.systemCoin(), address(coinJoinContract), _amt);
coinJoinContract.join(handler, _amt);
managerContract.modifySAFECollateralization(
@ -335,7 +335,7 @@ abstract contract GebResolver is Helpers, Events {
_amtDeposit = _amtDeposit == uint(-1) ? gebData.tokenContract.balanceOf(address(this)) : _amtDeposit;
}
gebData.tokenContract.approve(address(gebData.colAddr), _amtDeposit);
approve(gebData.tokenContract, address(gebData.colAddr), _amtDeposit);
gebData.tokenJoinContract.join(handler, _amtDeposit);
managerContract.modifySAFECollateralization(

View File

@ -78,8 +78,8 @@ abstract contract Helpers is DSMath, Basic {
isEth = address(_tokenB) == wethAddr;
convertEthToWeth(isEth, _tokenB, _amtB);
_tokenA.approve(address(router), _amtA);
_tokenB.approve(address(router), _amtB);
approve(_tokenA, address(router), _amtA);
approve(_tokenB, address(router), _amtB);
uint minAmtA = getMinAmount(_tokenA, _amtA, slippage);
uint minAmtB = getMinAmount(_tokenB, _amtB, slippage);
@ -141,6 +141,6 @@ abstract contract Helpers is DSMath, Basic {
TokenInterface uniToken = TokenInterface(exchangeAddr);
_uniAmt = _amt == uint(-1) ? uniToken.balanceOf(address(this)) : _amt;
uniToken.approve(address(router), _uniAmt);
approve(uniToken, address(router), _uniAmt);
}
}

View File

@ -114,7 +114,7 @@ abstract contract UniswapResolver is Helpers, Events {
bool isEth = address(_sellAddr) == wethAddr;
convertEthToWeth(isEth, _sellAddr, _expectedAmt);
_sellAddr.approve(address(router), _expectedAmt);
approve(_sellAddr, address(router), _expectedAmt);
uint _sellAmt = router.swapTokensForExactTokens(
_buyAmt,
@ -171,7 +171,7 @@ abstract contract UniswapResolver is Helpers, Events {
bool isEth = address(_sellAddr) == wethAddr;
convertEthToWeth(isEth, _sellAddr, _sellAmt);
_sellAddr.approve(address(router), _sellAmt);
approve(_sellAddr, address(router), _sellAmt);
uint _buyAmt = router.swapExactTokensForTokens(
_sellAmt,
@ -192,5 +192,5 @@ abstract contract UniswapResolver is Helpers, Events {
}
contract ConnectV2UniswapV2 is UniswapResolver {
string public constant name = "UniswapV2-v1";
string public constant name = "UniswapV2-v1.1";
}

View File

@ -1,8 +1,8 @@
pragma solidity ^0.7.0;
/**
* @title Basic.
* @dev Deposit & Withdraw from DSA.
* @title WETH.
* @dev Wrap and Unwrap WETH.
*/
import { DSMath } from "../../common/math.sol";
@ -50,7 +50,7 @@ abstract contract Resolver is Events, DSMath, Basic, Helpers {
uint _amt = getUint(getId, amt);
_amt = _amt == uint(-1) ? wethContract.balanceOf(address(this)) : _amt;
wethContract.approve(wethAddr, _amt);
approve(wethContract, wethAddr, _amt);
wethContract.withdraw(_amt);
setUint(setId, _amt);

View File

@ -82,14 +82,21 @@ abstract contract OneHelpers is Stores, DSMath {
* @dev Return 1Inch Address
*/
function getOneInchAddress() internal pure returns (address) {
return 0x111111125434b319222CdBf8C261674aDB56F3ae;
return 0x11111112542D85B3EF69AE05771c2dCCff4fAa26;
}
/**
* @dev Return 1inch swap function sig
*/
function getOneInchSig() internal pure returns (bytes4) {
return 0x90411a32;
function getOneInchSwapSig() internal pure returns (bytes4) {
return 0x7c025200;
}
/**
* @dev Return 1inch swap function sig
*/
function getOneInchUnoswapSig() internal pure returns (bytes4) {
return 0x2e95b6c8;
}
function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
@ -224,7 +231,7 @@ abstract contract OneInchResolver is OneProtoResolver {
assembly {
sig := mload(add(_data, 32))
}
isOk = sig == getOneInchSig();
isOk = sig == getOneInchSwapSig() || sig == getOneInchUnoswapSig();
}
struct OneInchData {
@ -585,5 +592,12 @@ abstract contract OneInch is OneProto {
}
contract ConnectOne is OneInch {
string public name = "1inch-1proto-v1";
string public name = "1inch-1proto-v1.2";
/**
* @dev Connector Details
*/
function connectorID() public pure returns(uint _type, uint _id) {
(_type, _id) = (1, 98);
}
}

View File

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

View File

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

View File

@ -32,13 +32,22 @@ abstract contract Basic is DSMath, Stores {
_sell = sell == maticAddr ? TokenInterface(wmaticAddr) : TokenInterface(sell);
}
function approve(TokenInterface token, address spender, uint256 amount) internal {
try token.approve(spender, amount) {
} catch {
token.approve(spender, 0);
token.approve(spender, amount);
}
}
function convertMaticToWmatic(bool isMatic, TokenInterface token, uint amount) internal {
if(isMatic) token.deposit{value: amount}();
}
function convertWmaticToMatic(bool isMatic, TokenInterface token, uint amount) internal {
if(isMatic) {
token.approve(address(token), amount);
approve(token, address(token), amount);
token.withdraw(amount);
}
}

View File

@ -0,0 +1,12 @@
pragma solidity ^0.7.0;
contract Events {
event LogSell(
address indexed buyToken,
address indexed sellToken,
uint256 buyAmt,
uint256 sellAmt,
uint256 getId,
uint256 setId
);
}

View File

@ -0,0 +1,23 @@
pragma solidity ^0.7.0;
import { TokenInterface } from "../../common/interfaces.sol";
import { DSMath } from "../../common/math.sol";
import { Basic } from "../../common/basic.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev 1Inch Address
*/
address internal constant oneInchAddr = 0x11111112542D85B3EF69AE05771c2dCCff4fAa26;
/**
* @dev 1inch swap function sig
*/
bytes4 internal constant oneInchSwapSig = 0x7c025200;
/**
* @dev 1inch swap function sig
*/
bytes4 internal constant oneInchUnoswapSig = 0x2e95b6c8;
}

View File

@ -0,0 +1,30 @@
pragma solidity ^0.7.0;
import { TokenInterface } from "../../common/interfaces.sol";
interface OneInchInterace {
function swap(
TokenInterface fromToken,
TokenInterface toToken,
uint256 fromTokenAmount,
uint256 minReturnAmount,
uint256 guaranteedAmount,
address payable referrer,
address[] calldata callAddresses,
bytes calldata callDataConcat,
uint256[] calldata starts,
uint256[] calldata gasLimitsAndValues
)
external
payable
returns (uint256 returnAmount);
}
struct OneInchData {
TokenInterface sellToken;
TokenInterface buyToken;
uint _sellAmt;
uint _buyAmt;
uint unitAmt;
bytes callData;
}

View File

@ -0,0 +1,128 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/**
* @title 1Inch.
* @dev On-chain DEX Aggregator.
*/
// import files from common directory
import { TokenInterface , MemoryInterface } from "../../common/interfaces.sol";
import { Stores } from "../../common/stores.sol";
import { OneInchInterace, OneInchData } from "./interface.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
abstract contract OneInchResolver is Helpers, Events {
/**
* @dev 1inch swap uses `.call()`. This function restrict it to call only swap/trade functionality
* @param callData - calldata to extract the first 4 bytes for checking function signature
*/
function checkOneInchSig(bytes memory callData) internal pure returns(bool isOk) {
bytes memory _data = callData;
bytes4 sig;
// solium-disable-next-line security/no-inline-assembly
assembly {
sig := mload(add(_data, 32))
}
isOk = sig == oneInchSwapSig || sig == oneInchUnoswapSig;
}
/**
* @dev 1inch API swap handler
* @param oneInchData - contains data returned from 1inch API. Struct defined in interfaces.sol
* @param ethAmt - Eth to swap for .value()
*/
function oneInchSwap(
OneInchData memory oneInchData,
uint ethAmt
) internal returns (uint buyAmt) {
TokenInterface buyToken = oneInchData.buyToken;
(uint _buyDec, uint _sellDec) = getTokensDec(buyToken, oneInchData.sellToken);
uint _sellAmt18 = convertTo18(_sellDec, oneInchData._sellAmt);
uint _slippageAmt = convert18ToDec(_buyDec, wmul(oneInchData.unitAmt, _sellAmt18));
uint initalBal = getTokenBal(buyToken);
// solium-disable-next-line security/no-call-value
(bool success, ) = oneInchAddr.call{value: ethAmt}(oneInchData.callData);
if (!success) revert("1Inch-swap-failed");
uint finalBal = getTokenBal(buyToken);
buyAmt = sub(finalBal, initalBal);
require(_slippageAmt <= buyAmt, "Too much slippage");
}
}
abstract contract OneInchResolverHelpers is OneInchResolver {
/**
* @dev Gets the swapping data from 1inch's API.
* @param oneInchData Struct with multiple swap data defined in interfaces.sol
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function _sell(
OneInchData memory oneInchData,
uint setId
) internal returns (OneInchData memory) {
TokenInterface _sellAddr = oneInchData.sellToken;
uint ethAmt;
if (address(_sellAddr) == maticAddr) {
ethAmt = oneInchData._sellAmt;
} else {
approve(TokenInterface(_sellAddr), oneInchAddr, oneInchData._sellAmt);
}
require(checkOneInchSig(oneInchData.callData), "Not-swap-function");
oneInchData._buyAmt = oneInchSwap(oneInchData, ethAmt);
setUint(setId, oneInchData._buyAmt);
return oneInchData;
// emitLogSellThree(oneInchData, setId);
}
}
abstract contract OneInch is OneInchResolverHelpers {
/**
* @dev Sell ETH/ERC20_Token using 1Inch.
* @notice Swap tokens from exchanges like kyber, 0x etc, with calculation done off-chain.
* @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
* @param sellAmt The amount of the token to sell.
* @param unitAmt The amount of buyAmt/sellAmt with slippage.
* @param callData Data from 1inch API.
* @param setId ID stores the amount of token brought.
*/
function sell(
address buyAddr,
address sellAddr,
uint sellAmt,
uint unitAmt,
bytes calldata callData,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
OneInchData memory oneInchData = OneInchData({
buyToken: TokenInterface(buyAddr),
sellToken: TokenInterface(sellAddr),
unitAmt: unitAmt,
callData: callData,
_sellAmt: sellAmt,
_buyAmt: 0
});
oneInchData = _sell(oneInchData, setId);
_eventName = "LogSell(address,address,uint256,uint256,uint256,uint256)";
_eventParam = abi.encode(buyAddr, sellAddr, oneInchData._buyAmt, oneInchData._sellAmt, 0, setId);
}
}
contract ConnectV2OneInchPolygon is OneInch {
string public name = "1Inch-v1";
}

View File

@ -43,7 +43,7 @@ abstract contract AaveResolver is Events, Helpers {
_amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
}
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.deposit(_token, _amt, address(this), referralCode);
@ -154,7 +154,7 @@ abstract contract AaveResolver is Events, Helpers {
if (isEth) convertMaticToWmatic(isEth, tokenContract, _amt);
tokenContract.approve(address(aave), _amt);
approve(tokenContract, address(aave), _amt);
aave.repay(_token, _amt, rateMode, address(this));
@ -211,6 +211,6 @@ abstract contract AaveResolver is Events, Helpers {
}
}
contract ConnectV2AaveV2 is AaveResolver {
contract ConnectV2AaveV2Polygon is AaveResolver {
string constant public name = "AaveV2-v1";
}

View File

@ -45,7 +45,7 @@ abstract contract Helpers is DSMath, Basic {
maticAmt = swapData._sellAmt;
} else {
address tokenProxy = AugustusSwapperInterface(paraswap).getTokenTransferProxy();
TokenInterface(_sellAddr).approve(tokenProxy, swapData._sellAmt);
approve(TokenInterface(_sellAddr), tokenProxy, swapData._sellAmt);
}
swapData._buyAmt = _swapHelper(swapData, maticAmt);

View File

@ -1,32 +1,42 @@
{
"1" : {
"1INCH-A": "0x2A6d6d4EE84015F7D64B4d1F66a409bA3f2BAC00",
"1INCH-B": "0x36880391afb430e99d43fe94217446b56d4f2c5b",
"AAVE-V1-A": "0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14",
"AAVE-V2-A": "0x497Bc53507DF17e60F731e9534cff74E8BC9DBb8",
"AUTHORITY-A": "0x351Bb32e90C35647Df7a584f3c1a3A0c38F31c68",
"BASIC-A": "0x9926955e0Dd681Dc303370C52f4Ad0a4dd061687",
"COMP-A": "0xB446e325D44C52b93eC122Bf76301f235f90B9c9",
"COMPOUND-A": "0x911F4e4e762AeFA6F2Fc1b24e6B1A928200a88a8",
"MAKERDAO-A": "0x29AA7b765008b5dDbD687413B7F0D6E9d349F765",
"UNISWAP-A": "0xA4BF319968986D2352FA1c550D781bBFCCE3FcaB",
"POLYGON-BRIDGE-A": "0x1b79B302132370B434fb7807b36CB72FB0510aD5",
"AAVE-CLAIM-A": "0x611C1FA59Aa1d6352c4C8bD44882063c6aEE85E0",
"AAVE-STAKE-A": "0xf73c94402bc24148b744083ed02654eec2c37d5b",
"G-UNISWAP-A": "0x2fca923c7535083f25f761dcf289d7d81f024dda",
"INST-STAKING-A": "0x37a63939e128d284e0eae5d3e517aad44f5204d4",
"AAVE-V2-IMPORT-B": "0x6fe05374924830B6aC98849f75A3D5766E51Ef10",
"COMPOUND-IMPORT-B": "0xdA101870ca6136539628F28041E1B55baf4EB6C0",
"INSTAPOOL-A": "0x5806Af7AB22E2916fA582Ff05731Bf7C682387B2",
"MAKERDAO-CLAIM-A": "0x2f8cBE650af98602a215b6482F2aD60893C5A4E8",
"WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3",
"INST-A": "0x52C2C4a0db049255fF345EB9D3Fb1f555b7a924A"
"connectors": {
"1" : {
"AUTHORITY-A": "0x351Bb32e90C35647Df7a584f3c1a3A0c38F31c68",
"BASIC-A": "0x9926955e0Dd681Dc303370C52f4Ad0a4dd061687",
"1INCH-A": "0x235fca310ac7be45c7ad45f111203468743e4b7c",
"1INCH-B": "0xaBac3dCf164eD827EAfda8e05eCc8208D6bc5E04",
"COMPOUND-A": "0xbb153cf09a123746e0eb3b3a436c544a7eeb24b6",
"AAVE-V1-A": "0x612c5CA43230D9F97a0ac87E4420F66b8DF97e9D",
"AAVE-V2-A": "0x68b27A84101ac5120bBAb7Ce8d6b096C961df52C",
"MAKERDAO-A": "0x4049db23c605b197f764072569b8db2464653ef6",
"UNISWAP-V2-A": "0x1E5CE41BdB653734445FeC3553b61FebDdaFC43c",
"COMP-A": "0xB446e325D44C52b93eC122Bf76301f235f90B9c9",
"UNISWAP-A": "0xA4BF319968986D2352FA1c550D781bBFCCE3FcaB",
"POLYGON-BRIDGE-A": "0x1b79B302132370B434fb7807b36CB72FB0510aD5",
"AAVE-CLAIM-A": "0x611C1FA59Aa1d6352c4C8bD44882063c6aEE85E0",
"AAVE-STAKE-A": "0xf73c94402bc24148b744083ed02654eec2c37d5b",
"G-UNISWAP-A": "0x2fca923c7535083f25f761dcf289d7d81f024dda",
"INST-STAKING-A": "0x37a63939e128d284e0eae5d3e517aad44f5204d4",
"AAVE-V2-IMPORT-B": "0x6fe05374924830B6aC98849f75A3D5766E51Ef10",
"COMPOUND-IMPORT-B": "0xdA101870ca6136539628F28041E1B55baf4EB6C0",
"INSTAPOOL-A": "0x5806Af7AB22E2916fA582Ff05731Bf7C682387B2",
"MAKERDAO-CLAIM-A": "0x2f8cBE650af98602a215b6482F2aD60893C5A4E8",
"WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3",
"REFINANCE-A": "0x9eA34bE6dA51aa9F6408FeA79c946FDCFA424442",
"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" : {
"AAVE-V2-A": "0xE84d8010Afc3663919F44685cB53ED88866da3eE",
"AUTHORITY-A": "0xf73C94402BC24148b744083eD02654EEc2C37D5B",
"BASIC-A": "0x1cAF5EC802ca602E98139AD96A8f2B7BC524264E",
"AAVE-CLAIM-A": "0xC7Cb1dE2721BFC0E0DA1b9D526bCdC54eF1C0eFC",
"PARASWAP-A": "0xFb3a1D56eD56F046721B9aCa749895100754578b"
"mappings": {
"InstaMappingController": "0xDdd075D5e1024901E4038461e1e4BbC3A48a08d4",
"InstaReflexerGebMapping": "0x573e5132693C046D1A9F75Bac683889164bA41b4"
}
}

View File

@ -1,12 +1,11 @@
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers");
require("@tenderly/hardhat-tenderly");
require("@nomiclabs/hardhat-etherscan");
require("@nomiclabs/hardhat-web3")
require("@nomiclabs/hardhat-web3");
require("hardhat-deploy");
require("hardhat-deploy-ethers");
require('dotenv').config();
require("dotenv").config();
const { utils } = require("ethers");
@ -24,35 +23,41 @@ module.exports = {
solidity: {
compilers: [
{
version: "0.7.6"
version: "0.7.6",
settings: {
optimizer: {
enabled: false,
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: {
// defaultNetwork: "hardhat",
kovan: {
url: `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`]
accounts: [`0x${PRIVATE_KEY}`],
},
mainnet: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000,
gasPrice: parseInt(utils.parseUnits("34", "gwei"))
gasPrice: parseInt(utils.parseUnits("30", "gwei")),
},
hardhat: {
forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
blockNumber: 12433781,
blockNumber: 12696000,
},
blockGasLimit: 12000000,
},
@ -60,17 +65,17 @@ module.exports = {
url: "https://rpc-mainnet.maticvigil.com/",
accounts: [`0x${PRIVATE_KEY}`],
timeout: 150000,
gasPrice: parseInt(utils.parseUnits("1", "gwei"))
}
gasPrice: parseInt(utils.parseUnits("1", "gwei")),
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY
apiKey: process.env.ETHERSCAN_API_KEY,
},
tenderly: {
project: process.env.TENDERLY_PROJECT,
username: process.env.TENDERLY_USERNAME,
},
mocha: {
timeout: 100 * 1000
}
timeout: 100 * 1000,
},
};

View File

@ -31,7 +31,7 @@
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.2",
"@nomiclabs/hardhat-etherscan": "^2.1.3",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/test-helpers": "^0.5.6",

View File

@ -0,0 +1 @@
[{"type":"event","name":"LogFlashBorrow","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiBorrow","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiPayback","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashPayback","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashBorrowAndCast","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiBorrowAndCast","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiPayback","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256[]","name":"getId","internalType":"uint256[]"},{"type":"uint256[]","name":"setId","internalType":"uint256[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashPayback","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"getId","internalType":"uint256"},{"type":"uint256","name":"setId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract InstaFlashV2Interface"}],"name":"instaPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]}]

View File

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

View File

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

View File

@ -9,6 +9,7 @@ async function main() {
const connectMapping = {
'1INCH-A': 'ConnectV2OneInch',
'1INCH-B': 'ConnectV2OneProto',
'AAVE-V1-A': 'ConnectV2AaveV1',
'AAVE-V2-A': 'ConnectV2AaveV2',
'AUTHORITY-A': 'ConnectV2Auth',
@ -27,15 +28,6 @@ async function main() {
for (const key in connectMapping) {
addressMapping[key] = await deployConnector(connectMapping[key])
}
const connectorsAbi = [
"function addConnectors(string[] _connectorNames, address[] _connectors)"
]
// Replace the address with correct v2 connectors registry address
const connectorsContract = new ethers.Contract("0x84b457c6D31025d56449D5A01F0c34bF78636f67", connectorsAbi, wallet)
await connectorsContract.addConnectors(Object.keys(addressMapping), Object.values(addressMapping))
}
main()

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

@ -0,0 +1,16 @@
const abis = require("./constant/abis");
const addresses = require("./constant/addresses");
const { web3 } = hre;
const encodeSpells = require("./encodeSpells.js")
module.exports = function (spells) {
const encodeSpellsData = encodeSpells(spells);
const targetType = "string[]";
let argTypes = [targetType, "bytes[]"];
return web3.eth.abi.encodeParameters(argTypes, [
encodeSpellsData[0],
encodeSpellsData[1],
]);
};

View File

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

View File

@ -0,0 +1,109 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js")
const buildDSAv2 = require("../../scripts/buildDSAv2")
const encodeSpells = require("../../scripts/encodeSpells.js")
const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js")
const getMasterSigner = require("../../scripts/getMasterSigner")
const addresses = require("../../scripts/constant/addresses");
const abis = require("../../scripts/constant/abis");
const constants = require("../../scripts/constant/constant");
const tokens = require("../../scripts/constant/tokens");
const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json")
describe("Instapool", function () {
const connectorName = "COMPOUND-TEST-A"
let dsaWallet0
let masterSigner;
let instaConnectorsV2;
let connector;
const wallets = provider.getWallets()
const [wallet0, wallet1, wallet2, wallet3] = wallets
before(async () => {
masterSigner = await getMasterSigner(wallet3)
instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2);
connector = await deployAndEnableConnector({
connectorName,
contractArtifact: connectV2CompoundArtifacts,
signer: masterSigner,
connectors: instaConnectorsV2
})
console.log("Connector address", connector.address)
})
it("Should have contracts deployed.", async function () {
expect(!!instaConnectorsV2.address).to.be.true;
expect(!!connector.address).to.be.true;
expect(!!masterSigner.address).to.be.true;
});
describe("DSA wallet setup", function () {
it("Should build DSA v2", async function () {
dsaWallet0 = await buildDSAv2(wallet0.address)
expect(!!dsaWallet0.address).to.be.true;
});
it("Deposit ETH into DSA wallet", async function () {
await wallet0.sendTransaction({
to: dsaWallet0.address,
value: ethers.utils.parseEther("10")
});
expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10"));
});
});
describe("Main", function () {
it("Should take 100 ETH flashloan from Instapool", async function () {
const amount = ethers.utils.parseEther("1") // 1 ETH
const flashloanAmount = ethers.utils.parseEther("100") // 100 ETH
const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
const IdOne = "2878734423"
const IdTwo = "783243246"
const spells = [
{
connector: connectorName,
method: "deposit",
args: ["ETH-A", flashloanAmount, 0, IdOne]
},
{
connector: connectorName,
method: "withdraw",
args: ["ETH-A", amount, IdOne, IdTwo]
},
{
connector: "INSTAPOOL-A",
method: "flashPayback",
args: [ethAddress, flashloanAmount, IdTwo, 0],
}
]
const calldata = encodeFlashcastData(spells);
const spells2 = [
{
connector: "INSTAPOOL-A",
method: "flashBorrowAndCast",
args: [
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
flashloanAmount,
0, // route
calldata,
],
}
]
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells2), wallet1.address)
const receipt = await tx.wait()
});
})
})

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